Base CS from zero
The processor: build a CPU simulator
You traced the toy CPU by hand. Now make the machine real: write a program that IS the CPU. When your fetch-decode-execute loop runs a chunk of machine code and lands the right number in the right memory cell, the processor stops being an abstraction and becomes something you built.
Turn the unit’s mental model into running code. You will implement the toy CPU’s state (registers, program counter, memory), its instruction set, and the fetch-decode-execute loop, then hand it a machine-code program as bytes and watch it compute the right answer — proving you understand the loop by making it execute.
Implement a software simulator of the unit's toy CPU in any language you like. It must hold its own state, decode raw machine-code bytes, run the fetch-decode-execute loop until it halts, and produce the correct final memory and register state for a given program.
- The sample addition program prints a four-cycle trace ending with mem[202] = 42 and PC = 8, matching the lesson's worked example exactly.
- Decoding is done from the raw bytes/bits (opcode and register extracted from byte 0), not by storing pre-parsed instruction objects — feeding in different bytes runs a different program with no code change.
- A program containing a JUMP visibly skips the instruction it jumps over in the trace, and the PC value after the JUMP equals the jump target, not PC + 2.
- A short write-up (a few sentences) explaining, in your own words, where in your code the Fetch, Decode, and Execute steps live and how the program counter drives the loop.
- Add a conditional branch: a JUMP-IF-ZERO opcode plus a one-bit zero flag set by ADD, then write a program that uses it to implement a countdown loop (decrement R0 until it reaches zero) — proving loops are just conditional jumps.
- Add a clock-cycle counter (1 cycle per instruction) and report total cycles and the simulated run time at an assumed clock rate (e.g. 2 GHz), connecting the trace to real timing.
- Hand-assemble a slightly larger program (e.g. sum the four numbers at addresses 200-203 into 204 using a loop) into machine-code bytes yourself, then run it — practising the compiler's job of turning intent into the instruction set.
- Write a tiny assembler: accept text like 'LOAD R0, 200' and emit the correct 2-byte encoding, so you can author programs in mnemonics instead of raw bytes.
Building the simulator forces every idea in the unit to become concrete: registers and the program counter are just variables, memory is just an array, machine code is just the bytes you load in, and the fetch-decode-execute cycle is a literal loop reading the bytes the PC points at. When your trace matches the lesson cycle for cycle and a JUMP visibly redirects the PC, you have proven the processor is not magic — it is a loop you can write yourself in an afternoon.