Fetch and decode
The datapath is the road data takes through a CPU. It starts at the program counter (PC), a register holding the next instruction's address: 4. Instruction memory reads addresses 4 and 5 and hands back two bytes: 0x51 0x00.
Every TINY-8 instruction is 2 bytes long, so a small adder works out PC + 2 = 6 straight away. That is where the next instruction starts. The PC only takes the new value at the end of this instruction, on the clock tick.
0x51 is 0101 00 01 in binary. Each group is just a few wires: 0101 is the opcode for ADD, 00 picks register r0 and 01 picks r1. The register numbers go to the register file; the opcode goes to the control unit.
Execute ADD r0, r1
The register file holds r0 to r3. It has two read ports, so it reads two registers at the same moment: port 1 gives r0 = 2 and port 2 gives r1 = 3. A port is one set of address and data wires.
A multiplexer (mux) is a switch: its select bit decides which input passes. This one picks the ALU's second input: read port 2 or the instruction's second byte. For ADD the select bit is 0, so 3 goes through.
The ALU, short for arithmetic logic unit, adds its two inputs: 2 + 3 = 5. The answer is not zero, has no carry out and its top bit is 0, so the Z, C and N flags are all 0.
A second mux picks what gets written: the ALU's answer, select 0. On the clock tick the register file's one write port stores 5 in r0, and the PC takes 6. ADD r0, r1 is done.
LD r2, [16]
Same road, new instruction. The PC sends 6, and instruction memory hands back 0x28 0x10. 0x28 is 0010 10 00: opcode 0010 means LD, and 10 picks r2. The second byte, 0x10, is the address 16.
The address byte runs straight to data memory. TINY-8 has no address maths, so the ALU sits idle. Data memory reads address 16 and returns the byte stored there: 7.
Now the write-back mux has select 1, so memory's 7 passes instead of an ALU answer, and the write port stores it in r2. Same wires as ADD, different mux settings. Setting them for each opcode is the control unit's job.
The working
;; step 1/10 · fetch ;; about: Program counter, Instruction memory program (TINY-8): 0 LDI r0, 2 2 LDI r1, 3 4 ADD r0, r1 6 LD r2, [16] 8 HALT 16 data byte 7 r0 = 2 and r1 = 3 already (the two LDIs ran). PC = 4 → bytes 0x51 0x00 = ADD r0, r1
A lit wire with moving dashes carries the value the step is about; the small white tags riding on it are those values. In Python, b0 >> 4 shifts the byte right by four bits, leaving the opcode, and & 3 keeps the lowest two bits, so (b0 >> 2) & 3 is d and b0 & 3 is s. & 0xFF keeps 8 bits, like the 8-bit ALU.