The DS cartridge interface: endless fun
Depending on your definition of fun, of course.

This is a bit of a pace change from all the recent OpenGL stuff: this is going to be some hardware infodumping slash juicy technical post. It all started with this bug report: Bug: Surviving High School (DSiWare) not booting.

Basically, this DSi game gets stuck on a black screen. This bug report piqued my interest, and oh boy, I had no idea what kind of rabbit hole it would be.


I first started by doing what I do in order to troubleshoot emulation bugs: track where each CPU is hanging around at, dump RAM, throw it into IDAPro. This gives me an idea what the game is doing, and hopefully provides a lead on the bug. Another possibility is to try modifying the cache timing constants to probe for a timing bug, but this made no difference here.

In this situation, the ARM9 was running an idle loop, but the ARM7 was stuck in an endless loop. Backtracking from this, I found that the game was reading the cartridge's chip ID, comparing it against the value stored at 0x02FFFC00, and panicking because the two were different. This code isn't atypical for a DS game...

But... wait?!

This game is a DSiWare title! There is no cartridge here.

No idea why it's reading the cartridge chip ID. Maybe the game was intended to be released in physical form? But in this case, the chip ID stored at 0x02FFFC00 is zero. When no cartridge is inserted, melonDS returns 0xFFFFFFFF instead of zero, hence one part of the bug.

Changing melonDS to return zero when no cartridge is inserted would fix the bug, but only when there's indeed no cartridge. If there's one, it will fail. This is because when booting a DSiWare title, the DSi menu forcefully powers off the cartridge if there's one inserted. melonDS doesn't emulate this bit.


This was the start of a longer journey, which ended up becoming its own branch, again. I don't like the way the cartridge code in melonDS has evolved over time.

On one hand, it was built quickly to answer simple needs in simpler times, and things were kind of just piled together. The object-oriented implementation of different cartridge types was a good idea, but they were kind of jammed into one code file, which turned into a bit of a mess.

With DSP HLE, I started experimenting with using subdirectories for specific areas of the emulator, rather than just dumping all the code files into one directory. I thought that doing the same for the cartridge code, after splitting it into separate files, was a good idea. Other areas of the emulator could use this kind of reorganization too, but one thing at a time.

On the other hand, the cartridge interface implementation in melonDS is technically wrong. This is where the fun begins.


In the DS, the cartridge interface is the piece of hardware that sits between the CPU and the cartridge. It can operate in two modes: ROM and SPI. ROM mode uses all the data lines to transfer one byte per cycle, and is typically used to access the ROM. SPI mode uses two data lines as a SPI interface, and is typically used to access the save memory.

The cartridge interface's registers are exposed to both ARM9 and ARM7, but only one CPU may use it at a given time - which CPU gets access is selected by bit 11 in EXMEMCNT. This is a rather important detail - if you happen to remember the old times, we've had a rather evil bug related to this.

I assumed that the cart interface was one set of registers, one hardware block, and that it was accessible to either one or the other CPU. But that's not how this works, actually.

Each CPU has its own register set, and its own cart interface hardware block. Technically, both sides are always functional, and can run transfers at the same time. The EXMEMCNT access right setting merely controls which one is connected to the actual cartridge - the other one will just be reading 0xFF bytes.

Another detail that is wrong is timings.

The way communication works in ROM mode is that you send a 8-byte command to the cartridge, and optionally send or receive data. For example, to retrieve the cart's chip ID, you send a command with the first byte set to 0xB8, then receive one data word: the chip ID.

Since the interface transfers one byte per cycle, it takes 8 cycle to transmit the full command, and 4 cycles per data word transferred. Since the cart's ROM chip may need extra time to fulfill the command, It is also possible to configure delays before the data transfer, and between each 512 bytes of data. The delays are specified in cycles. The duration of a cycle can be either 5 or 8 system cycles (ie 33.51 MHz).

Cartridge transfer delays in melonDS were modelled based on this. However, the model is missing one simple fact, and this ends up making cart transfers slower than they should be.

Jakly figured out that there is buffering involved. Basically, when a data word is transferred from the cartridge, it is placed in a buffer where it may be read out (at 0x04100010), and the DRQ signal is raised to notify the CPU (or DMA). However, the buffer can actually store two words worth of data, so when one data word is available, the interface can start transferring another word, rather than having to wait for the CPU to read out the data.

