Back from the Dead
Welp, it looks like I missed a monthly post. Although technically I never promised monthly updates, it is something that I’ve been trying to do! Unfortunately, last month I really didn’t have many improvements to write about. After the last progress post, I wasn’t exactly sure where to go with the project; I know I mentioned bug fixing, but debugging is a pain and not something I exactly look forward to. Honestly, I was pretty satisfied with how far I had gotten with NooDS, and I found myself losing motivation to go further with it. Combine that with the real world issues I’ve been struggling with, and you get a stagnated project.
Suddenly, the whole world devolves into chaos. School has been closed and moved entirely online, assignments have been pushed back, and everyone is encouraged to stay indoors. I had also finally taken some steps to start dealing with my other problems, so I found myself with an unexpected amount of free time and a bit more motivation than usual. So, I decided to try working on my emulator again. I managed to get some exciting things done, so let’s take a look!
Arisotura, the creator of melonDS, pointed out that I had a small but significant mistake in my sound code that was causing some weird issues with the ADPCM audio format. When reading the header, which contains initial values used for decoding the audio, I was using the memory address instead of the value at the memory address. It was a simple fix, and it made things sound much better. At this point I really wanted to get synchronous audio working, since my asynchronous method was still causing wonkiness due to inaccurate timing. It took a few tries, but I was eventually able to get beautiful, crackle-free synchronous audio! In doing this, I removed the old FPS limiter in favor of basing the emulation speed entirely on audio playback. This should make the experience better on Windows, where I was originally using a wasteful while loop to control speed since timers were too inaccurate.
I also started taking a look at optimizing the emulator. 3D rendering in particular was annoyingly slow, which might be clear from the low FPS in most of my past 3D screenshots. There were some smaller optimizations to both the 2D and 3D renderers, but the greatest improvement came from multithreaded 3D rendering. Unlike the 2D renderer, which can change things every scanline (technically every pixel, but there’s no reliable way to time it, and current DS emulators render per-scanline for performance), the 3D renderer can only recieve new geometry data once per frame, at V-blank. This pretty much guarantees that the rendered scene won’t be affected by the timing of its drawing (it might be possible to mess with texture data; that would be interesting to test on hardware). With this in mind, it was easy to implement threaded rendering. At the start of rendering, I split the scene into chunks of scanlines and have each chunk simultaneously rendered on a different thread. It’s currently hardcoded to 4 threads, but I plan on making it configurable in the future. There are other ways I can improve my implementation as well, but for now it still gives a satisfying performance boost with little risk. It’s enabled by default, and makes most 3D games run full speed on my system!
Although I still haven’t managed to get Mario Kart and Zelda working, I did fix a few bugs. Mainly graphics-related, I fixed some blending issues and implemented the geometry engine’s box test command. The box test is something that I had admittedly forgotten about; it tests if a given box is at least partially visible in the current 3D viewport, which can be used to determine if objects in that area are worth passing to the rendering engine. This seems fairly important, but since any off-screen polygons are just clipped into non-existence anyway I figured it was mostly unnecessary. Of course, some games do use it, and those that do experienced some pretty significant issues with it not implemented. Since the result bit was never being set, these games thought that none of the areas they tested were visible, so they never bothered rendering stuff there. After trying the easy solution of always setting the result to positive, I discovered that this was the source of the missing character models in Okamiden, the missing buildings in the gen 4 Pokémon games, and the entire missing overworld in the gen 5 Pokémon games! Of course, I didn’t want to take the easy way out, so I properly implemented the test, reusing my clipping code to see if a box is visible.
Something I mentioned last time that I did look into was anti-piracy measures. I thought this would require the massive undertaking of finally adding a proper timing system to NooDS (which I tried to do, but the performance sucked so I’ll have to try again), but the anti-piracy measures that I was running into were surprisingly easy to fix! The first one is used in Bowser’s Inside Story, which locks controls on the save select screen, and Pokémon Ranger Guardian Signs, which crashes when starting the game, among others. The first 32KB of a DS cartridge contains the “secure area”, which can only be accessed using special commands, and is loaded into the system memory by the BIOS/firmware. The command that games typically use to retrieve data from the cartridge doesn’t have access to this section, so attempted reads are redirected to a higher address. Presumably, flashcarts don’t have this protection, so games can check if the area is readable and trigger their anti-piracy if it is. I feel like a fool for not including this behavior back when I first implemented the cartridge protocol, but now that I have, these games pass the check and run as intended. The other anti-piracy measure that I dealt with is a rather well-known one which prevents exp. gain in the gen 5 Pokémon games. These games have special cartridges with IR ports, and since flashcarts don’t have this, they send a command to the IR and check for a response. It would be interesting to learn more about the inner workings of the IR, and maybe even properly emulate it someday (Pokéwalker, anyone?), but for now I’ll just send IR games the expected reply and call it a day.
That pretty much covers the interesting stuff I that did during my burst of motivation. If you look at the GitHub now, you’ll see that activity has dropped off again; even though school is online now, it is exam month and I’m right back to cramming assignments at the last minute and telling myself that I should study. When I find more time, I have some big things planned for NooDS. I’m going to add screen layouts, I’d like to try a proper timing system again, and I want to make some sort of debugging UI. It would also be nice to stub wireless communications to get things like PictoChat working, as well as preventing the crash when trying to load a save in the gen 4 Pokémon games. And eventually I want to write an ARM64 JIT so that NooDS can run well on the Switch. On top of all that, I might have another project in the works soon… It’s only a concept for now, but it could be interesting if it ends up happening. I hinted about what it might be in the patrons Discord channel, but there’s a bit more to it than that! Anyways, that’s all from me for now; I’ll see you (hopefully!) next month with some more news.