Saturn Controller Protocol MK80117 and Emulation

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
RDC
Posts:349
Joined:Thu Jul 03, 2008 9:55 pm
Location:VA
Contact:
Saturn Controller Protocol MK80117 and Emulation

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

After tinkering around with the standard 6 button Saturn pads, MK-80116 and MK-80100. http://forums.benheck.com/viewtopic.php?f=58&t=72308" onclick="window.open(this.href);return false;

I figured I'd give the 3D (Nights) Controller a look see. This is based on the reader understanding Binary, specifically 8-bits ( or 1 Byte) or at the very least being able to count to 15 (4 bits) which if you can't do, Google it up, it's fun, you'll learn something and you can then appreciate that old joke, "There are 10 kinds of people that understand Binary, those that do, and those that don't. ;)

Now, the 3D Controller is by far different from the protocol used on the standard 6 buttons pads. Those only use 6 lines for communication, 2 Select bits and 4 Data, while the 3D Controller uses 7 lines.

The connector pinout is the same, looking into the end of the controller's connector, beveled side up.

Code: Select all

/-----------\
| 987654321 |
|___________|
1 - 5v (Power to Controller)
2 - D1 (Data 1)
3 - D0 (Data 0)
4 - Request or REQ (Select 1, also known as TR)
5 - Select or SEL (Select 0, also known as TH)
6 - Acknowledge or ACK (also known as TL)
7 - D3 (Data 3)
8 - D2 (Data 2)
9 - Ground

NOTE* All 4 Data and the TR, TH, TL lines are pulled up to 5v with 100k Resistors inside the controller.

So here we see the data that's sent when the 3D Controller has the switch in the 'Digital' position.

Image

Then here it is with the switch in the 'Analog' position.

Image

The 4 Data lines still do the same thing, which is send 4 bits of Data at once. This is half of a Byte, which is 8 bits, so the 4 bits of Data here is called a 'nibble'. But the 3 T lines on the 3D controller differs in how that Data is sent.

The TH line acts like a 'Select', when it's pulled Lo then that controller is the one that's about to be queried for Data.

The TR line is a 'Request' from the Saturn for Data to be sent from the controller. When it changes states that's when it's Requesting Data to be sent, so from Hi to Lo is a Request, as is going from Lo to Hi.

The TL line is an 'Acknowledge' that the Data is ready for the Saturn to read it. This line 'follows' the TR 'Request' line, so when TR goes from Hi to Lo, after the Data is ready to be read, then the TL line also goes from Hi to Lo, or vice-versa depending on where you're starting from.

The 6 button pads only used 2 Bytes of Data, which were derived from the 4 states that the TR (S1) and TL (S0) lines could be put into. The 3D controller does this a little differently. The first Byte of Data sent is for both the ID for the type of controller, Digital, Analog, etc., and also the number of Bytes needed for the rest of the Data.

The first Byte sent goes like this.

First the SEL line is pulled Lo (yellow arrow down) This means the Saturn is getting ready to ask for Data from that device.
Second the REQ line goes Lo (green arrow down) This is the Saturn asking for Data from the controller.
Third the ACK line goes Lo (blue arrow down) This is the controller Acknowledging that the Data, 0000 for Digital controller in this case, is ready for the Saturn to read from D3~D0.
Fourth the REQ line goes Hi (green arrow up) This is the Saturn again requesting Data from the controller.
Fifth the ACK line goes Hi (blue arrow up) This is the controller again Acknowledging that the Data is ready, but this time it is 0010, which means 2 Bytes for the remaining Data.

This is how that first Byte looks on the Logic Analyzer.

Image

Now, following those same steps, when the controller is in the 'Analog' mode, the first Byte is then 0001-0110, which is 0001 for Analog, then 0110 for 6 Bytes of Data.

Image

Like the 6 button pads, my test setup used the PIC16F1516, PicKit 3, Saleae Logic Analyzer and 16x2 LCD screen.

Image

Close up of the screen in Digital mode.

Image

..Analog mode..

Image

..and no 3D controller connected.

Image

Another just awful video of this one working also - http://s50.beta.photobucket.com/user/RD ... 1.mp4.html" onclick="window.open(this.href);return false;

I'll post the code up for this one later on once I go back thru it all as I want to give it a bit of an overhaul.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Now, Emulating the 3D controller. That is, making the Saturn 'think' you have a 3D controller connected, when it fact, it's just a PIC, which is in this case again, the 16F1516. When using an external Crystal like I did here, this thing has just enough I/O (Input/Output) pins to pull this off.

This is also not 100% complete yet either. I do have both Digital and Analog modes working, but I don't have them setup so I can just flip a switch and go back and forth between them yet.

