/* Name: snes.c * Project: Multiple NES/SNES to USB converter * Author: Raphael Assenat * Copyright: (C) 2007 Raphael Assenat * License: Proprietary, free under certain conditions. See Documentation. * Tabsize: 4 */ #define F_CPU 12000000L #include #include #include #include #include #include "gamepad.h" #include "leds.h" #include "snes.h" #define GAMEPAD_BYTES 8 /* 2 byte per snes controller * 4 controllers */ /******** IO port definitions **************/ #define SNES_LATCH_DDR DDRC #define SNES_LATCH_PORT PORTC #define SNES_LATCH_BIT (1<<4) #define SNES_CLOCK_DDR DDRC #define SNES_CLOCK_PORT PORTC #define SNES_CLOCK_BIT (1<<5) #define SNES_DATA_PORT PORTC #define SNES_DATA_DDR DDRC #define SNES_DATA_PIN PINC #define SNES_DATA_BIT1 (1<<3) /* controller 1 */ #define SNES_DATA_BIT2 (1<<2) /* controller 2 */ #define SNES_DATA_BIT3 (1<<1) /* controller 3 */ #define SNES_DATA_BIT4 (1<<0) /* controller 4 */ /********* IO port manipulation macros **********/ #define SNES_LATCH_LOW() do { SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); } while(0) #define SNES_LATCH_HIGH() do { SNES_LATCH_PORT |= SNES_LATCH_BIT; } while(0) #define SNES_CLOCK_LOW() do { SNES_CLOCK_PORT &= ~(SNES_CLOCK_BIT); } while(0) #define SNES_CLOCK_HIGH() do { SNES_CLOCK_PORT |= SNES_CLOCK_BIT; } while(0) #define SNES_GET_DATA1() (SNES_DATA_PIN & SNES_DATA_BIT1) #define SNES_GET_DATA2() (SNES_DATA_PIN & SNES_DATA_BIT2) #define SNES_GET_DATA3() (SNES_DATA_PIN & SNES_DATA_BIT3) #define SNES_GET_DATA4() (SNES_DATA_PIN & SNES_DATA_BIT4) /*********** prototypes *************/ static void snesInit(void); static void snesUpdate(void); static char snesChanged(char report_id); static char snesBuildReport(unsigned char *reportBuffer, char report_id); // the most recent bytes we fetched from the controller static unsigned char last_read_controller_bytes[GAMEPAD_BYTES]; // the most recently reported bytes static unsigned char last_reported_controller_bytes[GAMEPAD_BYTES]; // indicates if a controller is in NES mode static unsigned char nesMode=0; /* Bit0: controller 1, Bit1: controller 2...*/ static void snesInit(void) { unsigned char sreg; sreg = SREG; cli(); // clock and latch as output SNES_LATCH_DDR |= SNES_LATCH_BIT; SNES_CLOCK_DDR |= SNES_CLOCK_BIT; // data as input SNES_DATA_DDR &= ~(SNES_DATA_BIT1 | SNES_DATA_BIT2 | SNES_DATA_BIT3 | SNES_DATA_BIT4 ); // enable pullup. This should prevent random toggling of pins // when no controller is connected. SNES_DATA_PORT |= (SNES_DATA_BIT1 | SNES_DATA_BIT2 | SNES_DATA_BIT3 | SNES_DATA_BIT4 ); // clock is normally high SNES_CLOCK_PORT |= SNES_CLOCK_BIT; // LATCH is Active HIGH SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); nesMode = 0; snesUpdate(); /* Snes controller buttons are sent in this order: * 1st byte: B Y SEL START UP DOWN LEFT RIGHT * 2nd byte: A X L R 1 1 1 1 * * Nes controller buttons are sent in this order: * One byte: A B SEL START UP DOWN LEFT RIGHT * * When an additional byte is read from a NES controller, * all bits are 0. Because the data signal is active low, * this corresponds to pressed buttons. When we read * from the controller for the first time, detect NES * controllers by checking those 4 bits. **/ if (last_read_controller_bytes[1]==0xFF) nesMode |= 1; if (last_read_controller_bytes[3]==0xFF) nesMode |= 2; if (last_read_controller_bytes[5]==0xFF) nesMode |= 4; if (last_read_controller_bytes[7]==0xFF) nesMode |= 8; SREG = sreg; } /* * Clock Cycle Button Reported =========== =============== 1 B 2 Y 3 Select 4 Start 5 Up on joypad 6 Down on joypad 7 Left on joypad 8 Right on joypad 9 A 10 X 11 L 12 R 13 none (always high) 14 none (always high) 15 none (always high) 16 none (always high) * */ static void snesUpdate(void) { int i; unsigned char tmp1=0; unsigned char tmp2=0; unsigned char tmp3=0; unsigned char tmp4=0; SNES_LATCH_HIGH(); _delay_us(12); SNES_LATCH_LOW(); for (i=0; i<8; i++) { _delay_us(6); SNES_CLOCK_LOW(); tmp1 <<= 1; tmp2 <<= 1; tmp3 <<= 1; tmp4 <<= 1; if (!SNES_GET_DATA1()) { tmp1 |= 1; } if (!SNES_GET_DATA2()) { tmp2 |= 1; } if (!SNES_GET_DATA3()) { tmp3 |= 1; } if (!SNES_GET_DATA4()) { tmp4 |= 1; } _delay_us(6); SNES_CLOCK_HIGH(); } last_read_controller_bytes[0] = tmp1; last_read_controller_bytes[2] = tmp2; last_read_controller_bytes[4] = tmp3; last_read_controller_bytes[6] = tmp4; for (i=0; i<8; i++) { _delay_us(6); SNES_CLOCK_LOW(); // notice that this is different from above. We // want the bits to be in reverse-order tmp1 >>= 1; tmp2 >>= 1; tmp3 >>= 1; tmp4 >>= 1; if (!SNES_GET_DATA1()) { tmp1 |= 0x80; } if (!SNES_GET_DATA2()) { tmp2 |= 0x80; } if (!SNES_GET_DATA3()) { tmp3 |= 0x80; } if (!SNES_GET_DATA4()) { tmp4 |= 0x80; } _delay_us(6); SNES_CLOCK_HIGH(); } /* Force extra bits to 0 when in NES mode. Otherwise, if * we read zeros on the wire, we will have permanantly * pressed buttons */ last_read_controller_bytes[1] = (nesMode & 1) ? 0x00 : tmp1; last_read_controller_bytes[3] = (nesMode & 2) ? 0x00 : tmp2; last_read_controller_bytes[5] = (nesMode & 4) ? 0x00 : tmp3; last_read_controller_bytes[7] = (nesMode & 8) ? 0x00 : tmp4; } static char snesChanged(char report_id) { report_id--; // first report is 1 return memcmp( &last_read_controller_bytes[report_id<<1], &last_reported_controller_bytes[report_id<<1], 2); } static char getX(unsigned char nesByte1) { char x = 128; if (nesByte1&0x1) { x = 255; } if (nesByte1&0x2) { x = 0; } return x; } static char getY(unsigned char nesByte1) { char y = 128; if (nesByte1&0x4) { y = 255; } if (nesByte1&0x8) { y = 0; } return y; } static unsigned char snesReorderButtons(unsigned char bytes[2]) { unsigned char v; /* pack the snes button bits, which are on two bytes, in * one single byte. */ v = (bytes[0]&0x80)>>7; v |= (bytes[0]&0x40)>>5; v |= (bytes[0]&0x20)>>3; v |= (bytes[0]&0x10)>>1; v |= (bytes[1]&0x0f)<<4; return v; } static char snesBuildReport(unsigned char *reportBuffer, char id) { char idx; if (id < 0 || id > 4) return 0; /* last_read_controller_bytes[] structure: * * [0] : controller 1, 8 first bits (dpad + start + sel + y|a + b) * [1] : controller 1, 8 snes extra bits (4 lower bits are buttons) * * [2] : controller 2, 8 first bits * [3] : controller 2, 4 extra snes buttons * * [4] : controller 3, 8 first bits * [5] : controller 3, 4 extra snes buttons * * [6] : controller 4, 8 first bits * [7] : controller 4, 4 extra snes buttons */ idx = id - 1; if (reportBuffer != NULL) { reportBuffer[0]=id; reportBuffer[1]=getX(last_read_controller_bytes[idx*2]); reportBuffer[2]=getY(last_read_controller_bytes[idx*2]); if (nesMode & (0x01<