We're way off in the weeds here, of course, but that's cool. I don't mind playing in the weeds.
Way out there. :)
You've defined a language that can only update eight bits at a time, and additionally you've said it updates them only in certain patterns. That's not Turing complete.
No you are mistaken.
From the point of view of the LANGUAGE, each bit is individually and directly accessible. All the language sees is
0, 1, 0, 1, 0 ...
The implementation of the language however, runs on x86, and uses a byte to represent each 0, and each 1. (as 00000000 and 00000001 respectively.
When the language saves a file out to disk, it writes out its binary bits one bit at a time, but they are each saved as a byte. When it reads them back in, the byte 00000001 is read, and stored in memory as 00000001 in a byte... but the language, just sees 1.
The problem if you try to write a C compiler in the language, is that as far as the language is concerned the first 8 bits of the program it compiled *IS* 11010001, however, what is in the physical computer memory to represent that is 8 bytes each 000000000 or 00000001. What gets written to the file on the disk is the same. The representation of the compiled C program I generated, is mathmatically equivalent to the actual C program ... but it cannot be run as the representation is wrong.
One of the very few things required by the definition of a Turing machine is that is has to be able to update memory one value at a time
The definition of a turing machine places NO restriction on the representation of its "memory".
So you are mistaken or misunderstood what I've done. I am *absolutely* free to use arrays of 8 bit bytes that each contain one of 2 bit patterns to simulate a turing tape.
You've defined a language that can only update eight bits at a time, and additionally you've said it updates them only in certain patterns. That's not Turing complete.
The language doesn't see 8 bits at a time. The arrays of bit patterns are an implementation detail that the language is not 'aware' of.
If we want it to be Turing complete, we can interpret it as one value by saying that the LANGUAGE writes "1" and the HARD DRIVE happens to store that physically with eight molecules.
Ok... yes. Exactly Right. Except that rather than molecules I'm asserting the language simply dumps it to the hard disk using the logical file system already in place (say by Windows or Linux or whatever) the way its stored in memory... as a stream of bytes, containing one of two patterns.
. Fine. The language can write 1010101, 11111, 0000, 01010, or any other series since it's writing one value at a time. Perhaps the hard drive stores "10" physically as 1111111100000000, but the hard drive is going to read back what was written to it. Write a "1", get a "1" back.
Right again. So far so good. The language writes a "1" and it reads back a "1". Yes.
Given that the tape doesn't change what's written to it, the language can write valid machine code and get valid machine code back.
Swing and a miss... ok not a miss... foul ball.
It can read and write "valid" machine code back, subject to the constraint that we view it from within the language / language implementation. If we look at what is actually in the physical computer memory, it is not valid machine code from the physical computers perspective.
This language can compute results that are logically equivalent to machine code, but they are not actually usable as machine code. We can't simply set the ACTUAL CPU instruction pointer to the spot in physical computer memory I'm storing my compiled C program because it is just a sequence of 111111111 and 000000000 ... a representation of the machine code, but not usable machine code itself.
Further because the language *implementation* provides no way of setting the physical bits of the *underlying* computer in an arbitrary way, there is no way to directly compile to usable machine code using this implementation of the language. DESPITE it being a Turing complete language, and despite the implementation being sufficient to simulate a Turing machine.
You can't have it both ways.
Can and do. We have 2 Turing machines!
The language / implementation / system only needs to be internally consistent with the requirements for Turing completeness to be Turing complete. If I *layer* Turing machines such that one runs on top of another one -- which is exactly what I've done by taking an actual computer (Turing machine 1) and implementing my toy language on it (Turing Machine 2). TM1 is therefore effectively simulating TM2 which is fine. But there is no requirement that TM2 be able to directly control TM1 for TM2 to be Turing complete.
TM2 -- might be able to control TM1 -- and in fact in many such layering scenarios this IS possible. But it is not a requirement, and some scenarios exist where TM2 can't control TM1.
Clearly then not all Turing machines are equal in all ways. ;)
They ARE of course equal in the specific way identified by the Church-Turing thesis, which effectively states (amongst other things) that any TM can simulate any other TM (implying that any TM can 'host' another TM).
But it does NOT make any claims about the hosted (or simulated) TM being able to control or directly reprogram the simulator simulating it (ie re-program and control its own host). And it turns out there exist TMs which CAN directly reprogram the TM hosting them, and TMs that cannot.
So to illustrate:
x86 PC (TM1) can simulate ARM PC (TM2), and then of course TM2 can turn around and emulate an x86PC (TM3)... etc ad nauseum per Church-Turing.
Suppose in this case though that TM2, being an emulation of an ARM based system was implemented such that not only can it arrange its own "simulated memory" in arbitrary ways, but can also effectively arrange the memory of TM1 in arbitrary ways, it can also, at least in theory, emit machine code and directly reprogram the TM1 that is simulating it.
It can control and program the *actual* TM1, not just TM3 which is itself a simulation of the same type of computer that TM1 is.
Church-Turing only requires that TM2 be able to simulate TM3, not control TM1.
contrast to:
x86 PC (TM1) can simulate my-toy-turingmachine (TM4), and TM4 being Turing Complete CAN simulate an x86PC (TM5)
Because the implementation of TM4 lacks the ability to arrange the memory of TM1 in arbitrary ways it cannot directly control TM1, even though it can simulate a machine equivalent to TM1. (In this example that's TM5)