melonDS RSS The latest news on melonDS. melonDS - now also for macOS! -- by WaluigiWare64 Sun, 29 Nov 2020 17:32:59 +0000 melonDS now also supports macOS!

If you want to test it, scroll down to the bottom of the post. I’ll be explaining about what needed to be changed for it to work.

This originally started as a little challenge. "It shouldn't be that hard," I thought. However, it wasn't as easy as I would have hoped, but I got there in the end.

- The JIT recompiler

Thanks Generic (aka RSDuck) for helping me out a lot here and guiding me!


It mapped memory using "memfd_create()" on Linux, which didn't exist on macOS. Instead, on macOS shm_open is used to create the fastmem memory.
macOS also didn't have "->gregs" in "uc_mcontext" and no "REG_RIP" either. This has to be changed to "->__ss.__rip" instead.
Then, it would crash with a "bus error" on attempting to load. This was caused because macOS returned "bus error" instead of "segmentation fault", so the signal handler couldn't handle it.
Note: fastmem was disabled because it caused all sorts of errors while trying to boot firmware or run games. If anyone manages to fix it, send a pull request!

The JIT itself

The JIT would build, but at link time it would complain about "ARM_Dispatch" and "ARM_Ret" being undefined. Apparently in the Mach-O format (used in macOS) global function names defined in assembly are required to be prepended by an underscore.
Then it would crash upon booting firmware or trying to load a game. This was caused by the line here which tried to reprotect some memory to make it executable. On macOS, new memory is now mmap'ed instead.

- The OpenGL renderer

macOS complained about not being able to find "GL/gl.h" and "GL/glext.h". These includes had to be changed to "OpenGL/gl3.h" and "OpenGL/gl3ext.h" on macOS and the OpenGL framework was linked.
Also the functions defined by the OpenGL macronator already existed on macOS, which caused "ambiguous reference" errors. The macronator was ifndef'd out.

- Direct Mode

Direct Mode used "AF_PACKET" to get the MAC address, which doesn't exist on macOS. "AF_LINK" was used instead.
The library names of libpcap had to be changed to "libpcap.A.dylib" and "libpcap.dylib" on macOS.

- Binding Keys

This was a simple fix. For some reason, macOS didn't give focus to the buttons in the key binding menu when they were pressed, which meant that they couldn't detect keys. I had to set the focus policy to Qt::StrongFocus to get them to accept focus.

- App Bundle

Now it built fine and it worked, but it came as a Unix executable, not a macOS app bundle. I had to add some lines in CMakeLists.txt to make it build an app bundle.
I also generated a macOS ".icns" icon file for melonDS, so now the icon showed up on the app bundle.

- No libslirp available in Homebrew

Homebrew (the package manager) didn't have libslirp in their repositories, so I created a pull request here which was merged.

Here are the downloads. If you find any issues, make sure you comment here and tell me so I can fix it!
You will have to install the appropriate libraries beforehand with the Homebrew Package Manager.
In Terminal paste the following command to install the required libraries.
brew install qt5 sdl2 libslirp

melonDS 0.9 beta for macOS x86_64
To unzip the above download, you may need to use a program like The Unarchiver.]]>
A lil' message to would-be translators -- by Arisotura Thu, 12 Nov 2020 12:11:09 +0000
I am wary about internationalizing software before the end of the dev cycle. That being said, is there really an 'end of dev cycle' for an emulator project? I think it'd be a good idea to make melonDS accessible to languages that aren't English. I have a couple concerns about this though:

- I'd like translators to stick around. If they can be around to fix up their respective translations before each release, that will be great. I just really want to avoid having translations become incomplete and/or obsolete because their author is long gone.

- I want to ensure the translators are good at English and understand the terminology used in melonDS's UI. Just basic quality insurance, no Google Translate crap.

- What about this website? It's a whole different can of worms. The interface could be translated, but having to translate each and every blog post would be a massive pain in the ass.

There are also a bunch of technical concerns, but, overall, maybe we can try and pull this off for melonDS 1.0, or even earlier?

If you're in, check out this thread.

Thank you!]]>
Changes to the website -- by Arisotura Sat, 31 Oct 2020 13:07:11 +0000
Of course, comments are also still open to guests.

There are more updates planned to this site, so, let me know asap if anything breaks.]]>
The DSi camera adventure -- by Arisotura Tue, 27 Oct 2020 13:34:06 +0000
Well, it's not been that easy.

I had started work in the dsi_camera branch, but so far it was a large trainwreck. I couldn't really understand how camera transfers work and how everything interacts together. My attempt at a guessed implementation was getting nowhere, which meant it was time for some hardware research.

