Sunday, September 11, 2011
I did the math and realized that it has been AGES since I last made a new post and it just so happens that I have made a ton of changes to my NES emulator so I figured I should throw something up here summarizing what I've done. To start off I decided to emulate all the dummy reads 6502 operations tend to do; dummy reads are extra addresses that the CPU reads from that are quite unrelated to the operation at hand. They are very forgettable and hardly worth emulating as only like 2 games will fail without them, however once I had every individual read/write emulated I could then just treat them all as 1 cycle instead of just treating instructions as 2 - 7 cycle chunks. This allows me to run the other components (APU, PPU, Mappers) at a much finer grain improving timing all around. Once I had this done I was able to fine tune my PPU timing and get it passing almost every test there is. Thanks to the more accurate PPU I can now run the notoriously picky Battletoads absolutely perfectly along with almost every other game out there. If I disregard mapper bugs, which I really don't care about outside the 4 or 5 major mappers, there is only one game I can think of that fails to run which pleases me greatly.
Super Mario Bros. 3 is objectively the BEST NES game and runs flawlessly.
After getting the timing all spruced up I went on a bit of a bug hunting spree and managed to fix some pretty big ones. I was unable to run Galaxian or Qix for over a year despite them once working perfectly, these weren't very demanding games so this bug really bothered me but once I sat down and poured over some trace logs I was able to fix it. I also fixed a bug I had accidently introduced when trying to emulate the I flag latency that was causing some pretty big scrolling errors with a lot of MMC 3 games. My MMC3 mapper had a big over sight that was causing a decent percentage of the MMC3 titles to fail to boot, while fixing this I gave my MMC3 code a much needed cleanup. And perhaps most pleasingly I fixed a bug that I had in the DMC channel that introduced a ton of clicking in games such as Kirby and really made my emulator sound quite sloppy. Once I started running out of bugs to fix I decided to do a slight overhaul of the APU which hadn't been cleaned up once since I initially wrote the thing, it was really a big mess all sitting in one file that was by far the largest of the emulation core. As I was moving all the separate NES audio channels into their own classes I decided to have a go at supporting the external audio channels that NES cartridges could provide. Not counting FDS titles, probably only 2% of games actually used this ability and they were all Japanese titles as the European and American Nintendo couldn't even use external sound. I added the FME-7, Namco 163, Konami VRC6, MMC5, and FDS sound chips so now the only one I am missing is the Konami VRC7 which was only used by one game and is wildly complicated and a completely different breed then the others. I however hope I can support it eventually as these sound chips really are excellent and can make some amazing sounding music and sound effects; previously I had no idea that the Japanese version of Castlevania 3 (using the VRC6 chip) sounded SO much better than its American counterpart. I also added some features outside of the emulation core to improve my quality of life while working on it. The debugger now gives a fair bit more information and has far more useful breakpoints, you can now use the interface to select what add-on is plugged into the expansion port of the NES, and I now have a way of kinda-sorta dumping video so I no longer need to use Fraps to get videos for this blog. I really didn't want to have to bundle a large project like FFmpeg just to dump a video once every six months so instead I quickly whipped up something to dump frames as bitmaps (which could also allow pixel perfect screen caps), combined that with the wav file dumper I already had, and then had it automatically produce an AviSynth script that mixes it all together. The AviSynth script can easily be transcoded like any other media format for distribution or for uploading to YouTube and gives me a completely lossless source to transcode from. With all that done it seems that the thing I need to work on in the future will be improving my APU timing, as of right now it tends to be within one or two cycles of being perfect but that really is not enough. Once I that out of the way I'll probably get Sprite and DMC DMA timing down along with interrupts interrupting interrupts, this stuff is really quite finicky and I don't know anything outside of test ROMs that rely on it but I just can't reduce the urge of increasing accuracy.