//Sega Megadrive/Genesis 6-button controller adapter by Kurg 2.9.2016
//Tested with Arduino Pro Micro (ATmega32U4, 5V) from Ebay ($4) 
//Selected board from Arduino IDE: Arduino Leonardo
//Joystick2 library from https://github.com/MHeironimus/ArduinoJoystickLibrary
//https://www.cs.cmu.edu/~chuck/infopg/segasix.txt

#include "Joystick2.h"

//DB9 pin (9-pin D-connector)
//Select low  Select high   3rd pulse
//1=Up        Up            Z
//2=Down      Down          Y
//3=Ground    Left          X
//4=Ground    Right
//5=+5V
//6=A         B
//7=Control("Select")
//8=Ground
//9=Start     C

#define EVENTS_TOTAL 4+6+1 //4 directions, 6 fire-buttons and Start
#define INPUT_PINS_TOTAL 6

//DB9 (port1):                      1   2   3   4   6   9
const uint8_t inputPinsPort1[] =  {10, 16, 14, 15,  3, A1};
#define VCC_PORT1 A0 //DB9 (port1) pin 5 //comment out if not connected to IO-pin
#define MODE_SELECT_PORT1 A3 //DB9 (port1) pin 7
//DB9 (port 2) pin 8 = GND

//DB9 (port2):                      1   2   3   4   6   9
const uint8_t inputPinsPort2[] =  { 5,  6,  7,  8,  4, A2};
#define VCC_PORT2 9 //DB9 (port 2) pin 5 //comment out if not connected to IO-pin
#define MODE_SELECT_PORT2 2 //DB9 (port2) pin 7
//DB9 (port 2) pin 8 = GND

uint8_t lastStatusPort1[EVENTS_TOTAL];
uint8_t newStatusPort1[EVENTS_TOTAL];
uint8_t lastStatusPort2[EVENTS_TOTAL];
uint8_t newStatusPort2[EVENTS_TOTAL];


void modeSelect(uint8_t m) {
  digitalWrite(MODE_SELECT_PORT1, m);
  digitalWrite(MODE_SELECT_PORT2, m);
  delayMicroseconds(20);
}


void setup() {

  //clear statusarrays (1=OFF, 0=ON)
  for (uint8_t i = 0; i < EVENTS_TOTAL; i++) {
    lastStatusPort1[i] = 1;
    newStatusPort1[i] = 1;
    lastStatusPort2[i] = 1;
    newStatusPort2[i] = 1;
  }

  #if defined(VCC_PORT1) && defined(VCC_PORT2)
  pinMode(VCC_PORT1, OUTPUT);
  pinMode(VCC_PORT2, OUTPUT);
  digitalWrite(VCC_PORT1, HIGH);
  digitalWrite(VCC_PORT2, HIGH);
  #endif

  for (int i=0; i < INPUT_PINS_TOTAL; i++) {
    pinMode(inputPinsPort1[i], INPUT_PULLUP);
    pinMode(inputPinsPort2[i], INPUT_PULLUP);
  }
  
  pinMode(MODE_SELECT_PORT1, OUTPUT);
  pinMode(MODE_SELECT_PORT2, OUTPUT);
  modeSelect(HIGH);

  Joystick[0].begin(false);
  Joystick[1].begin(false);

}


void read3buttons() {
  
  modeSelect(LOW);
  
  newStatusPort1[4] = digitalRead(inputPinsPort1[4]); //A1
  newStatusPort1[7] = digitalRead(inputPinsPort1[5]); //Start1
  newStatusPort2[4] = digitalRead(inputPinsPort2[4]); //A2
  newStatusPort2[7] = digitalRead(inputPinsPort2[5]); //Start2  

  modeSelect(HIGH);

  for (uint8_t i=0; i < 4; i++) {
    newStatusPort1[i] = digitalRead(inputPinsPort1[i]); //AXES1
    newStatusPort2[i] = digitalRead(inputPinsPort2[i]); //AXES2
  }
  newStatusPort1[5] = digitalRead(inputPinsPort1[4]); //B1
  newStatusPort1[6] = digitalRead(inputPinsPort1[5]); //C1
  newStatusPort2[5] = digitalRead(inputPinsPort2[4]); //B2
  newStatusPort2[6] = digitalRead(inputPinsPort2[5]); //C2
  
}


