Saturn Controller Protocol MK80116-MK80100

Includes but not limited to: SNES, Genesis, Sega CD, PlayStation 1, Nintendo 64, Dreamcast, Game Gear and I guess the Virtual Boy.

Moderator: Moderators

Post Reply
User avatar
Posts: 349
Joined: Thu Jul 03, 2008 9:55 pm
Location: VA

Saturn Controller Protocol MK80116-MK80100

Post by RDC » Fri Mar 15, 2013 4:06 am

Hey all,

Most people that have seen my work know it's pretty much all done on the hardware side of things, but here over the last few years I've been trying to get into the coding side of things as well.

I've been tinkering around here with the different protocols controllers use, so instead of hacking the thing up up I can just plug it into something. The PSX (neGcon specifically), N64 and now I've been messing with the Saturn.

The Saturn controller connector has 9 pins. Looking into the end of the plug on the controller, beveled side up..

Code: Select all

| 987654321 |
1 - 5v (Power to Controller)
2 - D1 (Data 1)
3 - D0 (Data 0)
4 - S1 (Select 1)
5 - S0 (Select 0)
6 - Detect/?
7 - D3 (Data 3)
8 - D2 (Data 2)
9 - Ground

The standard 6 button Saturn controller uses 2 Select Bits, S1 and S0 and then it has 4 Data Bits, D3, D2, D1 and D0. From those 2 select bits, 4 different states can be set, 10, 01, 00 and 11 and then from each of those 4 different states 4 bits of data can be read from the D3, D2, D1 and D0 lines.

Select Bits - Data Bits
S1 S0 - D3, D2, D1, D0

10 - DR, DL, DD, DU
01 - ST, A, C, B
00 - R, X, Y, Z
11 - L, *, *, *

* are not used.

To 'read' what button on the controller is pressed, you change the Select bits and then 'see' what's going on with the 4 Data bits. Any button that is pressed will be a Logic 0 (ground) while unpressed buttons will have a Logic 1 (voltage) there.

This is what it looks like on a Logic Analyzer with no buttons pressed. You can see the S1 (green) and S0 (yellow) lines changing states. The D3, D2, D1 and D0 lines are all Hi (Logic 1) during these changes, when there is a 0 there then you know a button is pressed.


Now if only the A button were pressed, you'd get this..

10 - 1,1,1,1
01 - 1,0,1,1 <-- The A button is pressed here
00 - 1,1,1,1
11 - 1,*,*,*

Then what it looks like on the Logic Analyzer. Only the Data line for A is Lo, and only during the time that the Select Bits are 01.


Then if DR, C and X are pressed..

10 - 0,1,1,1 <-- DR is pressed here
01 - 1,1,0,1 <--C is pressed here
00 - 1,0,1,1 <--X is pressed here
11 - 1,*,*,*

Then again what that looks like on the LA, and again those Data lines are only Lo during the times the Select Bits are for each one.


This all may seem like a lot to check, with having to switch between the 4 different states and then checking 4 lines for each one, but it takes around 150us for all of that to happen 1 time, and the Saturn does this check roughly once every 16ms, or in the time it takes you to blink it's already done it more than 20 times. This can also be done faster as well

This was my test setup. MK-80100 controller. PIC16F1516 on the bread board. PicKit3 for powering the PIC and Controller. Saleae Logic for seeing what's going on, then a 16x2 LCD display that's powered from an external 5v PSU. (because I can only watch LEDs blink on a port for so long)


Close up of the LCD. The second line are the buttons, first the D-pad, Up, Down, Left and Right, then Start, followed by the A,B,C,X,Y and Z buttons then the L and R shoulder buttons.


Very, very, crappy video of it working. Apologies for how awful it is, but my camera is in fact just a camera, not a video camera, and some blame to PB for resizing it, as it looks a little better in it's native resolution, but still nothing to write home about.

When a button is pressed that disappears from the LCD screen, unpressed and it's back - ... 5.mp4.html" onclick=";return false;

This was done on a PIC16F1516, Source code was made in MikroC PRO. The LCD portion of the code has been removed, as I doubt anyone that wants to tinker with this will want to specifically do just that, but if you have one and want to wire it all up like I did let me know and I can post up everything for how to do it.

Code: Select all

