Building my own Sega Genesis Mini

It’s been several years since I posted Building a own custom retro console running RetroPie. That project was a lot of fun at the time, but I haven’t built another custom system since.

I just wanna play Sega Genesis

My original intent was to build something that focused on playing Sega Genesis games. The Genesis remains my favorite childhood console, and my dream was to build an emulation machine that looked like a Sega Genesis externally, but loaded with the entire library of games.

However the Raspberry Pi was still in its infancy back then, and I couldn’t bring myself to cannibalize an actual Genesis for its shell. Instead I settled on building my custom console with a generic case and (Xbox 360) controllers featuring a similar six-button layout. But that build just didn’t hold my interest for very long and I ended up digging out my original Sega Genesis, picking up one of Krikzz’s EverDrive flashcarts, and never looking back.

Fast forward to 2019, and Sega released the Sega Genesis Mini, which I happily picked up. It was my favorite of the official mini consoles, and it earned its spot on my desk at work. Despite the quick availability of hacks to add more games (and systems), I appreciated having such a tight, curated experience, even if I didn’t agree with every game choice.

Home office plans

Now it’s 2022 and that mini has mostly sat unused while I worked from home. As our offices have reopened, I’ve decided to split my time and to finally build myself a proper home office. And while I’ve kept a spot on my desk earmarked for a small “retro hardware setup”, I also have nice large TV and couch just begging for its own game system.

My first thought was to bring the Genesis Mini home, but it’s nice to have a self-contained setup at work for the days I go in. My second thought was to just pick up a Xbox Series S, giving me access to my modern digital library instead.

Then the Genesis Mini 2 was announced, so that became a contender. I looked at the game list, but unfortunately it doesn’t really excite me. However, in the process of watching various YouTubers’ opinions on the system, I stumbled upon some other projects that I’d completely missed before.

Blast 16 and the RetroFlag MEGAPi case

Blast 16 is a Raspberry Pi image designed to help you build your own Genesis mini console. Rather than just another RetroArch and Emulation Station setup, designed to host the full library of every retro console ever, Blast 16 is very Sega-oriented, and only supports the Genesis, Sega CD, 32X, Master System, and Game Gear. More than that, the beautiful, box-art-focused UI can’t handle more than a couple hundred entries without slowing down. So it’s meant to make you curate your experience, and only put on games you really want to play.

On top of that, and the real icing on the cake for me, is it’s easy to pair the Blast 16 software with the RetroFlag MEGAPi Case, which looks just like an actual Model 1 Sega Genesis. Seeing the two together in this video, my original dream was rekindled, and building my own Genesis Mini jumped to the top of my to-do list.

Getting the parts

My first concern was the age of these projects – they’d all come to market years ago, in response to the official Genesis Mini release. And if there’s one problem with these kinds of niche retro products, is that they’re often impossible to get if you didn’t buy them on day one.

Thankfully the Blast 16 website was still up and running, and I was happy to find that the MEGAPi case is still available on Amazon. I put in my order, and it’s quite lovely:

Next I gathered up two 8Bitdo M30 Bluetooth controllers (which I already had). The only thing missing was a Raspberry Pi.

I almost had a heart-attack seeing my (lack of) options. The MEGAPi case supports the Raspberry Pi 2B, 3B, or 3B+, but almost no one carries them anymore. I’d have to shell out $120 just for the low-end 2B, which is three times its original retail price of $40, and doesn’t even have Bluetooth.

Thankfully, past me had me covered. Checking my Amazon order history, I saw I’d picked up a 3B back in 2017 (who knows for what), and sure enough I there it was in my parts bin, still in the box. Bingo!

Building the hardware

Installing the Raspberry Pi into the MEGAPi case was simple and straight-forward – just plug in the two USB plugs, the single plug for the GPIO, and screw the board down:

Finally I flipped on the switch labeled “Smart Shutdown” (more on that later), closed the case and screwed it shut:

Initial software setup

With the hardware done, the next part was setting up a Micro SD card with Blast 16. I used a 16 GB card (no reason to waste anything bigger), and following the excellent instructions on the Blast 16 website, got the card imaged.

