Alright, so we’ve got the main parts for our board picked out based on the specs we want to meet. How do we hook it all up? Let’s start with the ROM.
This is easier than you think! There are 21 address lines on the 27C322, A0-A20. So, intuitively, let’s hook them up in order. Connect A0 to A0, etc, and for A16 on the EPROM, hook up BA0 and continue like that. Now, let’s see how to connect the data lines. This is where we hit a snag- remember how the 27C322 only has a 16 bit data bus? How are we going to fit all of the information we need on the SNES when the EPROM data has twice the density? There are a few tricks we could try. Let’s try and think about how the data is stored in the EPROM and come up with a good solution.
So, the SNES has an 8 bit data bus. This means that each memory location holds a byte of information, usually stored as two hex words. So, let’s try and make up a scenario to get a better grip on what’s happening (all this data is made up):
8 bits | EPROM least significant bit (A0) -> | 0 | 1 |
Upper hex digits (A4-A15) | (Last hex digit) , bits | ||
0x000 | (0/1) , 000* | FF | 8E |
0x000 | (2/3) , 001* | 78 | 4E |
0x000 | (4/5) , 010* | 34 | 6F |
— | — | — | — |
0x000 | (E/F) , 111* | 4E | 3D |
0x001 | (0/1), 000* | 7E | DF |
The left column is the 16 bit address bus, with the last hex digit expanded as binary. The last binary digit is * to indicate that it is changing in that row. Do you remember how the address bus worked from the previous post? Each address contains one byte (for an 8 bit data bus). So, when we want to get information out, we point to where we want the data. Suppose in the above case that we want the byte that’s stored in 0x0000. The answer is FF! So, how can we access the other bytes? Well, starting with the least significant bit, A0, we can start incrementing. In binary, we’re at the address 0b0000 0000 0000 0000 (0x0000 in hex) right now. So, to move to the next cell, we change A0 to 1. This gives us the next value stored in the memory, which for the above example is 8E. Does this make sense? We’re just counting up one by one in binary, which helps us to the de-mystify the address bus by removing abstraction of a hex digit.
Now let’s try to extend this model into 16 bits and see what happens.
16 bits | EPROM least significant bit (A0) -> | 0 | 1 |
Upper hex digits (A4-A15) | (last hex digit) , bits | ||
0x000 | (0/1) , 000* | 8EFF | 4E78 |
0x000 | (2/3) , 001* | 6F34 | * |
0x000 | (4/5) , 010* | * | * |
— | — | — | — |
0x000 | (E/F) , 111* | 3D4E | DF7E |
0x001 | (0/1) , 000* | * | * |
Note: The order might seem reversed from what you would expect, but think about it- you’re stacking the additional hex digit on top of the first one (A0-A7 are already full), so it should occupy the upper bits of the bank (A8-A15).
The bold asterisks are just because I don’t want to make new data/so you don’t get confused with new data. Notice how that we have half as much information now- because we doubled the resolution of each memory bank, but the data stored is almost in the same form as it was before. This is the key into getting the info we want out now. So, if we can find a way to separate the upper byte and the lower byte, we can send the right information to the SNES. Keep in mind because the SNES thinks that there is an 8 bit data bus, it will never ask for the upper and lower banks at the same time- giving us our solution. So, in the first 8 bit example, when the CPU was looking for a bank, the way to look at two sequential banks is by toggling the lowest significant bit. In our case, that’s A0. What if we use SNES A0 to toggle between the upper and lower 8 bits of the 16 bit data bus? Remember that the 16 bit data bus is 16 physical wires, so choosing between them is as easy as selecting which 8 you want. That creates an issue with the current setup- because EPROM A0 is also connected onto the address bus, you would never be able to access half of the banks where the switch is active. Why don’t we just shift what the EPROM thinks is A0? So, try to connect SNES A1 to EPROM A0, and follow that sequentially. That gives us a table like this:
16 bits | EPROM least significant bit (A1) -> | 0 | 1 |
Upper hex digits (A5-BA0) | (last hex digit) , bits | ||
0x000 | (0/1) , 000* | 8EFF | 4E78 |
0x000 | (2/3) , 001* | 6F34 | * |
0x000 | (4/5) , 010* | * | * |
— | — | — | — |
0x000 | (E/F) , 111* | 3D4E | DF7E |
0x001 | (0/1) , 000* | * | * |
We still get 16 bits of resolution on the address bus, because we’ll just add another bank address from the SNES- we have more lines than pins on the EPROM. With this new setup, toggling SNES A1 gives us the two different banks. Then, if we were to connect up some sort of switch controlled by A0, we could swap between the lower and upper byte of the bank. Like this:
16 bits | EPROM least significant bit (A1) -> | 0 | 0 | 1 | 1 |
Upper hex digits | A0 state | 0 | 1 | 0 | 1 |
0x000 | (0/1) , 000* | FF | 8E | 78 | 4E |
And just like that, we’ve got our 8 bit resolution back on the data bus! If you think about it, every bit that you add to a binary number doubles the resolution. So, by by adding another bit to the address selection (A0 and A1) we doubled our resolution: 2 bits for 2 byte resolution is the same as 1 bit for 1 byte resolution, which is what we had in the 8 bit case. So, what exactly is the switch we are going to use to toggle between the upper and lower banks?
There is a type of digital logic chip called a multiplexer, or mux, that does exactly this: given two inputs, and a selector, it routes one of the inputs to the output. All that is needed is a toggle signal (which we have as A0). If you do a google search for a 16 input to 8 output mux, you will likely come back empty handed or, with an empty wallet. However, muxes with smaller resolutions are very easy and cheap to find. The one I’m going to use for this project is the 74HC257. Since it is an 8 to 4 multiplexer, we need two, because we have a 16 bit data bus- effectively 16 to 8 multiplexing. So, let’s connect the muxes to the EPROM like this: Stick the upper byte on top of the lower byte, and then match up the bits. This is how we’ll wire the mux- the selector will choose between bit Q0 and Q8, Q1 and Q9, etc.
A0 = 1 | Upper byte | Q8-Q15 |
A0 = 0 | Lower byte | Q0-Q7 |
Output from multiplexer (to SNES) | D0-D7 |
So, by using A0, we can choose which byte we want to read out of the memory on data lines D0-D7 without losing any resolution on the address bus, but doubling the amount of data on the output for us to choose from. We do need to take care when designing the schematic to hook up the selector correctly so that we are retrieving the right byte, otherwise we’d need an inverter on the input to swap it. Great! So now we’ve added two more components to the list. What pins are left to hook up now?
There is a output enable (/OE) and a chip select (/E or /CS). If we look back at the SNES connector pinout, there is a line for the /CART signal. This goes low whenever the SNES is looking for information in the ROM area. Since we want to select the ROM as the active chip when looking in ROM space (duh) let’s hook that up to the chip select. The output enable /OE can be hooked up to the SNES /RD- we want to receive data when we want to read from the EPROM. Not too bad!
Note: CS and OE have very similar functions. In order to output data, both of the lines have to be active. In fact, while I was writing this, Ben Eater released a fantastic and much more in depth video on the idea of computer memory and how the hardware timing can effect system performance. However, this mostly matters for systems that are operating at high speeds. For our purposes, it likely won’t matter. This is still good information to learn though, and could come in handy in future projects that you have, or as a good tool for troubleshooting.
Let’s hook up the SRAM next. This is more straightforward at first than the EPROM, since it’s got an 8 bit data bus. Just hook up A0 through A12 up and D0 through D7 up. Now, we’ve got a few pins left over: chip select (/CS or /E), output enable (/OE or /G), and write enable (/WE). Note: the slash in the start of the name indicates that the pin is active low, meaning that we pull it low before it starts being active. The write enable pin is easy- connect it to the SRAM (can’t write to an EPROM so it’s not going anywhere else!) We can do the same for /OE- we only want it to output data when the SNES wants to read information, so let’s connect that to /RD on the SNES. Now, there’s a bit of an issue- we have two devices (ROM and RAM) that are both connected on the same OE signal (/RD). We need to decide when to enable the ROM and when to enable the RAM. This was easy for the ROM- we had a dedicated signal for when the CPU was accessing the ROM region. The problem is that you can’t enable RAM access during all other times, since there will be a lot of times (most of the time, in fact) when the CPU will write an address on the bus and it’s not looking for SRAM data. If you look at the memory map, we only want to enable it when we’re in the dedicated region. Other addresses could be looking for internal work RAM, or interfacing with hardware acceleration, or any number of things, so we should take care to only enable it in the proper region.
A good place to start with something like this is to reference how the OEM does it. There are games that use a 74HC139 (like Super Mario World) which is a demultiplexer. Nintendo also came up with another chip, the MAD-1 (Memory Address Decoder). This was a custom IC, or at least we’d never know it wasn’t, since it is not a commercially available part. The only place to get these if you wanted to use one in your own PCB is to scalp one from an existing SNES cart. However, the 74HC139 is a commercially available chip, so we can both easily buy the chip and find the datasheet to understand how it works. Both the MAD-1 and the 74HC139 fulfill the same purpose, but are not interchangable. There are also other chips that have been used in SNES carts, but they interface with other custom cartridge chips we won’t have access to (like graphics acceleration) so we don’t really need to worry about interfacing those yet.
The 74HC139 is a demultiplexer. That means that given a set of inputs it will choose an output. Imagine you have two inputs- there are four possible output states that can be selected based on the state of the inputs (see the truth table above for clarity). The ‘139 has two demuxes inside, completely separate from each other. However, we could connect the output from one into the enable for the other, giving us a two stage decoder. Each decoder has two inputs and an enable, so combined with the linked enable signal, gives us 5 bits of control over the decoding. So, what are we going to use them for?
We need to reference the memory mapping. The SRAM region is from addresses 0x6000 to 0x8000, and from banks 0x30 to 0x3F. Let’s start with the address region. Let’s chop off the last three hex digits, and keep the most significant. Here is a visual aid:
A15 | A14 | A13 | A12 | |
0x8 (upper limit) | 1 | 0 | 0 | 0 |
0x6 (lower limit) | 0 | 1 | 1 | 0 |
OK, so there is a nice symmetry here: if A15 is 0, then we’ll be below 0x8000. Note that this bit splits the total address region in half- half of the available addresses are below 0x8000 (when A15 is 0) and half are above (when A15 is 1). Since the enable for the ‘139 is active low, and we want the decoder to be active when the address is below 0x8000, let’s use that for the enable. Then we can give A13 and A14 an input, and when they are both high, use that output (active low) to enable the next stage of the encoder (active low). We want them to both be 1 because any address where A13 or A14 are 0 is below 0x6000 (while A15 is also 0). If you reference the main CPU memory map, addresses below this are used for DMA (direct memory access at 0x4200) and PPU (picture processing unit at 0x2000), which are both very important for normal operation, so we wouldn’t want to interfere. Let’s move onto the SRAM bank region.
The SRAM bank region is between 0x30 and 0x3F. Let’s do the same exercise as before to better visualize what bits can be used to define the region. In this case, only the last hex digit changes between the upper and lower limit (BA0-BA3) so let’s map that out:
BA3 | BA2 | BA1 | BA0 | |
0x3F (upper limit) | 1 | 1 | 1 | 1 |
0x30 (lower limit) | 0 | 0 | 0 | 0 |
Well, that’s not very useful at all. We started with 5 bits of control and we used up 3 of them for the address control, with no wiggle room (remember the DMA and PPU regions below those addresses and the ROM above). That leaves just 2 bits left for the decoder, and with this region here we’d need 4 to define the region fully. We could get additional hardware and buy another ‘139, but what if we get a little more creative? If you reference the CPU memory map, it doesn’t make any important notes about the adjacent banks to the SRAM region, just what’s in the addresses below. What if we just extend the space by a little bit? The CPU isn’t using those areas and so we wouldn’t need to worry about what’s stored there. Let’s try to see if increasing the region by raising the upper limit will help (we wouldn’t want to make the space any smaller, since the SNES might need the full region!). Instead of ending at 0x3F, how about we end at 0x40 (just one address higher)? In this case only the upper nibble is changing, so I’ll leave the other off:
BA7 | BA6 | BA5 | BA4 | |
0x40 | 0 | 1 | 0 | 0 |
0x30 | 0 | 0 | 1 | 1 |
This is better, but still would require 3 bits of control (BA 4, 5, 6). How about just lowering the bottom limit so it’s from 0x3F to 0x2F instead (just one address lower)? Remember that only the upper hex digit is changing, so that’s all we need to pay attention to:
BA7 | BA6 | BA5 | BA4 | |
0x3F | 0 | 0 | 1 | 1 |
0x2F | 0 | 0 | 1 | 0 |
Perfect! Now we just need to pay attention to BA 4 and BA5, which is only 2 bits! If BA4 and BA5 are 1, then the bank is too high. If BA5 is 0, then the bank is too low. We want to send a chip select signal to the SRAM when BA 5 is 1, but BA4 is 0, and the address criteria from the previous decoder are met as well. This takes our address and bank lines and uses it on a hardware level to decode where in memory the SNES is looking for information. Now we have a way to decode where it’s looking for SRAM data and ROM data, and we’ll only enable the the relevant chip when it’s peeking in the right area.
As a note, I’d like to highlight that all of what I’ve shown you is just derived the from the memory map. The memory map is different for LoROM games, and you can/have to use different hardware schemes depending on what functionality you need (SRAM/no SRAM, exHiROM, exLoROM, etc). I am trying to walk you through the process of understanding how to approach these problems: the information you would need to design boards for your own purpose is all easily available and just takes some thinking through to arrive at the proper conclusion. There should be no mysteries when you are tackling a project like this and if you take the time to understand why things are the way they are, it will pay off immensely both in troubleshooting but also generally in your life. It’s never a bad idea to take a step back and understand not just what you’re doing, but also why. If you are just guessing or following what someone else did without understanding, it is very unlikely you will be able to solve any problems if they arise.
Back to the SNES: while we’re on the topic of SRAM, let’s figure out that battery circuit, eh? The goal here is to have an unwavering power supply for the SRAM that will switch between the SNES 5V supply and some other power source. Because you don’t want to have to keep your games plugged into an outlet in the wall, let’s use a battery. Our SRAM chip is built using CMOS technology. These chips usually use 5V for Vcc, but the chip can stay in standby on voltage as low as 2.6V. If the voltage goes below this, then there is a risk of data corruption or loss. Let’s use a CR2032 watch battery. These are easy and cheap to find, and product 3.3V for 220mAh. Note this capacity is rather low, so we’re interested in lowering the standby current as much as possible.
Before we tackle that, how can we switch between two voltage sources? If we connect them just directly together, we’d end up doing something like effectively trying to charge the battery. The battery does not have much voltage or current to drive against the SNES power supply, so we’ll want to separate them. Here is what I am talking about:
I hope that schematic is easy enough to understand- we have our two voltage sources, and two pins we want to power (E2 is required to be active for chip operation). There is a resistor there because otherwise we would be shorting to ground. I chose the value based on this article (which doesn’t fully apply since this isn’t technically a pull-down, I think?) But we don’t want this design because of the battery and 5V situation, so let’s use diodes to restrict the flow of current towards the chip. Like this:
That’s better for power swap- the SNES won’t backdrive the battery, and while there will be a small voltage drop across the diode, the voltage at that junction should be high enough to prevent the battery from discharging while the SNES is turned on. But we’re not done yet- there’s something about E2 I want to cover.
There are two enable pins on the 6264 SRAM, and there is a useful purpose for both. If you look at the datasheet, you’ll see that there is /E1 and E2. Note they’re opposite polarities- for use with other CMOS designed parts, the /E1 is used for enabling by other devices as part of the standard (active low). So what is E2 used for? We could just tie E2 to Vcc, leaving it permanently enabled, but think about some edge cases. Since the point of SRAM is being able to read and write during operation, we should need to worry a little bit about data corruption during transitional states. Typically this sort of behavior is caused by the CPU making noise on the connected lines during powerup and shutdown. While it seems quick to us that the power is off, on the scale of the clock speed the CPU is using, there are actually several cycles where the power is fluctuating due to the power supply turning off and bouncing from the power or reset switches and other sources. We can use the second of these enable pins as a safeguard for data protection, since the chip design won’t allow reads or writes while either of the enables is inactive. There will still be bouncing activity on the address and data busses and other lines, but as long as the chip is in standby, it won’t effect the data inside. So, we want to separate the Vcc line from E2 with some sort of logic, at the least.
Luckily, we have a line on the SNES that will indicate when we’re in a transitional state: the /RESET line. This line is high normally, but goes low when the reset button is pushed or when the CPU is forcing a reset (this can be done in software), and will obviously be off when the SNES is off. So, we would ideally want to find a part that lets us pass a signal when there is a high signal, and block a signal during low signals. This sound like a great job for a transistor! If you’re not familiar with transistors, you should read up a little on them! They’re the basis of all digital logic and an extremely useful tool for your design toolbox, and we’ll use one here like a little gate. Let’s draw up what this should look like:
NPN transistors work when you source current to the base. In this case, when the /RESET line is high, it will source current to the base of the transistor, which causes a current gain at the emitter from the collector. In simple words, it’s a gate, and NPN means that we supply power to the base to pass a signal. PNP is the complement and we can sink current from the base to make it pass a signal. In this case, we want to turn the transistor off when the base is low because that means the SNES is in a transitional state (the /RESET is active low) and could potentially damage the data on the SRAM. If you’re wondering about the extra resistor there, it’s to prevent dragging down the /RESET line voltage. The digital logic lines used here are intended for use with high-impedance inputs. This is designed so that there is less power consumption and more devices can be connected to a bus/logic lines. An easy way to think about this is that the voltage is seen by the input, but no current flows into or out of the pins. This is why we can have multiple devices connected to the same lines without dragging them down when we read them- there is no voltage drop due to current consumption by the devices. However, with a transistor like the one we’re using, current consumption happens due to the physics involved with how they work. So, we don’t want to drag the line down while we’re using the little bit of current that it provides, so we put in a resistor to limit the current that makes it’s way through the base. I picked 10K because that sounds reasonable to me- 5V/10k is .5mA (V=IR).
I would like to note that I don’t have any formal experience in electronics engineering or anything like that- so I don’t have a formal background in making circuits or understanding some of the effects of designs. I would like to say thanks to poorstudenthobbyist for not only publishing his design, but talking over some engineering points and actually testing his design for current draw and the like. On my boards I chose to use his design since he says that he tested it for current draw and compared it with information from the datasheet for low-power modes used in the 6264 SRAM. Like I have been getting at through theses posts, I would like to emphasize the importance of design by need. The battery in a SNES cart only has 220mAh capacity- if you want to actually be able to rely on your saves for years, the SRAM needs to be operating in the microamp consumption range. Like stated in his post, his design was engineered to meet the spec stated by the datasheet, but still using few and cheap parts to meet the requirements. That being said, don’t be afraid to try and make something even if you’ve never done it before. Make your own circuit! Test it for current draw. Do some math and find out the battery life! Maybe there is a better design out there. Feel free to explore- it’s your project.
Also, don’t forget to add some filter caps on the SRAM Vcc line- any power dips could cause your data to disappear! Some small caps peppered on the supply lines would help prevent this in several places on our board. Mr. Carlson’s Lab has a lot of really excellent videos on basics of electronics design, especially power conditioning, and I would highly recommend his Youtube channel if you want to learn some more about how to start on some designs yourself.
There is one last piece we need to have a working SNES cart- the lockout chip. Nintendo included one of these on every cart, and it served as a region and counterfeit lockout. This chip was called the CiC, and for a while, the only way to make your own boards was to scalp one of these from an existing board. However, someone reverse engineered it a while ago and wrote some micro-code that you can put on a small microcontroller to simulate the behavior of the CiC. The nice thing about this is that it will work for all regions, like a sort of super-key. They also reverse engineered the SNES internal part of the lock, so you can tamper with that and then run any game without any need for a CiC chip at all. To emulate the CiC, just pick up one of the cheap and common PIC microcontrollers, burn the code from the article to it, and pop it in. There are a few different chips you can use, but the only difference is pinout. I used the one mentioned in the readme, the PIC12F629. If you’re using the TL866 programmer to program your EPROMs, you can also use it to program these microcontrollers too. Take a look through the code included for a neat sneak peek into the world of hardware security!
So, now we’ve got it all. The EPROM, the SRAM, ways for the EPROM to talk to the SNES, ways for us to find out where the CPU is looking for data, ways to keep the SRAM powered, and ways to fool the SNES into thinking we’re running legitimate code. All that’s left is to make the schematic! I recommend KiCad for circuit and PCB design- it’s a really streamlined software suite that does pretty much everything, has a ton of keyboard shortcuts and is free and open source. It runs best on Linux, but I think you can get it on any OS you wish. There are other alternatives too, especially an interesting browser one that I’ve been checking out recently. I will leave the circuit and PCB layout to you to figure out for yourself- there are a million and one videos and articles online on electronics CAD. In my next post, I just want to cover some smaller hardware things you might not have thought about but are worth mentioning, and also how to use the programmer and split up data in a way that makes sense for how we designed our boards.