So I started work on a DSi camera test homebrew. I first went and implemented the initialization procedure found in GBAtek, only to be rewarded with a hang when trying to activate a camera. I tried many things, taking the init procedure from some open-source Aptina MT9V113 driver (the model of camera the DSi uses), reverse-engineering the DSi camera app to use its exact init procedure, all to no avail.

I felt stuck there. I even tried looking for existing examples using the DSi cameras, found this one by Epicpkmn11, but at the time it seemed to have the same issue I was having.

I eventually went out and asked for help on several places. A side effect is that I'm now found in some Discord servers. I also posted a thread at nesdev, knowing nocash hangs around there. The documentation in GBAtek implied he did get the cameras working, so I figured he'd be able to help. And he did, thanks there.

I first looked at the code he provided, checking for any meaningful differences in the init procedure, but it looked like I had all the essential stuff right. I was stumped.

It eventually occured to me that maybe I should try initializing both cameras simultaneously, like Nintendo does, rather than only initializing one camera. You know how it is, when you're desperate, anything can look like a valid solution. Anyway, that didn't cut it, but it revealed something interesting when I tried to read some registers from both cameras. Some reads were getting corrupted. So I knew something was up with the I2C code.

Looking at nocash's I2C code, I was able to spot and fix the issue. Turns out that during an I2C read, you don't raise an ack when reading the last byte. This fixed the corruption I was observing, and finally allowed the camera to activate successfully. At the same time, Epicpkmn11 happened to be in the same Discord server I was in, so they could fix their code too (turns out it did have the same issue as mine).

Next thing I did was enable a camera transfer, and, lo and behold, I was able to display camera input on my DSi. From this, I tested the camera transfer hardware in several ways, to try figuring out how it works.

Details are still hazy, but this time I was able to make a working implementation in melonDS.

I made the data register return a fixed value, hence the red/blue stripes. But now that we have a working base, next step is feeding an image buffer into this, and ironing out the remaining issues (for example, taking a picture causes a system error).

But after that, I felt like chilling some, and figured I would try finding out why ZXDS was running abysmally slow in melonDS. Basically, ZXDS is a ZX Spectrum emulator for the DS. I'm not into Spectrum emulation, but from what I could read, the emulator is quite impressive technically, and I enjoyed reading the author's developer diary.

Anyway, ZXDS abuses the DS's writable VCount to limit the framerate to 50FPS. To quote its author:

Having this common master frequency, it was now simple to use it for converting T cycles passed to amount of samples to generate, as well as to use it to set up a timer which would count the 50Hz which would drive the LCD, and still keep everything in perfect sync. The refresh rate of the LCD itself can't be set directly, though, however the DS features a writable VCOUNT register which can be used to delay the start of the next frame as needed. It is normally intended to be used to synchronize display of machines participating in multiplayer games, but it was trivial to abuse it for holding the retrace after each frame displayed until the interrupt handler driven by the 50Hz timer allowed it to go, effectively slowing the LCD refresh rate to 50Hz as well.

Technically, ZXDS uses two cascading timers to achieve this. Timer 2 is set to an interval of 16 cycles, and drives timer 3 which is set so that its IRQ will fire every 20ms. ZXDS will then hold VCount at a certain value until the IRQ fires. All fine and dandy.

Of course, as far as melonDS is concerned, this is where the problem was lying. ZXDS would hold VCount for way too long, causing each frame to last absurdly long. Sure enough, the issue came from the timers. What, a timer issue in melonDS in 2020? Madness!

In particular, it came from timer 2 and its tight interval. melonDS updates its timers more loosely than the actual DS, but it assumed a timer would only overflow once after each update. What happened here was that updates were far enough apart that timer 2 had the time to overflow more than once. The assumption that it would only overflow once caused it to start behaving wrong, and that is why the timer 3 IRQs were so far apart.

So I did something similar to audio timers, which are updated every 1024 cycles (and do take into account the fact that they could overflow several times in one update). This fixed the issue, ZXDS now runs at the expected 50FPS.

Sorry for the silence lately -- by Arisotura Sun, 11 Oct 2020 15:26:20 +0000
Anyway, what can we attempt doing, at this point?

Besides dealing with the pull requests and issue reports?

One thing I was working on lately was DSi camera support, but I didn't get too far. I'm going to need hardware tests to figure out how the camera hardware works. Considering the lenghty initialization procedure for those, it's not quite something I look forward to. So I'll post more about this when I get further into it.

I have ideas for the OpenGL renderer, namely, a better method for rendering quads. It would need more work for an implementation though, but might be worth it.

But, one of my main concerns is about wifi, especially local multiplayer.

At this point, melonDS is mainly known as 'the wifi emulator'. It's a bit sad that, 3 years after we got it working, we're still telling people to disable their framerate limiter and pray. We can probably do better.

