|Home | Downloads | Screenshots | Forums | Source code | RSS|
Jan 23rd 2017, by StapleButter
melonDS has progressed nicely since the last post. To give you an idea:
It's got some graphics capabilities, even though those are quite limited. Rotated/scaled sprites, like those used for the clock up there, gave me some trouble, but I eventually got it working.
Speaking of the clock, it's frozen in time. I had to implement the RTC to get the firmware to actually boot instead of constantly entering "please enter time/date" mode, but for now, I hardcoded the time/date. The RTC is real fun to work with btw-- DS software talks to it by bitbanging a GPIO register. From the emulator's point of view, you get a series of zeros and ones which you must put back together to get data you can work with.
I also implemented proper-ish support for the DS cart hardware. This is quite the fun too. There's an initialization sequence done by the BIOS, and two different encryption methods are used. The BIOS starts by retrieving the ROM header and chip ID, then switches to Key1-encrypted command mode and retrieves the secure area (a protected part of the ROM not readable via the normal read command), and finally switches to Key2-encrypted command mode, under which the game will operate.
It is also worth noting that on actual carts, the first 2KB of the secure area are Key1 encrypted. The first 8 bytes are a double-encrypted identifier (string 'encryObj') that is used by the BIOS to verify whether the decryption was successful. However, all the DS ROM dumps out there have that 2KB block decrypted. Thus, we need to re-encrypt it for it to be loaded successfully.
Getting Key1 to work took a while (and uncovered a bug in the CPU core). Key1 is based on Blowfish and is the most complicated encryption algorithm used. Key2 is based on a XOR stream generated from two 39-bit registers, but it can be ignored, it is entirely implemented in hardware.
The really hacky part in the current implementation is how cart DMA is implemented. Cart DMA basically works by automatically transferring a word from the cart data output register to memory as soon as there's data ready. The DMA engine doesn't know how long the transfer is, it only knows to wait until there's data available, transfer it, then repeat.
When you take the lazy approach of making cart reads return data instantly, you may run into trouble when implementing cart DMA properly. A read from the cart data output register would advance the read pointer, and if DMA is enabled, perform a DMA transfer, which would trigger another read, and so on until the recursion overflows the stack.
So instead, there's an entirely separate code path for cart DMA. Atleast until the transfer delays are emulated. Then again, I also envision optimizing common DMA transfers with special code paths avoiding a lot of address decoding work, like I did in blargSNES. Common DMA transfers would mostly be transfers to VRAM, palette memory or OAM, though. Optimizing cart DMA would only speed up loading times at best.
Noting that with all that, the firmware does detect the cartridge, the game title and icon show up fine... booting a game doesn't quite work yet, though.
Well, actually, the game tested (NSMB) does boot, but it hangs before doing anything visible. The twist is that it isn't quite stable, sometimes it behaves differently, but still hangs. This tends to indicate that either the timings are really bad, or there's some evil "out-of-bounds array write" type bug hiding somewhere.
The main loop/scheduler needs rewritten badly anyway. The current system is messy at best and incorrect in certain cases (fast timers would be a good example). I should use absolute 64bit timestamps instead of trying to keep track of cycle differences here and there.
|3 comments have been posted.|