After that, I installed the card through the little lift door on the side of the MEGAPi:

After running the device once for the initial setup, and pairing the M30 controllers, it was time to install some games!

Installing games

Doing so was super easy. All you have to do is copy your ROMs (with matching box-art) onto a USB flash drive in a particular folder structure. Then if you boot the system with flash drive plugged in, Blast 16 will automatically install them onto the system. Note: if you want to remove games later, you can do so from within the Blast 16 menus.

I installed a little over a hundred games, mostly Genesis favorites, but also select few 32X, Sega CD, and Master System titles. It may not be the forever list, but it’s a great start.

Installing the safe shutdown scripts

One of the biggest gotchas with building a Pi-based anything is that the board doesn’t have a power switch. Out of the box, you’re expected to plug in the power to turn it on, and make sure to safely shutdown from within the running software to turn it off. If you don’t, and just pull the power, you risk corrupting the SD card and breaking your system.

Thankfully, the MEGAPi case wires its power switch and reset button to the Pi’s GIPO pins, and RetroFlag provides scripts you can install on your system to trigger a safe shutdown or restart when they’re used.

Update: Whoops! Apparently the base Blast 16 image already includes a working safe shutdown script that I completely missed. My version is unnecessary and installing it may give you some random error messages.

Unfortunately, RetroFlag hasn’t touched their code in a while, so the scripts have bugs and the install instructions didn’t work for me with Blast 16. I ended up forking their code and fixing the scripts myself for the Blast 16 / MEGAPi combo. I’ve written up revised instructions here, but long story short, if you want to copy my work, once you’ve gotten your Pi’s network configured, you just need to run the following from a terminal:

wget --no-check-certificate -O - "https://raw.githubusercontent.com/jonthysell/retroflag-picase-blast16/main/blast16_install.sh" | sudo bash

After it reboots the power switch and reset button will work as expected.

Final thoughts

I really love how well this project turned out. It looks so nice under my TV:

The UI is beautiful, and while I normally like the clean, pixel-perfect look when emulating, I’ve found I really like simulating an old CRT on this setup by setting the scaling to 4:3, turning on scan lines, and enabling the NTSC composite filter. Note: that last option meant going into RetroArch’s menus, so while that isn’t strictly necessary, I like that it’s still available.

In fact, having access to RetroArch’s menus meant I was also able to enable RetroAchievements, which was listed in Blast 16’s FAQ as unsupported. It works great, and I find adds a whole new level of fun when playing these old games.

Overall I’m thrilled to finally fulfill this old dream of mine, and while it doesn’t have literally every single game, I expect to spend plenty of hours playing my custom Sega Genesis Mini.

/jon

SegaController v1.3.0 now available

I’ve released an updated version of the SegaController Arduino library. If you don’t know, SegaController enables your Arduino sketches to read Sega Genesis (Mega Drive) and now Master System (Mark III) controllers.

What’s new in v1.3.0?

  • Now supports Sega Master System (Mark III) controllers
  • Optimized code to reduce compiled binary size
  • Reduced read latency
  • Fixed issue with errant button reads when inserting/removing controller
  • Temporarily disables interrupts while reading to prevent misreads
  • Added DB9 pinout diagram to readme and examples

So if you’re interested in reading Sega controllers in your Arduino projects, check out  SegaController Arduino library on GitHub.

I’ve also updated How To Read Sega Controllers with information on reading Master System (Mark III) controllers.

Enjoy and happy hacking!

/jon

Introducing the SegaController Arduino library

A couple years ago I tried my hand at building an interface for reading Sega Genesis / Mega Drive controllers with an Arduino. I documented my first attempt with Reading Sega Genesis controllers with Arduino, and my updated version with Sega Genesis controllers and Arduino revisited.

That was three years ago, and I’ve gotten a lot of feedback about those projects. While they worked well enough for me, many people had problems getting my code to work. My research had led me to an implementation that relied heavily on having correct timing delays, and others found they had to constantly tweak those timings.

Of course, as any decent programmer will tell you, if your code relies on a bunch of seemingly random sleeps to work properly, you’re probably doing it wrong. And it turns out I was.