It's not like we haven't tried, though. You might have seen that branch named 'betterer_wifi' in the repo. I was hoping to run the wifi with more stable timing, but it was a trainwreck, it performed even worse than our current method.

The main issue with local multiplayer is that it requires tight synchronization to function. You might remember how finicky it was back in the old days, you would start lagging and disconnecting as soon as your friend was more than 10m away from you. Long story short, the protocol works by having the host repeatedly poll its clients, multiple times per frame, and each client is given a narrow window to respond (the time given is barely greater than what it takes to transfer the response frame).

On melonDS, things are even worse as it's difficult to tell whether wifi issues arise from bad emulation of the wifi hardware, or from transmit errors, or both. We use BSD sockets as a means of transmitting frames, which inherently adds some lag. The way melonDS runs is also problematic in that it's just running as fast as possible, which can result in the wifi system running faster than it should. The throttling mechanisms, be it audio sync or framerate limiter, only kick in every once in a while, so they only make things worse at the scale of wifi operation.

So, for local multiplayer to function correctly, we would need to overhaul it. Basically, synchronize things tightly based on multiplayer frame exchanges. How to do so without ruining performance, good question.

One possibility I thought of would be running multiple DSes inside one melonDS instance, akin to NO$GBA. However this would require quite some refactoring, as the melonDS codebase was built around the assumption that it would only ever emulate one system per instance.

The other possibility is, well, reworking how we do the whole IPC. Figuring out a fast way to do IPC (we're talking about microsecond-order timings). Synchronizing melonDS instances tightly. And so on.

Welp. Time will tell how this goes, I guess.]]>
melonDS 0.9 is out! -- by Arisotura Fri, 04 Sep 2020 12:14:06 +0000
And it's big.

So, what are the highlights of this release?

- JIT recompiler

Brought to you by Generic (aka RSDuck), the new JIT recompiler enables melonDS to run much faster, and quite often reach fullspeed even when emulating DSi titles!

There are a few settings you can try out to get the most out of this JIT. While it has been heavily tested and worked on, it's still imperfect.

- DSi emulation

This is the other flagship feature of this release: melonDS now emulates the DSi!

You will need a NAND and firmware dump from your DSi, as well as augmented BIOS dumps (such as those that NO$GBA requires). The following BIOS dumper can dump the required BIOS files from a homebrew-enabled DSi.

Do note that this is still experimental. For one, direct boot will not work under DSi mode. Not everything is running yet, either, but we're getting there. melonDS does not yet emulate some hardware like the DSi cameras (but on the other hand, emulates DSi wifi).

melonDS has a small hack to bypass the region check. However, DSiware titles can't be run standalone yet, they will need to be installed to the NAND.

- GBA slot support

melonDS now supports loading a GBA ROM to simulate having a cartridge in the GBA slot. This is done by selecting a .gba file while your game is running. Note that we don't support emulating the GBA in any way.

We also support the Boktai solar sensor addon. Support for more addons is planned.

- New Qt UI

This means we get to do more without needing to work around the limitations that libui had. For the end user, this also means a bigger binary, but on the other hand, on Windows, all the dependencies are linked statically, so melonDS does not come with external DLLs anymore.

The new interface adds features that were long requested: for example, you can open the emu settings dialog to point melonDS at your desired BIOS/firmware/etc files, without needing to move/rename them around.

- Cheats

This is another popular request, and here it is. melonDS lets you enter Action Replay codes to mess with your games in whatever ways you want.


melonDS now supports letting homebrew access files and directories on a SD card image. Note that for now, you will need to provide an image file, melonDS does not yet build such images on the fly like DeSmuME can do.

Similarly, you can also use a SD card image to emulate the console's SD card in DSi mode.

- and the rest!

As usual, there are plenty of little improvements with this melonDS release, that are too many to list here, but you may discover them on your own! Or find them in the changelog (which we are still busy compiling - it's a big release).


melonDS 0.9, Windows 64-bit
melonDS 0.9, Linux 64-bit

If you're feeling generous: here's our Patreon]]>
Getting there... -- by Arisotura Thu, 03 Sep 2020 13:12:53 +0000

I'll let you guess ;)]]>
Messin' around with the GL renderer -- by Arisotura Wed, 19 Aug 2020 13:52:17 +0000
Also, if you ever wondered why progress is slow: an emulator project is like a tree in that once you're done with the trunk, it branches off in a billion different directions. At this point, melonDS is too big to be a one-man show. There are a few people working on it now, but there's only so much we can do with our time and motivation.

Anyway, while Generic is busy polishing the JIT, I figured I'd go around and try fixing some of the issues on the issue tracker.

