The Next Logical Step
Another almost-month, another NooDS progress update. This time, you might’ve already guessed what’s been accomplished. Well, let’s just jump right into it!
So after getting the firmware booting, the first thing I did was implement some form of object rendering. You might’ve noticed that in the previous post’s screenshots, the firmware was looking pretty bare. Well, that’s because most of the firmware’s UI is rendered using objects. The basics of object rendering weren’t too difficult; the actual drawing of the sprite tiles works about the same as the drawing of the background tiles, so I just needed to get the screen coordinates and the memory location of the tile data and I could start drawing! And that’s about the extent of what I did. There’s still plenty to do in the object rendering department, like rotated and scaled sprites, 2D tile mapping (this relates to how the tile data is arranged in memory; right now I’ve only implemented 1D), and object priority relative to the background layers. But even with my basic implementation, the firmware was now able to properly render its UI!
You might also notice in that screenshot that the calendar is no longer overflowing. Yep, I implemented the Real-Time Clock (RTC)! Actually, normally the firmware would refuse to boot into the menu and instead take you to a setup screen if the RTC wasn’t implemented, because it would think that the system hasn’t been properly set up yet. For whatever reason, I didn’t experience this on NooDS, or at least not at first. At some point during the implementation of the RTC I did end up seeing this screen, but the first time I got the firmware booting it just worked. Speaking of the implementation of the RTC, it was a bit confusing. The documentation on GBATEK wasn’t sufficient for me, so I ended up looking at the source code of libnds to figure out how it works. So yes, I did look at external source code, but it wasn’t emulator source code so it doesn’t count as cheating! Anyways, the RTC works somewhat similar to the SPI that I described in the last post. The main difference is that you can only transfer a single bit at a time with the RTC. After I ironed out the quirkiness of how and when the bits are actually transferred, the RTC seemed to be working well enough. At first I just hardcoded a time value, but soon after I went back and made it read the actual time.
Aside from all that, the most important thing I did was probably cartridge data transfers. I knew that the BIOS and firmware set up a lot of things in memory that commercial games rely on, so I figured a good step towards booting them would be to actually launch them from the firmware, since my direct boot implementation is pretty incomplete. I took my previous cartridge implementation, which was more or less a quick hack to make the BIOS think there was no cart inserted, and started feeding it actual data from the ROM. This also meant that I had to actually interpret the commands that were being sent to the cartridge. At this point I had to start seriously thinking about the dreaded encryption. This, surprisingly, turned out to not be so bad. Most ROMs have a secure area, and this area needs to be loaded with special encrypted commands. Of the secure area, the first 2KB are also encrypted. What I had to do was decrypt incoming commands, and since the secure area comes decrypted in ROM dumps, manually encrypt the first 2KB before sending it to the emulated system. The encryption is based on the Blowfish algorithm, and uses a table of values stored in the ARM7 BIOS as well as the game ID value of the inserted cartridge. Luckily GBATEK supplies pseudocode for the encryption algorithms, so huge thanks for that because it meant all I had to do was translate the code to C++. After that, I tested the algorithms on incoming encrypted cartridge commands and they seemed to be decrypting successfully! Unfortunately, it seemed that the commands to load the secure area simply weren’t being sent.
Regardless, I went on to implement the other cartridge commands, and the firmware was able to load the icon and title from a game. While this was cool, there was little hope of actually booting anything commercial without the secure area. And unfortunately even homebrew, which doesn’t use the secure area, didn’t seem to want to boot. So I was stuck. I had no idea where to go from there, and that dealt a heavy blow to my motivation, which was already struggling to begin with. Days went by, and nothing really got done. But then, PSISP (of CorgiDS fame) started demanding that I get Pokémon White to boot. It was all in good fun, but it did end up motivating me enough to start working on NooDS again, and I managed to find out why the secure area load commands weren’t being sent. Turns out it was a CPU bug, and one that I was aware of but had pushed aside for later because I had mistakenly assumed it would only matter in obscure cases. After fixing this, the secure area commands were finally being sent, so I implemented secure area loading and encryption, and hoped for the best.
Of course, this wasn’t enough for games to start booting. In fact, the firmware wasn’t even jumping to the game code yet! But PSISP’s nagging continued, and the next day I discovered that I had made a stupid mistake early on in development. The DISPSTAT register, which controls GPU-related interrupts, is a register that exists separately on both the ARM9 and ARM7 CPUs. This makes sense, because it means that one CPU can enable certain interrupts, while the other CPU can enable a different set of interrupts, or even none at all. But for some derpy reason, I had mistakenly assumed that the CPUs shared this register. This was a problem for the firmware because upon launching a game, the ARM9 disables V-blank interrupts, while the ARM7 keeps them on and executes important code when they trigger. Because of my mistake, the ARM9 disabling its interrupts meant that the ARM7’s interrupts also got disabled, and their code was never executed. This resulted in a fun endless loop of waiting, and the game would never start. I fixed this up, and while it didn’t yet give any visible results from commercial games, I was successfully able to boot and run a homebrew from the firmware!
At this point, I thought that enough should be implemented for commercial games to be able to boot. So why weren’t they? I felt stuck again, and I wasn’t looking forward to another day of debugging. I missed PSISP’s deadline for booting Pokémon by a day, but surprisingly I didn’t immediately catch fire and die. So the next day, after procrastinating for a while, I fired up the debugger and went on another bug-hunting wild goose chase. And eventually, I found another CPU bug. This bug was affecting arithmetic instructions, and it was a problem I had fixed earlier for my branch instructions but had not considered that it would affect other instructions as well. Basically, there are cases when a value written to the program counter is the same as the value it already contains, but because of pipelining it should still jump ahead an instruction. By setting bit 0 of the program counter (which is normally unused) to indicate a change, NooDS can detect that it needs to adjust for pipelining even if the value didn’t actually change, and then clear the indicator bit after. I applied this to my arithmetic instructions as well, and…
Oh, wow. Pokémon White actually boots! After all that, one silly little bug was holding me back from a breakthrough. And this isn’t the only game to boot, of course; of the games in my library, Super Mario 64 DS and The World Ends With You are able to make it through their logo screens, Pokémon Ranger and Pokémon Ranger Shadows of Almia are able to make it to their menus (but are unable to continue due to lack of touchscreen support), Ace Attorney Investigations has navigatable menus but freezes when trying to start the main game, and the generation 4 Pokémon games can make it to the title screen before freezing due to lack of 3D support (like Pokémon White), but have a lot of missing graphics during their intros. I’m sure there are more working games too, but this is what worked out of the games I have to test at the moment.
So it finally feels like NooDS is becoming a proper DS emulator. There are still a lot of things that need to be done, though. For the immediate future, things like touchscreen and save support, as well as more work on the 2D engine, are planned. I’d also like to start looking into some optimizations, and probably clean up my code a bit while I’m at it. Being able to boot commercial games directly instead of having to go through the firmware might be nice, too. And eventually I’ll have to tackle the 3D engine, which I’m sure will be both challenging and rewarding for me, as someone who has almost no knowledge of 3D rendering. But hey, I’ve made it this far, so anything is possible! That’s it for now; look forward to future updates!