melonDS aims at providing fast and accurate Nintendo DS emulation. While it is still a work in progress, it has a pretty solid set of features:

• Nearly complete core (CPU, video, audio, ...)
• OpenGL renderer, 3D upscaling
• RTC, microphone, lid close/open
• Joystick support
• Savestates
• Various display position/sizing/rotation modes
• (WIP) Wifi: local multiplayer, online connectivity
• and more are planned!









Download melonDS

If you're running into trouble: Howto/FAQ

If you're feeling generous: melonDS Patreon
On the way to 3D graphics
Unrelated note: I changed the way the site displays comments, to start from the oldest. It feels more natural this way, atleast to me.


So a while ago, I was at the point where getting further with melonDS required emulating the 3D engine.

As tempting as it is to hack things together to get some graphics showing up, I decided I'd take the time to do things right. Plus, GBAtek documents the 3D engine well, so let's make good use of it.

This also means that the renderer I'm building is a software one. This doesn't exclude the possibility of implementing hardware rendering for 3D or even 2D later on, but starting this way is more interesting than mapping the DS 3D features to OpenGL. I'm able to learn about deeper aspects of the 3D engine, and emulate it more closely than a hardware renderer can get. There are also features and details of the original hardware that can't be reproduced well with OpenGL, as is typical with older console GPUs, and it isn't always super-specific details, it can also be about basic features.

For example, the DS is able to draw triangles and quads natively, but modern GPUs can only draw triangles -- the more complex primitives supported by OpenGL are broken down into triangles. This can affect how vertex attributes (color and texture coordinates) are interpolated across a quad. Below are two renders of a quad with the same attributes:

   

The left one shows color interpolation across the original quad. The right one shows color interpolation after the quad was broken down into triangles. You can notice the difference.


At this point, melonDS's renderer is able to draw polygons without exploding, which is a good start. It can do color interpolation, but it lacks perspective correction for now. It also lacks more important features, like texturing.


Anyway, melonDS isn't too far away from a first release. Here's a quick list of the things that remain to be done before it can be shipped:

* a better 3D renderer, as explained above
* emulating DMA timings
* building a somewhat proper interface
* misc. tidbits: timers that suck less, a few missing bits of the 2D renderer, etc...


Stay tuned for more!
Deeper than it seems
I've been wanting to work out the DS's color precision. Incoming colors (palettes, vertex colors...) are all 15-bit, but the screens actually output 18-bit color. The 3D engine is well documented in this regard, we know that the colors are converted to 18-bit before being blended together and even how they're converted. However, details on the 2D engines are at a loss. GBAtek doesn't say much, other than that master brightness is applied to 18-bit color.

For reference, GBAtek's diagram of the whole video system.

I first tested master brightness. The idea is to configure it in "brightness decrease" mode with a factor of 8, such that incoming colors are halved. Then, fill two parts of the screen with colors 0,2,0 and 0,3,0 respectively. If the colors are processed as 15-bit, they will come out the same, but if they're converted to 18-bit beforehand, there will be a difference. Noting that I picked green colors to make the difference as visible as possible, as the human eye sees green best.

The test reveals that there is a difference, subtle but visible. Which confirms that master brightness is applied to 18-bit color.

To find out where the color conversion takes place, I did the test again, using the "brightness decrease" special effect instead of master brightness. It works the same, but it's applied earlier than master brightness. The test also showed a difference, meaning that color special effects are applied to 18-bit color.

So now we know that colors are converted as soon as possible, but there is one remaining detail to work out: how they're converted.

We know that the 3D engine converts color components the following way:

if (in == 0) out = 0;
else out = in*2 + 1;

From there, the idea is to display identical colors from the 2D and 3D engines, side by side, and compare them. By doing it for each possible intensity (from 0 to 31), we can work out where they differ and infer how the 2D engine converts colors.

The result of this test is a little surprising. For all the intensities above 0, the colors differ.

... read more
A lot closer to a finished product
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.

... read more
Getting somewhere
melonDS has progressed nicely since the last post. To give you an idea:

melonDS running the DS firmware

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.

... read more
So what's melonDS?
melonDS is a new emulator project. My goals for it are to do things right and fast. Time will tell how far we can get in that direction.

If you know me, I'm not new in the emulation scene. I'm responsible for lolSNES/blargSNES, among others. I'm not new in the DS scene either, I worked a lot on DeSmuME back in the days. Why I'm straying away from DeSmuME is not something I will get into here.

So here I am, writing my own emulator for the second time. The hardest part in starting an emulator project from zero is managing to build something that resembles an emulator. It takes a fair amount of work before you can start getting results.

To this day, melonDS has gotten over the initial stage and does give some results. Nothing actually works aside from the very simple ARMWrestler CPU test, but hey, it's a start. melonDS has a 'good enough' subset of the ARM instruction set implemented, as well as a few hardware features, so getting things to run is mostly a matter of implementing the rest of the DS hardware.

One thing I want to do is being able to boot from the BIOS, like an actual DS. I was unable to get it to work in DeSmuME back then, but manually loading a DS firmware and booting it (thus skipping the BIOS) did work.

The progress of melonDS in regard to this is encouraging. The BIOS loads the firmware into memory and boots it. The firmware gets to the point where it tries to use the IPC FIFO, which I haven't implemented yet. Interesting things ahead!

Of course, melonDS will later have the option to boot a game directly, like all the emulators out there do. But for the sake of accuracy, I want to be able to boot from the BIOS.

The holy grail would be wifi emulation, especially local multiplayer, which I could never get working in DeSmuME. Time will tell if that goal is achievable.
melonDS dev blog
I will use this blog to post about the development of melonDS, my DS emulator project. I expect it to be lots of fun.