~90 minutes. Open your homebrew ROM in Mesen's debugger; step through the first 50 instructions the CPU executes; annotate what each one does.
Goal: see your week-3 6502 knowledge applied to a real NES game's startup code; identify common patterns (memory initialization, hardware register setup, jumps and subroutines).
Estimated time: 90 minutes
Prerequisites: lab 3.1 complete (basic 6502 literacy); homebrew NES ROM loaded in Mesen
Steps
Step 1: Open Mesen's debugger (5 minutes)
Launch Mesen. Load your homebrew ROM. Pause the game (F3 or whatever key you have mapped). Open Tools → Debugger.
The debugger window shows:
- A disassembly panel (the instructions being executed; the highlighted line is the current PC)
- A registers panel (A, X, Y, P, SP, PC values)
- A breakpoint panel (you do not need this for this lab)
- A memory viewer (optional; show or hide)
Step 2: Reset to the start (5 minutes)
In the debugger, find the "Reset" button (or similar). Click it. The PC (Program Counter) jumps to the reset vector, the first instruction the NES runs after power-on.
The reset vector lives at memory addresses $FFFC-$FFFD, which the 6502 reads at power-on to find the first instruction's address. You should see PC = whatever address that vector points to.
Step 3: Step through the first 50 instructions (60 minutes)
Use the debugger's "Step Into" button (often F11) to advance one instruction at a time.
For each instruction, do these three things:
- Note the instruction's address (the hex address shown in the disassembly)
- Note the instruction's mnemonic (what 6502 instruction is it? LDA? STX? JMP?)
- Note what it appears to be doing (clearing memory? setting a hardware register? initializing the stack?)
You may not understand every instruction yet. That is OK. Write "I don't know" if needed; the point is to expose yourself to real NES startup code, not to fully understand every byte.
Look for these common patterns you may recognize:
- SEI (Set Interrupt disable flag): one of the very first instructions; the NES disables interrupts while initializing
- CLD (Clear Decimal flag): the 6502 has a decimal-arithmetic mode the NES doesn't use; the CPU clears it
- LDX #$FF / TXS: set up the stack pointer. The NES uses $0100-$01FF as the stack; the stack pointer is set to $FF (the top of the stack) at startup
- STA $2000 / STA $2001: writes to the PPU's control registers (PPUCTRL at $2000; PPUMASK at $2001). The startup code disables PPU rendering before initializing
- A loop that writes 0 to memory addresses $0000-$07FF: zero-init RAM. This is a tiny loop you should be able to recognize once you see it
Step 4: Identify ONE meaningful instruction (10 minutes)
Pick ONE instruction from your annotated 50 that you understand fully. Write a 3-4 sentence explanation in your journal:
- What is the instruction?
- What is its operand?
- What does it do at this moment?
- Why might the game be doing this here?
Example: "Instruction at $C002 is STA $2001. This stores the accumulator's current value into PPUMASK ($2001), which controls whether the PPU renders the background and sprites. The accumulator is 0 right now, so this turns off rendering. The game is doing this because it is in startup and has not yet loaded any graphics data; turning off rendering prevents flickering garbage."
Step 5: Journal the experience (10 minutes)
Open ~/spk-101/journal/lab-3-2-notes.md. Write:
- Your list of the 50 instructions (just the mnemonics; the full annotated list is your work product, but it is OK if it lives in scratch form)
- The detailed explanation of the ONE instruction you chose
- A reflection: how does reading real game code compare to writing your own in Easy 6502? Was it easier or harder than you expected? What was the most foreign part?
Expected output
- 50 annotated instructions from your ROM's startup code
- One detailed explanation of a specific instruction
- A journal entry capturing your experience
Common pitfalls
- Stepping too fast: in Mesen's debugger you can hit Step Into 50 times in 10 seconds. Slow down. Look at each instruction. Try to recognize what it is doing
- Hitting an interrupt or BRK: if the debugger jumps into unexpected code, the program may have triggered an interrupt or hit a BRK. Use "Run to Here" to skip past these for now
- Trying to follow every JSR (subroutine call): JSR jumps into a subroutine; the subroutine may be 200 instructions long. For this lab, "Step Over" the JSR (often F10), execute the subroutine fully but treat it as one logical step
- Feeling lost in detail: NES startup code does mundane work (zero RAM, set up the stack, disable rendering). The first 50 instructions of any NES game look similar. You are not missing some deep insight; the game does not actually start doing interesting things until later
Stretch (optional)
If you finished early:
- Continue stepping past instruction 50. Try to find the moment when the game starts loading actual graphics data
- Use Mesen's Memory Viewer alongside the debugger. As you step, watch RAM change. Pick a byte that is being written and identify which instruction wrote it
- Read NESdev's wiki page on "Init code" at
https://www.nesdev.org/wiki/Init_code. Compare what the wiki describes to what your ROM's startup code actually does
Lab 3.2 v0.1. The applied-6502 lab. Real game code; real debugger; real learning.