NOTICE: This research and implementation in this post is not 100% correct. Please check out my SegaController Arduino library for better code and How To Read Sega Controllers for details on how it works.
Background
The Sega Genesis was my first and favorite childhood game console, so when I first picked up an Arduino a couple years ago, my first thought was to build something, anything, that used a Genesis controller. Unfortunately I got side-tracked by other projects, and the Arduino Uno I’d purchased got set aside.
Fast-forward to this year’s picade build, when I had to re-flash the main controller board, which at its heart is an Arduino Leonardo. Seeing how easy it was to work with, I finally decided to break out my Uno. After a couple sample sketches, I figured it was time start interfacing with some Genesis controllers.
Research
I started poking around online to see what others had done, and couldn’t find quite what I was looking for. What I did find was plenty of information on how the three-button pads worked. Some used that info to implement full three-button support – others, it seems, were satisfied with having just some of the buttons working (essentially using the controller’s innate backward compatibility with the simpler Sega Master System’s two-button controller design). No one had six-button controllers working.
What I want is full three and six-button support, something that I can plug any Genesis controller into and it’ll “just work”, like an actual Genesis console. My requirements therefore are:
- Correctly reads connected three-button controllers.
- Correctly reads connected six-button controllers.
- Automatically detects which type of controller is connected, with hot-swapping.
- Bonus: Support more than one controller at a time.
The real godsend to the first two was finding Charles Rosenberg’s Sega Six Button Controller Hardware Info. There he describes almost everything you need to know about how Genesis controllers work. I highly recommend giving it a full read for the really gory details before continuing, but here’s a “quick” overview:
How Sega Genesis controllers work
All Genesis controllers use a standard nine-pin DB9 serial port. On your regular three-button controller, you really have a total of eight buttons: Up, Down, Left, Right, A, B, C, and Start. With nine pins to work with, Sega could easily have gone with one +5v in and eight outputs back to the console and be done with it. But instead, in the interest of backwards compatibility with the Sega Master System (and potentially other DB9 based controllers like the old Atari joysticks), they implemented a simple multiplexer circuit.
Essentially you have three control pins (+5v power, ground, select) and six output pins. By default, all of the output pins are set high (meaning a button press will bring the pin down to ground). The Genesis (or more specifically, the game running on the Genesis) sets the select pin (DB9 pin seven) low, then reads the state of the six output pins to get the states for the Up, Down, A, and Start buttons. Then the game toggles the select pin to high, and re-reads those same six output pins to get the states of the Up (again), Down (again), Left, Right, B, and C buttons.
DB9 Pin | Select is low | Select is high |
1 | Up | Up |
2 | Down | Down |
3 | Ground | Left |
4 | Ground | Right |
5 | Control: +5V | |
6 | A | B |
7 | Control: Select | |
8 | Control: Ground | |
9 | Start | C |
The algorithm is pretty straight forward to implement on the Arduino, polling the controller exactly the way a Genesis game would. This satisfies my first requirement. Now, things get a little more complicated with the six-button controller:
DB9 Pin | Select is low | Select is high | Every 3rd select pulse |
1 | Up | Up | Z |
2 | Down | Down | Y |
3 | Ground | Left | X |
4 | Ground | Right | |
5 | Control: +5V | ||
6 | A | B | |
7 | Control: Select | ||
8 | Control: Ground | ||
9 | Start | C |
First, let’s call each dropping of the select pin to low then back to high a “select pulse”. Now, on every third select pulse the six-button controller will instead report back the states of the X, Y, and Z buttons (instead of Up, Down, and Left). On its face, it looks like we couldn’t have a game that supports both three and six-button controllers, because how does a game know what kind of controller is connected? On every third pulse how does a six-button enabled game know whether to use the first table or the second? On every third pulse, how does a six-button controller know not to report X, Y, Z for games that only support three-buttons? If the game and controller aren’t on the same page and they use the wrong mappings, they’ll record incorrect button presses.
How do the games and controllers make the right decisions?
One part of the answer (as described in Rosenberg’s notes) is in how often games actually poll the controller. The three-button controller uses dumb direct logic, which means it always uses the first table. It also means that technically you can poll the controller state super-fast (say every 50 microseconds) or super-slow (say every 20 milliseconds) and always get the same result. Now typically, a game is only going to poll the controller once per game frame (sixty times per second, or every 16.6 milliseconds). Which means, at the time of the six-button controller’s release, the vast majority of the games already published (which were three-button enabled only) only sent a single select pulse every ~16 milliseconds.
The six-button controller can use this to its advantage. Instead of dumb direct logic, it uses an IC to watch how often the select pulses come in. The IC knows that (given the game indicates it wants six-button mode), it should return the states of X, Y, and Z on every third select pulse. But it also knows that most games only support three-button mode, so a safe default is to just pretend to be a three-button controller and ignore reporting X, Y, and Z on every third pulse.
How does the controller decide? The frequency of the select pulses. If the IC only sees one select pulse every ~16 milliseconds, or one pulse per game frame, then its best bet is to take the safe route and assume three-button mode. In this way, the six-button controller is backwards-compatible, and most games will never get any incorrect button presses.
If that’s the case, how does a game indicate that it actually wants those X, Y, and Z buttons?
If the game believes that a six-button controller is attached, it will instead pulse the select line three times very quickly in one game frame. The first two times the game reads (and the controller reports) the button presses for three-button mode. Then the game pulses the controller a third time. At that point, the controller’s IC, seeing how quickly those pulses came in, presumes the game wants X, Y, and Z, so it reports X, Y, and Z.
So to sum up: if a game just wants the three-button control states, it pulses once every frame and uses the first table to read the results. If a game wants six-button control states, it pulses three times in one frame, using the second table to read the results. With this in mind, we can now read both controller types, which satisfies the first two of my requirements. We can easily implement an Arduino sketch that implements one or the other algorithm, if we already know which type of controller we’re going to have connected.
But what about my third requirement? What if we want one sketch that implements both modes? How do we make our board detect what kind of controller is connected?
This one took a little bit of experimenting to figure out, since Rosenberg’s notes don’t address the issue. Turns out the six-button controller’s IC has another trick up its sleeve with watching how fast those select pulses come in. As we just saw, since most games only expect three-button controls, the controller can default to three-button mode, and seeing slow pulses, will stay in three-button mode.
But now, if the game wants to check for a six-button controller, it can send rapid select pulses when a controller is connected, and if it’s a six-button controller, the IC will report that both the Up and Down buttons are being pressed at the same time!
Under normal circumstances this is impossible, as the controller’s d-pad rocks in the direction you press it. So with this neat trick, the controller lets the game know that a six-button controller is connected, giving the game to option to start polling the controller in six-button mode.
The way we implement this is simple: by default we poll in three-button mode very quickly. For three-button controllers, this works perfectly. After every pulse, we can check for both Up and Down being pressed at the same time. If we see that, we know a six-button controller is attached, so we switch to six-button mode, pulsing more slowly so that we don’t reset the IC.
This solves the first part of my third requirement: detecting when a six-button controller is connected. But what about the other way? The way it stands, once we connect a six-button controller, and our board switches to six-button polling, it’s stuck that way until we reset the board. If we hot-swap from a six-button to a three-button controller, we’ll get those annoying paired inputs (specifically, pressing Up will return Up and Z, Down will return Down and Y, Left will return Left and X).
What we need is a way of knowing when a controller is disconnected, so that we can switch back to the default three-button polling. Turns out we have everything we need in the tables above, something that works for both three and six-button controllers.
At the very beginning I said that by default the console puts all six of the DB9 output pins high, so that a button press causes the pin to drop low. So if no controllers are connected those output pins should stay high. Only a button press from a connected controller will drop a pin low, right? We could just press a button to let the board know we have a controller connected, but wait, there’s a better way!
As we can see in the tables above, when the select pin is low, DB9 pins three and four both go low, regardless of any button presses. So in effect, the controller presses imaginary buttons on pins three and four when select is low automatically. So, all we have to do is watch those pins – if they go low when select is low, then we know a controller is connected. If they’re high when select is low, it means the controller is no longer connected.
In implementation terms, when select is low, we can simply check those two pins like we would for any other button, and map the results of those imaginary “On” buttons. Watching those “buttons” we know when a controller is connected or not, and therefore we can easily switch back to three-button mode when a controller is disconnected. With that we now have everything we need to satisfy my main three requirements for the board. As for the 4th and final bonus requirement, recognizing that we only needed seven pins to read one controller, we’ve got plenty of left-over pins on the Uno to cover connecting one more.
The Sketch
Ok, so now for the sketch. Our basic algorithm is the following:
- Default to three-button polling as fast as possible, using the first table and select pulsing algorithm.
- If you ever see both Up and Down pressed at the same time, switch to six-button polling, using the second table and select pulsing algorithm.
- If you ever see the “On” button state go away, switch back to three-button controller polling.
/* * Sega Controller Reader * Author: Jon Thysell <thysell@gmail.com> * Version: 1.0 * Date: 7/26/2014 * * Reads buttons presses from Sega Genesis 3/6 button controllers * and reports their state via the Serial connection. Handles hot * swapping of controllers and auto-switches between 3 and 6 button * polling patterns. * */ // Controller Button Flags const int ON = 1; const int UP = 2; const int DOWN = 4; const int LEFT = 8; const int RIGHT = 16; const int START = 32; const int A = 64; const int B = 128; const int C = 256; const int X = 512; const int Y = 1024; const int Z = 2048; // Controller DB9 Pin 7 Mappings const int SELECT[] = { 8, 9 }; typedef struct { int player; int pin; int lowFlag; int highFlag; int pulse3Flag; } input; // Controller DB9 Pin to Button Flag Mappings // First column is the controller index, second column // is the Arduino pin that the controller's DB9 pin is // attached to input inputMap[] = { { 0, 2, UP, UP, Z}, // P0 DB9 Pin 1 { 0, 3, DOWN, DOWN, Y}, // P0 DB9 Pin 2 { 0, 4, ON, LEFT, X}, // P0 DB9 Pin 3 { 0, 5, ON, RIGHT, 0}, // P0 DB9 Pin 4 { 0, 6, A, B, 0}, // P0 DB9 Pin 6 { 0, 7, START, C, 0}, // P0 DB9 Pin 9 { 1, A0, UP, UP, Z}, // P1 DB9 Pin 1 { 1, A1, DOWN, DOWN, Y}, // P1 DB9 Pin 2 { 1, A2, ON, LEFT, X}, // P1 DB9 Pin 3 { 1, A3, ON, RIGHT, 0}, // P1 DB9 Pin 4 { 1, A4, A, B, 0}, // P1 DB9 Pin 6 { 1, A5, START, C, 0} // P1 DB9 Pin 9 }; // Controller State int currentState[] = { 0, 0 }; int lastState[] = { -1, -1 }; // Default to three-button mode until six-button connects boolean sixButtonMode[] = { false, false }; void setup() { // Setup input pins for (int i = 0; i < sizeof(inputMap) / sizeof(input); i++) { pinMode(inputMap[i].pin, INPUT); digitalWrite(inputMap[i].pin, HIGH); } // Setup select pins for (int i = 0; i < 2; i++) { pinMode(SELECT[i], OUTPUT); digitalWrite(SELECT[i], HIGH); } Serial.begin(9600); } void loop() { readButtons(); sendStates(); } void readButtons() { for (int i = 0; i < 2; i++) { resetState(i); if (sixButtonMode[i]) { read6buttons(i); } else { read3buttons(i); } } } void resetState(int player) { currentState[player] = 0; } void read3buttons(int player) { // Set SELECT LOW and read lowFlag digitalWrite(SELECT[player], LOW); delayMicroseconds(20); for (int i = 0; i < sizeof(inputMap) / sizeof(input); i++) { if (inputMap[i].player == player && digitalRead(inputMap[i].pin) == LOW) { currentState[player] |= inputMap[i].lowFlag; } } // Set SELECT HIGH and read highFlag digitalWrite(SELECT[player], HIGH); delayMicroseconds(20); for (int i = 0; i < sizeof(inputMap) / sizeof(input); i++) { if (inputMap[i].player == player && digitalRead(inputMap[i].pin) == LOW) { currentState[player] |= inputMap[i].highFlag; } } // When a six-button first connects, it'll spam UP and DOWN, // which signals the game to switch to 6-button polling if (currentState[player] == (ON | UP | DOWN)) { sixButtonMode[player] = true; } // When a controller disconnects, revert to three-button polling else if ((currentState[player] & ON) == 0) { sixButtonMode[player] = false; } delayMicroseconds(20); } void read6buttons(int player) { // Poll for three-button states twice read3buttons(player); read3buttons(player); // After two three-button polls, pulse the SELECT line // so the six-button reports the higher button states digitalWrite(SELECT[player], LOW); delayMicroseconds(20); digitalWrite(SELECT[player], HIGH); for(int i = 0; i < sizeof(inputMap) / sizeof(input); i++) { if (inputMap[i].player == player && digitalRead(inputMap[i].pin) == LOW) { currentState[player] |= inputMap[i].pulse3Flag; } } delayMicroseconds(1000); } void sendStates() { // Only report controller states if at least one has changed boolean hasChanged = false; for (int i = 0; i < 2; i++) { if (currentState[i] != lastState[i]) { hasChanged = true; } } if (hasChanged) { for (int i = 0; i < 2; i++) { Serial.print((currentState[i] & ON) == ON ? "+" : "-"); Serial.print((currentState[i] & UP) == UP ? "U" : "0"); Serial.print((currentState[i] & DOWN) == DOWN ? "D" : "0"); Serial.print((currentState[i] & LEFT) == LEFT ? "L" : "0"); Serial.print((currentState[i] & RIGHT) == RIGHT ? "R" : "0"); Serial.print((currentState[i] & START) == START ? "S" : "0"); Serial.print((currentState[i] & A) == A ? "A" : "0"); Serial.print((currentState[i] & B) == B ? "B" : "0"); Serial.print((currentState[i] & C) == C ? "C" : "0"); Serial.print((currentState[i] & X) == X ? "X" : "0"); Serial.print((currentState[i] & Y) == Y ? "Y" : "0"); Serial.print((currentState[i] & Z) == Z ? "Z" : "0"); Serial.print((i == 0) ? "," : "\n"); lastState[i] = currentState[i]; } } }
All you have to do is upload the sketch and connect the controller’s DB9 pins to the Arduino following the mapping in inputMap in the code. Then start up the Serial Monitor so you can see the output from the controller on your PC.
Here’s a closeup of how I’ve wired my Uno to a male DB9 breakout board (you can wire straight to the controller, or a male DB9 port, but I found the board the easiest solution):
The sketch is also set up for a second controller connected to the analog pins on the other side of the Arduino, though I only had one male DB9 breakout board. Also note that this is my first real Arduino sketch, so I’m sure there are some best practices I’m breaking in my design.
I can already see room for future improvements. Right now I report the controller states over the serial connection as strings – easy to debug, but slow and wasteful. Of course, in your own sketches, you can just read the currentState integers directly.
Hope this helps anyone else out there trying to interface with Genesis controllers. Happy hacking!
/jon
PS. Obviously in the process of plugging and unplugging controllers, you may see some errant random button presses recorded. In the world of video games, this is to be expected, and only lasts for a second. If however, you’re planning on doing anything “bad” with a button press (say enabling a thermonuclear detonator), you might want to avoid hot-swapping controllers.
PPS. Final note, concerning the six-button controller’s “mode” button. Some games (notably Ms. Pac Man), don’t follow the rule of only polling the controller once per game frame, and select pulses more often. This causes erratic behavior as incorrect buttons presses are recorded. Ostensibly, when you plug in a six-button controller with the mode button held down, that signals the IC in the controller to always be in three-button mode and never report X, Y, or Z. Unfortunately, I couldn’t get it to work with my sketch and gave up because I saw no reason to limit my six-button controller to three-buttons anyway.
Update 29-SEP-2014: I’ve uploaded a new version of the sketch in Sega Genesis controllers and Arduino revisited, which includes support for reading the “Mode” button and reporting button presses as keyboard key presses (on compatible Arduino boards).
Update 26-JUN-2017: The source is now available on GitHub here.
Update 28-JUN-2017: I’ve announced a newer, stable, reusable version at Introducing the SegaController Arduino library.
Hi Jon, thank you for your excellent article and code examples it helped me a lot:-) There are two things I would like to add:
1. It looks like UP and DOWN are not reported when select is high. Only when select is low they are reported (At least this is the case for the 6-Button controller). But In your code example this does not matter.
2. On PIN 4 int “pulse 3” you can also get the status of the MODE button for the 6-Button controller.
Thanks for the info! Yes I kind of ignored the mode button, since its purpose was to fix incompatibilities with old games (which isn’t an issue with my code), but it’s good to know that the state of the button can be read.
Thanks a lot for this info. I find the information on the 6-button interface very useful.
I noticed that with the 6-button interface that I am using, when I am pulsing fast, UP and DOWN appear together only when select is in the low state. I only sample UP and DOWN when select is in the high state, so that doesn’t affect the 3-button operation with a 6-button controller at a high pulse rate. I wonder if that is consistent with all 6-button controllers though. The controller that I have doesn’t say “Sega” on it, so I think this is a third-party controller.
eep! I should have read through point 1 of soe’s comment before posting. At least I have confirmed that over here.
I also noticed this. I could work around this issue adding a delayMicroseconds(500) in the end of the loop. For my application it has worked fine.
Btw, excellent work Jon!
I also noticed that pin 4 is high when select is low on the third pulse for 6-button controllers. That is what I ended up implementing when trying to decide which controller is connected. It took me a long time to debug my 6-button interface because there seems to be a sweet spot for clock speed. Too slow and it would operate as a 3-button controller. Too fast and the clock would go out of sync. I use a 500 us period.
Hi Jon,
Thanks for creating this vividly detailed document. I have been exploring and experimenting with creating novel ways to interact with technology. I found this tutorial at a perfect junction in my tinkerings.
I built a version of this project by repurposing an obsolete db9 data switching box that houses the Leonardo snugly and provides a rock Solid housing. I am much more of a ‘hardware guy,’ but am working my
Way through learning the software/programming.
The gist:
could you show me how to have the Leonardo output the HID device as a MIDI (or secondarily as joysticks.)
I will share photos of my build as well.
Thanks again
Hello my friend I checking your proyect for Arduino and Sega Controllers but I I want to do the opposite, I need to mount an Arcade Stick of Sega Genesis, that is, send the signal of each button and that the arduino interprets the signal and sends it to Sega Genesis. Since you already have the logic that interprets Sega Genesis I think it will be easy. Do you think you can help me with this?
Hi, good tutorial. Can you share the IC code/model used now a days in 6-button sega genesis control? Thank you.
All of my code to read Sega 6-button controllers with Arduino is here: https://github.com/jonthysell/SegaController
Thanks for the quick response, but I was referring to the code printed on the IC of the 6-button control. I have a control with standard DIP IC, and it is not working, I would like to change this IC but I do not know the IC code.
I haven’t opened a controller in a long while, but AFAIK the IC was a custom by Sega. I’m not sure that you can easily source replacements based on the code. Here’s a picture of the board I found online:
Hello Jon! I am a rom hacker that wishes to Rom Hack a game! So, from reading this, does that mean that I have to change the ROM to turn the game into 6 controller mode by changing the code to quickly “pulsing” 3 times?
So I am using the GitHub repo made for the controllers, however, I notice that my LR buttons are always high and AB are always high as well. C only goes high if I am pressing the start button. Is this an issue with my wiring? I am using a Sega Genesis Gen 1 controller.
Turns out the wiring diagram I was looking at was flawed. Totally got it figured out 😛