SEGA Saturn 6 Button Controller Tester/Interface (Tested on the MK80116 and MK80100)

PIC16F1516 @16MHz Internal OSC

Select Bits S1 and S0, will always start at 11 and end at 11, but are never
11 while reading data from the D3, D2, D1 and D0 lines.

The Select Bit "stepping" for the MK80116 and MK80100 pads are..

11    // Waiting
10    // First half of Data1 (Byte1) on the D3, D2, D1 and D0 lines = DR, DL, DD, DU
01    // Second half of Data1 = ST, A, C, B
00    // First half of Data2 = R, X, Y, Z
11    // Second half of Data2, = L, -, -

There is roughly a 16ms interval between reading the last bits of data
and then starting over, so the controller is checked every 16ms or so
for any changes to the button states

The Saturn controllers run from a 5v power source. They can run from 3.3v at the very least, but a 5v source is recommended

2013 RDC

char DR, DL, DU, DD, ST, AB, CB, BB, RB, XB, YB, ZB, LB;

char BYTE1, BYTE2;

// Initialize the PIC
void INIT() {
OSCCON = 0b01111010;                     // 8MHz 0b01110010; // 16MHz 0b01111010;
ANSELA = 0b00000000;                     // All AN are Digital
ANSELB = 0b00000000;                     // All AN are Digital
ANSELC = 0b00000000;                     // All AN are Digital
TRISA = 0b00000000;                      // PORTA
TRISB = 0b11000000;                      // PORTB 6 and 7 reserved for ICSP
TRISC = 0b00001111;                      // PORTC
PORTC = 0b00110000;                      // Select Bits, S1 = 1 and S0 = 1

void main() {


    PORTC = 0b00100000;                      // Select Bits are 10

    TEMP_NIBBLE = PORTC & 0b00001111;        // Read lower 4 bits from PORTC

    BYTE1 = TEMP_NIBBLE <<= 4;               // Shift that data over and save

    PORTC = 0b00010000;                      // Select Bits are 01

    TEMP_NIBBLE = PORTC & 0b00001111;        // Read lower 4 bits from PORTC

    BYTE1 = BYTE1 + TEMP_NIBBLE;             // Add new data to shifted data
                                             // this makes the 8 Bits of Byte1
    PORTC = 0b00000000;                      // Select Bits are 00

    TEMP_NIBBLE = PORTC & 0b00001111;        // Read lower 4 bits from PORTC

    BYTE2 = TEMP_NIBBLE <<= 4;               // Shift that data over and save

    PORTC = 0b00110000;                      // Select Bits are 11

    TEMP_NIBBLE = PORTC & 0b00001111;        // Read lower 4 bits from PORTC

    BYTE2 = BYTE2 + TEMP_NIBBLE;             // Add new data to shifted data
                                             // this makes the 8 Bits of Byte2

// Masking to look at each bit of the Bytes individually
// The Bit is >= 1 when not pressed, or 0 when pressed

    DR = BYTE1 & 0b10000000;
    DL = BYTE1 & 0b01000000;
    DD = BYTE1 & 0b00100000;
    DU = BYTE1 & 0b00010000;
    ST = BYTE1 & 0b00001000;
    AB = BYTE1 & 0b00000100;
    CB = BYTE1 & 0b00000010;
    BB = BYTE1 & 0b00000001;

    RB = BYTE2 & 0b10000000;
    XB = BYTE2 & 0b01000000;
    YB = BYTE2 & 0b00100000;
    ZB = BYTE2 & 0b00010000;
    LB = BYTE2 & 0b00001000;
// From here on out you can code for whatever you're wanting the PIC to do
// when a button on the controller is pressed. 

// This is where some of the LCD output code was from my testing, but anything can be put here.

/******************************* EXAMPLE 1 *************************************

  if (DU == 0)     // If DU is pressed..
  {}               // this code
  else             // Otherwise..
  {}               // this code
******************************** EXAMPLE 2 *************************************

  PORTA = BYTE1;    // PORTS A and B display the button states
                    // LEDS would be on, then turn off with button press

  // Alternately
  PORTA = ~BYTE1;   // Would make the LEDs turn on when a button is pressed

Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.
Re-mappable Wireless 360 Controller - The CGnome

Post Reply