This buffering scheme is definitely worth implementing. In melonDS, the lack of buffering ends up adding about 400 cycles per 512 bytes of data transferred.

I remember that Rabbids Go Home suffers from a related issue. Basically, the game runs several ROM transfers and measures how long they take. The total needs to fall within a certain range, or the anti-piracy kicks in and the game freezes. So implementing buffering into melonDS might fix this.

I've also been finding out that write transfers (when transferring data to the cartridge) suffer from a couple hardware bugs, too. For example, under certain specific circumstances, the cart interface may accidentally send out one data word despite the buffer being empty. Some of this is worth documenting, but not necessarily worth emulating, atleast for now.


Then you have the DSi, which has two cartridge interfaces.

Yes, you read this right.

Basically, the DSi was planned to have two cartridge slots. In the end, Nintendo didn't like it, so it was scrapped.

The functionality is still part of the retail DSi. The second cartridge interface is completely functional. It even appears that the retail SoC has the required signals for a second cart slot, but they aren't connected to anything on the motherboard.

So how does this all work?

The second cart interface has the same register layout as the first one. The registers may be found at 0x040021A0 instead of 0x040001A0 (and 0x04102010 instead of 0x04100010 for the transfer buffer). There are also new IRQ lines for it (IRQ 26 and 27, instead of 19 and 20 respectively), a new DMA trigger line, and a separate CPU access setting in EXMEMCNT (bit 10). This cart interface can't be used with old DMA, but NDMA has an adequate trigger mode for it (0x05).

Register SCFG_MC was added to control the cartridge slots. Each cartridge may be turned on or off in software. This is used to reset the cartridge - initializing a DSi cartridge requires a reset, but the original interface provided no way to do that in software. SCFG_MC also serves to turn off the cartridge when loading DSiWare, for example.

An empty cartridge slot is also forced off by hardware. The nonexistant second slot is considered to always be empty, so it is always off.

It's also worth noting that the on/off status applies to the cartridge itself. The cart interface is functional regardless, but it will read 0x00 bytes if the cart is off.

SCFG_MC also provides another setting to swap the two cartridge interfaces. The point would be that games could be loaded from either cart slot without needing to know which slot is in use, and it would also guarantee backwards compatibility with DS games. This setting merely swaps the address at which each cart interface appears, as well as the IRQ lines and all.


So all in all, this requires some foundational changes to the way cartridge stuff is emulated in melonDS. As well as a lot of hardware testing to figure out every detail, answer every question that is raised along the way, and so on.

But, I hope, the improved accuracy will be worth it.
DoctorDizzy says:
Mar 1st 2026
I can't believe that my interest in semi-obscure DSiWare led to this rabbit hole! Really interesting read. I didn't even know the DSi had two cartridge slots at first :O

Toya_9 says:
Mar 1st 2026
Hey there just wanna say huge fan of this emulator, it has come an incredibly long way and its evident an indescribable amount of hard work has gone into it just had 2 questions/issues i was hoping to get resolved. I notice when speeding up on top of the game audio there is a super crackly sound affect that occurs (crazy popping sounds) i know in no realm does speed up ever sound pleasant but i was wondering if there's any possible setting i can change to stop this crackling sound from occurring. lastly when playing games like Pokémon Heartgold/Platinum i notice there's really weird/subtle black lines/dots that occur. is there any setting i can change/apply to get rid of this issue. i ended up downloading (Desmume X432R) solely for testing purposes just to see if it was maybe a hardware limitation but on that emulator both issues didn't occur once, audio when speeding up was as crisp as it could be without any other external noises occurring (crackling sounds) and the black lines disappeared once enabling (anti aliasing) apologies for rambling on and again massive fan of this emulator and if these issues happened to be fixed in a future patch words could not express my gratitude :)
poudink says:
Mar 1st 2026
The crackling doesn't happen in DeSmuME because it has asynchronous audio. melonDS doesn't have that, nor does it have a setting to mitigate the crackling.

The black lines in melonDS only happen with the OpenGL classic renderer. Use the OpenGL compute renderer instead.