This one, Deformed floor textures in the Celestial Tower (Pokemon Black/White) (OpenGL only), is an interesting problem. The base issue is that, if you happen to remember, the DS can draw quads natively, while modern GPUs can't. Actually, it's even worse: clipping on the DS cuts through polygons but doesn't create more polygons, which means that you can end up with a maximum of 10 vertices per polygon. For example, a triangle that sticks out of the view volume can become a quad, a pentagon, or more.

The base issue here is faulty rendering of these polygons that have more than 3 vertices. The DS employs a scanline-based convex polygon renderer, so it doesn't care how many vertices your polygon has. Software renderers used in DS emulators use similar filling algorithms, so no problem there either. However, when you use OpenGL, it's another deal entirely -- modern OpenGL-compatible GPUs are very good at drawing triangles, but... that's about it.

So, what do you do when you need to render a quad? Easy, split it into two triangles!

Suppose the quad below.

All fine and dandy. Now we split this, like this:

You can notice that, compared to the original quad, it looks distorted now. Maybe if we try splitting it in the other direction?

... well, yeah. There's only so much we can do with this.

Enter Arisotura. I've always found this to be an interesting problem, and had a few ideas to alleviate it. There isn't going to be a perfect fix, short of implementing custom interpolation somehow, but we can try to reduce the distortion by dividing these polygons more. We can't overdo it either, lest it kill performance.

A simple way to do it is the following:

It's still not perfect, but somewhat better. Keep in mind that this is an extreme example to show the issue at hand. In real-world cases, polygons are going to be smaller, and the distortion generally more subtle.

Speaking of which:

The polygon right under the bridge is a quad and exhibits the issue we're talking about. For reference, here's how it looks with the software renderer:

So I tried implementing the polygon splitting algorithm shown above. The idea is simple: find the center of the polygon, calculate the color/texcoord/depth attributes at that point, and create triangles around it. In practice, it isn't always easy to get these things working.

Once the bugs were fixed, it started looking more promising.

Not perfect, but it looks much better this way.

Still got some testing to do, and considering whether I should add an option for this, but I'm confident this will make it in 0.9. We have been neglecting the OpenGL renderer since the day it was created, it's more than time to give it some love.]]>
maxmod fixes -- by Arisotura Thu, 23 Jul 2020 11:33:35 +0000
Anyway, just yesterday, asie told me that maxmod's interpolated mode was broken in melonDS, which piqued my curiosity.

What's maxmod, you ask? It's a real fancy audio library for the GBA and DS. I don't know much about the GBA side of it, but on the DS, it supports three modes: hardware mode, interpolated mode, and extended mode. Hardware mode is fairly straightforward. Interpolated mode resamples audio on the fly, applying interpolation to make up for the lack of hardware interpolation. Extended mode does its own mixing, adding support for more channels than the hardware can offer.

In our case, interpolated mode was broken, outputting pretty much short high-pitched beeps and nothing else. I was curious to see if maxmod used any of the fancy audio capture/output modes that melonDS doesn't support (because no commercial game uses them, and my policy is to avoid implementing things until I have test cases). melonDS is set to report if any such features are being used, but in this case, it didn't report anything out of the ordinary. So this meant I'd have to dig further.

Quick regression testing showed that interpolated mode worked okay-ish on melonDS versions prior to 0.6. Well, it didn't sound as it should, but it was atleast reasonably close, instead of just being high-pitched beeps.

So apparently it was completely broken when sound FIFOs were implemented. The FIFO logic was fine, but the addition of that feature worsened the consequences of a bug that had always been there.

I logged what was going on during playback, to try and figure out where it failed. It appeared that the data being fed to the audio channels was fine, but, for whatever reason, the channels themselves failed to actually pull the data from memory. More logging revealed some strange things, like how certain things started at zero when they shouldn't. Notably, the channel timers started at zero, when they're supposed to start at the SOUNDxTMR reload value. But also, the FIFO level started at zero, causing it to immediately go negative and break the FIFO filling logic.

For a while, I scratched my head at all that, until it finally clicked.

In interpolated mode, maxmod will first disable the mixer, then sequentially initialize the channel registers, then enable the mixer, letting all channels start in perfect sync.

Thing is, at this point, for each channel, SOUNDxCNT is initialized with the start bit set, and before the other channel registers are set (because SOUNDxCNT comes first). This messed with the way melonDS initialized its channels, because it expected everything to be ready by the time the SOUNDxCNT start bit is set. But, obviously, here, it wasn't the case, and most things being zero fucked everything up.

So I quickly revised melonDS to only perform channel initialization as soon as the channel can actually run. And, hey, that did the trick -- maxmod's interpolated mode sounds fine now.

That being said, I still want to check for any tests that use the unimplemented capture/output modes.]]>
hi -- by Arisotura Thu, 25 Jun 2020 04:22:05 +0000