The buttons at the top are, from left to right, L, R, X, Y, Z, Start, A, C, B, D-pad R, L, D and U. The Stick is just from an old 360 controller. The original 3D controller uses a Hall Sensor type of Stick, which uses magnets, sensors and an OpAmp to do pretty much the exact same thing that a POT style Stick does.

Image

This is the Data being sent between the PIC and the Saturn, which looks a lot like the pic above from the Data capture from the 3D controller in the Analog mode, but that was the idear. ;)

Image

This is just the Digital portion of the code.

Code: Select all

/*******************************************************************************
SEGA SATURN 3D CONTROLLER EMULATOR

PIC16F1516
EXTERNAL OSC @ 16MHz
5v POWER FROM SATURN

RDC 2013
*******************************************************************************/

char SPID, DATA_SIZE;

char DATA1_1, DATA1_2;
char DATA2_1, DATA2_2;
char DATA3_1, DATA3_2;

#define TH_SEL RA3_bit                   // Configure as Input
#define TR_REQ RA4_bit                   // Configure as Input
#define TL_ACK RA5_bit                   // Configure as Output

// Initialize the PIC
void INIT() {
ANSELA = 0b00000011;                     // AN0 and AN1 Analog to X and Y Axis
ANSELB = 0b00000000;                     // All AN are Digital
ANSELC = 0b00000000;                     // All AN are Digital
TRISA = 0b00011111;                      // PORTA
TRISB = 0b11111111;                      // PORTB, pins 6 and 7 also for ICSP
TRISC = 0b11110000;                      // PORTC
TRISE = 0b00001000;                      // PORTE, only 1 pin, RE3
TL_ACK = 1;
}


void main() {

   INIT();
     
  SPID = 0b00000000;
  DATA_SIZE = 0b00000010;

///// DIGITAL:

/////  Set Initial Button DATA for power up

    DATA1_1 = 0b11111111;                 // DATA1_1
    DATA1_2 = 0b11111111;                 // DATA1_2
    DATA2_1 = 0b11111111;                 // DATA2_1
    DATA2_2 = 0b11111111;                 // DATA2_2
    DATA3_1 = 0b00000000;                 // DATA3_1
    DATA3_2 = 0b00000001;                 // DATA3_2

  delay_ms(500);   // Wait for the Saturn to start up (takes around 1.5s)

  while(1) {

///// START COMMS WITH SATURN

    while(TH_SEL == 1) {}   // Wait for the Saturn to select controller
    delay_us(40);

    while (TR_REQ == 1) {}  // Wait for Saturn to request SPID
    PORTC = SPID;           // SPID  0000 = Digital  // 0001 = Analog
    TL_ACK = 0;             // Here is the data
     
    while (TR_REQ == 0) {}  // Wait for Saturn to request Data Size
    PORTC = DATA_SIZE;      // DATA_SIZE  0010 = 2 Bytes, 0110 = 6 Bytes
    TL_ACK = 1;             // Here is the data
     
///// START FIRST BYTE
   
    while (TR_REQ == 1) {}  // Wait for Saturn to request first half of Data1
    PORTC = DATA1_1;        // DR, DL, DD, DU
    TL_ACK = 0;             // Here is the data
      
    while (TR_REQ == 0) {}  // Wait for Saturn to request second half of Data1
    PORTC = DATA1_2;        // ST, A, C, B
    TL_ACK = 1;             // Here is the data
    
///// END FIRST BYTE

///// START SECOND BYTE

    while (TR_REQ == 1) {}  // Wait for Saturn to request first half of Data2
    PORTC = DATA2_1;        // R, X, Y, Z
    TL_ACK = 0;             // Here is the data

    while (TR_REQ == 0) {}  // Wait for Saturn to request second half of Data2
    PORTC = DATA2_2;        // L, 1, 1, 1 
    TL_ACK = 1;             // Here is the data

///// END SECOND BYTE

///// START THIRD BYTE, END OF DATA

    while (TR_REQ == 1) {}  // Wait for Saturn to request first half End
    PORTC = DATA3_1;        // 0000
    TL_ACK = 0;             // Here is the data

    while (TR_REQ == 0) {}  // Wait for Saturn to request second half of End
    PORTC = DATA3_2;        // 0001
    TL_ACK = 1;             // Here is the data

///// END THIRD BYTE

    while(TH_SEL == 0) {}   // Wait for the Saturn to deselect controller
    
///// END COMMS WITH SATURN
    
/////  Get Current Button DATA (in less than 15ms)

    DATA1_1 = PORTC >> 4;                 // DATA1_1
    DATA1_2 = PORTB;                      // DATA1_2
    DATA2_1 = PORTB >> 4;                 // DATA2_1
    DATA2_2 = (PORTA << 1) + 7;           // DATA2_2
    DATA3_1 = 0b00000000;                 // DATA3_1
    DATA3_2 = 0b00000001;                 // DATA3_2

  }
}
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