I figured out why none of the other 3D games were working, and it was a stupid bug.
As mentioned before, the GPU has a FIFO that holds commands waiting to be executed. The GPU can send an interrupt request to the ARM9 depending on whether the FIFO is empty or half-empty. However… a peculiar feature of this FIFOIRQ is that the bit associated with its request will always remain set as long as the condition is true, even if the ARM9 tries to clear that register. Because I wasn’t emulating this, games would get stuck in infinite loops waiting for a FIFOIRQ that would never come. I didn’t catch this problem before for the following two reasons:
- Super Mario 64 DS, the only game that worked well enough for me to do extensive 3D testing, is badly programmed and never uses the FIFOIRQ in-game; rather, it sits in a busy loop checking to see if GXSTAT says the FIFO is empty, which I emulated correctly.
- I had not implemented FLASH saving, which several 3D games use. Therefore, I could not test that many games in the first place.
On that note, every 3D game in my library can at least get to the title screen now:
Many issues of course (Not visible: MKDS hangs in-game and Pokemon Diamond doesn’t display any sprites in-game like the character and TVs), but they aren’t relevant for this article.
Now, since I implemented FLASH saving, there’s a huge problem that has to be addressed before the release.
DS games can have radically different save sizes, ranging from 512 bytes to 8 megabytes. Furthermore, there are three different protocols games use depending on the size: 512-byte games use “tiny” EEPROM, 8-64K games use regular EEPROM, and 256K+ games use FLASH. Each protocol has different commands, and while the protocol can be inferred from the save size, there is no 100% accurate way to figure out the save size.
It is possible to use heuristics to determine what could be the proper save size. This is because each save size has a maximum possible transfer length: for instance, you can only read up to 32 bytes at a time with 8K EEPROM. melonDS and medusa both use this heuristic for certain, and the other emulators likely make use of it as well. (Someone correct me if that’s not the case.)
However, there are ways this can be defeated:
- A game could use a transfer length that’s lower than the maximum supported for this size. If an emulator forces the save size to be a certain length, like melonDS, the save file will be garbage.
- A game could write out-of-bounds and check if the write works, presenting a “save error” screen if it does. This is often done intentionally to prevent piracy; when the DS was still relevant, pirates would stick as much save memory as possible into flashcarts in order to save (haha) on costs.
In either case, heuristics are not 100% reliable, and so I wish to avoid them in CorgiDS. Thus, I want to implement the following:
- If a save file exists, get the save size from that and we’re done.
- If there is no save file, ask the user to select a save size. If the initial size that is selected doesn’t work, then the user can modify it until everything works.
Making the user select the save size isn’t terribly convenient, however. Thus, at some point in the future, I want CorgiDS to be able to read from a database that stores all of this information and other stuff as well, somewhat akin to the GameINI files in Dolphin. However, I do not plan on doing this for v0.1; instead I will use the above solution directly until I have the time to extend it further.
For the time being, I’m going to figure out why my games are being derpy. 🙂