DeSmuME X432R has been obsolete for about a decade by now and shouldn't be used. All of the useful features it added were added to upstream DeSmuME ages ago.
Lockstar64 says:
Mar 1st 2026
Yeah, really good answer there. The compute renderer makes emulating those games a great experience. The latest version of the renderer is particularly great, as there was occasional glitching at higher resolution scales that has now, by my limited testing, been entirely resolved!
Chris Jones says:
Mar 1st 2026
Can I ask you to try and get local multiplayer working between ds and computer I know it works for online play and computer to computer but ds to computer would be really cool
Toya_9 says:
Mar 1st 2026
Yoooooo @Poudink appreciate the answer homie thanks so much. ill definitely try the "compute renderer" and see how i go, in regards to the audio do you by chance know if "asynchronous audio" will ever be integrated in a future patch or even just a "toggle mute" hotkey just cause at times i speed up quite frequently especially in the Pokemon games. Again thanks so much for the help man much appreciated :)
poudink says:
Mar 2nd 2026
There is already an option to mute games while fast forwarding in the latest nightly builds. I don't believe asynchronous audio will ever be implemented, IIRC Arisotura was against it since it's much less accurate.
Toya_9 says:
Mar 2nd 2026
Ahhh i see i see. Well im praying Arisotura has a change of heart down the line, only reason i say that is while trying to find a fix for this ive actually seen this issue bought up quite a bit from other people so its a implementation countless people would appreciate. Regardless though thanks so much for your help/patience man greatly greatly appreciated. And lastly just thought id ask but did u have any final recommendations on my circumstance even if its minimal is there anything i can do at all to mitigate this audio issue or am i pretty much cooked unless Arisotura has a change of heart in the future. Only fix i can think of is solely playing the pokemon games on desmume where audio while using speedup constantly dosent have the static crackling/popping sound affect but even then melon trumps desmume in all other areas so id really rather not resort to that outcome if i can help it.
poudink says:
Mar 2nd 2026
The only solution that currently exists as far as melonDS is concerned is the option to mute while fast forwarding that I mentioned earlier. Conceivably, time-stretching could eventually be implemented, which solves the crackling and is compatibly with synchronous audio, though it requires a significant increase in audio latency to work and still doesn't sound quite as nice as asynchronous audio. DeSmuME is the only DS emulator I know of that has asynchronous audio.
Toya_9 says:
Mar 2nd 2026
Ahhh gottya man no worries. Yeah wow a time stretching setting would be an incredible addition but i guess for now ill have my fingers crossed and pray arisotura hopefully sees this and implements something in order to fix this issue with audio, also just with the muting option when speeding up do you by chance know which tab thats under so i can enable it? and my version of melon is just the latest one (1.1) from late last year is that the correct build in which this feature is available? Absolute legend man thanks so much again :)
poudink says:
Mar 2nd 2026
It's not in 1.1, you need to be using a nightly build. You can find those in the downloads page of this site.

Also, I just checked Dolphin (which exclusively uses synchronous audio) and honestly the audio when speeding up sounds significantly better than I remembered. Leagues better than melonDS even when using the minimum (16ms) audio buffer size. I don't know if it's just using time-stretching or if it has other tricks up its sleeve, but either way if melonDS manages to get its audio that good then I guess asynchronous audio won't be so necessary anymore. But Dolphin is always ahead of the curve when it comes to that kind of thing...
Toya_9 says:
Mar 2nd 2026
Yeah wowwww I just tried dolphin myself and that’s more then ideal in terms of audio, if melon somehow was able to also implement that this would hands down be the most perfect emulator to ever exist. The thing is melons audio is fantastic already just the one little finicky thing with speed up. I really do hope arisatora sees this cause he would help soooooo many people with a future patch including a fix for this. I was gonna ask man in melons discord is there a sorta “bug fix” channel to report any problems to? Although in saying that despite how small this issue is I don’t wanna come off as annoying there cause I’m sure arisatora has got plenty on his plate already, I just know he’d help a crazy amount of people me included. Thanks again man legend :)
poudink says:
Mar 2nd 2026
This is the place to report bugs: https://github.com/melonDS-emu/melonDS/issues

Just open a new issue, assuming one doesn't already exist about this.
Toya_9 says:
Mar 2nd 2026
Ahhh gottya man thank you so much :)
Dionicio3 says:
Mar 3rd 2026
if the 2nd cart slot still exists on the SoC, I wonder if someone can do anything funky with that. Most likely not, but it's fun to think about
Post a comment
Name:
DO NOT TOUCH