Development of NES (Famicon) emulator from scratch is fun, and tough
Why I started developing game emulator?
After the development of https://medium.com/@komamitsu/what-i-learned-from-implementing-raft-consensus-algorithm-in-ocaml-17c71b1b412f, I was seeking something to develop for my spare time. I noticed I had never developed OS-ish lower layer thing from scratch. BTW, I have a son who loves game programing with Scratch. I thought developing game console emulator was better than simple OS so that I cloud proudly show off the game emulator to him.
This is my hobby NES emulator project. I believe Super Mario Bros also runs with it.
Which emulator to develop?
There are some kinds of game emulators: Atari, Amiga, Nintendo Entertainment System (NES, or Famicon), Super Nintendo Entertainment System (SNES, or Super Famicon), Game boy family, and so on. I thought Atari was a bit too old. I’m familiar with NES, GB and SNES. The development of SNES seems too hard for newbies according to some comments on http://forums.nesdev.com/ and . So the candidates were NES and GB (not Advance or later.) The difficulties and complexities of the both developments looked similar and I chose NES.
How I started the development of NES emulator?
Yeah, I didn’t have any knowledge about NES emulator development and needed to get good documents easy to read for a newbie like me. https://wiki.nesdev.com/ is a great site to get many detailed information, but I needed to grab kind of abstract overall idea what NES emulator development was. I found this slide (https://speakerdeck.com/bokuweb/huamikonemiyuretafalsechuang-rifang) written in Japanese and I understood the whole picture. Of course I needed to read https://wiki.nesdev.com/ and some detailed contents later, though.
Technical stack
I chose Kotlin and TornadoFX for this project.
- Kotlin
The reasons for using Kotlin was that it’s easy to use Java libraries and enough convenient and productive with its ecosystem although it’s not so interesting as OCaml ;)
2. TornadoFX
I tried to simply use JavaFX at first, but it sounded TornadoFX was a better choice in Kotlin. Actually I didn’t throughly evaluate both JavaFX and TornadeFX with Kotlin, though. One thing I struggled with TornadeFX was how to use Campus-like component in TornadeFX. I finally solved the question as follows:
- Render a screen image on javafx.scene.image.WritableImage (https://github.com/komamitsu/konessem/blob/96d02a897078f62aacafb13a09a8c3c0155d20e2/src/main/kotlin/org/komamitsu/konessem/ppu/Renderer.kt#L143-L145)
- Set javafx.scene.image.ImageView#image to the updated WritableImage (https://github.com/komamitsu/konessem/blob/96d02a897078f62aacafb13a09a8c3c0155d20e2/src/main/kotlin/org/komamitsu/konessem/view/Game.kt#L87)
Implementations
I think NES emulators are supposed to consist of these components:
- ROM loader: parser of iNES header
- CPU: opcode, operand, addressing mode, register
- PPU (Graphical module): background tile, sprite, scroll, palette, register
- Keypad
I started with ROM loader and then just rendered nametable data that is kind of bitmap image data on screen to motivate me. I recommend this first step.
Next step was to implement CPU opcodes and PPU. The NES’s CPU is a Ricoh 2A03 which is based on MOS Technology 6502, so I needed to implement all the opcodes of 6502. But it was not so exciting since I didn’t have any UI in Konessem other than just printing CPU instructions, registers and stack to stdout. I fortunately got very simple “Hello, world” ROM at http://hp.vector.co.jp/authors/VA042397/nes/sample.html which requires only several basic opcodes in CPU and background rendering w/o sprite rendering in PPU. I implemented the minimum requirements of the ROM in CPU and PPU. When I saw the ROM showed white “Hello, world” string on black screen, it excited and motivated me.
Test ROMs
Many NES emulator developer created many test ROMs. I found https://github.com/christopherpow/nes-test-roms that has collected many test ROMs. I recommend to try some in the following order:
- https://github.com/christopherpow/nes-test-roms/tree/master/tutor : This requires the implementation of sprite rendering in PPU and some other basic opcodes
- https://github.com/christopherpow/nes-test-roms/tree/master/instr_test-v3 : This requires all official opcodes implementation
- https://github.com/christopherpow/nes-test-roms/tree/master/instr_misc : This tests some corner cases to be finally fixed
- https://github.com/christopherpow/nes-test-roms/blob/master/other/nestest.nes : I think this is one of the most popular NES test ROMs
CPU
I read the following contents for reference:
- Opcodes: http://www.obelisk.me.uk/6502/reference.html and https://www.masswerk.at/6502/6502_instruction_set.html
- Addressing modes: http://www.obelisk.me.uk/6502/addressing.html and https://wiki.nesdev.com/w/index.php/CPU_addressing_modes
6502 instructions have ADC (Add with Carry) and SBC (Subtract with Carry.) Especially around overflow flag, I introduced many bugs in them. I fortunately found http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html and it’s very helpful for me.
Also, some addressing modes (Indexed Indirect and Indirect Indexed) in 6502 are bit complicated and I also introduced some bugs in them…
PPU
The implementation of rendering background tiles and sprites are basically not so difficult. But I needed to take care of other features (scroll, 8x16 sprite, nametable mirroring, sprite 0 hit, sprite overlap, etc.) and I struggled with them for a while. I recommend to read PPU-related contents in https://wiki.nesdev.com/ when you notice you’ve spent a lot of time in investigation of unexpected PPU behavior.
Pitfalls in implementation
I think there are some pitfalls in NES emulator development. It took long time for me to notice them…
- Indirect addressing mode bug: https://everything2.com/title/6502+indirect+JMP+bug
- Nametable mirroring: What is that “Mirroring” flag? on http://fms.komkon.org/iNES/iNES.html#LABL
- Palette memory address mirroring: https://wiki.nesdev.com/w/index.php/PPU_palettes#Memory_Map
- PPUDATA read buffer: https://wiki.nesdev.com/w/index.php/PPU_programmer_reference#The_PPUDATA_read_buffer_.28post-fetch.29
How do I like NES emulator development?
Yeah, it was basically exciting experience since this was the first time of creating OS-ish lower layer thing from scratch by myself. When I ran some Famicon games with the emulator and showed it to my son, I felt so happy since he seemed to look up to me (just my misconception? 😉)
On the other hand, the development of low layer component from scratch is tough because I need to debug it with no useful information. What I can do is only dump all instruction, stack and registers logs and guess what’s happening. And the PPU spec seems a bit tricky and complicated to me although I guess it’s for reducing memory usage. But, even NES emulator development contains some tough parts, it’s a great chance to enjoy lower layer component development and I bet running NES game on your own emulator is very exciting experience.