uint8_t flag1 = 0;
uint8_t flag2 = 0;

void loop() {

  read3buttons();

  //read X,Y,Z
  modeSelect(LOW);
  modeSelect(HIGH);
  modeSelect(LOW);
  modeSelect(HIGH);
  newStatusPort1[8] = digitalRead(inputPinsPort1[2]); //X1
  newStatusPort1[9] = digitalRead(inputPinsPort1[1]); //Y1
  newStatusPort1[10] = digitalRead(inputPinsPort1[0]); //Z1
  newStatusPort2[8] = digitalRead(inputPinsPort2[2]); //X2
  newStatusPort2[9] = digitalRead(inputPinsPort2[1]); //Y2
  newStatusPort2[10] = digitalRead(inputPinsPort2[0]); //Z2
  delayMicroseconds(1000);

  //check for changes - do not raise a flag if nothing changes
  for (uint8_t i=0; i < EVENTS_TOTAL; i++) {
    if (newStatusPort1[i] != lastStatusPort1[i]) {
      lastStatusPort1[i] = newStatusPort1[i]; 
      flag1 = 1;
    }
    if (newStatusPort2[i] != lastStatusPort2[i]) {
      lastStatusPort2[i] = newStatusPort2[i]; 
      flag2 = 1;
    }
  }

  if (flag1) {
    Joystick[0].setYAxis(0);
    Joystick[0].setXAxis(0);
    if (!newStatusPort1[0]) Joystick[0].setYAxis(-127); //UP
    if (!newStatusPort1[1]) Joystick[0].setYAxis(127); //DOWN
    if (!newStatusPort1[2]) Joystick[0].setXAxis(-127); //LEFT
    if (!newStatusPort1[3]) Joystick[0].setXAxis(127); //RIGHT
    Joystick[0].setButton(0, !newStatusPort1[4]); //BUTTON1
    Joystick[0].setButton(1, !newStatusPort1[5]); //BUTTON2
    Joystick[0].setButton(2, !newStatusPort1[6]); //BUTTON3
    Joystick[0].setButton(3, !newStatusPort1[7]); //BUTTON4
    Joystick[0].setButton(4, !newStatusPort1[8]); //BUTTON5
    Joystick[0].setButton(5, !newStatusPort1[9]); //BUTTON6
    Joystick[0].setButton(6, !newStatusPort1[10]); //BUTTON7
    Joystick[0].sendState();
    flag1 = 0;
  }
    
  if (flag2) {
    Joystick[1].setYAxis(0);
    Joystick[1].setXAxis(0);
    if (!newStatusPort2[0]) Joystick[1].setYAxis(-127); //UP
    if (!newStatusPort2[1]) Joystick[1].setYAxis(127); //DOWN
    if (!newStatusPort2[2]) Joystick[1].setXAxis(-127); //LEFT
    if (!newStatusPort2[3]) Joystick[1].setXAxis(127); //RIGHT
    Joystick[1].setButton(0, !newStatusPort2[4]); //BUTTON1
    Joystick[1].setButton(1, !newStatusPort2[5]); //BUTTON2
    Joystick[1].setButton(2, !newStatusPort2[6]); //BUTTON3
    Joystick[1].setButton(3, !newStatusPort2[7]); //BUTTON4
    Joystick[1].setButton(4, !newStatusPort2[8]); //BUTTON5
    Joystick[1].setButton(5, !newStatusPort2[9]); //BUTTON6
    Joystick[1].setButton(6, !newStatusPort2[10]); //BUTTON7
    Joystick[1].sendState();
    flag2 = 0;
  }

 
}
