|Home | Downloads | Screenshots | Forums | Source code | RSS | Donate|
|Register | Log in|
|< Getting somewhereDeeper than it seems >|
A lot closer to a finished product
Feb 6th 2017, by Arisotura
Even if still very far.
After a while, melonDS finally boots commercial games. Among my small game library, the compatibility rate is encouraging, even. Here are a few screenshots, for example:
Getting there took a while of implementing new features, but also bashing my head against obscure bugs that often turn out to come from silly little things.
As an example, a bug that prevented games from booting.
On ARMv4/v5, the LDR opcode has the particularity that if you read from an address that isn't word-aligned, it aligns the address, and rotates the word it read so that the LSB is the byte pointed by the original address. In melonDS, it was implemented the following way:
u32 val = ROR(cpu->DataRead32(offset), ((offset&0x3)<<3));
At first glance, looks alright, doesn't it? Except ROR() is a macro, defined as follows:
#define ROR(x, n) (((x) >> (n)) | ((x) << (32-(n))))
This basically ends up calling cpu->DataRead32() twice. This isn't a big deal when reading from RAM, it only wastes some CPU time, but the bug goes unnoticed. However, it has nasty side-effects when reading from I/O registers like the IPC FIFO.
Next up, aside from GPU-related work, were features like touchscreen or save memory.
Touchscreen emulation isn't too hard. Saves are a little more involved. In itself, it's nothing big, the save memory is an EEPROM or Flash chip accessed over a dedicated SPI bus. The issue is how to determine the correct memory type. The ROM header doesn't contain that information, so we must guess it. The current implementation waits for the game to start writing to the save memory and tries to determine the memory type from the length of the longest write. The idea is to guess the memory page size, from which the memory type and size can be inferred. If a save file is already present, those variables are inferred from the file's size.
With that covered, games can do something more interesting than sitting on a "failed to erase data" screen.
So far, this is what I have tested:
New Super Mario Bros: non-3D minigames playable, freezes when going ingame
Super Mario 64 DS, Rayman Raving Rabbids 2, Meteos demo: "playable", but no 3D graphics
Mario Slam Basketball, Rayman DS: get stuck trying to do a GX FIFO DMA
Mario & Sonic at the Olympic Games, Mario Kart DS: freeze when trying to display 3D graphics
Super Princess Peach: playable, 3D effects missing
Worms 2 Open Warfare: seems to work, but menus invisible -- this game is all 3D
It appears that there are now two main immediate directions for melonDS: UI and 3D support.
The UI part will need some thinking to pick the best framework. The current UI is something I quickly threw together using the Win32 APIs so I could see graphics, but for the "final" product, I want something cross-platform. An idea is to provide a quick SDL-based interface and a more complete Qt interface. I don't like some aspects of Qt, but regardless, it's a possible candidate, and a powerful one.
A decent UI would also support things like selecting a ROM file instead of hardcoding the filename, choosing a save memory type should autodetection fail, choosing whether to boot from the BIOS or from the game directly, all those things.
3D support is going to be required to get further into emulating games at this point. Past the obvious reason that they can be unplayable without 3D graphics, some require the hardware support to get further.
The 3D GPU has a FIFO for sending commands to it, called GX FIFO. It can be set to trigger an IRQ when it gets empty or less than half-full. Some games wait for the IRQ before sending more commands, some others use DMA to send their command lists automatically. Without proper support, these games would just hang.
The most bizarre game is probably Super Mario 64 DS. It does enable the GX FIFO IRQ at times, but never waits for it -- instead polling the GXSTAT register with the following code:
0205A390 LDR R12, =0x4000600
0205A394 LDR R4, [R12]
0205A398 AND R4, R4, #0x7000000
0205A39C MOV R4, R4, LSR#24
0205A3A0 ANDS R4, R4, #2
0205A3A4 BEQ #0x0205A394
This is basically an inefficient way of checking whether bit 25 of GXSTAT (0x04000600) is set.
((GXSTAT & 0x0700000) >> 24) & 0x2
Could have as well been:
GXSTAT & 0x02000000
Why it doesn't just wait for the GX FIFO IRQ is a mystery (waiting for an IRQ lets the CPU go idle and saves power, unlike this kind of busy loop).
Stay tuned for more reports of the melonDS adventure!
|6 comments have been posted.|
|< Getting somewhereDeeper than it seems >|