Jekyll2024-02-01T15:57:58-05:00https://hydr8gon.github.io/feed.xmlHydra’s LairEmulators and StuffCatching Up2022-01-16T17:25:00-05:002022-01-16T17:25:00-05:00https://hydr8gon.github.io/noods/2022/01/16/catching-up<p>Hey, it’s been a while. A whole year has passed since the last update to this blog, and a lot has happened since then. It’s hard to explain why I haven’t written anything in so long, but I’ll give it a shot. My brain has a tendency to overthink things, and sometimes I get completely stuck because of it. When I was writing that post last year, I had originally planned to publish it on January 1st. I think I even had a rough draft finished before then, but no matter how many times I read over it and made changes, I was never happy with it. Working on it began to feel like a chore, and it got to the point where I was considering scrapping it entirely. Eventually I did manage to get it done, and it was posted two weeks later than it was supposed to. I’m glad I finished it, but I haven’t really felt like writing anything since.</p>
<p>I’ve been having similar feelings towards my programming projects lately as well. I kept trying things and getting stuck, having ideas that didn’t work, and eventually I lost motivation to work on anything. I didn’t want my projects to die though, so I forced myself to keep doing it. Of course, this achieved nothing aside from making me feel even worse. I’m trying to find ways to improve on this, and one of those ways is to stop overthinking everything so much. So, that’s what I’m going to do. I’m going to write this post without stressing over every little detail. There’s a lot to talk about, and not all of it is NooDS-related; I started a few side projects this year, and some of them should probably get their own write-ups eventually. For now though, I’ll just stick to the classics and go over what’s new with NooDS.</p>
<p>Performance has improved significantly thanks to a variety of optimizations, not the least of which being a proper scheduler. All events used to be hardcoded, and things like timers had to be updated on every cycle to ensure they triggered at the right time. With a scheduler, the only thing that needs to be updated is the time until the next closest event. That alone gives a sizable boost, but other optimizations such as software fastmem helped greatly as well. Instead of going through a bunch of conditions and logic every time something accesses the memory map, a giant lookup table is generated and the memory at any address can be found almost instantly. There are some special cases that fall back to the old method, but the majority of memory accesses are sped up significantly. There have been plenty of smaller optimizations as well, but it would take too long to detail them all. To sum it up, let’s compare a build of NooDS from a year ago to a build from today. The old build nets me around 290 FPS on the DS menu’s health and safety screen. On the current build, I get 720 FPS.</p>
<p>There have also been some accuracy improvements over the past year. Timing is tricky on the DS, mainly because of its memory cache, but I did add basic CPU instruction timings. These are essentially best-case timings; they assume that memory accesses always take one cycle, like they would on a cache hit. I’d like to attempt some form of memory timing eventually, but for now this is better than nothing! Another important addition was proper CPU pipelining. This doesn’t mean much for most games, but the Classic NES games on GBA abuse the pipeline as a weird anti-piracy check. These games are pretty crazy; if you’re interested in more details on what they do, I recommend this <a href="https://mgba.io/2014/12/28/classic-nes/">mGBA article</a>. On the topic of GBA, it was brought to my attention that The Legend of Zelda: The Minish Cap was crashing before it got in-game. Turns out it relies on open bus shenanigans; essentially, it was reading from invalid memory which results in all sorts of weird behavior on hardware. There’s still work to be done here, but I did implement some basic open bus behavior which allows Minish Cap to work properly. These are only a few of the accuracy fixes that came in 2021, but there’s still a lot to cover, so let’s move on.</p>
<p><img src="/images/blog/2022-01-16/1.png" alt="Super Mario Bros. and Zelda: Minish Cap" /></p>
<p>When it comes to features, NooDS is still a bit barebones. However, I did add a few things that make life a tad easier. There’s finally basic save type detection, and it works surprisingly well for most games. The manual override still exists in case you need it, but saving should generally be a painless experience now. I want to make the emulator more accessible, and another step towards that is firmware generation. If the user doesn’t provide a firmware file, NooDS will automatically build a basic one that’s good enough for directly booting games. BIOS files are still required, but without the need for a firmware, open-source alternatives like the DraStic BIOS can be used. This essentially eliminates the requirement for a user to dump anything except their games. Or, if you’re looking to run homebrew, you might be interested in the addition of DLDI support. Provided an SD image, homebrew can now perform file I/O just like on a flashcart! Lastly, although it’s more of an enhancement than a feature, I did some work on improving the emulator’s frame pacing. Especially on my 120Hz monitor, old versions were pretty choppy. Now, it’s nice and smooth on any refresh rate that’s a multiple of 60Hz.</p>
<p><img src="/images/blog/2022-01-16/2.png" alt="GameYob running with DLDI" /></p>
<p>Guess what? There’s still more. We welcome a new platform to the NooDS family, as the PlayStation Vita got an official port! I picked up a Vita on impulse one day, and equally impulsely ported NooDS to it. It doesn’t run that great, but I figured I’d add it to the official builds anyway. Speaking of builds, these are now set up to automatically publish as a release on GitHub, which means you no longer need an account to download them. The macOS build got some awesome upgrades thanks to Nadia, and it now produces a proper app bundle that you can install to your system. The Switch build also got some love, and now features touch controls in docked mode using either gyroscope or a joystick.</p>
<p>I think I covered most of the important improvements that NooDS has seen since the last time I posted here. For the future, I’m interested in writing my own high-level BIOS implementation, and also experimenting with local multiplayer support. I had some success in getting Mario Kart DS to detect open multiplayer lobbies, so there’s definitely potential there. I mentioned before that I started some side projects as well; I won’t go into detail here, but I added them to the projects page of this site and I might write some blog posts about them in the future. I’m not going to commit to any regular updates here, but I’ll definitely write more if I feel like it. Honestly, I’ve still been catching myself overthinking things as I write this, but it only took 2 days to finish it this time, so I guess I’m making progress? Anyway, if you got this far, thanks as always for reading my silly things. Take care of yourself, everyone!</p>
<p><img src="/images/blog/2022-01-16/3.png" alt="Mario Kart DS multiplayer lobby" /></p>Hey, it’s been a while. A whole year has passed since the last update to this blog, and a lot has happened since then. It’s hard to explain why I haven’t written anything in so long, but I’ll give it a shot. My brain has a tendency to overthink things, and sometimes I get completely stuck because of it. When I was writing that post last year, I had originally planned to publish it on January 1st. I think I even had a rough draft finished before then, but no matter how many times I read over it and made changes, I was never happy with it. Working on it began to feel like a chore, and it got to the point where I was considering scrapping it entirely. Eventually I did manage to get it done, and it was posted two weeks later than it was supposed to. I’m glad I finished it, but I haven’t really felt like writing anything since.Living on the Edge2021-01-15T20:54:00-05:002021-01-15T20:54:00-05:00https://hydr8gon.github.io/noods/2021/01/15/living-on-edge<p>Happy New Year! Er, it’s a bit late for that, but considering my last update here was over four months ago… I suppose another couple weeks doesn’t hurt. If you read my previous post, you’ll know that I started an internship, and that’s what I’ve been doing for these past months. It’s certainly been keeping me busy, but when the holidays came around I finally found time to tackle one of the bigger things on my NooDS to-do list: 3D accuracy. I made a lot of progress and discovered some interesting things; it’s been a while, but let’s jump in!</p>
<p><img src="/images/blog/2021-01-15/1.png" alt="Black lines in Pokémon" /></p>
<p>My quest for 3D accuracy began with this. If you’ve been around DS emulation for a while, you may be familiar with these black lines in the fourth generation Pokémon games that plague inaccurate 3D renderers. A <a href="http://melonds.kuribo64.net/comments.php?id=56">post</a> on the melonDS blog sheds some light on the issue; to summarize the relevant bits, the DS precalculates polygon edges and applies special rules to these pixels. Certain edges are skipped during rendering if the pixels are opaque, but only if edge effects are disabled; if edge marking or anti-aliasing are enabled, edges are always rendered so the effects can properly be applied. In the case of Pokémon, both edge marking <em>and</em> anti-aliasing are enabled, so it should be drawing polygons in all their edged glory. Unfortunately, when I first designed my renderer, I didn’t have edge effects in mind. By not properly calculating edge pixels, not only were these effects off the table, but edges weren’t included in the polygon fill either! This resulted in polygons always being drawn at roughly the size of hidden-edge polygons on hardware, which, as you can guess, was leaving gaps in Pokémon’s geometry.</p>
<p>Because I wanted to do things right, I first needed to better understand how edges are calculated on hardware. I wrote a simple homebrew program that would allow me to freely manipulate the vertices of a quad, as well as toggle the various attributes that affected edges; I’ll link it <a href="/files/3dtst.zip">here</a> in case it’s of use to anyone else. The first step of edge calculation is vertex traversal; looking at the melonDS article again, we can see that when rendering a polygon, the DS starts at the topmost vertex and follows the edges on both sides until it finds the ones that intersect with the current scanline. Looking into it further on my own, I found that the hardware only considers an edge to be intersecting if one of its Y values is greater, but <em>not</em> equal to the current scanline. This raises a problem for perfectly horizontal edges; they’re never considered to intersect! Only being designed to render convex polygons, the DS gets around this by assuming that horizontal edges can only ever be at the very top or very bottom of a polygon. If the current scanline is at either of these positions, the entire span between the two intersecting edges is considered an edge as well. However, if you try to draw a concave polygon with a horizontal edge in the middle of its Y-span, that edge will be missing!</p>
<p><img src="/images/blog/2021-01-15/2.png" alt="Horizontal polygon edges" /></p>
<p>Now that I had vertex traversal down, it was time to nail the edges themselves. For a scanline-based renderer, edge calculation is a simple linear interpolation between the X coordinates of an edge’s vertices, using its Y coordinates and the current scanline. For Y-major edges (where the Y-span is greater than the X-span), it ends there, but for X-major edges, it’s possible for the edge to span multiple pixels on the same scanline. For this, a second interpolation is required to find where that pixel span ends; at least, that’s how I ended up doing it. With this I was able to draw wireframe polygons, but the edges weren’t quite right when compared to hardware. I spent a fair amount of time studying the edges and adjusting my renderer until it was mostly accurate, but there are still some minor imperfections with X-major edges so I won’t go into the details here. Instead of driving myself insane by staring at pixelated edges for the rest of my life, I decided that what I had was good enough for now and went on to investigate the curious phenomenon of edges becoming “dotted” when crossed.</p>
<p>To understand the dotted edges, we first need to look at how the DS determines polygon orientation. Vertices are normally stored in counter-clockwise order; a polygon is considered front-facing if its vertices are counter-clockwise after being projected, and back-facing otherwise. This winding order of vertices is calculated before clipping by taking the 3D cross product of the second and third vertices (using the first vertex as a base), and then “projecting” that by taking the dot product of it and the first vertex. This works great for triangles, but quads have a fourth vertex that can complicate things. If the vertices used in the calculation form concave edges, the resulting orientation will be the opposite of what it should be. It gets even weirder when a quad has crossed edges; since orientation is calculated per-polygon, one part of the quad will always wind the wrong way. During vertex traversal, the DS uses a polygon’s orientation to decide which direction from the starting vertex is left, and which is right. If these directions are swapped, edge calculation malfunctions and the span across X-major edges is reduced to only one pixel. After replicating this behaviour, I was pretty satisfied with the accuracy of my edges.</p>
<p><img src="/images/blog/2021-01-15/3.png" alt="Polygons with dotted edges" /></p>
<p>Finally, it was time to fill the polygons again. I was able to adapt my old fill code to the new edges without much trouble, and even implemented the special edge-hiding rules fairly easily. The renderer was back to a usable state, and the black lines in Pokémon were gone! I wasn’t satisfied yet, though; now that I had proper edge calculation, I could implement actual edge effects as well. I started with edge marking, which required reworking multi-threaded rendering to support a final pass that’s performed only once neighbouring scanlines are fully rendered; this was needed so that adjacent pixels can be checked when determining if a given edge pixel should be marked. I managed to come up with a solution that introduces minimal wait time, and it was pretty straightforward after that. Anti-aliasing was a bit more complicated; I’ll once again refer to a melonDS <a href="http://melonds.kuribo64.net/comments.php?id=32">article</a> for some explanation. In short, the DS keeps track of the pixels underneath edge pixels on a separate layer, and then blends these pixels together during the final pass using an alpha value based on the edge’s slope. It’s also worth noting that edge-marked pixels are special cases that always take on an alpha of 50%. Unlike with the edges, I didn’t spend much time perfecting the alpha calculations; I was mainly concerned with getting something that works, and I was happy with that for now. But I quickly noticed something that put a damper on my happiness; the black lines that I thought I had finally defeated had come back.</p>
<p>I knew my anti-aliasing wasn’t perfect, but surely it wasn’t <em>that</em> imperfect. I was stumped for a while, until something suddenly occurred to me. I did a quick hardware test and confirmed my suspicion; anti-aliasing doesn’t blend when the back pixel has an alpha value of 0 (put simply, the pixel doesn’t exist)! I knew there was more to it, though; Mario Kart DS has anti-aliasing on its character select screen even when there are no 3D pixels behind the characters. Further testing showed that when anti-aliasing has no back pixel to blend with, it instead simply sets the alpha value of the front pixel, preserving the effect by allowing it to be blended by the 2D engine. But if that’s the case, what prevents the black lines from showing up in Pokémon? The answer is simple; the game doesn’t have 2D blending enabled! While figuring this out, I also got a little sidetracked and worked out another somewhat interesting detail. When 3D blending is disabled, transparent polygons have their edges hidden as if they’re opaque; however, the alpha values are preserved, so if there’s nothing below them, you can still blend using the 2D engine and draw transparent hidden-edge polygons! In practice there isn’t much use for this, but it’s still a quirk of the renderer and there’s no harm in getting it right.</p>
<p><img src="/images/blog/2021-01-15/4.png" alt="Edge marking and anti-aliasing" /></p>
<p>Aside from the edge work, there have of course been various other improvements from last time such as bug fixes, optimizations, and UI additions. One of the more notable changes was the implementation of sound capture; this allows copying output from the audio mixers into memory, similar to what display capture does for graphics. Since the DS sound hardware is fairly limited when it comes to effects, this allows for any number of effects to be applied via software by manipulating the captured audio data and then playing it back. Games like Super Mario 64 DS and Mario Kart DS use this to create a “surround sound” effect, although the latter seems to suffer from minor crackles with my current implementation so I still need to look into that. It’s also probably worth mentioning that I finally added an icon! It’s just something I whipped up in a couple hours, so it’s nothing mind-blowing, but it works well enough.</p>
<p><img src="/images/blog/2021-01-15/5.png" alt="NooDS icon" /></p>
<p>To avoid making this drag on for any longer than it already has, I’ll wrap it up here. The edge work turned out to be more of an adventure than I expected, but it was something that I’ve wanted to take care of for a while, and I’m glad I finally did it. I think NooDS is finally at a point where I could genuinely recommend it as a way to play DS games on other platforms, and that’s pretty cool. I still enjoy working on it, even if I’ve been finding less time to do so lately, and I still have so many things I’d like to do! Now that this is done, the next big thing I’d like to tackle is finally getting some sort of timing system going. NooDS is probably the first emulator to come this far while still considering everything to only take one cycle! Well, that’s it for the great comeback post; see you next time, whenever that may be.</p>Happy New Year! Er, it’s a bit late for that, but considering my last update here was over four months ago… I suppose another couple weeks doesn’t hurt. If you read my previous post, you’ll know that I started an internship, and that’s what I’ve been doing for these past months. It’s certainly been keeping me busy, but when the holidays came around I finally found time to tackle one of the bigger things on my NooDS to-do list: 3D accuracy. I made a lot of progress and discovered some interesting things; it’s been a while, but let’s jump in!Looking to the Future2020-09-13T21:11:00-04:002020-09-13T21:11:00-04:00https://hydr8gon.github.io/noods/2020/09/13/looking-to-future<p>Welp.</p>
<p>Things have been a little crazy lately. I recently moved to a new city for an internship, and this past week was my first week of work. It hasn’t been too bad; aside from having to wake up in the morning, I’m actually kind of excited to continue with it! Unfortunately, having a job means that I’ll be pretty busy during weekdays, and I’m not sure how much coding I’ll feel like doing after I finish 8 hours of… coding. That’s not to say I want to stop working on NooDS, though; I’m sure I’ll still find time to work on it here and there. Obviously progress will be slower, but considering it’s pretty much a fully usable DS emulator at this point, I think I’m okay with that. Anyway, I do have a few things to show off this month, so let’s take a look!</p>
<p>I don’t have much in terms of core emulation improvements, but I did add proper VRAM mirroring, which is something that is undocumented in GBATEK and unimplemented in NO$GBA. Most games don’t care about this, since there’s not much reason to use a mirrored address over a base address, but a few games, such as <em>New Super Mario Bros.</em> and <em>Fire Emblem: Shadow Dragon</em>, won’t render certain graphics if it isn’t handled properly. I probably could have looked at melonDS’ code for this, but since I’m stubborn and didn’t want to “cheat”, I wrote hardware tests to determine the mirrors myself. VRAM mirroring is more complicated than regular memory mirroring, because the VRAM can be remapped; the mirroring is different depending on which block is mapped where. I crudely documented the mappings, which I’ve uploaded <a href="https://pastebin.com/w4qxnuJ1">here</a>. The formatting isn’t very user-friendly, but I just needed it as a quick reference. Based on my findings, I rewrote NooDS’ VRAM mapping to support mirroring, which actually ended up giving a slight speed boost as well.</p>
<p><img src="/images/blog/2020-09-13/1.png" alt="VRAM mirror comparison" /></p>
<p>After the VRAM shenanigans, I became interested in further improving performance. I used a few tricks, such as only redrawing 3D when it’s actually updated (which is beneficial for 30 FPS games), but most of the improvements rely on threading. The 3D renderer was already threaded, but I worked on improving its efficiency when splitting scanlines across the different threads. I also added an option for threaded 2D rendering, which is where the biggest improvements can be seen! Of course, I can’t just keep throwing everything on a thread; computers only have so many CPU cores to work with, and if you don’t have enough cores then these improvements won’t mean much. Still, with threaded 2D and 3D rendering, NooDS has finally caught up speed-wise to my port of melonDS on the Switch! <a href="https://github.com/RSDuck/melonDS">RSDuck’s port</a> is still faster since it has an ARM64 JIT, but NooDS can at least run some light DS games at full speed now. And if I write an ARM64 JIT of my own, NooDS might actually become a real competitor!</p>
<p>The UI hasn’t been getting much attention lately, so I decided to rectify that this month. On Android, I added screen layouts and a settings menu, bringing the Android port to pretty much feature parity with the Switch port. The desktop port saw some exciting new features: fast forward and full screen hotkeys, as well as controller mapping support! There are currently some issues with joystick events being triggered on Windows (because of course, cross-platform UIs can never work the same across platforms), but on Linux it works pretty well. With full screen and controller support, hooking my computer up to the TV results in a pretty nice experience! Another exciting feature, although not entirely UI-based, is the ability to load NDS and GBA ROMs simultaneously. If you have a ROM for one system loaded and attempt to load one for the other system, NooDS will ask if you want to load the ROMs alongside each other. This allows for some fun things, such as transferring Pokémon from the GBA to the DS! Speaking of that, I also finally fixed the Pokémon WiFi error, so your transfers won’t be interrupted by any annoying crashes or freezes.</p>
<p><img src="/images/blog/2020-09-13/2.png" alt="Transferring Pokémon" /></p>
<p>So yeah, that’s what’s new with NooDS this month. I’m pretty happy with how far I’ve come with this project, and I hope to take it even further, even if development will be slower for a while. There probably won’t be another progress post next month, but I’ll try to write one up whenever I think I have enough to write about. Feel free to hit me up in the comments or on Discord; even if I’m not working on the emulator, I’ll probably be around. And as always, thank you so much for being interested in my silly little projects!</p>Welp.The Great Bug Hunt2020-08-04T20:14:00-04:002020-08-04T20:14:00-04:00https://hydr8gon.github.io/noods/2020/08/04/great-bug-hunt<p>I feel like I say this a lot, but… my motivation has been all over the place lately, so I didn’t get as much done on NooDS this month as I would have liked. That being said, I do have a few interesting things that I think are worth writing about, and a fairly long story about a bug that’s eluded me for months. So, as always, here we go!</p>
<p>Before I get into the main focus of this post, I’d like to talk about a strange graphical priority issue in Golden Sun for the GBA. <a href="https://github.com/Hydr8gon/NooDS/issues/7">An issue report</a> brought this to my attention, and my initial look into it yielded some puzzling results. On one of the background layers, there were graphics meant to be displayed above the character sprites. That’s all well and good, but the same layer also contained a carpet that was meant to be displayed <em>below</em> the characters! Since each layer can only have a single priority value, this didn’t seem to make any sense. And yet, on actual hardware it worked! NooDS was rendering the carpet above the characters, which hurts my brain to look at. NO$GBA has the same issue, so GBATEK wasn’t much help here. I let the issue sit for a while and worked on other things, until fleroviux, creator of <a href="https://github.com/fleroviux/NanoboyAdvance">NanoboyAdvance</a>, chimed in with some incredibly helpful information. Basically, the GBA has a weird quirk where transparent objects can still update the priority of a pixel, even though they don’t update the color value. I had to rework my object rendering a bit for this (I previously had each object priority level as a separate layer, which wouldn’t allow changing the priority of an existing pixel), but I managed to get it working and even got a bit of a speed boost from the refactor! Everything looked good until I noticed new priority issues on the title screen of Mario & Luigi: Partners in Time. I was worried that I messed something up, but after looking at how that game renders its graphics, it only made sense for this issue to occur. So why doesn’t it on hardware? I had assumed that since the GBA and DS have near-identical 2D engines, the quirk must apply to both. However, I started doubting this, and after loading up Golden Sun in GBARunner2 to see if the game’s quirk exploitation would work in DS mode, I confirmed that it is, in fact, unique to the GBA. This isn’t a revolutionary discovery by any means, but I certainly found it interesting.</p>
<p><img src="/images/blog/2020-08-04/1.png" alt="Golden Sun priority issue" /></p>
<p>So, now let’s talk about that bug I mentioned in the beginning. If you’ve been reading my previous progress posts, you might know that Mario Kart DS and the DS Zelda games have managed to stay unbootable on NooDS for a long time. Mario Kart just ended up needing misaligned memory accesses handled properly, but the Zelda games continued to leave me stumped. They were getting stuck in a code loop, but they passed through that loop hundreds of times before actually getting stuck, making it pretty hard to debug. Eventually I enlisted the help of melonDS; I made it spit out debug information that I could directly compare to what I had NooDS spitting out. After a lot of searching and backtracking, I finally found a load instruction that was getting a different value on NooDS than it was on melonDS. I tracked writes to that memory address, and found that the value the game expects <em>is</em> put there by a cartridge DMA transfer, but it’s overwritten by another transfer before being accessed by the code. My first thought was that it must be a timing issue, so I threw a quick hack together to delay cartridge reads. This did manage to make the games boot, but I was a little disappointed because it meant that I’d need to set up that scheduler in order to implement a proper fix (which I’ve still been putting off, bleh). I was later talking about it with Arisotura, who mentioned that for a long time DeSmuME didn’t have cartridge timing either. I thought that was a little strange; if the issue really was timing, then the Zelda games shouldn’t have worked on DeSmuME either. PSISP also told me that he didn’t experience any timing issues with the Zelda games when he was working on CorgiDS. I started to doubt my conclusion, so I looked into it deeper.</p>
<p>I tracked down the exact moment when the overwriting DMA transfer occurred, and immediately knew something was wrong. For context, I’ll first describe how cartridge DMA transfers work. To receive data from the cartridge, a game must set parameters such as the size of the transfer, and then issue a command to start it. The cartridge will then begin sending data, which can be read one word at a time from a specific memory address. This data can be read in manually, but a DMA channel can be set up to make things easier. When the DMA is set to cartridge mode, it’s only triggered when data from the cartridge is ready to be read. The length of the DMA should be set to one word, the source address should be fixed to the cartridge read address, and the destination address should increment after every transfer. It should also be set to repeat so that it keeps transferring every time the cartridge event is triggered, which would continue until the cartridge has sent its previously set amount of data. However, even after the transfer has finished, the DMA channel remains active, waiting for another trigger. It’s therefore a good idea to disable the channel until it’s needed again. Now that that’s established, let’s take a look at what Zelda is doing. It transfers data from the cartridge to memory in 512-byte blocks, using the DMA like I described. Everything is fine up until the point where it disables the channel. For some reason, it first clears the mode and repeat bits, but leaves the channel enabled. It then disables the channel separately afterwards. This leaves the channel enabled for a short time while set to immediate mode with no repeat. During this time, NooDS would perform an immediate DMA transfer of one word based on the settings of the channel, and then automatically disable it. Now, there are a few problems with this. First of all, it’s this extra transfer that’s overwriting the data the game wants. Secondly, if this extra transfer was supposed to happen, why would the game bother explicitly clearing the enabled bit, when non-repeating transfers do that themselves upon completion? It was very clear that this transfer was not supposed to happen.</p>
<p>I asked Arisotura how melonDS handles DMA parameters being changed while active, and she said that it only updates a channel’s parameters when that channel is restarted. This makes sense, because it would mean that the channel would stay in cartridge mode and not transfer the extra word. Just to make sure, I wrote a quick hardware test to confirm. Surprisingly, I found that DMA parameters <em>can</em> actually be changed while the channel is running, and it does take effect. So then, how do the Zelda games work? I worked on my test some more, looking to see how various parameter changes affect the DMA while it’s running. I found that it mostly works the way I already had it implemented in NooDS; all parameters aside from source address, destination address, and word count can be changed and take effect while the channel is running. There was one exception though; one edge case that does not behave the way I expected. When changing a repeating DMA channel in any mode to immediate mode with no repeat, an immediate transfer does <em>not</em> occur. Instead, the channel stays enabled indefinitely, as if waiting for a trigger event that will never come. I’m not exactly sure why this happens, but it could be related to how immediate transfers are triggered. Perhaps the trigger is the channel being enabled, in which case it wouldn’t be triggered if switching to immediate mode while the channel is already enabled. Regardless, that’s how it works on hardware, and that’s why the Zelda games can do what they do (even if I still don’t really know why they do it).</p>
<p><img src="/images/blog/2020-08-04/2.png" alt="Zelda games booting" /></p>
<p>That was a pretty lengthy story for such a small bug; I feel like I just wrote one of endrift’s famous “Holy Grail” bug posts! I wouldn’t say it’s quite that significant, though. Most, if not all, games should work fine with melonDS’ implementation, because changing the DMA parameters while a channel is running is pretty dangerous and probably not very useful. Maybe other emulators are doing something similar to melonDS, or maybe this weird behavior is already known and I just missed the memo; I haven’t checked, so I’m not sure. Either way, the Zelda games now work on NooDS, so that’s all I care about! Aside from these bugs, there were also some smaller fixes and improvements, including a timer optimization that gave a pleasing speed boost. I’ll end this post with a somewhat big new addition that isn’t directly related to emulation improvements or anything like that. Instead, it’s a port to a new platform: Android! It’s nothing special yet, but it has a basic file browser and I whipped up some button images so it doesn’t look completely terrible. The biggest problem right now is that it’s, of course, slow. And also the fact that it’s on Android, which isn’t exactly my go-to platform for playing games. Well, I suppose I have even more reason now to look into writing an ARM64 JIT :)</p>
<p><img src="/images/blog/2020-08-04/3.png" alt="NooDS running on Android" /></p>I feel like I say this a lot, but… my motivation has been all over the place lately, so I didn’t get as much done on NooDS this month as I would have liked. That being said, I do have a few interesting things that I think are worth writing about, and a fairly long story about a bug that’s eluded me for months. So, as always, here we go!A Hint of Wi-Fi and a Sprinkle of Graphics2020-07-02T08:04:00-04:002020-07-02T08:04:00-04:00https://hydr8gon.github.io/noods/2020/07/02/wifi-sprinkle-graphics<p>Well, I’m not really sure how I can top last month’s GBA support, but I did manage to get some noteworthy things done this month! I still haven’t figured out how to write creative intros, so let’s just pretend I said something clever and get on with the improvements.</p>
<p>Something that’s been on the to-do list for a while is wireless communications. Even though the official Nintendo DS Wi-Fi servers have long been discontinued, plenty of games still offer local multiplayer support, and most things that use some form of wireless connectivity expect the hardware to be functioning. At the moment I’m not looking to emulate any sort of actual connection between systems, but it would be nice to at least have some basic functionality so that things don’t crash and burn. Previously, stuff like PictoChat and DS Download Play would freeze while trying unsuccessfully to initialize the wireless hardware. Games like Pokémon spit out an error screen if they can’t get things set up properly, which is especially annoying in the fourth generation games where it prevents you from selecting your save file! As I mentioned previously, I added a “hack” to stall the initialization long enough to get around this, but it’s far from ideal. So I set to work implementing some of the behavior that these initialization procedures expect.</p>
<p><img src="/images/blog/2020-07-02/1.png" alt="PictoChat and DS Download Play" /></p>
<p>Eventually, as you can see, I managed to get both PictoChat and DS Download Play to work! Well, by “work” I just mean that they don’t freeze. But hey, it’s a start! Unfortunately I wasn’t able to get the Pokémon error screen to go away just yet, but it gets much further into the initialization process and now gives you plenty of time to select your save file. The hack is still in place for the time being, but hopefully it won’t be needed for much longer.</p>
<p>Outside of the Wi-Fi shenanigans, I mainly worked on improving existing components. I finally got around to implementing the last few interpreter instructions, as well as fixing some issues with existing ones. NooDS now passes every test in ARMWrestler! I also focused a lot on graphical improvements, specifically to the 3D renderer. Here are a variety of screenshots taken with last month’s build of NooDS:</p>
<p><img src="/images/blog/2020-07-02/2.png" alt="Old screenshots" /></p>
<p>All of these screenshots exhibit some sort of graphical issue. Now, let’s look at the same set of screenshots from the latest build:</p>
<p><img src="/images/blog/2020-07-02/3.png" alt="New screenshots" /></p>
<p>Things are looking much better! I’ll go through the improvements one by one. In the top-left we have Mario Kart: Super Circuit. Its mode 7 effect was completely broken, because I wasn’t emulating the internal background registers properly. Basically, I was calculating the background offset from scratch for every scanline, but on actual hardware there are registers that are incremented after every scanline to keep track of the offset. If the registers are incremented by the same value each line then there’s no problem, but the mode 7 effect works by using different values each line. Understandably, this caused the effect to break horribly on NooDS. That’s pretty much the only fix on the 2D side of things; the rest is all 3D-related.</p>
<p>Speed round! Next we have the star select screen in Super Mario 64 DS. Missing texture transformations were implemented to fix the shiny effect, and W normalization was improved to fix the eyes. portalDS shows a good example of blocky interpolation, which has plagued my renderer from the start. The DS interpolates colors with 9-bit precision, which I knew, but derpy me kept converting back to 6-bit values before interpolating across the scanline. Pokémon Platinum on the bottom left is pretty straightforward; it’s showing off the new fog implementation. Pokémon Black 2 has an example of what I thought was a pretty clever way of handling transparency rendering. Each polygon can be assigned an ID, and any transparent pixel will not be rendered on top of a pixel with the same ID. I learned about this from Arisotura’s <a href="http://melonds.kuribo64.net/board/thread.php?id=13">GBATEK addendum</a>, which has a lot of useful information that GBATEK either lacks or got wrong. Finally, Final Fantasy IV shows another issue that was fixed thanks to the addendum. GBATEK’s description of the stencil buffer (used for shadow polygons) turned out to be entirely wrong; shadow polygons should now work properly for all games.</p>
<p>There are more improvements than what I covered here, but for the sake of time I’ll leave it at that. I’d like to mention that during my efforts to improve the 3D renderer, I started to consider edge marking and anti-aliasing implementations. I implemented wireframe polygons, and since I can reuse that code to detect polygon edges, I thought I was pretty much ready to go. Well, it turned out to be a bit more complicated than that. I was messing around with some tests I whipped up on my actual DS when I noticed that the edges used for wireframe, edge marking, and anti-aliasing can be one pixel larger than the normal polygon, depending on the edge slope. Arisotura has <a href="http://melonds.kuribo64.net/comments.php?id=56">a great write-up</a> on this phenomenon, and I want to get it right before I go any further with the effects that use it. Another plan for the future is to finally get a proper scheduler going, so I can set up some actual timing and maybe even get a speed boost if I play my cards right. I only mention this because I’ve already started the prep work for it, so it’s more likely that I’ll actually work on it instead of getting sidetracked with other things. Anyways, that’s enough of my rambling for now. Thanks a ton for making it to the end; I enjoy writing these, and if anyone enjoys reading them, that’s even better :)</p>Well, I’m not really sure how I can top last month’s GBA support, but I did manage to get some noteworthy things done this month! I still haven’t figured out how to write creative intros, so let’s just pretend I said something clever and get on with the improvements.A Blast from the Past2020-06-01T22:00:00-04:002020-06-01T22:00:00-04:00https://hydr8gon.github.io/noods/2020/06/01/blast-from-past<p>Hello again! I really don’t know how to write creative intros for these posts… Anyways, I’ve got some more fun things to share with you all this month, so let’s just get right into it.</p>
<p>Before the DS, there was the Game Boy Advance (GBA). The GBA was considerably less complex than the DS, but it shared a lot of the same hardware. Consequently, the DS came with a GBA backwards-compatability mode to allow playing the older console’s games without having to carry around two systems. When the DS enters GBA mode, most of its hardware is disabled, and the secondary processor, which happens to be the same ARM7 CPU that the GBA has, starts executing the GBA BIOS. The DS takes on a different memory map, and allows access to its built-in GBA sound hardware; aside from that, the rest of the GBA hardware (2D graphics, DMA, and timers) is more or less the same as on the DS.</p>
<p>So why am I talking about this? Well, as you may have guessed, NooDS now has support for GBA games! Now, I’m not the first one to add GBA support to a DS emulator. PSISP beat me to it with CorgiDS (although I don’t think it was ever finished), and there are of course NO$GBA and medusa (but they started as GBA emulators and later got DS support, so it’s not exactly the same). Regardless, it’s still pretty cool, if I do say so myself! You can boot GBA games directly or through the DS firmware, and optionally have the screen layout automatically crop out the unused screen and border. If you’d rather have a border, NooDS also displays the VRAM border around the GBA screen when in non-crop mode. Although nothing official actually uses this (to my knowledge), so you’d have to write a homebrew that copies your border into memory before entering GBA mode :P</p>
<p><img src="/images/blog/2020-06-01/1.png" alt="Various GBA games" /></p>
<p>Because of how GBA mode works on an actual DS, it was relatively simple to get it working on NooDS. I set up a new memory map, disabled the ARM9 and other unnecessary hardware, and that was more or less enough to get things going! I had to tweak the 2D GPU a bit, and the GBA audio and save protocols had to be implemented, but other than that everything is mostly reused from the DS code. After only a few days I had a fully functional GBA emulator on my hands, and it felt pretty good!</p>
<p>Implementing the GBA save types was relatively straightforward, and as a bonus the save type is actually detectable from the ROM, unlike with the DS. Audio, on the other hand, was a bit more tricky. I had no problem with the DMA channels and their nice, simple 8-bit PCM data, but the other channels were where the real trouble laid. These channels are essentially the same as what the original Game Boy has, and allow games to specify frequencies and other parameters for waveforms that are then generated by the hardware. There are additional features, such as frequency sweeping and volume envelopes, to make this even more fun. It reminded me of when I was emulating the NES’ audio, back when I had no idea what I was doing. Luckily I have a bit more understanding of how audio works now, so this didn’t turn out to be as much of a trainwreck as it did back then. In the end I got audio sounding close enough to the real thing, but it won’t be entirely accurate due to the analog nature of the hardware. Generating sample-perfect audio is something that might be fun to look into in the future, but for now I’m happy with what I have.</p>
<p>Aside from GBA support, a few other noteworthy things got done. Shadow polygons were properly implemented for the 3D renderer, and I finally got Mario Kart DS to boot! Still no luck with the Zelda games, though. I would have liked to get more done on the DS side, but my motivation was lacking, as usual. Part of the reason why I decided to work on GBA support was because the feedback of getting all those new games to boot is much more rewarding than the minor visual progress I would get from working on anything DS-related at this point. That’s not to say I don’t want to keep working on DS emulation, though; there are still a lot of things I’d like to do with this project, so stick around! I’ll leave you now with these last screenshots, becuase trying to speculate what I’ll work on next usually ends up being totally wrong. Bye for now!</p>
<p><img src="/images/blog/2020-06-01/2.png" alt="Mario Kart DS and shadows" /></p>Hello again! I really don’t know how to write creative intros for these posts… Anyways, I’ve got some more fun things to share with you all this month, so let’s just get right into it.Let There be Layouts2020-05-06T22:11:00-04:002020-05-06T22:11:00-04:00https://hydr8gon.github.io/noods/2020/05/06/let-there-layouts<p>Whew, looks like I made it in time for this month’s progress post! As I mentioned in the last one, I was busy with exams this past month. I was also very “busy” with Animal Crossing… but I did manage to get some work done on NooDS! Let’s take a look, shall we?</p>
<p>The biggest addition (as you may have guessed from the title) is screen layouts. Screen layouts are nothing new for DS emulators; in fact, some might even consider them essential. There are a few things that bugged me about the layout options in other emulators though, so I tried to design mine to be better. My personal favorite is the integer scaling option; I like the look of unfiltered pixels, but they just look so bad when they’re not scaled properly! The integer scaling option works with all of the other options, including enlarging one screen or the other. Speaking of, I worked a little magic there as well. After scaling the large screen to the maximum size, the small screen is scaled to fill the remaining space, as opposed to being locked to a 1:1 scale. I find this works really well when paired with integer scaling and the horizontal screen arrangement. Finally, there’s the screen gap. Instead of having fixed pixel values for the gap size, it scales with the screens to keep the ratio intact. In the case where the screens are different sizes, the gap will be based on an average of the two scales. And of course there’s rotation, although there wasn’t much I could revolutionize in that department. The possibilities are (not really) endless, but here are a few fun things you can try:</p>
<p><img src="/images/blog/2020-05-06/1.png" alt="Best layout" /></p>
<p><img src="/images/blog/2020-05-06/2.png" alt="Rotation" /></p>
<p><img src="/images/blog/2020-05-06/3.png" alt="Big gap" /></p>
<p>And, of course, all of these options are available on the Switch build as well. On the topic of the Switch, something interesting happened there. After making a seemingly innocent change to reduce memory usage in the GPU code, the Switch build magically got a significant speed boost! I can only assume that this has something to do with the CPU cache; the improved code must be able to fit within the cache, while the old code couldn’t. It may still not be able to run much more than Yoshi’s Island DS at full speed, but it’s starting to get speeds comparable to my port of melonDS on the Switch, which gives me hope that NooDS will be a worthy competitor once I write an ARM64 JIT for it.</p>
<p>Before I wrap this up, there is one last thing that I think is worth mentioning. The 3D renderer got some improvements to its interpolation and coordinate scaling, resulting in more accurate output. It’s by no means pixel perfect yet, but it’s a step in the right direction! The below comparison shows both of the improvements fairly well. On the left is before, and on the right is after. Most notably, the jaggy artifact on the road caused by the old interpolation formula is gone, and the player sprite is scaled correctly and is no longer missing any pixels. These improvements can be seen in most 3D games, to varying degrees.</p>
<p><img src="/images/blog/2020-05-06/4.png" alt="3D improvements" /></p>
<p>And that’s all I have to show for this month; now we’re at the part where I list the things I want to do but probably won’t have done before the next post. I did attempt a WiFi stub, but I don’t have it working enough to pass any initialization yet. In the meantime, I added a bit of a hack that stalls the initialization, giving you enough time to select your save in the gen 4 Pokémon games, if you’re quick enough. It’s nice to have those games easily accessible for testing now, but a proper stub is definitely still on the to-do list. Anything else mentioned in the last post that I haven’t got to yet is also still on the table. School’s out now, and my job status is uncertain, so I may find myself with more time to work on NooDS if I can keep my motivation up. I guess there’s nothing left but to wait and see where this month takes us. See you next time!</p>Whew, looks like I made it in time for this month’s progress post! As I mentioned in the last one, I was busy with exams this past month. I was also very “busy” with Animal Crossing… but I did manage to get some work done on NooDS! Let’s take a look, shall we?Back from the Dead2020-04-02T21:32:00-04:002020-04-02T21:32:00-04:00https://hydr8gon.github.io/noods/2020/04/02/back-from-dead<p>Welp, it looks like I missed a monthly post. Although technically I never promised monthly updates, it is something that I’ve been trying to do! Unfortunately, last month I really didn’t have many improvements to write about. After the last progress post, I wasn’t exactly sure where to go with the project; I know I mentioned bug fixing, but debugging is a pain and not something I exactly look forward to. Honestly, I was pretty satisfied with how far I had gotten with NooDS, and I found myself losing motivation to go further with it. Combine that with the real world issues I’ve been struggling with, and you get a stagnated project.</p>
<p>Suddenly, the whole world devolves into chaos. School has been closed and moved entirely online, assignments have been pushed back, and everyone is encouraged to stay indoors. I had also finally taken some steps to start dealing with my other problems, so I found myself with an unexpected amount of free time and a bit more motivation than usual. So, I decided to try working on my emulator again. I managed to get some exciting things done, so let’s take a look!</p>
<p>Arisotura, the creator of melonDS, pointed out that I had a small but significant mistake in my sound code that was causing some weird issues with the ADPCM audio format. When reading the header, which contains initial values used for decoding the audio, I was using the memory address instead of the value at the memory address. It was a simple fix, and it made things sound much better. At this point I really wanted to get synchronous audio working, since my asynchronous method was still causing wonkiness due to inaccurate timing. It took a few tries, but I was eventually able to get beautiful, crackle-free synchronous audio! In doing this, I removed the old FPS limiter in favor of basing the emulation speed entirely on audio playback. This should make the experience better on Windows, where I was originally using a wasteful while loop to control speed since timers were too inaccurate.</p>
<p>I also started taking a look at optimizing the emulator. 3D rendering in particular was annoyingly slow, which might be clear from the low FPS in most of my past 3D screenshots. There were some smaller optimizations to both the 2D and 3D renderers, but the greatest improvement came from multithreaded 3D rendering. Unlike the 2D renderer, which can change things every scanline (technically every pixel, but there’s no reliable way to time it, and current DS emulators render per-scanline for performance), the 3D renderer can only recieve new geometry data once per frame, at V-blank. This pretty much guarantees that the rendered scene won’t be affected by the timing of its drawing (it might be possible to mess with texture data; that would be interesting to test on hardware). With this in mind, it was easy to implement threaded rendering. At the start of rendering, I split the scene into chunks of scanlines and have each chunk simultaneously rendered on a different thread. It’s currently hardcoded to 4 threads, but I plan on making it configurable in the future. There are other ways I can improve my implementation as well, but for now it still gives a satisfying performance boost with little risk. It’s enabled by default, and makes most 3D games run full speed on my system!</p>
<p>Although I still haven’t managed to get Mario Kart and Zelda working, I did fix a few bugs. Mainly graphics-related, I fixed some blending issues and implemented the geometry engine’s box test command. The box test is something that I had admittedly forgotten about; it tests if a given box is at least partially visible in the current 3D viewport, which can be used to determine if objects in that area are worth passing to the rendering engine. This seems fairly important, but since any off-screen polygons are just clipped into non-existence anyway I figured it was mostly unnecessary. Of course, some games do use it, and those that do experienced some pretty significant issues with it not implemented. Since the result bit was never being set, these games thought that none of the areas they tested were visible, so they never bothered rendering stuff there. After trying the easy solution of always setting the result to positive, I discovered that this was the source of the missing character models in Okamiden, the missing buildings in the gen 4 Pokémon games, and the entire missing overworld in the gen 5 Pokémon games! Of course, I didn’t want to take the easy way out, so I properly implemented the test, reusing my clipping code to see if a box is visible.</p>
<p><img src="/images/blog/2020-04-02/1.png" alt="Games using box test" /></p>
<p>Something I mentioned last time that I did look into was anti-piracy measures. I thought this would require the massive undertaking of finally adding a proper timing system to NooDS (which I tried to do, but the performance sucked so I’ll have to try again), but the anti-piracy measures that I was running into were surprisingly easy to fix! The first one is used in Bowser’s Inside Story, which locks controls on the save select screen, and Pokémon Ranger Guardian Signs, which crashes when starting the game, among others. The first 32KB of a DS cartridge contains the “secure area”, which can only be accessed using special commands, and is loaded into the system memory by the BIOS/firmware. The command that games typically use to retrieve data from the cartridge doesn’t have access to this section, so attempted reads are redirected to a higher address. Presumably, flashcarts don’t have this protection, so games can check if the area is readable and trigger their anti-piracy if it is. I feel like a fool for not including this behavior back when I first implemented the cartridge protocol, but now that I have, these games pass the check and run as intended. The other anti-piracy measure that I dealt with is a rather well-known one which prevents exp. gain in the gen 5 Pokémon games. These games have special cartridges with IR ports, and since flashcarts don’t have this, they send a command to the IR and check for a response. It would be interesting to learn more about the inner workings of the IR, and maybe even properly emulate it someday (Pokéwalker, anyone?), but for now I’ll just send IR games the expected reply and call it a day.</p>
<p>That pretty much covers the interesting stuff I that did during my burst of motivation. If you look at the GitHub now, you’ll see that activity has dropped off again; even though school is online now, it is exam month and I’m right back to cramming assignments at the last minute and telling myself that I should study. When I find more time, I have some big things planned for NooDS. I’m going to add screen layouts, I’d like to try a proper timing system again, and I want to make some sort of debugging UI. It would also be nice to stub wireless communications to get things like PictoChat working, as well as preventing the crash when trying to load a save in the gen 4 Pokémon games. And eventually I want to write an ARM64 JIT so that NooDS can run well on the Switch. On top of all that, I might have another project in the works soon… It’s only a concept for now, but it could be interesting if it ends up happening. I hinted about what it might be in the patrons Discord channel, but there’s a bit more to it than that! Anyways, that’s all from me for now; I’ll see you (hopefully!) next month with some more news.</p>Welp, it looks like I missed a monthly post. Although technically I never promised monthly updates, it is something that I’ve been trying to do! Unfortunately, last month I really didn’t have many improvements to write about. After the last progress post, I wasn’t exactly sure where to go with the project; I know I mentioned bug fixing, but debugging is a pain and not something I exactly look forward to. Honestly, I was pretty satisfied with how far I had gotten with NooDS, and I found myself losing motivation to go further with it. Combine that with the real world issues I’ve been struggling with, and you get a stagnated project.Music to my Ears2020-02-06T02:20:00-05:002020-02-06T02:20:00-05:00https://hydr8gon.github.io/noods/2020/02/06/music-to-ears<p>Welcome back to your favourite monthly progress report! NooDS has seen a lot of fun improvements over the past month, so buckle up and I’ll take you through them.</p>
<p>First up, of course, we have audio output. Compared to my previous experience emulating the NES’s audio unit, the DS’s was surprisingly straightforward. The DS has sixteen audio channels, which is eleven more than the NES has, but most of these channels only support playing PCM8, PCM16, or ADPCM data. Some of the channels also support pulse waves or noise, similar to the NES, but with none of the fancy features such as sweeps or envelopes. The DS also has a sound capture functionality that allows copying audio data from the channels into memory. This can be used for software manipulation of the output data, similar to the display capture functionality. The audio unit runs at half the speed of the ARM7 processor, so roughly 16.8MHz. It outputs an audio sample every 512 cycles, so dividing the previous frequency by 512 gives us a sample rate of 32768Hz.</p>
<p>NooDS doesn’t emulate the entirety of what the DS has to offer just yet; it only outputs PCM data, and sound capture isn’t implemented. However, what it has is enough for a large portion of the audio found in DS games to work. During my testing, I found that a lot of games rely almost entirely on ADPCM data. While the PCM8 and PCM16 formats are uncompressed and can more or less be written directly to the audio buffer (after volume and other adjustments are applied), the ADPCM format is compressed and must be, well, decompressed. Of course, GBATEK supplies a handy algorithm to convert ADPCM data to PCM16 data, so while it was certainly more complicated than PCM8 and PCM16, ADPCM support didn’t give me too much trouble.</p>
<p>Aside from the missing functionality, another issue with the current audio implementation is the timing. The way I have it implemented right now is what I suppose you would call asynchronous; the sound unit is running entirely separate from the rest of the emulator, generating samples for the audio buffer as they’re needed. This works well enough, and it even sounds good when running at slower speeds becuase the samples will continue to be generated at the correct speed. However, when running at full speed, you might start to notice some issues. DS games tend to use the built-in CPU timers to decide when a sound should start playing; if the audio isn’t synchronized to the CPU, the current sample could be ahead or behind of what it should be, causing sounds to potentially start playing at slightly incorrect times. I plan on eventually switching to a synchronous solution for accuracy’s sake, but as it is now you should still be able to jam out to your favourite DS soundtracks (with only minor inaccuracies!).</p>
<p>After audio, I got a lot of graphics-related work done as well. The 2D engine got support for blending, allowing for transparency effects with the 2D objects and background layers. The 3D engine got alpha blending, allowing for transparency, as well as culling, allowing for polygons facing away from (or towards, depending on the setting) the screen to be skipped during rendering. The latter can be used for some interesting effects, such as the character outlines in Okamiden.</p>
<p>In non-blending-related news, a few features and fixes were added to allow for proper dual-screen 3D support. The DS can only output 3D to one screen at a time, so some tricks are employed to create the illusion of dual-screen 3D. Each frame, the 3D output alternates between the top and bottom screens. Display capture is used to save a bitmap of the previous frame, which is then displayed on the non-3D screen to make it look like the 3D output is still there. To display the bitmap, one screen uses an extended affine background layer, and the other uses bitmap objects. I haven’t looked into the specifics, but the reason why both screens use different methods is probably because of VRAM constraints. I had to implement display capture and bitmap objects for this, as well as fix a timing issue that was causing the screens to be swapped. With all of this, NooDS is coming very close to being feature-complete in the graphics department!</p>
<p><img src="/images/blog/2020-02-06/1.png" alt="Various games rendering better" /></p>
<p>As I briefly mentioned in the previous post, I’ve been thinking about doing some sort of release. To that end, I also decided to work on some much-needed UI features. I added a settings system, including toggles for direct boot and the FPS limiter, customizable BIOS and firmware file paths, and control remapping. I also added a dialog that pops up when booting a game that has no save file; it asks the user to specify the save type, allowing for a save file to be created and saving to be possible. Previously, games without save files would just not save, because there was no way to detect which save type the game uses. Of course, if the user selects the wrong save type, saving will still be broken. I mentioned this back when I first added save support, but since the save type isn’t stored anywhere in a cartridge dump, the options for determining save type automatically are to either use some sort of database, or write some hacky detection code. Since neither method is likely to be completely accurate, this dialog will probably stick around as a manual override in the future.</p>
<p>So, what about that release? Well, the more I think about it, the more unsure I become. It’s not uncommon for emulators these days to have very outdated releases, while the development builds are months, or even years, ahead. NooDS already has automatic development builds set up, so it feels like a release would merely be a formality. I can’t see myself ever recommending that someone use a release build over a development build, so I might just adopt the rolling release ideology for this project. Or maybe I’ll do a v1.0 release once NooDS is finally ready to compete with the likes of melonDS and DeSmuME. Either way, doing a release right now just doesn’t seem like a worthwhile idea.</p>
<p>That being said, NooDS is coming along very nicely, and I’m really excited to see where it goes from here! I think I’ll start focusing on bug fixes next; Mario Kart DS doesn’t boot, and neither do the Legend of Zelda games. I’d love to test the 3D renderer out with these titles, so getting them working is high on the to-do list. It would also be interesting to start working on being accurate enough to avoid triggering the anti-piracy measures that I’ve encountered in a number of games. Well, see you next time, and thanks as always for your continued support!</p>Welcome back to your favourite monthly progress report! NooDS has seen a lot of fun improvements over the past month, so buckle up and I’ll take you through them.Emerging from the 3D Maze2020-01-10T19:32:00-05:002020-01-10T19:32:00-05:00https://hydr8gon.github.io/noods/2020/01/10/emerging-3d-maze<p>Last month I said that I would probably be showing off a mostly usable 3D renderer in my next post. Well, that’s true! But there were certainly some twists and turns along the way.</p>
<p>Let’s start from where we left off, shall we? After getting polygon fills working, most games were looking pretty boring without any lighting or textures; I decided to start with the latter. It turned out to be pretty straightforward! …Or so I thought. My initial implementation seemed to work well enough for simple things, such as games that use the 3D engine to render 2D elements. However, more complex 3D scenes had some nasty texture warping. I discovered that I had overlooked something extremely important: perspective correction. When you look at something in 3D, the parts that are farther away appear smaller, right? Well, the same idea applies to texture mapping; if a polygon extends away from the screen, the texture should appear larger up front and gradually get smaller as it goes back. My original, naïve interpolation attempt was a simple linear interpolation, meaning that effect was lost. Here’s an example of what this looked like, using the portalDS homebrew:</p>
<p><img src="/images/blog/2020-01-10/1.png" alt="Warped portalDS" /></p>
<p>The squares at the back of that tile are way longer than the ones at the front, right? Well, they’re actually the same! But it looks wrong becuase of the perspective. This might give you a better idea:</p>
<p><img src="/images/blog/2020-01-10/2.png" alt="Pixel measurements" /></p>
<p>The solution to this problem is a modified interpolation formula that incorporates the W values of vertices. The W values are essentially depth values; when a vertex goes through a projection matrix, the original X, Y, and Z coordinates are multiplied with values of the projection matrix to create the W value. Depending on the projection values, you can create different kinds of perspective effects, but generally you want vertices that are farther away to have higher W values, and vertices that are closer to have lower W values. Since the W values were already being calculated by my geometry code, all I had to do was adjust the formula in the renderer, and voilà! That confusing perspective problem was no more.</p>
<p>The other major area of issue for me was clipping. Remember the exploding vertices that I mentioned in the last post? Those were actually the result of vertices outside of the view area being projected onto the screen. When out-of-bounds vertices go through a projection matrix, chaos ensues. 3D renderers commonly use clipping to avoid this issue; polygons that are completely outside of the view area are removed, and polygons that are partially visible are reconstructed with new vertices located at the intersection points between the old polygon edges and the edges of the view area. This results in a 3D scene where all rendered polygons are completely within the view area, so they can all be properly projected on the 2D viewing plane. The DS, of course, has clipping too. However, I, not knowing anything about 3D, incorrectly assumed that clipping wasn’t important, and that I could get away with simply not drawing any sections of the polygons that were out of bounds.</p>
<p>Well, I was clearly wrong. After a while of messing around with my code, trying to figure out the source of my vertex woes, I slowly came to realize the true importance of clipping. I dreaded having to implement it, but I found <a href="https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm">the Sutherland-Hodgman algorithm</a> and managed to write a fairly competent clipping system based on it. With that out of the way, I was finally able to see all the beautiful geometry of those previously broken games!</p>
<p><img src="/images/blog/2020-01-10/3.png" alt="Lightless rendering" /></p>
<p>At this point, the renderer was starting to look almost competent. The last big feature it needed was lighting. GBATEK graciously provides the formulas the DS uses to calculate lighting, so there wasn’t much that I needed to figure out on my own for this one. Basically, lighting works by assigning a normal vector to each vertex; this vector indicates the direction that the vertex is facing. The normal vector is then multiplied with a light vector to determine the intensity of the light hitting that particular point. With the help of those fancy formulas, a color is calulated for each vertex, which is then interpolated across the polygon surface and blended with a texture during rendering. It was easy enough, although I struggled for a stupid amount of time trying to figure out why my math wasn’t working, until I finally figured out that I was supposed to shift the result right to “normalize” it after multiplying two colors together. GBATEK, you’re great and all, but some clarification on that point would have been nice!</p>
<p><img src="/images/blog/2020-01-10/4.png" alt="Various games rendering good" /></p>
<p>So now we’ve reached the present, and the NooDS renderer is looking pretty good! There are of course still accuracy issues to iron out, as well as the remaining missing features such as alpha blending and shadow polygons. Oh, and it’s in dire need of optimization. But aside from that, most 3D games should be pretty playable! So, what’s next? Even though there are still things left to tackle in the 3D department, I think I might move on to audio emulation soon. 3D is cool and all, and I’ve learned a lot, but it’s been all about 3D for a while now and I think it’s time to change it up a bit. Looking further into the future, I’ll probably make some sort of settings system for the UI, including must-haves like control remapping and screen layouts. After that, I think it would be cool to do some sort of formal release! I’ll figure out the specifics on that later, though. For now, I’m just glad I was able to create a fairly competent 3D renderer! Well, see you all next time for the audio edition of these posts! (Although, how am I going to screenshot audio…?)</p>Last month I said that I would probably be showing off a mostly usable 3D renderer in my next post. Well, that’s true! But there were certainly some twists and turns along the way.