CONN BURANICZ
TECH ARTIST
GRAPHICS - SHADERS - CODING - TOOLS
I purchased David Murray's 6502 Shirt as a testament to my relative understanding of the CPU!
INTRODUCTION
This here is the story of Conntendo; my first foray into the world of retro game emulation. Five years ago, I wouldn't of believed I could accomplish this. Programming an NES Emulator has achieved one of my lifetime goals and has been vindication as to how far I've grown as a developer. That said, I don't deserve much credit. I chose an NES Emulator precisely because there is so much documentation online about it. Everyone and their mother has created one, and I couldn't have done this without the giants who walked before me.
This article I've written is a horrid hodge-podge of technical descriptions and anecdotal development stories. It's divided into Three Sections:
-
-
where I go over how I started and concluded the project
-
-
-
I give my personal rundown on the NES, and how to conquer emulation
-
-
Stories of Glitches and Solutions:
-
a series anecdotal issues I overcame during development
-
I hope that it leaves readers with a decent (if not shallow) understanding of the NES. Moreover, I hope my silly mistakes brings a chuckle, and perhaps inspiration to overcome programming obstacles!
INSPIRATION
In Summer 2017, after my Wolfenstein-inspired “RaycastEngine Project”, I was ready for a more ambitious retro undertaking. Two sources heavily inspired me to pursue creating an emulator. First, I loved Brian Provinciano's presentations on NES and DOS Development, and really wished I understanding of retro hardware like he does.
ONE: THE HISTORY
So many folk, including myself, really romanticize the 80's, 90s era of game development, where one man could realistically master all the ins and outs of a console or microcomputer. Afterall 8-Bit CPUs only had several thousand transistors compared to today's several billion!
My second inspiration was a book I received in December 2016 called “Making Games for the Atari 2600” by Steven Hugg. The entire book was such a wonderful little window into development during the late 70s and early 80s.
Reading about 6502 assembly instructions really fired me up. I really wish I took a second semester of Machine Language in college. Originally, my plan was to follow the book, and develop a “demake” for the Atari 2600 platform. Ultimately, I shifted gears, and decided on creating an emulator. I reasoned that an emulator would be much more educational and beneficial for my Portfolio.
EXILE IN CHICAGO
I actually started Conntendo back in August 2017, but didnt really sink my teeth into it until half a year later. In February 2018, I landed an amazing new position at Arkane Studios in Texas. My California lease expired that month, and I realized I had a rare oppurtunity of not being tied down to anything. Arkane graciously allowed me to start in April 2018. So, for all of March, I stayed with my family in Chicago, and got to enjoy spending time with my five brothers and old friends. It was brutally cold, but there's nothing like St Patricks Day in Chicago! While my friends and little brothers attended school and work, I devoted my time to my NES emulator project. I was forced to use my family's cheap Lenovo Desktop, but transformed it into my modest little workstation.
At first, the project was very slow-going. For days I toiled away at attempting to emulate the CPU and PPU (I explain the specifics more in Section Two ). I was wrting alot of code, but it didnt feel like I was making much progress because the screen remained blank. The nature of emulating consoles is that everything has to be functional before even seeing a single pixel on screen. Morale was luke-warm. But then one day....my emulator read my test ROM and displayed characters....
Photo of the first time my Emulator displayed NES Characters from a ROM File
Woah, I did it! It was just a bunch of scrambled nonsense...but my emulator was clearly displaying something from the ROM! At that point, Conntendo really became an addiction for me, and I couldnt stop. By the end of the month, Conntendo v1.0 was ready!
ACCIDENTALLY v2.0
I wrapped up developing Conntendo just before my time was up in Chicago. My project was more than just programming the emulator though; I needed to write (this) article, clean up my source code, and post them on my portfolio to truly wrap up the project. After moving to Texas, I focused my time on adapting to my new life, job, and environment. Through Summer 2018, I tried finding time for Conntendo. Again, my goal was to merely clean up the source code, and write up (this) document, but I was eventually compelled to do ALOT more work than I anticipated.
My emulator was obviously not perfect nor 100% compatible with the NES library. I was especially frustrated that key games were not working. I was content with BattleToads being unplayable ( infamously difficult NES game to emulate) However, I was really bothered by Conntendo not being able to run Dragon Warrior 1, 3, and 4. Those games are easily in my Top 10 favorite NES games. It was so strange how only DWII was working. So, my goal extended to geting at least Dragon Warrior 1 working. This ended up not being a trivial task at all. I ended up diving into a rabbit hole of aggressivly improving my emulator's accuracy. I slowly started improving my emulator on a general basis, hoping that one of my several fixes would be the solution that magically got DW1 working. (I go into more detail about fixing Dragon Warrior in Section 3 of my retrosepctive)
After several dozen more hours, I found the needle in the haystack, and Dragon Warrior 1 ran! Getting that game working emboldened me to no end...I was compelled to keep going! I implemented several more Mappers, including the legendary MMC5 for Castlevania 3. By the end of September 2018, I improved all aspects of my emulator. It wasnt just an update, I tunneled out with Conntendo v2.0!
SANITIZING FOR THE INTERNET
After pushing myself to reach over 90% compatibility of the American NES Library. My final step was to completely clean up my code, and have the entire Source Code ready to share on GitHub. I wanted my project's code to be super clean and readable. Other emulation projects are extremely optimized. For example, Bisqwit's 2011 realtime C++ NES Emulator had entire functions written using nothing but macros and tokens, its a site to behold, but extremely difficult to understand.
Other emulation projects are extremely optimized. For example, Bisqwit's 2011 realtime C++ NES Emulator had entire functions written using nothing but macros and tokens, its a site to behold, but extremely difficult to understand.
For my own sanity, I'd chosen readability over packing the code as tight as possible, especially the CPU instructions. I didn't want myself or GitHub viewers to get lost in spaghetti. Typically, my coding and scripting is always very organized, but I wanted to push it further. I reordered header files, renamed macros, and added a ton more comments. I meticulously indented, rewrote code logic, and anything else I could do to make my project easy to navigate.
Project Portability was also huge. My goal was that as long as all proper versions of C++, Visual Studio etc were installed, the project would compile from ANY location. It was a huge pain to restructure all the folders and sanitize all project Paths to be relative, especially in the Visual Studios Properties pages. I used several external libraries, and those paths needed to be cleaned too.
I was constantly rebuilding both DEBUG and RELEASE to make sure I didn't break functionality along the way. To make my life easier, I added Batch Test features. My emulator would automatically scrub through N number of ROMs and so I could easily ensure they were still fully functional. After the clean up, Conntendo's source code became academically presentable and portable across any modern Windows Machines. In October 2018, I posted the source code to GitHub.
TWO: INSIDES OF EMULATION
SPRITE ZERO HIT FLAG
The CPU controls the PPU logic through the use of eight registers. I want to discuss the PPUStatus register, how it ties to scrolling, and a major flaw Conntendo had at this early stage.
The PPU Status register has three useful flags;
-
VBlank- set after the 240th scanline, when its safe to overwrite PPU Memory
-
Sprite Overflow- set when over eight sprites are being drawn on one scanline
-
Sprite Zero Hit- set when the first sprite in the OAM is drawn
The Sprite Zero Hit Flag is often exploited by NES programmers to draw the HUD. Triggering the flag let the programmers know when the PPU has reached a particular part of the screen. NES games only have sprite and background tiles to work with. For most games, the “HUD” is just background tiles in the Nametable. The NES needs to know what part of the screen to remain static and what part to offset horizontally for scrolling. Quite ingeniously, a Sprite is hidden within the HUD. Once the CRT electron beam hits the sprite on that particular scanline, flag is set, and the code can update horizontal scrolling!
While the Sprite Zero Hit methods allowed programmers to split the screen, it does have huge limitations. For one, the flag can only be triggered once per frame. Second, it wouldnt work for splitting the screen vertically. Third, this method explains why earlier NES games could NOT have a Status Bar on the bottom of the screen for scrolling videogames. Sprites in the playfield would trigger the Flag WAY before the CRT reached the bottom. Later NES games include mappers that feature Scanline Interrupts to rectify these limitations!
NEVER BEING SET
Several days after getting Popeye working, Conntendo had many games running. However, games like Super Mario Bros and KungFu froze at the title screen. It turns out my emulator's Sprite Zero Flag was being cleared prematurely. I made a very embarrassing bitwise operation mistake. When attempting to clear the VBlank Flag, my line read;
// To clear VBlank on PPU-Staus Register
// I used "!" to create negation mask
status &= !(u8)PPU_STATUS::VBLANK;
// I should of used "~"
status &= ~(u8)PPU_STATUS::VBLANK;
This meant I was clearing the entire PPU Status register, instead of just VBlank. Games like Super Mario Bros froze because they couldn't exit from a Loop that was checking for the Sprite Zero Flag.
After resolving the issue above, the HUDs in several games would not stay in place....they scrolled along with the rest of the screen, wrapping around. Turns out, games like Castlevania were not using Sprite Zero Flag to separate the Status Screen from the gameplay. My emulator would have to resolve that issue later on.
The Status Bars for games like Kung Fu and Castlevania were scrolling with the rest of the playfield
Sprite Zero Hit Flag is just one of the many mistakes I made while writing my emulator. Section 3 is filled with more examples of ROMs not working, and how I overcame them!
COLOR PALETTE
The NES is an 8-Bit machine, but uses 2-Bits of palettized colors for each pixel. In theory, the NES has 64 colors, but alot end up as black, so people say there are really 55 colors. Background and Sprite tiles each have four Color Palettes to choose from. Programmers can assign any color to each of these palettes, but the 4th color in each palette is always the universal background color. Actually, Sprites use the universal background color as their Alpha, meaning tiles really only have three colors to work with. Many NES characters have the illusion of more than 3 colors because of overlapping tiles. Megaman is the best example since he uses 5 tiles, with the 5th being overlayed as his face!
Megaman is made up of 9 Sprites and two Sprite Palettes. The Face Tile is overlayed over the center of the 8 body Tiles
CACHING THE PALETTE
The “true” way to emulate the NES would be to emulate the actual NTSC video signal they generate and decode it in real time. However, I chose to do what many other emulators have done; create an array of predefined 32-bit color values, and use it as a color palette lookup table.
ARCADE RGB VS CONSOLE COMPOSITE
Pretty uniquely, NES Color is generated directly as an NTSC Composite signal, and not an RGB value. Therefore, there is no “true” RGB color values. Based on the TVs used, people growing up saw the NES in many color variations! The Play Choice-10 (the arcade version of the NES) actually outputs RGB color, so its very common for people to use that Palette in their emulators. Conntendo first used the Play-Choice 10 palette...but it always felt too saturated for me. Growing up, I remember the colors feeling much duller. So, I decided to investigate color palettes that would be closer to my memories. I ended up using FireBrandX's Composite Direct Palette. By default, my NES uses this palette. http://www.firebrandx.com/nespalette.html
Left is FireBrandX Composite, Right is Play Choice 10 RGB
COLOR PALETTE FROM ALL OVER
For fun, Conntendo also features color palettes that mimic other consoles such as the Game Boy and Commodore 64. The alternate color palettes either use 4 or 16 colors. On startup, Conntendo generates a new 64-sized Color array for each console. Every color in the RGB palette is switched out for a color in the other Console palette. So when using the GameBoy Palette, the PPU will still sample the same array element for “blue”, but pull out one of the 4 GameBoy greyscales. I used Batman as a litmus test since his palette are all similar colors. It took a while to tune the Color Picker so his three shades of blue would come out as 3 shades of GameBoy. Conntendo generates these new palettes every time the emulator is reset. I really should cache them into a config file.
Left is Composite NES, Middle is first-pass on Auto Generate, Right is Conntendo's current generated Gameboy Palette
APU
The NES Audio processing unit is embedded in the CPU chipset. The APU generates mono sound through five fixed Audio Channels;
-
Two Pulse Waves: Identical, with three available pulse widths
-
Triangle Wave: Often used for basslines
-
Noise: Psuedo-Random 1-Bit Noise Effects
-
DPCM: For playing samples
Unlike the Commodore 64, whose three audio channel can change waveforms on the fly, the NES channels are stuck like this. However, it's been enough for developers to compose a wide range of memorable tunes.
EMULATING THE APU
I implemented Sound Emulation after I already had MMC 1 working. There are two major portions of Sound Emulation for the NES; emulating the APU logic and actual sound synthesis from the ROM's sound data. So first, I implemented the APU programming logic. Writing to the APU was very similar to the PPU; the CPU writes to specific regions in the memory address space designated for the APU. My CPU was already reading/writing Audio Data from the ROM files, but until this point, the APU functions returned 0.
SHAY GREEN'S SOUND GENERATION LIBRARY
Reading/Writing audio data to the APU is simple enough, but actual waveform generation is a WHOLE other beast entirely. I consider it another project all onto itself. The scope of my project was to create an emulator, but researching waveform generation would take weeks onto itself. So, I decided to utilize Shay Green's popular NES Sound Generation Library. From NesDev forums, Shay Green's (Blargg's) Test ROMs have been so helpful to me already. Many other NES Emulation Projects already use his open source library. Incorporating Shay Green's audio library was very seamless. Coincidentially, it also used SDL. On start, Conntendo's APU instantiates three Blargg objects. When the CPU reads or writes to APU, it actually passes the values to the instantiated blarggAPU object. In addition, at the end of every CPU cycle, the APU passes elapsed time over to the Blargg objects.
EXTRA AUDIO FEATURES
Compared to my emualtor's options for tweaking visuals, my audio additions are a little more vanilla. My only options are adjusting volume and muting individual channels. As an aside, I sometimes like to set the Conntendo's Pallette to CGA, and mute all but the Noise Channel...pretend I'm playing DOS ports of NES games! Since I used a 3rd party library, the APU emulation was pretty easy-going overall.
INPUT
I actually didn't implement player input until after I started implementing MMC1. At the beginning, I had to rely on attract modes to see the NES ROMs running. The NES has two controller ports, with standard NES controllers housing 8 Buttons ( A, B, Select, Start, Up, Down, Left, Right ). This is quite convenient because the entire state of the controller can be stored in exactly one byte! The NES CPU reads and writes controller input to address 0x4016 and 0x4017 (These addresses are also used by the APU...) 0x4016 is used as a 1-Bit shift register strobe. When its set then cleared, the 8 button states need to be read one at a time. For the scope of this project, I chose not to emulate any other type of NES controller, or even emulating the NES Four Score accessory.
CUSTOMIZING CONTROLS
Customizing Button Input is very important for emulators. I personally find it annoying when other emulators require users to select multiple boxes to customize every button. Unlike all the other Conntendo SDL Windows, I used WinForm directly to create the Menu because I wanted a Button interface. I created my own “conncfg” file format to store and load the Input. The config file stores Keyboard Input for two controllers as well as two Joystick Inputs. All input is wrapped nicely in a struct called InputConfig.
The Input Menu is barebones, but efficient
At Start, Conntendo checks if a conncfg file exists in the Conntendo directory. If not, it will use the default inputs. In the Input Menu, when users press either a Keyboard or Joystick button, they're prompted to press their desired keys. Before a button is assigned, Conntendo makes sure the key is not currently in use! While the emulator is listening for Input, I also force the Option Window to always be on Top to ensure it receives input.
XBOX CONTROLLER SUPPORT
Controller support for emulators is a must. SDL supports both Xinput and DirectInput APIs. I used the Xbox One Controller as my target controller; I tweaked controller sensitivity, default layout etc to that controller. Unlike Keyboards, which are all buttons, the Xbox Controller has three input types; buttons, axis, and hats. I needed to store both the binded input and it's type so I could correctly check for its button state. Buttons are just like Keyboard keys, they are either down or up. Most modern controllers have analog sticks that need to be treated as D-Pads. It took a while to tune the Dead Zone and Threshold to feel just right. Too high, and it gets frustrating to line up, too low and its impossible for a NES character to walk right without ducking!
THE UI
The UI was very fun to do, and wholly my own design ( for better or for worse ). Actually, getting my emulator to simply have drop down menus was a lot more obtuse than I anticipated. I very strongly wanted a UI with window buttons and menus ( not just SDL text on screen ). Not being very experienced with developing GUIs ( outside of Maya, 3DSMax, etc ), I wanted to go with what I was already familiar with Using Visual Studio, I simply wanted to use WinForms. I figured that would be a path to least resistance. Eventually,I successfully got WinForms working with Conntendo. However, the integration made my project a lot more clunkier than I would of liked.
To start off, to my shock, Microsoft deprecated the WinForm library, and it wasn't officially available on their website. I was stubborn and did not want to try WPF. I eventually found WinForms through a 3rd Party extension. Another issue was that WinForms was a .NET Framework, and my project was being written in C++. So, I needed to configure my project to have C++/CLI support; a hybrid of C++ and C#. I was unfamiliar with all of this, my experience with C# was confined to XNA and Unity.
Lastly, the SDL API and the WinForm GUI were two distinct programs running. Getting them to work concurrently together was a problem. I came up with a clunky solution. I hid the border of Conntendo's SDL2 Window. I then embedded the SDL2 window into the WinForm window, with the latter taking full control of both window's scaling, exiting etc. All of the WinForms C# logic is confined to the Main.cpp file. When Menu items are pressed, Delegates call my emulator functions to create a savestate, change the audio etc. To this day though, my emulator suffers from not receiving input if the GUI is clicked...because its giving focus to another window. Oh well. In addition to the buttons on the Menu, I've added several Keyboard shortcuts so Players can easily adjust volume, load save state, and reset the current game.
ADDITIONAL WINDOWS
Conntendo has three additional windows that can be opened up via the UI;
-
The Nametable Viewer-for viewing the four Nametables. This Viewer does not display correctly for mappers with special cases such MMC5 and it's “third” nametable
-
The Pattern Table Viewer-for viewing the upper and lower tile tables, as well as the eight palettes.
-
Input Options-For customizing Player 1 and 2 Keyboard or Controller buttons
To save on performance, this Viewer windows only update every ten frames. The two Viewers are create new SDL windows when opened. Meanwhile, the Input Options is a new WinForm window because I needed to embed buttons to interact with the input options. Compared to other emulators, these Windows are very bare bones, but they do get the job done.
All Conntendo Windows are opened and displayed while playing Gauntlet. Notice that the Nametable Viewer has four unique Nametables instead of two. Gauntlet and Rad Racer 2 are two games that have extra Video RAM to allow this
MAPPERS
The Nintendo Entertainment System's Japanese counterpart, the Famicom, was released in 1983, and was very sophisticated for it's time. It's milestone accomplishment was it's ability to play ports of Nintendo Arcade hits; Donkey Kong and Donkey Kong Jr better than any home microcomputer or console at the time. The NES was widely released to the USA market in 1986 (a limited launch in 1985). By that time, Donkey Kong visuals were already old news. The base hardware capabilities of the NES are stuck in 1983..pretty much the technological pinnacle is Super Mario Bros. However, the hardware developers ingeniously allowed for hardware extension through the use of extra chips in the cartridges, known by the NESDev community as Mappers. It's absolutely incredible and a credit to the forward-thinking design of the hardware and it's extendiblilty.
Mappers allowed the NES to break through its 1983 hardware limitations and deliver vastly more complex videogames. The most basic extension Mappers provided was allowing Nametable Mirroring to change on the fly. Stock NES games were hardwired to scroll either horizontally or vertically. Most Mappers allowed the game logic to change this on the fly so that games like Super Mario Bros 2 could have both types of directional scrolling.
A second core function of Mappers was access to more memory. Out of the box, the NES CPU Address Space for ROMs can only access 40K ( 32K of PRG ROM, 8K for CHR ROM on the PPU). Mappers are capable of Bank Switching; where the Mapper changes which bank of memory the NES is reading from. This allows the CPU and PPU to read memory far beyond the 32K and 8K limitation. Kirby's Adventure for example reached 768K!
In short, Mappers gave the NES technology to go from Super Mario Bros (1985) to the vastly more rich and complex Super Mario Bros 3 (1988)
Comparing Mario 1 and Mario 3 is one of the best examples of demonstrating the power of Mappers. The Status Bar on the bottom is only possible because of the MMC3
The NESDev community recognizes over 200 Mappers. Most Emulator developers do not bother supporting every Mapper. A majority of NES games run on less than a dozen Mappers. The community suggests the most vital Mappers to emulate are; MMC1, UxROM, and MMC3. With those three, over 70% of the US NES catalog become compatible. For Conntendo, I decided to push beyond the common Mappers and emulate some more difficult ones as I was unintentionally developing version 2.0, leading to over 90% compatibility. The following section contains brief breakdowns of the compatible Mappers and my journey emulating them.
NROM (Mapper 0)
Stock NES hardware, technically not a mapper. Cartridges can only access up to 32 KB Program ROM and 8K CHR ROM. Mirroring is fixed to either Horizontal or Vertical.
Notable Games- Donkey Kong, Golf, Ice Climbers
UxROM (Mapper 2)
The NES Emulation community recommends to start with this mapper because its very straightforward and unlocks some iconic games. The mapper was a solid introduction to Bank Switching; It features two 16K PRG Banks, one Fixed and one Switchable for typically up to 128K of PRG ROM. Coding Mapper2 is more like Practice Mode, but gives access to the first entries of many beloved series. Mirroring is still fixed.
Notable Games- Contra, Castlevania, Megaman
MMC1 (Mapper 1)
A big step up! This Mapper features toggabable Mirror Modes; making games like Legend of Zelda's four-directional scrolling world map possible. Unlike with UxROM, both the PRG and CHR ROMs feature Bank Switching. Depending on the ROM, CHR can either be one 8K bank or two 4K by 4K banks. What was very difficult for me was that the Shift Register needed to be written to one bit at a time. This was pretty confusing at the start, especially because the Shift Register could end up writing to either the PRG Bank, CHR Banks, or the Control Register. Most MMC1 games went no larger than 256K. However, MMC1 games also came in “Large ROM” varierty. I did not account for this, which lead to a major headache that I describe in the Dragon Warrior section. Lastly, the MMC1 had the option of 8K PRG RAM. This also allowed for Battery-Backup Save Data!
Notable Games- Legend of Zelda, Dragon Warrior IV, Metroid
CNROM (Mapper 3)
A pretty simple Mapper used by many Third-Party developers in the early NES days. Remarkably, it has CHR Bank switching, but PRG ROM is fixed to 32 KB.
Notable Games- Gradius, Karate Kid, Mickey Mousecapade
MMC2 (Mapper 9)
The Mapper was only used for Punch Out and is the reason the game could have such large characters! I write more about my challenges with this mapper in Part 3 of this blog. Worth it!
Notable Games- Mike Tyson's Punch Out
AXROM (Mapper 7)
The company Rare on NES was crazy rogue and awesome. Their NES games pushed hardware limits; they simply did things differently than Japanese developers. Its surprising then that Rare's Mapper is so simple; a single register for bank switching. This Mapper's most famous trait though was it's Single-Screen Nametable Mirroring, which I talk about in Part 3 of this blog.
Notable Games- RC-ProAm, Battletoads, Nightmare on Elm Street
MMC3 (Mapper 4)
This Mapper is even more complex than MMC1, featuring six switchable CHR Banks! The major addition here is that the mapper hardware features IRQ; allowing the cartridge to send interrupt signals to the CPU. This allows games like Super Mario Bros 3 to have their Status Bar on the bottom of the screen! Using CHR RAM was very common on MMC3, allowing for more robust animation, scrolling, palette shifting etc. Most of the famous NES games after 1989 take advantage of this mapper.
Notable Games- Super Mario Bros 2, Kirby's Adventure, ShatterHand
MMC3 (Mapper 4)
Lo and behold, turns out the MMC4 is very similar to the MMC2. This was exclusively used on Nintendo's Famicom strategy games! All three games use background tiles for all Player/Enemy Units. Sprites are used for the Cursor, Character Portraits and during the combat screen.
In this GIF, all Sprites are Highlighted in Magenta. On the battlefield, all the Player/Enemy units are Background Tiles. During combat, notice how the characters switch from background to sprite tiles. Look how quickly the Pattern Tables switches thanks to power of MMC4
In September 2019, I met Norman Caruso who previewed his latest Gaming Historian episode about Mike Tyson's PunchOut. In the video, he mentions the MMC2 mapper. I tried to tell him about the similarities between the MMC2 and MMC4 so that he could help spread that knowledge. However, I guess he never bothered to address these two mappers at all. Oh well.
Notable Games- Famicom Wars, Fire Emblem
COLOR DREAMS (Mapper 11)
The infamous unlicensed Bible Games were created by Wisdom Tree, formerly Color Dreams. I added this mapper after MMC3, MMC2 etc, so it was a cakewalk. It has single PRG and CHR bank switching, fixed mirroring. These games came very late in the NES, but used mappers less powerful than UxROM.
Notable Games- Crystal Mines, Bible Adventures, Master Chu and the Drunkard Hu
GXROM (Mapper 66)
Turns out this is the Mapper that Color Dreams derived from. A very simple Mapper that I added because we were watching episodes of DragonBall during my exile in Chicago, and I wanted to get the NES game running.
Notable Games- Super Mario Bros / Duck Hunt Multi-Cart, DragonBall
VRC4 (Mapper 23)
This Famicom-only Konami Mapper is my great failure. Despite hours of attempt, I can't get it to fully work. The games run, but the CHR Banks are incorrect, leaving the games a garbled mess. The Mapper uses eight switchable 1 KB CHR Banks, and I can't get them to line up.
VRC4 games are playable, but the CHR Banks are incorrect
Conntendo still allows these games to be loaded because they technically sort of work. Man, I really wish I knew what happened with this one...
Notable Games- Super Mario Bros / Duck Hunt Multi-Cart, DragonBall
FME-7 (Mapper 69)
Sunsoft's advanced mapper was more powerful than the MMC3. I was demoralized that I wasn't able to get the VRC4 Mapper working, so I tried shifting to this one. To my shock, I got the entire thing working in less than three hours! The mapper features a “Command” register that programmers write one of 16 commands to. These commands order the bank switching, mirroring, and IRQ. The one US released game, has such large sprites, its a tour-de-force.
Notable Games- Batman Return of the Joker
MMC5 (Mapper 5)
The mapper is so powerful, it allows for Multiplication instructions, and four distinct types of Bank Switching Modes for both PRG and CHR ROM. The mapper also has 1 KB of extra RAM that can be used for 4 different purposes, including an extra Nametable. My scope was getting the US version of Castlevania 3 working, so I didnt bother emulating the three extra audio channels. The MMC5 also powers the largest licensed NES game; Metal Slader Glory at 1 MB (512 KB PRG, 512 KB CHR).
In September 2019, I succeeded with the FME-7 Mapper, and decided to wrap up Conntendo 2.0. I called my brothers to tell them I'm sending the new version of my emulator. My small brother Max specifically asked about Castlevania 3...the most powerful Mapper on the NES. I wasn't going to even dare trying to get that to work...but I went ahead and tried anyway. To my shock, I got Casltevania 3 working! At first, the CHR bank switching was incorrect (all the backgrounds were offset). I eventually resolved the issues for Castlevania 3, but some the MMC5 games are still broken (such as Uncharted Waters and Sim City). Still, I'm very happy with this accomplishment.
Notable Games- Castlevania III, Metal Slader Glory, Romance of the Three Kingdoms II
SAVESTATES
SaveStates are both the miracle and bane of playing with emulators. They take a “snapshot” of the game and allow users to resume playing EXACTLY at that point. They are super convenient for allowing people to resume their progress at anytime...but also super tempting to exploit and make any game a cakewalk.
I'm proud to say that I pulled Conntendo's SaveState system right out of my ass....that is to say, without researching anyone else's implementations or guidelines. Then again, it's a pretty obvious solution, so I can't give myself too much credit. SaveStates are “snapshots” of all the memory in the NES at that point. That sounds extreme, but for 8-bit consoles, all the CPU, PPU, ROM memory combined totals up to mere kilobytes of data. Writing and reading the save data to files feels instantaneous to the user.
Camera Photos of Legend of Zelda and Super Mario Bros loaded with incorrect SaveData. On the Left, Link's Tunic is Dark Gray, and he has the 2nd Quest Sword. On the right, a Zelda SaveState was used, corrupting Super Mario Bros
WRAPPING THE DATA
For Conntendo, I wrapped the SaveState data into a struct called 'SaveData' which contains matching variables for all registers, flags, counters etc in the CPU. In addition, the PPU and all Mappers have their own personal “SaveData” structs. When the SaveState menu button or hotkey is pressed, the GrabSaveData() function is called for the CPU, PPU, and current Mapper. A new instance of my struct is created; with all the memory passed through the parameters. In turn, when Load SaveState is called, the saved struct is fetched and all the CPU, PPU, and Mapper variables are overwritten with the data inside the struct; and there's your snapshot!
CONNSAV FILE FORMAT
Allocating a struct while the emulator is running means SaveStates will only exist until the emulator is closed. The next step was to put the SaveState data into actual files to load later on. I defined my own file extensions for every file type I used; connsav, conncfg, and connpic ( just a renamed png )!
I used <ostream> from the C++ Standard Library. Since all the save data was wrapped in my SaveData struct, I simply had to call the ofstream write function to write the binary into my passed-in file path. Loading the data was just as easy. I call the ofstream read function, memcopying all the binary information into an empty SaveData struct.
FIXED MAPPER SNAPSHOT
The CPU and PPU always have the same amount of data to save and load no matter which game is being played. However, the Mappers run the gamut; they might have extra RAM, registers, etc. Since the SaveState 'connsav' files are memcopying structs, the arrangement of memory must always be the same, otherwise the files won't work. I already ran into this problem as I was iterating on my SaveState system. I modified my emulator's 'SaveData' struct, making my older 'connsav' files incompatible.
To keep things simple; all Conntendo Mappers use the same struct, with enough data allocated for the most memory-intensive mapper (MMC5 in this case). Unfortunately this means that Mapper0 games like Popeye have over-bloated SaveState files. In the end, its only kilobits bloated of data. The Mapper struct has an all-purpose byte array (I future-proof allocated to 64 elements). Each Mapper cpp has an overloaded GrabSaveData() function. If a Mapper has extra registers, flags, etc, I store them in the all-purpose array. Then when LoadSaveData() is called, I need to make sure those same registers receive the data back from the matching all-purpose array element.
// MMC1 SaveData Functions. This Mapper uses 6 slots from the All-Purpose Array
MAPPER::SaveData Mapper1::GrabSaveData()
{
MAPPER::SaveData savedMapper = Mapper::GrabSaveData();
savedMapper.mapperData[0] = control;
savedMapper.mapperData[1] = prgBank;
savedMapper.mapperData[2] = chrBank0;
savedMapper.mapperData[3] = chrBank1;
savedMapper.mapperData[4] = shiftRegister;
savedMapper.mapperData[5] = writeCount;
return savedMapper;
} // GrabSaveData()
void Mapper1::LoadSaveData(MAPPER::SaveData loadedData)
{
Mapper::LoadSaveData(loadedData);
control = loadedData.mapperData[0];
prgBank = loadedData.mapperData[1];
chrBank0 = loadedData.mapperData[2];
chrBank1 = loadedData.mapperData[3];
shiftRegister = loadedData.mapperData[4];
writeCount = loadedData.mapperData[5];
SetBanks();
} // LoadSaveData()
ZLIB COMPRESSION
Brute force taking snapshots of entire NES game states amounts to only several dozen KBs of data. Why bother making the SaveState files smaller? My inspiration was Brian Provinciano's DOS Port Presentation where he liberally used zLib Compression to squeeze Retro City Rampage into a 1.4 MB Floppy Disk. For the first time in my life, I implemented zLib compression, shrinking the 'connsav' files from ~24 KB down to ~8KB on average.
REWIND FEATURE
Traditional SaveStates are so old hat now. With the speed of modern computers, and the miniscule size of retro games, its very common now to constantly capture frames to store entire buffers of SaveStates that the users can then scrub between. Xbox's Rare Replay has that feature (makes BattleToads a cakewalk).
On January 5th 2020, in a spur of the moment, I was inspired to quickly implement the Rewind Feature for Conntendo (even though I should have been working on this document instead). It's really just an extension of the SaveState system. The emulator auto-creates a new SaveState every N Frames. When Players hold down the reset button, the SaveStates then rapidly override the GameState, Last-In-First-Out. As a first pass, I brute forced it, the emulator created a new Connsav file for every frame saved. This actually took a hit on the CPU. My final implementation allocated an array of SaveData structs. The speed of rewind plus the maximum number of seconds to reverse were dependent on three things; the array's fixed size, how often a state was saved, and how quickly they cycled while rewinding. I tuned it so Conntendo saves a Rewind Frame 15 times a second, saving up to 150 frames: totaling ~10 seconds of rewind.
THREE: GLITCHES AND SOLUTIONS
Part 3 is my favorite section; a series of anecdotal stories about problems with my emulator and how I overcame them. I ran into many stumbling blocks while developing Conntendo. Many of these anecdotes are about getting specific ROMs and Mappers working. A bunch of these issues, are retrospectively very embarrassing...I made careless mistakes. The important thing though is that I did overcome them. I hope other emulation programmers might read this and relate to some of the wacky stuff that went on. I'd love to hear other folks' wacky stories too.
BATTLETOADS WOES
Battletoads was running, but garbage CHR tiles were everywhere. The game originally froze shortly after this cutscene
BATTLETOADS OPTIMIZATION
BattleToads is one of the most notorious NES games to properly emulate. Rare's Mapper 7 is simple enough, but accurate CPU and PPU timing is vital. Conntendo 1.0, was simply not accurate enough, and I needed to focus on improving emulation accuracy. I relied heavily on the NesDev Forum Test ROMs. After resolving the NameTable memory leak (mentioned below), Battletoads started looking better. However, Level 2: The Wookie Hole would have nondeterministic crashes. Luckily, I found that users on the NesDev forums back in 2015 were talking about that exact issue!
https://forums.nesdev.com/viewtopic.php?f=3&t=12470&start=15
MEMORY LEAKING ALL OVER THE PLACE
I worked a long time on getting Battletoads working. In Chicago, the graphics would be distorted and freeze during Level 1. However, in Texas, the game still had graphical glitches...but ran! Same code, different computer. I finally learned the problem when testing RC-ProAM 2 (another Mapper 7 game). Everything was fine on the title screen, but starting the first race...my emulator debug flags were enabled! Holy cow!! How is that even happening...the PPU Pixel Process function only reads from the debug boolean flags. This is what led me to learn that my Single-Screen Mirroring was flawed. Unlike a majority of NES games, Rare's Mapper 7 uses Single-Screen Nametable Mirroring Mode. In this mode, all nametable addresses point to one nametable. There are then two nametables that are switched at specific times while the frame is drawing. NesDev refers to these as the Lower and Upper Nametables. Unfortunately, I took the terminology literally, and thought their addresses were the Top Left and Bottom Left Nametables in the NesDev charts.
One-Screen Mirroring should address the red and blue nametables. Instead, I was attempting to address the red and yellow. When I was addressing the yellow nametable, it was addressing beyond 2 KB array limit
Conntendo's Nametable RAM array is allocated to exactly 2 KB. My mistake meant the “Upper” screen was accidentally reading/writing beyond the scope of the array. This meant that I was then manipulating data that the compiler allocated to other variables in my Conntendo builds. That explains why depending on the computer, Battletoads was behaving differently! So, the graphics were broken because they were reading garbage data! These things are so obvious in hindsight, but when I'm looking through hundreds lines of code, I make stupid mistakes like that.
BUSTED SAVESTATES
As I mentioned in the SaveState section, I created Conntendo's SaveState system based on gut feelings how it would work. My first implementation actually worked on many games, but other games would break when I attempted to load a SaveState. My main examples was the game Gradius, which became my litmus test for this problem. As long as Conntendo was open, running Gradius, Saving and Loading worked fine. However, when exiting and reopening Conntendo (or switching games), the Gradius' loaded-in SaveStates would restore the screen visuals...but the game would otherwise be frozen. It turns out I made wrong assumptions.
Gradius' SaveStates would restore the image, but the game would be immediately locked up
It was obvious that an emulator Savestate was simply a “snapshot” of allocated memory at that particular frame. At first, I surmised that not every piece of memory needed to be saved and restored. After much debugging, I learned that OAM memory was the problem. I skipped saving the OAM memory because I concluded that it would be repopulated as soon as the game ran the next frame. I was wrong in assuming it wouldn't affect the game's logic. So, from this point on, I my SaveState files backed up every variable, and all the NES games worked.
LOVE LETTER TO MIKE TYSON
Punch Out's visuals were a garbled mess. In the attract mode, either half off Mike Tyson's tiles were garbled, or the Text below
LATCH PROBLEMS LEAD TO GARBLED MESS
Towards the end of March 2018, I devoted an entire day to emulating the MMC2 mapper. It's essentially a fan letter to Punch Out, since it's the only game using that mapper specifically. Although later I learned that the MMC4 is a modified version of MMC2. I had alot trouble with the MMC2's CHR ROM bank switching logic. The MMC2 features two 4KB switchable CHR ROM banks that seamlessly switch during rendering. The purpose of this logic is to break tile limit from 256 to 512, allowing for Punch Out's signature large animated sprites! The switching CHR Banks use a latch system that toggle as the PPU reads from the CHR bank addresses. I had a hard time understanding the latch logic, and so, half the tiles were broken.
BROKEN BACKGROUND COLOR
Eventually I resolved the latch logic, and the proper the MMC2 CHR Banks were being read. There was still one problem though...the shared background color was incorrect! I thought it was related to the CHR bank switching. I spent hours debugging the mapper, and reading data that was sent to the PPU. Everytime I ran the emulator, the color changed. Sometimes, Little Mac and the background were magenta..other times cyan. This non-deterministic color swap was the clue...the emulator was reading garbage data! It turns out that certain NES games sample from outside the color palette's range. At that point, the PPU ends up just leaving the pixel black. To solve this, I added a conditional statement when the emulator is selecting a color from the palette. If color index is out of range, then return 0x3F (black). Little did I know, this out-of-range color palette issue plagued other games like Bubble Bobble.
IDE TROUBLES
Throughout the Conntendo project, Debug and Release Builds had different quirks. One example was back in March 2018, on Release Builds, I found that Double Dragon and Kid Icarus had bizarre behavior. I could navigate passed the title screen, start the level...and then immediately get a Game Over. I was baffled...they weren't crashing, they were Game Overs! It turns out, the C++ compiler was aggressively optimizing out vital logic to my code. In the Project Property Pages, I set the Optimization from “Optimize Speed (/O2)” to “Disabled (/OD).” This fixed the problem on both the games. I still don't know exactly why those games were behaving like that. It felt almost like an anti-piracy setting on those ROMs. For Conntendo 2.0, enabling Optimization on Release Builds breaks the emulator, so that it can't run any games at all! So I simply keep it disabled.
I had another issue specifically in Release Build. The SDL Window refused to be embedded into WinForms. StackOverflow forums came to my rescue; only one line needed to be added. SDL_SysWMinfo variable needed to be initialized with the current SDL Version data. Quirks like these were hair-pulling stumbling blocks, but I was able to overcome them thanks to forums more knowledgeable than I.
THE ROAD TO DRAGON WARRIOR
On the Left, WinMerge is comparing Output Instructions between Conntendo and a healthy emulator. Yellow indicates identical Output!
Towards the end of releasing Conntendo v1.0, I was able to run about 70% of the NES library. However, my emulator completely black-screened on 3 of the 4 NES Dragon Warrior tiles. This was devastating to me as Dragon Warrior is one of the most important videogame series to me. It was so strange to me; out of all the NES games, why couldn't Conntendo play those 3...and to taunt me, why did Dragon Warrior 2 work? Just like with BattleToads, I relied on the NesDev Test ROMs, periodically running the games in hopes that one of my changes fixed it...but it wasn't happening. So my debugging had to become more aggressive.
BATCH TESTING
I created Batch Testing functionality that would play 2 seconds of a every game in a folder. Running the Batch Test every time would ensure that my optimizations were not breaking compatibility. This greatly increased my productivity. Unfortunately, I still have to manually observe the Batch to ensure the visuals look correct. I have yet to think of a truly clever way for the emulator to verify the ROMs are running as expected. Maybe check the contents of OAM at a specific time interval?
COMPARING EMULATION OUTPUT
I already created Execution Dump Output when trying to get my CPU emulation off the ground. At this time though, I overhauled my functions and macros to make it easier for myself to use. Despite my optimizations, Dragon Warrior I was still not working. So, I decided to compare Conntendo with the healthy FCEUX emulator. I downloaded the source code for FCEUX, and modified it so it would output the instructions identically as my emulator does. I then used the software WinMerge to compare the differences in the output. This helped me illuminate where my emulator was wrong; the Cycle count was off, the A register had a different value etc.
I updated many portions of my CPU and PPU until I found the solution...all my other updates were not necessary. It turns out that the 6502 has a quirk with it's BRK Interrupt; it does a dummy read, which increments the Program Counter. For some reason, the Dragon Warrior game logic relies on this PC increment...this truly was a needle in the haystack, but I found it. I was so overjoyed...I recorded my emulator running DW. It was 2am, I spent over 10 hours on the problem, but I found it!
PC++ on BRK is the ONE reason Dragon Warrior wasnt working on Conntendo. It took me over 10 Hours of debugging to catch this
TOO LARGE FOR MMC1
I was so happy, Conntendo could finally run Dragon Warrior 1! With this success, I was determined to get Dragon Warrior 3 and 4 running as well. It turns out that both games had the same problem; their ROM files were too large, and my code did not account for this. Both Dragon Warrior III and IV use the MMC 1 Mapper. Until this task, I erroneously thought Dragon Warrior IV used MMC3. Anyway, most MMC1 ROM sizes are 1 or 2 Megabits. However, III and IV are both 4 Megabits. MMC1 cartridges that are larger than 256K are considered “large ROMs” and access that memory via shifting to Upper Character Banks.
I discovered this was the problem when looking at the source code of a healthy NES Emulator called “neveraway.” That Emulator had an assert if MMC1 ROMs were larger than 256K. This made me realize that the ROM Size was the key. I set breakpoints in the MMC1 mapper, and stepped through the output. Sure enough, the mapper was never accessing memory addresses beyond 2 Megabits. So, I revisited the MMC1 documentation on the NESDev wiki. I updated MMC1 so that it accounts for “Large ROMs” and both games worked!
Accounting for MMC1 "Large ROMs" fixed DW3, but my Upper CHR Bank switching was initially incorrect, causing the wrong CHR Banks to be read
At last, all four Dragon Warrior games worked on Conntendo. However, the Japanese Dragon Quest ROMs still have Audio issues. Sound doesn't work at all for the first two games. Maybe, I'll address those in the future, but I'm very content with the US versions working!
EXTRA FEATURES
Once Conntendo 1.0 was pretty stable and working, I had a tons of fun playing around with it and adding features. I already talked about many of the features, such as additional color palettes, SaveStates etc, but I'd like to talk about a few others.
SCANLINE EFFECT
Its been very popular for emulators and “retro-style” videogames to include a graphical mode that emulates the look of CRT Arcade monitors and home televisions from the 80s and 90s. This includes visible raster scanlines and blurry analog video signals. I wanted to at least include scanlines in my emulator too. The “scanline visual” is gaps between each row of pixels. I originally thought I might be able to get away with only drawing the odd frames of the NES game, but that looked terrible! What I really needed was creating lines on a “subpixel” level. However, my emulator's Frame Buffer was the same resolution as the NES (256x240), I didn't have “subpixel” information to work with. So, when “Scanline Mode” is enabled, my Frame Buffer is doubled in size (512x480), and each row of pixels is drawn twice. On the duplicated row, I multiply the color values with a Mask (0x3F3F3F3F) so those lines are darkened (but still have some detail). I'd very much like to revisit this feature in the future, I'm sure there's a much more efficient ways in SDL to pull this off.
DEBUG DISPLAYS
As I was trying to get the PPU working, I added some Debug flags for fun. I decided to keep some of them as Options in the Release Builds. Highlight Sprites replaces all Sprite Colors with Magenta. This is very educational because it highlights exactly what part of the screen is background and what are sprites (sometimes its very surprising). I also have a bizarre Debug Flag, with not much purpose, that disables the Sprite Offset, so that the Sprite Tiles do not offset as they move across the screen. In the future, I'd like to expose other Debug Flags that help illuminate how the NES works under the hood. For example, it would be cool to toggle per-pixel scrolling, make the game feel like a 80s PC game.
FUTURE IMPROVEMENTS
Obviously, Conntendo can still improve in many ways. I'm not sure if I'll revisit the project for the long-term, but I can definitely imagine having the inspiration to try and add another mapper, or other half day projects. Another example is I could cache my palettes into files, rather than having Conntendo create the arrays at startup every time. Little things like that.
RETROPALOOZA SUGGESTIONS
In September 2018, I went to Retropalooza ( and bought that awesome 6502 shirt from David Murray )
I had the chance to talk with several Retro Game YouTube personalities; Ryan Schott and Pat Contri. I asked them about what features they would love to see in NES Emulators. Virtually all their suggestions were related to networking; connecting with Twitch etc...all about streaming culture! Networking is a whole other beast, and I'm putting it out-of-scope for the Conntendo project. In the future, I'm definitely better off asking developers for advice and feature requests, instead of YouTubers.
SCRUBBING FEATURE
I'd love to upgrade my Rewind Feature so that Users are able to scrub the Rewind timeline back and forth to precisely tune the frame they want saved. Plus, it's real fun just scrubbing the game back and forth like that in real time ( seeing Megaman fall in and out of the hole etc)
FULLY-FEATURED DEBUG INTERFACE
The FCEUX emulator has very fleshed out Debug Features, and I'd love to add more to my Emulator. At the very least, I'd love to add a Viewer that displays the current Register, Flag, and memory values. I want features that could help me debug an NES videogame project and future mappers.
REVISITING MAPPERS
Lastly, I definitely need to redeem myself and get the VRC4 and RAMBO-1 mappers fully functional. I'd also love to revisit MMC5 so that the other games will work, especially the Sim City prototype.
CONCLUSION
There's nothing ground-breaking about Conntendo. In fact, browsing through GitHub alone reveals hundreds of NES Emulation projects in a cornucopia of coding languages and features. So, what did I accomplish with this project exactly? It's been a wonderful, personal experience for me. As an educational project, it challenged me to better understand system architecture and assembly. Especially helpful though, Conntendo gave me huge demands in debugging and sanitizing the project for portability. Programming skills like these are universal, and I truly feel I bring much more to the workplace because of Conntedo!
Apart from educational, Conntendo connected me closer that mythical gamdev past. I adore retro-gaming and unashamedly romanticize the simplicity of the development hardware back then. Reading stories about 8-Bit game development bring me so much joy:
https://games.greggman.com/game/programming_m_c__kids/
Of course, the NES development days were very challenging too; with limited documentation and communication. I guess more accurately though, Conntendo has connected me to the hombrew, emulation scene. My first exposure was in 2002 to Nester.
Nester was my first emulator. First time I played Ninja Gaiden with Nester on Windows 2000. I remember The scrolling for second level was broken
I was dumbfounded that such “magic” could be possible. Even when my career started in early 2014, emulation felt like such impossible feat, way beyond my programming abilities. Simply being able to create Conntendo gives me self-validation...even if by today's homebrew standards, NES is “trivial” and well-documented. I hope to continue being connected with retro gaming. Maybe one day, I'll actually break new ground in the retro gaming community!