/*
Genesis / Atari / Megadrive :
LOOKING AT THE PLUG ON FRONT OF CONSOLE ( not coming from controller )
1 2 3 4 5
- - - - - - - - - - - - - - - - - - - - - - -
\ o o o o o /
\ o o o o /
\ - - - - - - - - - - - - - - - - - /
6 7 8 9
PIN # USAGE
5 : 5 V VCC
8 : GND
*/
# include "Arduino.h"
# ifndef GAMEPAD_COUNT
# define GAMEPAD_COUNT 2
# endif
# define BUTTON_COUNT 9
# define PIN_COUNT 6
# include "gamepad/Gamepad.h"
enum
{
// this is the button mapping, change as you wish
SC_BTN_A = BUTTON_Y ,
SC_BTN_B = BUTTON_B ,
SC_BTN_C = BUTTON_A ,
SC_BTN_X = BUTTON_L ,
SC_BTN_Y = BUTTON_X ,
SC_BTN_Z = BUTTON_R ,
SC_BTN_START = BUTTON_START ,
SC_BTN_MODE = BUTTON_SELECT ,
SC_BTN_HOME = BUTTON_MENU ,
SC_BTN_UP = 1 ,
SC_BTN_DOWN = 2 ,
SC_BTN_LEFT = 4 ,
SC_BTN_RIGHT = 8 ,
} ;
# include "pins.h"
static const int DATA_PIN_SELECT [ GAMEPAD_COUNT ] = {
OR_PIN_5 ,
# if GAMEPAD_COUNT > 1
OR_PIN_7 ,
# endif
} ;
# if CODE_PLATFORM == 2
# define OPT_PIN_READ1(X) (bitRead(reg1, DATA_PIN[c][X]))
# define OPT_PIN_READ2(X) (bitRead(reg2, DATA_PIN[c][X]))
//individual data pin BIT for each controller, they are read in bulk
static const int DATA_PIN [ GAMEPAD_COUNT ] [ PIN_COUNT ] = {
{ 3 , 2 , 1 , 0 , 4 , 7 } ,
# if GAMEPAD_COUNT > 1
{ 7 , 6 , 5 , 4 , 3 , 1 } ,
# endif
} ;
# else
# define OPT_PIN_READ1(X) (digitalRead(DATA_PIN[c][X]))
# define OPT_PIN_READ2(X) (digitalRead(DATA_PIN[c][X]))
//individual data pin for each controller
static const int DATA_PIN [ GAMEPAD_COUNT ] [ PIN_COUNT ] = {
{ OR_PIN_1 , OR_PIN_11 , OR_PIN_2 , OR_PIN_3 , OR_PIN_4 , OR_PIN_6 } ,
# if GAMEPAD_COUNT > 1
{ OR_PIN_18 , OR_PIN_19 , OR_PIN_20 , OR_PIN_21 , OR_PIN_14 , OR_PIN_15 } ,
# endif
} ;
# endif // CODE_PLATFORM
const byte SC_CYCLE_DELAY = 10 ; // Delay (µs) between setting the select pin and reading the button pins
class SegaControllers32U4 {
public :
SegaControllers32U4 ( void ) ;
void readState ( ) ;
byte currentDpadState [ GAMEPAD_COUNT ] ;
word currentState [ GAMEPAD_COUNT ] ;
// Controller previous states
word lastState [ GAMEPAD_COUNT ] ;
byte lastDpadState [ GAMEPAD_COUNT ] ;
void poll ( void ( * controllerChanged ) ( const int c ) ) {
readState ( ) ;
for ( int c = 0 ; c < GAMEPAD_COUNT ; c + + ) {
if ( currentState [ c ] ! = lastState [ c ] | | currentDpadState [ c ] ! = lastDpadState [ c ] ) {
controllerChanged ( c ) ;
lastState [ c ] = currentState [ c ] ;
lastDpadState [ c ] = currentDpadState [ c ] ;
}
}
}
bool down ( int controller , int button ) const {
return currentDpadState [ controller ] & button ;
}
bool dpad ( int controller , int button ) const {
return currentDpadState [ controller ] & button ;
}
# ifdef DEBUG
void printState ( byte gp ) {
auto cs = currentState [ gp ] ;
Serial . print ( _connected [ gp ] ? " + " : " - " ) ;
Serial . print ( _sixButtonMode [ gp ] ? " 6 " : " 3 " ) ;
Serial . print ( ( cs & SC_BTN_UP ) ? " U " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_DOWN ) ? " D " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_LEFT ) ? " L " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_RIGHT ) ? " R " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_START ) ? " S " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_A ) ? " A " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_B ) ? " B " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_C ) ? " C " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_X ) ? " X " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_Y ) ? " Y " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_Z ) ? " Z " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_MODE ) ? " M " : " 0 " ) ;
Serial . print ( ( cs & SC_BTN_HOME ) ? " H " : " 0 " ) ;
Serial . println ( " sending " ) ;
}
# endif
private :
boolean _pinSelect ;
byte _ignoreCycles [ GAMEPAD_COUNT ] ;
boolean _connected [ GAMEPAD_COUNT ] ;
boolean _sixButtonMode [ GAMEPAD_COUNT ] ;
# if CODE_PLATFORM == 2
void readPort ( byte c , byte reg1 , byte reg2 ) ;
byte _inputReg3 ;
# if GAMEPAD_COUNT > 1
byte _inputReg1 ;
byte _inputReg2 ;
# endif // GAMEPAD_COUNT
# else
void readPort ( byte c ) ;
# endif // CODE_PLATFORM
} ;
SegaControllers32U4 : : SegaControllers32U4 ( void ) {
for ( int c = 0 ; c < GAMEPAD_COUNT ; c + + ) {
// Setup output pin
pinMode ( DATA_PIN_SELECT [ c ] , OUTPUT ) ;
digitalWrite ( DATA_PIN_SELECT [ c ] , HIGH ) ;
// Setup input pins
for ( byte i = 0 ; i < PIN_COUNT ; i + + ) {
pinMode ( DATA_PIN [ c ] [ i ] , INPUT_PULLUP ) ;
}
}
_pinSelect = true ;
for ( int c = 0 ; c < GAMEPAD_COUNT ; c + + ) {
currentState [ c ] = 0 ;
lastState [ c ] = 0 ;
currentDpadState [ c ] = 0 ;
lastDpadState [ c ] = 0 ;
_connected [ c ] = 0 ;
_sixButtonMode [ c ] = false ;
_ignoreCycles [ c ] = 0 ;
}
}
# if CODE_PLATFORM == 2
void SegaControllers32U4 : : readState ( ) {
// Set the select pins low/high
_pinSelect = ! _pinSelect ;
if ( ! _pinSelect ) {
PORTE & = ~ B01000000 ;
PORTC & = ~ B01000000 ;
} else {
PORTE | = B01000000 ;
PORTC | = B01000000 ;
}
// Short delay to stabilise outputs in controller
delayMicroseconds ( SC_CYCLE_DELAY ) ;
// Read all input registers
_inputReg3 = PIND ;
# if GAMEPAD_COUNT > 1
_inputReg1 = PINF ;
_inputReg2 = PINB ;
# endif
readPort ( 0 , _inputReg3 , _inputReg3 ) ;
# if GAMEPAD_COUNT > 1
readPort ( 1 , _inputReg1 , _inputReg2 ) ;
# endif
}
# else
void SegaControllers32U4 : : readState ( ) {
// Set the select pins low/high
_pinSelect = ! _pinSelect ;
if ( ! _pinSelect ) {
for ( int c = 0 ; c < GAMEPAD_COUNT ; c + + ) {
digitalWrite ( DATA_PIN_SELECT [ c ] , LOW ) ;
}
} else {
for ( int c = 0 ; c < GAMEPAD_COUNT ; c + + ) {
digitalWrite ( DATA_PIN_SELECT [ c ] , HIGH ) ;
}
}
// Short delay to stabilise outputs in controller
delayMicroseconds ( SC_CYCLE_DELAY ) ;
for ( int c = 0 ; c < GAMEPAD_COUNT ; c + + ) {
readPort ( c ) ;
}
}
# endif
// "Normal" Six button controller reading routine, done a bit differently in this project
// Cycle TH out TR in TL in D3 in D2 in D1 in D0 in
// 0 LO Start A 0 0 Down Up
// 1 HI C B Right Left Down Up
// 2 LO Start A 0 0 Down Up (Check connected and read Start and A in this cycle)
// 3 HI C B Right Left Down Up (Read B, C and directions in this cycle)
// 4 LO Start A 0 0 0 0 (Check for six button controller in this cycle)
// 5 HI C B Mode X Y Z (Read X,Y,Z and Mode in this cycle)
// 6 LO --- --- --- --- --- Home (Home only for 8bitdo wireless gamepads)
// 7 HI --- --- --- --- --- ---
void SegaControllers32U4 : : readPort ( byte c
# if CODE_PLATFORM == 2
,
byte reg1 ,
byte reg2
# endif
) {
if ( _ignoreCycles [ c ] < = 0 ) {
if ( _pinSelect ) // Select pin is HIGH
{
if ( _connected [ c ] ) {
// Check if six button mode is active
if ( _sixButtonMode [ c ] ) {
// Read input pins for X, Y, Z, Mode
( OPT_PIN_READ1 ( 0 ) = = LOW ) ? currentState [ c ] | = SC_BTN_Z : currentState [ c ] & = ~ SC_BTN_Z ;
( OPT_PIN_READ1 ( 1 ) = = LOW ) ? currentState [ c ] | = SC_BTN_Y : currentState [ c ] & = ~ SC_BTN_Y ;
( OPT_PIN_READ1 ( 2 ) = = LOW ) ? currentState [ c ] | = SC_BTN_X : currentState [ c ] & = ~ SC_BTN_X ;
( OPT_PIN_READ1 ( 3 ) = = LOW ) ? currentState [ c ] | = SC_BTN_MODE : currentState [ c ] & = ~ SC_BTN_MODE ;
_sixButtonMode [ c ] = false ;
_ignoreCycles [ c ] = 2 ; // Ignore the two next cycles (cycles 6 and 7 in table above)
} else {
// Read input pins for Up, Down, Left, Right, B, C
( OPT_PIN_READ1 ( 0 ) = = LOW ) ? currentDpadState [ c ] | = SC_BTN_UP : currentDpadState [ c ] & = ~ SC_BTN_UP ;
( OPT_PIN_READ1 ( 1 ) = = LOW ) ? currentDpadState [ c ] | = SC_BTN_DOWN : currentDpadState [ c ] & = ~ SC_BTN_DOWN ;
( OPT_PIN_READ1 ( 2 ) = = LOW ) ? currentDpadState [ c ] | = SC_BTN_LEFT : currentDpadState [ c ] & = ~ SC_BTN_LEFT ;
( OPT_PIN_READ1 ( 3 ) = = LOW ) ? currentDpadState [ c ] | = SC_BTN_RIGHT : currentDpadState [ c ] & = ~ SC_BTN_RIGHT ;
( OPT_PIN_READ2 ( 4 ) = = LOW ) ? currentState [ c ] | = SC_BTN_B : currentState [ c ] & = ~ SC_BTN_B ;
( OPT_PIN_READ2 ( 5 ) = = LOW ) ? currentState [ c ] | = SC_BTN_C : currentState [ c ] & = ~ SC_BTN_C ;
}
} else // No Mega Drive controller is connected, use SMS/Atari mode
{
// Clear current state
currentState [ c ] = currentDpadState [ c ] = 0 ;
// Read input pins for Up, Down, Left, Right, Fire1, Fire2
if ( OPT_PIN_READ1 ( 0 ) = = LOW ) {
currentDpadState [ c ] | = SC_BTN_UP ;
}
if ( OPT_PIN_READ1 ( 1 ) = = LOW ) {
currentDpadState [ c ] | = SC_BTN_DOWN ;
}
if ( OPT_PIN_READ1 ( 2 ) = = LOW ) {
currentDpadState [ c ] | = SC_BTN_LEFT ;
}
if ( OPT_PIN_READ1 ( 3 ) = = LOW ) {
currentDpadState [ c ] | = SC_BTN_RIGHT ;
}
if ( OPT_PIN_READ2 ( 4 ) = = LOW ) {
currentState [ c ] | = SC_BTN_A ;
}
if ( OPT_PIN_READ2 ( 5 ) = = LOW ) {
currentState [ c ] | = SC_BTN_B ;
}
}
} else // Select pin is LOW
{
// Check if a controller is connected
_connected [ c ] = OPT_PIN_READ1 ( 2 ) = = LOW & & OPT_PIN_READ1 ( 3 ) = = LOW ;
// Check for six button mode
_sixButtonMode [ c ] = OPT_PIN_READ1 ( 0 ) = = LOW & & OPT_PIN_READ1 ( 1 ) = = LOW ;
// Read input pins for A and Start
if ( _connected [ c ] ) {
if ( ! _sixButtonMode [ c ] ) {
( OPT_PIN_READ2 ( 4 ) = = LOW ) ? currentState [ c ] | = SC_BTN_A : currentState [ c ] & = ~ SC_BTN_A ;
( OPT_PIN_READ2 ( 5 ) = = LOW ) ? currentState [ c ] | = SC_BTN_START : currentState [ c ] & = ~ SC_BTN_START ;
}
}
}
} else {
if ( _ignoreCycles [ c ] - - = = 2 ) // Decrease the ignore cycles counter and read 8bitdo home in first "ignored" cycle, this cycle is unused on normal 6-button controllers
{
( OPT_PIN_READ1 ( 0 ) = = LOW ) ? currentState [ c ] | = SC_BTN_HOME : currentState [ c ] & = ~ SC_BTN_HOME ;
}
}
}
SegaControllers32U4 controllers ;
GAMEPAD_CLASS gamepad ;
void controllerChanged ( const int c ) {
# ifdef DEBUG
controllers . printState ( c ) ;
# endif
// if start and down are held at the same time, send menu and only menu
gamepad . buttons ( c , controllers . currentState [ c ] ) ;
if ( controllers . down ( c , SC_BTN_START ) & & controllers . dpad ( c , SC_BTN_DOWN ) ) {
gamepad . buttons ( c , 0 ) ;
gamepad . press ( c , BUTTON_MENU ) ;
gamepad . setHatSync ( c , DPAD_CENTERED ) ;
return ;
}
if ( controllers . dpad ( c , SC_BTN_DOWN ) ) {
if ( controllers . dpad ( c , SC_BTN_RIGHT ) ) {
gamepad . setHatSync ( c , DPAD_DOWN_RIGHT ) ;
} else if ( controllers . dpad ( c , SC_BTN_LEFT ) ) {
gamepad . setHatSync ( c , DPAD_DOWN_LEFT ) ;
} else {
gamepad . setHatSync ( c , DPAD_DOWN ) ;
}
} else if ( controllers . dpad ( c , SC_BTN_UP ) ) {
if ( controllers . dpad ( c , SC_BTN_RIGHT ) ) {
gamepad . setHatSync ( c , DPAD_UP_RIGHT ) ;
} else if ( controllers . dpad ( c , SC_BTN_LEFT ) ) {
gamepad . setHatSync ( c , DPAD_UP_LEFT ) ;
} else {
gamepad . setHatSync ( c , DPAD_UP ) ;
}
} else if ( controllers . dpad ( c , SC_BTN_RIGHT ) ) {
gamepad . setHatSync ( c , DPAD_RIGHT ) ;
} else if ( controllers . dpad ( c , SC_BTN_LEFT ) ) {
gamepad . setHatSync ( c , DPAD_LEFT ) ;
} else {
gamepad . setHatSync ( c , DPAD_CENTERED ) ;
}
}
void setup ( ) {
# ifdef DEBUG
Serial . begin ( 115200 ) ;
# endif
gamepad . begin ( ) ;
}
void loop ( ) {
if ( gamepad . isConnected ( ) ) {
controllers . poll ( controllerChanged ) ;
}
}