NES Game Showdown - Chapter 1
I have had my first peak at the a NES game assembly. It’s a little enlightening to see. Not only seeing how my tools work, but seeing how things are pieced together. So I will drive straight into it, shall I?
First things to look at is the vectors, for NMI, IRQ and RST.
$ python dis6502.py -V mario.bin
Disassembler6502
Loading file 0x8000 bytes
Vectors:
NMI 8082
RST 8000
IRQ FFF0
Now, I had a look at each of these. NMI is called when Vblank is set by the PPU, and RST gets called on power up. Both of these access the PPU registers early on $2000 and $2002 mainly.
What baffles me is the IRQ. the disasembled code doesn’t make too much sense, especially as it’s 0x10 (16 bytes) in legnth as it’s effectively at the end of the Cart address.
The RST call starts on the card at 0x8000 and ends neatly at 0x8081 with a RTI (return interrupt) opcode. However it is unlikely to reach there as there is the following opcode:
0x8057 JMP MemType: ABSOLUTE (0x8057)
That line makes sure it’s stuck there. All other binary between 0x8058-0x8081 has a large number of unrecognized op codes, those picked out are not very well decoded. I assume the RTI at 0x8081 is there intentionally even if its not meant to be executed, as some auto-reset/protection?
This while loop being stuck is common practice in embedded systems, when you just rely of interrupt driven code. Which in this case we have NMI. This will be called for each frame. This is regular and predictable. And as it’s tied to the frame rate, it enables smooth game playback.
In RST the first subroutine is called:
0x802B JSR MemType: ABSOLUTE (0x90CC)
I can easily pull out this routine with the following command in my dissembler:
$ python dis6502.py -r 0x90CC -s mario.bin
This seems to be an odd function that initilises some of the stack, mostly memory 0x0000 - 0x0060. That is if I am understandng indirect- Store calls correctly. It makes sense. But it does it 5-6 times? Zeroing those memory addresses, is this to stall the program or just paranoia in relying on the hardware?
I guess that it leaves 0xFF-0x61 for the stack.
This function is called else where by running
$ python dis6502.py -Rs 0x90CC -s mario.bin
Also in the NMI and RST calls, i find references to high memroy address in the 0x7FF0 - 0x7FFF range. They seem to be tracking states of the PPU/drawing.
I have created a memhints class to help label and name registers, memory locations and subroutines to better help disassemble. But presently I am manually saving the -s output to a text file and adding my own comments. hopefully with a good enough memhints file, it can automatically dis-assemble in a human readable/friendly fashion.
I think one of the next things I will be do, is now that I am getting a better idea what certain opcodes do, is to write tests for my CPU emulation. So i can at least automatically step through some of this code with some confidence.