Brainstorming a Modern Retro Console, Part 3
In which the author channels his inner mad scientist and lays out ideas for a modern, retro-built video game console, continuting with part 3: the cartridge system and BIOS.
By: TheHans255
4/19/2024
A few weeks ago, the work I've been doing with the MOS 6502 gave me some inspiration for a modern retro console design that uses the processor, much like the NES and other consoles of the 1980s. I thought: if I wanted to build something that would carry the same design ethos as those consoles, with retro processors and bespoke graphics and audio hardware, yet had the benefit of modern technology and wanted to build something that made more unique and capable games than those original consoles could, what would I build?
This post is the third of a four-part series. Part 1 and Part 2 were about the CPU and GPU respectively. This part will cover the cartridge interface, and Part 4 will cover the rest of the system.
(And again, a disclaimer: I have not built or tested any of this. All of this comes from my knowledge of the 6502 and other electrical engineering that I have picked up passively over the years, and while I believe that these designs would work to the best of my knowledge, I provide no warranty on any of these designs' effectiveness or their safety in mission critical systems. If I ever get around to physically building these designs, I will update this article with my results.)
The Game Media Interface
Back in the 80s, when most of these systems based on the 6502 were being made, there were several main choices for loading programs and games into external memory:
- Entering the program by hand through a keyboard
- Plugging in a cartridge, a PCB housed in plastic that would integrate itself directly with the hardware of the console below
- Loading the program from signals on a magnetic tape (either a dedicated tape drive or a standard audio cassette tape)
- Loading the program from a floppy disk
For most home game consoles of the day, cartridges were the obvious choice. RAM was too expensive to put much of it inside the console (the Atari 2600, for instance, had only 128 bytes of RAM available), so granting the processor inside instant access to a much larger game program on a ROM chip made things much easier. Cartridges could also contain other hardware, such as additional RAM, save batteries, and coprocessors, thus extending the console hardware's useful life.
Home computers, which were more expensive and could thus afford more RAM, had other options. Entering a program with a keyboard was good for hobbyists, but most people want to have their programs ready to go without having to think about them. This led to storing programs on tape, which was quickly superseded by storing programs on disk, since the computer could have full control of the disk and thus decide, on its own, which parts of the program to load.
Our modern retro console is closer to these 8-bit home computers in design, where not only do we have 48 KiB of centrally available memory, but also have 16 KiB of private memory per processor. Since this means that we can still store substantial parts of our program into RAM, we have no need to directly map to the game program and can instead use DMA or serial access to access the media where the game is stored - in other words, for our system, we give the media interface a turn in our Round Robin central memory rotation, and add memory control addresses that can direct it to either give data directly to processors or to insert it into central RAM.
This does not preclude us from using cartridges, though - indeed, the Nintendo 64, while still able to execute from the cartridge directly, would typically copy programs and data into RAM first for speed reasons. Cartridges (or any solid-state ROM access, really) are going to be much faster to access than any sort of spinning disk, be it a floppy disk, hard disk, or CD; and cartridges also just fit the general aesthetic of this system really well. Therefore, the design of our modern retro console will use ROM cartridges with data copied to RAM via DMA. The same interfaces would also be used to allow the console to write back to the cartridge, such as to access battery-backed SRAM or a Flash interface.
(Alternatively, for a more hobbyist-oriented approach, we can also have a design where some flash ROM is kept onboard the system, and the user flashes a project to it over a USB/serial connection like they would for an Arduino board. This design might be a good fit for development units, where game developers might want to quickly get their code onto the system without having to write to a cartridge).
The BIOS
Of course, there is one issue with this system: since we are not mapping the
cartridge ROM into memory, we are still going to need a separate BIOS ROM to
load at least some of the cartridge into memory and execute it. This is easy
enough - have a ROM chip that is mapped to the highest page of memory,
have that ROM chip issue a DMA from the start of the cartridge to, say,
address $4000
, and jump all the processors to that address once the DMA
finishes. We can then either leave in the ROM to provide access to built-in
subroutines, or map in the high RAM page in order to get full access to RAM.
Here, too, we are going to take the opportunity to explore something slightly insane. Since we're already going to have an on-board BIOS copy code to a cartridge and execute it, we can take this opportunity to ensure that the cartridge code is cryptographically signed, either by us, or by a (officially approved or otherwise) third-party manufacturer.
Cartridge Integrity: A Primer
Starting with the NES, video game consoles often took steps to ensure that they only played officially licensed games. The NES, for instance, required that all game cartridges contained a "CNC Lockout chip", which exchanged information with a matching chip in the host console to ensure it was genuine (and Nintendo would only sell these chips for games that they approved of). If the CNC Lockout chip in the console was not able to complete its proper handshake with the lockout chip in the cartridge, it would continually reset the console and prevent the game from progressing. (Youtuber MattKC has an interesting video about multiple companies' attempts to circumvent this chip)
The Game Boy, released a few years later, took a different approach that didn't rely on a hardware chip, and instead used a small software BIOS to verify that the game was officially licensed by Nintendo - it would read the first few bytes of the cartridge ROM and verify that the Nintendo logo was properly stored there - if the logo was corrupt or simply not there, the BIOS would halt and refuse to run the game. (The hope was that Nintendo could catch unlicensed games using trademark law, though a US court ruling against SEGA for a similar scheme suggests that this argument does not hold water - check out the Game Boy Pandocs for more information).
Of course, the primary reason Nintendo did both of these things was to enforce legal control on what games were released on their consoles, which, as a hacker well entrenched in the world of open source, I am not interested in - I would like hobbyists to be able to publish their own games to this thing without requiring the console manufacturer's approval.
However, these checks do some other things I am quite interested in: these checks prevent the console from running with no cartridge inserted or trying to run a cartridge with dirty contacts (since, after all, a handful of cubic centimeters of air in the shape of a licensed Nintendo game is most certainly not a licensed Nintendo game, and a licensed Nintendo game whose cartridge contact points are covered in Cheeto dust is not going to manifest as a licensed Nintendo game). They do also provide some quality assurance - when you run a game that has been officially licensed by the console manufacturer, you at least know that it abides by the manufacturer's standards and that it's something they're willing to sell.
And, of course, in the modern day, software integrity checks have gotten much better due to the development of stronger cryptographic algorithms such as the Elliptic Curve Digital Signature Algorithm. Most modern PCs, for instance, use a system called Secure Boot, which ensures that only genuine operating systems (and not, say, programs that have been hacked by an attacker to look like genuine operating systems but actually run Bitcoin miners in the background) are booted by your computer. (This is, of course, again done for legal reasons, but has strong security properties that I'm interested in and also makes sure that your system is in a good state at all before booting it).
Now, For Our Integrity Check Design
So, if our system has enough computing power for it, I would be interested in adding a cryptographic signature integrity check, in the vein of Secure Boot, to our modern retro console. We do, of course, have limitations imposed by the fact that we are only working with a handful of 14 MHz 8-bit processors, but also have less stringent requirements, since we are working with a retro console and not a mission-critical computer. In particular, each game run on the console is already designed to take full control of the system and does not have access to data stored for any other game or program - we just need to worry about quality assurance that the console is not going to damage hardware, and can thus sacrifice some security in favor of openness and/or speed.
- As outlined in part 1, each game run on the console assumes total control of the system - there is no separate kernel code (other than the BIOS itself) that depends on separation from user code, and there is only one batch of user code. This means that personal data from one game will not be accessible to other games.
- It is likely possible, though not yet clear will be necessarily true, to damage hardware via invalid inputs to the serial ports or through improper CPU instructions. Having some assurance that this will be protected against will be helpful.
- If possible, we also want to provide some assurance that games booted in this console meet our standards, but do not believe it behooves our business to absolutely require this (we're not reeling in the wake of the Video Game Crash of 1983, after all).
Therefore, our modern retro console will implement a cartridge verification system as follows:
- When the console boots, it will start running a BIOS program mapped to
$C000-$FFFF
. This BIOS program will use DMA to copy the first few bytes of the cartridge - the header - to address$4000
. This header includes a name, a developer name, an ECDSA signature for the entire cartridge, a cartridge length, a few bytes of code to get things rolling, and an FNV hash for the header. - One processor will calculate the FNV hash of the header and then compare it against the given hash - if it does not match, the console will display a message and lock up. (This provides preliminary protection against booting a missing or dirty cartridge).
- Once the header has been checked, the cartridge validity check proper can begin. The total size of the cartridge is read from the header, and one of the processors will turn off its access to central memory and begin using the memory-mapped IO interface to read bytes from the cartridge, calculating an SHA-256 hash (or perhaps a different hashing algorithm, if SHA-256 takes too long). At the same time, the other processors will work on displaying a boot logo.
- Once the SHA-256 hash is complete, the processor calculating the hash will rejoin
the others and use that hash to verify the digital signature. The console will keep
a library of several public keys (mapped to
$8000-BFFF
) that could match the private keys used for signing cartridges:- One of these keys would be kept exclusive to the console manufacturer.
- A few more of these keys would be given to third-party publishers.
- Crucially, one or more of these keys would be the "homebrew" key, whose private signing key would be publicly disclosed. This would allow homebrew developers to sign their own cartridges and thus run their own code on the system (although we would likely have the BIOS display a message saying that the game being played is not officially licensed by the console manufacturer).
- If the console identifies a match with any of the stored signing keys, it will
display an appropriate logo, then jump to address
$4000
to begin execution of code in the header (which will then be responsible for unmapping the BIOS). Otherwise, the console will display an error message and lock up.
If tests indicated that verifying the whole cartridge in this way took too long, and we couldn't improve the hardware speed to compensate, we could introduce abbreviated checks, such as using a faster hash function or only checking the first several KiB of the header. We could also use a tiered loading/verification approach, allowing one processor (with central memory access externally switched off) to start executing cartridge code once the first 16 KiB has been cryptographically verified, and freeing up the other processors and restoring central access once the rest of the cartridge is verified.
Some other ideas around key management:
- If I decide to place any additional protections around booting homebrew software, I could make the homebrew key only available if the user unscrews the console open and flips a switch on the motherboard - that is, if a user wants to run homebrew software, they would need to be comfortable enough with their system to open it, but would not necessarily need to make irreversible, cryptic motifications requiring extra parts.
- In addition, production and development units of the console would have different keys, so that if a development build of a game is leaked, it cannot be played on a consumer unit unless it were re-signed with the homebrew key (and so that the developer issued the key can be tracked).
- The publicly disclosed key treatment could also be given to a list of multiple keys, given for different conventional purposes. If a third party developer leaks one of the additional keys, future releases of the console could treat the key as a homebrew key.
- Of course, it might well be a good idea to allow users to install keys, or, if the console
has some sort of Internet connection, to allow keys to be downloaded and verified
automatically from a certificate authority.
This would avoid grouping "homebrew" software all into the same bucket
and instead allow players to verify individual independent authors. The system
would also allow compromised keys to be revoked or reclassified.
- One possible good way to do this would be to store the extra signing keys on a removable ROM chip in the console. That is, if the user wants to run games not published by official publishers, they need to open the console and replace a chip with one given by the unofficial publisher - something that a casual user is not going to do, but if a user does want to do it, it's just a matter of buying the chip for $10 USD and spending a few minutes with a common screwdriver.
That's it for Part 3. Join me next time for Part 4, where we round out the modern retro console concept with other components and ideas!