TheHans255.com

Brainstorming a Modern Retro Console, Part 3

Or: How I would make a game system open while still protecting it from shenanigans.

by: TheHans255

April 20, 2023

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:

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.

Therefore, our modern retro console will implement a cartridge verification system as follows:

  1. 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.
  2. 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).
  3. 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.
  4. 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).
  5. 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:


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!


Copyright © 2022-2024, TheHans255. This work is licensed under the CA BY 4.0 license - permission is granted to share and adapt this content for personal and commercial use as long as credit is given to the creator.