I recently found the Six Button Controller page on SegaRetro, where I gleaned some new vital pieces of information:

  1. The controller must be cycled through 8 different states.
  2. Reading the controller involves knowing the current state.
  3. If the state isn’t changed for 1.5 ms, the controller resets.

Using this information, I decided to take another crack at my Arduino code. And with a little bit of work (not all of the information on the SegaRetro page was accurate) I created a much more deterministic, stable, delay-free program. Taking it a step further, I refactored the code into a proper Arduino library, both to learn how to do that, and to make it much easier for Arduino enthusiasts to use in their own sketches.

So if you’re interested in reading Sega controllers in your Arduino projects, check out my new SegaController Arduino library on GitHub. Replacing the original sketches are two new examples, one which reports the button states via the serial port (good for testing) and one which sends Keyboard key presses (good for driving other software).

Also, since my research into how the controllers worked led me to mixed results (no one’s documentation had it 100% right) I’ve created some documentation on my own in the SegaController wiki at How To Read Sega Controllers.

Enjoy and happy hacking!

/jon

Consolidating the Sega Genesis, Sega CD, and Sega 32x power supplies

As I’ve mentioned before, the Sega Genesis was my first and favorite childhood gaming console, and though over the years I’ve come to rely on emulators, re-releases and clone systems to get my Sega nostalgia, I recently decided to set up some original hardware.

Whether you’re new to the system, or a long-time fan, the holy trinity is to get a Sega Genesis along with its two biggest add-ons, the Sega CD and the Sega 32X. Now, one of the major annoyances is that each has its own wall-wart power supply, which are a pain to use: they’re hard to get situated on power strips due to their size and they generate a lot of heat, even with nothing turned on.

I thought there had to be a way to have a single consolidated power supply that supports all three systems at once. And after a little research, I found two solutions:

  1. The Sega Trio, which is a commercial power supply made specifically to solve this problem at the reasonable cost of $28.99. Only problem seems to be the very limited availability.
  2. Follow what others have done (see this forum post), and build my own custom harness on top of a generic power supply.

So I decided to build my own, and document the build along the way.

The Investigation

The first step was to collect all of the relevant power requirements for each system (thanks to this page for a lot of it). For my own setup, I have a model 2 Genesis, model 2 Sega CD, and a 32X. Here’s what the original power supplies look like:

System Voltage Amperage Plug Type Polarity
Sega Genesis Model 2 10V 0.85A 1.7mm Pos. center
Sega CD Model 2 9V 1.2A 2.1mm Neg. center
Sega 32X 10V 0.85A 1.7mm Pos. center

Now the next step is to find a suitable generic power supply. It is important to note that none of these systems actually needs exactly 9 or 10 volts to operate. In fact, the rating on the label is more like the “average voltage” that adapters of that model produce. So a particular one of these power adapters may actually produce anywhere from 8 to 12V.

This is all fine because the first thing each system does is step down that input power to the steady 5 volts that the chips inside are designed to use.

Since it’s very easy to source 9V power supplies, I’ll go with 9V for my generic.

As for the amperage, I need to add up the ratings of each power supply to get a minimum safe value. Amperage ratings show the maximum amperage that the power supplies can safely provide, not the actual amount the systems will necessarily draw. To be safe, OEM manufacturers typically provide power supplies that can offer slightly more amps than they expect their product to draw at full intensity.

So in the original setup, 2.9A is probably safely above the maximum current I’d expect the three systems to draw at once. Round that up to 3A, and now I’m looking for a 9V, minimum 3A power supply, preferably one that isn’t a wall-wart.

The next consideration is how to get one power supply to have three plugs. So I’m going to need to make or buy some kind of harness to split or daisy-chain three separate plugs. As for the plugs themselves, I have couple of things to consider.

Generic power supplies typically come standard with barrel plugs with a 2.1mm diameter center hole with positive polarity. The Genesis Model 2 and 32X both use the more common positive polarity center. Unfortunately, they use the less common EIAJ-03 barrel plugs with a smaller 1.7mm diameter center hole. The Sega CD Model 2 uses the common 2.1mm center hole, but the less common negative polarity center.

So in both cases I’m going to need some adapters.

