Tixty4 Development - Week 1
By certain definitions of “starting a project”, my Tixty4 Emulator Project is now just a little over a week old. Considering it’s my first ever attempt of emulating a console that’s more complicated than an 8-bit brick, I’d consider the progress made in that time quite a success. While it can’t boot up any actual games yet, I might be able to get to that point in just another week if things keep going as smoothly as they are. (Which, let’s be honest, they probably won’t, but one can always hope)
The beautiful image you see just above is the logo I created for my emulator, trying to stay in the style of the original N64 icon. Everyone knows that fancy icons immediately make things look better than they actually are.
Now for some more technical stuff. I was initially struggling with getting the project on track - that’s why the oldest code files are actually from 2 weeks ago. Turns out that never having written a proper project in C makes getting started with one somewhat challenging, but once I had the ROM loading mechanism in place everything started to fall into place.
The first proper obstacle of the emulator was the MMU - the Memory Management Unit. This is a pretty important part of the whole emulator, being in charge of translating memory addresses, moving data back and forth, etc. A major issue I faced with it was Endianness. For those who don’t know: Endianness describes the order of bytes in memory. On Big Endian systems the highest byte comes first, on Little Endian systems the lowest comes first. While a ton of computers these days run on Little Endian, the Nintendo 64 was actually a Big Endian system, causing a lot of trouble with converting back-and-forth between the two variants. I actually ended up coming up with an idea of how to make both Endiannesses work on the same system, but the implementation of that idea was much more challenging than the system I wrote before, causing a lot of bugs and undefined behavior. Eventually I’ll re-write the MMU to use that system, but not right now.
With a basic MMU in place it was time to start working on the big boy - the Processor. After a ton of confusion about whether it was 32- or 64-bit-based (the result of which was that, while it is mostly 64-bit, a few parts aren’t), I had a basic Fetch - Decode - Execute loop started, with a simple command line interface yelling at me for encountering unimplemented instructions. That was to be expected, since I hadn’t actually implemented any instructions yet, but it also meant that the basic system was all working and ready to go.
Next came a bunch of instruction implementation. Loading values, storing values, adding values, you name it, were implemented one after another as I encountered them in the N64 boot code. Another thing that caused a bit of uncertainty was sign extension - almost every basic instruction works with only 32-bit values. However, since the CPU has 64-bit registers, all the 32-bit values had to be “extended” to 64-bit without changing their sign (plus or minus) - that process is called “sign extension”. Once that was all implemented (and I had wrapped my head around the strange workings of branching instructions - keyword “Delay Slot”), I was soon at a point where I was correctly calculating the CRC checksum of the ROM. A first success!
Next up - Graphics. One of the many parts I was afraid of was the use of external libraries in my project, but the time for a user interface had come and there was no way around it. Thankfully, an amazing website by LazyFoo Productions (which can be found here) helped me set up Visual Studio properly to get the whole thing running. It didn’t take very long to get an actual emulator window to open and, once I had a basic understanding of what the N64 framebuffer was and how it worked, I was actually displaying my first graphics, running Dillonb’s N64 Test ROMs!
After making it pass all of those, krom’s N64 Test ROMs were next up. While those did go for a few more edge cases (like a division by zero), making the basic CPU tests pass wasn’t much of an issue either, as seen in the next screenshot:
What I haven’t mentioned at this point is that I’m still far from having all CPU instructions implemented. I’m still taking the “implement as I encounter them” approach, just with an actual game ROM rather than the boot code or test ROMs. A suggestion, which the N64 Emulator Development community seems to agree on, is that Namco Museum 64 is one of the easiest games to get started - not only due to the fact that it doesn’t use a lot of features like the TLB (which I’ve heard is a nightmare), but also because it only uses the framebuffer for graphics, rather than any actual 3D.
Back to the topic on hand - the CPU. After a while I was hitting instructions that I was also already dreading - the FPU (aka Floating Point Unit). In essence, it’s a coprocessor to the main CPU that’s capable of doing calculations with decimal numbers rather than only whole integers. While that seems relatively simple, it really is not. For a long, long time the test ROMs I was trying to pass were looking like this:
It took me a total of 4 re-writes of the whole FPU system to finally make sense of what was going wrong in the first attempts. Long story short - a combination of overly complicated hardware behavior, issues with Endianness and own stupidity. And after all of those had been fixed, I was finally looking at a beautiful column of green
PASS labels. However, Namco Museum was still not booting, though that wasn’t much of a surprise to me either. It’s supposed to get stuck in an infinite loop waiting for an interrupt - which I haven’t even started implementing yet - so that’s exactly what it does. Maybe, if I keep making progress at the same pace as I have been up to now, I could be having a first title screen showing up within the next week. That would be pretty awesome.
And that’s a summary of the first week of my Nintendo 64 Emulator Development Journey. Thank you for taking the time to read through it all, and stay tuned for further status updates, coming (potentially) on a weekly basis! :)