Inside MIPS
A browser-based, MARS-compatible MIPS emulator and assembler. Write real assembly and watch every clock cycle move through a 3D datapath, its pipeline, caches and memory.
- TypeScript
- three.js
- Vite
- CodeMirror
- Vitest
- Playwright
Problem
In a computer architecture course, the hardest ideas (pipeline hazards, forwarding, cache mapping, the call stack) are dynamic, but students meet them as static textbook diagrams, and simulators like MARS show registers without the hardware behind them. Inside MIPS lets students run their own homework code and watch the machine execute it, one clock cycle at a time.
My role
Solo project. I wrote the product spec, the assembler and emulator, the 3D datapath, the lesson content and the test suite. The illustrated, lesson-driven format is inspired by Learn Arduino by Dilum Sanjaya; the code and art are my own.
Approach
- Assembler and emulator in strict TypeScript with no DOM access (its own tsconfig): all 61 MIPS I integer instructions, 62 MARS pseudo-instructions using MARS's own expansions, MARS's memory layout and 15 of its syscalls, so homework code pastes in and runs.
- The instruction set lives in one data table that the decoder, assembler, disassembler, datapath and trace builder all read, so each instruction's encoding, control signals and buses are defined in exactly one place.
- Two CPU models: single-cycle, and a five-stage pipeline with toggleable forwarding, branches resolved in ID or MEM, load-use and RAW stalls, flushes and precise exceptions.
- A memory hierarchy with split L1 instruction and data caches, an optional unified L2 and a write buffer, configurable size, block size, associativity, and LRU, FIFO or random replacement.
- Every cycle emits a trace of the signals on each bus. A three.js scene of procedurally built parts animates that trace, with a 2D textbook-style schematic as an alternate view.
- Ten guided lessons, from fetch and decode through the pipeline and caches, plus a full-datapath Lab mode with share links and .asm import/export.
Key decision: run a real emulator, never script the visuals
The app that inspired this one animates hand-scripted timelines for each lesson. I went the other way: every wire, register and cache line on screen comes from the emulator's actual state for that cycle. That's more work up front, but it means the visuals stay correct for any program a student writes, not just the lesson examples.
Stepping backwards uses the same idea. Instead of storing snapshots, the emulator is deterministic (even its random-number syscalls match Java's generator, as MARS does), so stepping back replays from reset to the earlier cycle and lands on exactly the same state.
Testing
- 287 Vitest unit tests: instruction semantics and encoding, assembler diagnostics, exact stall and flush counts for each hazard configuration, cache behavior, and MARS-matching syscall I/O.
- Cross-checks that the pipelined and single-cycle CPUs produce the same results, and that seeking to any cycle matches a straight run to it.
- Whole programs: recursive fib(10) and 6! with the stack restored, array sums, and string and number printing.
- 18 Playwright end-to-end tests covering lessons, challenges, inspector knobs, file import/export and screen layouts.
What's next
- The R2000-accurate pipeline preset with branch delay slots and no load interlock.
- The R2010 floating-point unit, the TLB and virtual memory, and full CP0 exception handlers.