At this point I have a spectrum of options. On one end is to buy all raw parts and try to solder up a completely custom harness. More extreme, I could even build my own power supply from scratch. On the other end of the spectrum is to try and get away with buying off-the-shelf cables and adapters.

Building from scratch is fine if you have the time, skills and tools. To save money you’ll need to buy parts in bulk, which is great if you’re going to build a bunch, or might use the parts in other projects.

Anyone can buy off-the-shelf parts and just plug them all together. No special skills required. But you will pay a premium.

For this project, I thought it would be interesting to source off-the-shelf parts, just to prove that I could make it work with no soldering required. Yes it probably wouldn’t look as slick, but anyone could replicate it. Also I was feeling lazy. I can always play cable doctor later and transform the off-the-shelf parts into a slicker, more permanent solution.

Bill of Materials

Item Product Quantity Unit Price
9V 3A DC Power Supply Output 9V 3A AC/DC Power Supply Cord Charger IEC320 C8 Socket Adapter 1 $14.37
AC Power Cord SF Cable, 6ft 18 AWG 2-Slot Non-Polarized Power Cord (IEC320 C7 to NEMA 1-15P) 1 5.75
3-Way DC Power Cable Splitter 1:3 DC Power Splitter Cable Cord 1 Female to 3 Male 5.5×2.1mm Port Pigtals 12V 1 3.99
2.1mm to 1.7mm DC Power Adapter Cable 4.8mm x 1.7mm Male Plug to 5.5mm x 2.1mm female socket DC Power Adapter cable 2 3.59
2.1mm Power Jack Polarity Changer MODE 2.1MM JACK-PLUG POLARITY CHANGER BLACK 68-102-0 1 3.80
Subtotal $35.09
Shipping 4.49
Total $39.58

First off, I’ll say that I didn’t spend a ton of time sourcing the parts. Just a couple days casually browsing Amazon and Ebay. If I found something that looked like it’d work, I bought it. For $40, this is definitely not the cheapest way to do this, but it’s not much worse than buying three new power supplies at $9.99 each.

The nicest thing was finding out that the CCTV security camera market supports an industry of nice DC power cable splitters in a variety of configurations, which was the part I assumed I’d have to make myself.

The Build

Once I had the parts, it was a simply a matter of connecting everything. If it’s not straight-forward for you, it goes like this:

  1. Connect AC cord to the power supply.
  2. Connect the 3-way splitter to the power supply.
  3. Connect each of the two 2.1mm to 1.7mm adapter cables to a cable coming out of the 3-way splitter (one each for the Genesis and 32X).
  4. Connect the 2.1mm polarity changer to the remaining cable coming out of the 3-way splitter (for the Sega CD).

The end result should look something like this:

Since the plugs have different sizes, it shouldn’t be too hard to figure out which plug connects to each system. For the sake of simplicity, I put a bit of colored tape on each: red for Genesis, blue for CD, yellow for 32X, corresponding with the primary colors of the game boxes for each system.

The power supply works like a charm. I’m free from the three-wart tyranny!

Have questions or comments? Sound off below.

/jon

Sega Genesis controllers and Arduino revisited

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.

Version 1.0

A couple of months ago I detailed the process by which I used an Arduino Uno to read the button presses of a pair of Sega Genesis controllers. It was my first bit of micro-controller programming, and it has exposed me to a whole new world of possibilities for hardware hacking. So it is with some certain future projects in mind that I’ve revisited and updated that initial code.

Version 1.1

Other than some minor code cleanup, the only major feature-add to the core of the sketch is to support reading the “Mode” button on six-button controllers (thanks to a comment from soe for that). The other change, now that I’ve acquired an Arduino Leonardo, is to create a fork of the sketch which reports the button presses as keyboard key presses via the ATmega32u4 chip’s ability appear as a USB keyboard.

Anyway, here’s the updated code (for reporting over the Serial connection):

/*
 * Sega Controller Reader
 * Author: Jon Thysell <thysell@gmail.com>
 * Version: 1.1
 * Date: 9/29/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.
 *
 */

const int PLAYERS = 2;

// 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;
const int MODE = 4096;

// 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, remaing columns are the button flags
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,  MODE }, // 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,  MODE }, // 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 < PLAYERS; i++)
  {
    pinMode(SELECT[i], OUTPUT);
    digitalWrite(SELECT[i], HIGH);
  }

  Serial.begin(9600);
}

void loop()
{
  readButtons();
  sendStates();
}

void readButtons()
{
  for (int i = 0; i < PLAYERS; 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 < PLAYERS; i++)
  {
    if (currentState[i] != lastState[i])
    {
      hasChanged = true;
    }
  }

  if (hasChanged)
  {
    for (int i = 0; i < PLAYERS; 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((currentState[i] & MODE) == MODE ? "M" : "0");

      Serial.print((i == 0) ? "," : "\n");
      lastState[i] = currentState[i];
    }
  }
}

As with the previous version, 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.

Now, if you have an Arduino Leonardo (or compatible board), and want the controller button presses to map to keyboard keys instead, you can upload the following sketch:

/*
 * Sega Controller Reader (Keyboard)
 * Author: Jon Thysell <thysell@gmail.com>
 * Version: 1.1
 * Date: 9/29/2014
 *
 * Reads buttons presses from Sega Genesis 3/6 button controllers
 * and reports their state via keyboard button presses. Handles hot
 * swapping of controllers and auto-switches between 3 and 6 button
 * polling patterns.
 *
 */

const int PLAYERS = 2;

// 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;
const int MODE = 4096;

// 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,  MODE }, // 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,  MODE }, // P1 DB9 Pin 4
  { 1,  A4, A,     B,      0    }, // P1 DB9 Pin 6
  { 1,  A5, START, C,      0    }  // P1 DB9 Pin 9
};

typedef struct
{
  int player;
  int flag;
  char key;
} output;

// Controller Button Flag to Keyboard Mappings
// First column is the controller index, second column
// is the button flag, third is keyboard key
output outputMap[] = {
  { 0, UP,    KEY_UP_ARROW },
  { 0, DOWN,  KEY_DOWN_ARROW },
  { 0, LEFT,  KEY_LEFT_ARROW },
  { 0, RIGHT, KEY_RIGHT_ARROW },
  { 0, START, KEY_RETURN },
  { 0, A,     'z' },
  { 0, B,     'x' },
  { 0, C,     'c' },
  { 0, X,     'a' },
  { 0, Y,     's' },
  { 0, Z,     'd' },
  { 0, MODE,  'q' },
  { 1, UP,    'i' },
  { 1, DOWN,  'k' },
  { 1, LEFT,  'j' },
  { 1, RIGHT, 'l' },
  { 1, START, 't' },
  { 1, A,     'v' },
  { 1, B,     'b' },
  { 1, C,     'n' },
  { 1, X,     'f' },
  { 1, Y,     'g' },
  { 1, Z,     'h' },
  { 1, MODE,  'r' }
};

// 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 < PLAYERS; i++)
  {
    pinMode(SELECT[i], OUTPUT);
    digitalWrite(SELECT[i], HIGH);
  }

  Keyboard.begin();
}

void loop()
{
  readButtons();
  sendStates();
}

void readButtons()
{
  for (int i = 0; i < PLAYERS; 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()
{
  for (int i = 0; i < sizeof(outputMap) / sizeof(output); i++)
  {
    int last = (lastState[outputMap[i].player] & outputMap[i].flag);
    int current = (currentState[outputMap[i].player] & outputMap[i].flag);

    if (last != current)
    {
      if (current == outputMap[i].flag)
      {
        Keyboard.press(outputMap[i].key);
      }
      else
      {
        Keyboard.release(outputMap[i].key);
      }
    }
  }

  for (int i = 0; i < PLAYERS; i++)
  {
    lastState[i] = currentState[i];
  }
}

If you want to change the button to key mapping, simply update outputMap in the code. You can use any key on the keyboard, including special keys and modifiers.

I hope this is a worthy updated to the sketch – it was a great delight to test it by playing emulated games on my laptop with real Genesis controllers. It worked wonderfully, and I can’t wait to get this integrated into a bigger project.

So happy hacking and stay tuned for more!

/jon

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.