1
0
mirror of https://github.com/raphnet/4nes4snes synced 2024-11-14 21:15:06 -05:00

SNES multitap support

This commit is contained in:
Raphaël Assénat 2012-04-09 04:05:43 +00:00
parent fd19b71d38
commit efe9d049d3
2 changed files with 237 additions and 86 deletions

View File

@ -35,6 +35,11 @@
#define SNES_DATA_BIT3 (1<<1) /* controller 3 */ #define SNES_DATA_BIT3 (1<<1) /* controller 3 */
#define SNES_DATA_BIT4 (1<<0) /* controller 4 */ #define SNES_DATA_BIT4 (1<<0) /* controller 4 */
#define MULTITAP_SELECT_PORT PORTB
#define MULTITAP_SELECT_DDR DDRB
#define MULTITAP_SELECT_PIN PINB
#define MULTITAP_SELECT_BIT (1<<5)
/********* IO port manipulation macros **********/ /********* IO port manipulation macros **********/
#define SNES_LATCH_LOW() do { SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); } while(0) #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_LATCH_HIGH() do { SNES_LATCH_PORT |= SNES_LATCH_BIT; } while(0)
@ -46,6 +51,9 @@
#define SNES_GET_DATA3() (SNES_DATA_PIN & SNES_DATA_BIT3) #define SNES_GET_DATA3() (SNES_DATA_PIN & SNES_DATA_BIT3)
#define SNES_GET_DATA4() (SNES_DATA_PIN & SNES_DATA_BIT4) #define SNES_GET_DATA4() (SNES_DATA_PIN & SNES_DATA_BIT4)
#define MTAP_SELECT_HIGH() do { MULTITAP_SELECT_PORT |= MULTITAP_SELECT_BIT; } while(0)
#define MTAP_SELECT_LOW() do { MULTITAP_SELECT_PORT &= ~MULTITAP_SELECT_BIT; } while(0)
/*********** prototypes *************/ /*********** prototypes *************/
static void fournsnesInit(void); static void fournsnesInit(void);
static void fournsnesUpdate(void); static void fournsnesUpdate(void);
@ -62,6 +70,7 @@ static unsigned char last_reported_controller_bytes[GAMEPAD_BYTES];
// indicates if a controller is in NES mode // indicates if a controller is in NES mode
static unsigned char nesMode=0; /* Bit0: controller 1, Bit1: controller 2...*/ static unsigned char nesMode=0; /* Bit0: controller 1, Bit1: controller 2...*/
static unsigned char fourscore_mode = 0; static unsigned char fourscore_mode = 0;
static unsigned char multitap_mode = 0; // SNES
static unsigned char live_autodetect = 1; static unsigned char live_autodetect = 1;
void disableLiveAutodetect(void) void disableLiveAutodetect(void)
@ -69,6 +78,45 @@ void disableLiveAutodetect(void)
live_autodetect = 0; live_autodetect = 0;
} }
static void autoDetectSNESMultiTap(void)
{
// Detection is done by observing that DATA2 becomes
// low when LATCH is high.
//
// Not sure which state of MTAP_SELECT_HIGH is reliable
// so I'm simply trying with both states.
MTAP_SELECT_LOW();
if (SNES_GET_DATA2()) {
SNES_LATCH_HIGH();
_delay_us(12);
if (!SNES_GET_DATA2()) {
SNES_LATCH_LOW();
_delay_us(12);
if (SNES_GET_DATA2()) {
multitap_mode = 1;
}
}
}
MTAP_SELECT_HIGH();
if (SNES_GET_DATA2()) {
SNES_LATCH_HIGH();
_delay_us(12);
if (!SNES_GET_DATA2()) {
SNES_LATCH_LOW();
_delay_us(12);
if (SNES_GET_DATA2()) {
multitap_mode = 1;
}
}
}
}
static void autoDetectFourScore(void) static void autoDetectFourScore(void)
{ {
unsigned char dat18th_low = 0; unsigned char dat18th_low = 0;
@ -129,6 +177,11 @@ static void fournsnesInit(void)
// LATCH is Active HIGH // LATCH is Active HIGH
SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); SNES_LATCH_PORT &= ~(SNES_LATCH_BIT);
MULTITAP_SELECT_DDR |= MULTITAP_SELECT_BIT;
MULTITAP_SELECT_PORT |= MULTITAP_SELECT_BIT;
nesMode = 0; nesMode = 0;
fournsnesUpdate(); fournsnesUpdate();
@ -160,35 +213,13 @@ static void fournsnesInit(void)
} }
autoDetectFourScore(); autoDetectFourScore();
autoDetectSNESMultiTap();
SREG = sreg; SREG = sreg;
} }
/* static void fournsnesUpdate_fourscore(void)
*
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 fournsnesUpdate(void)
{ {
int i; int i;
unsigned char tmp1=0; unsigned char tmp1=0;
@ -200,8 +231,6 @@ static void fournsnesUpdate(void)
_delay_us(12); _delay_us(12);
SNES_LATCH_LOW(); SNES_LATCH_LOW();
if (fourscore_mode)
{
/* Nes controller buttons are sent in this order: /* Nes controller buttons are sent in this order:
* One byte: A B SEL START UP DOWN LEFT RIGHT */ * One byte: A B SEL START UP DOWN LEFT RIGHT */
@ -249,9 +278,130 @@ static void fournsnesUpdate(void)
last_read_controller_bytes[3] = tmp4; last_read_controller_bytes[3] = tmp4;
return; return;
} }
/*
*
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 fournsnesUpdate(void)
{
int i;
unsigned char tmp1=0;
unsigned char tmp2=0;
unsigned char tmp3=0;
unsigned char tmp4=0;
if (fourscore_mode)
{
fournsnesUpdate_fourscore();
return;
}
if (multitap_mode)
{
SNES_LATCH_HIGH();
_delay_us(12);
SNES_LATCH_LOW();
_delay_us(12);
MTAP_SELECT_HIGH();
_delay_us(6);
for (i=0; i<8; i++)
{
SNES_CLOCK_LOW();
_delay_us(6);
tmp1 <<= 1;
tmp2 <<= 1;
if (!SNES_GET_DATA1()) { tmp1 |= 1; }
if (!SNES_GET_DATA2()) { tmp2 |= 1; }
SNES_CLOCK_HIGH();
_delay_us(6);
}
last_read_controller_bytes[0] = tmp1;
last_read_controller_bytes[2] = tmp2;
for (i=0; i<8; i++)
{
SNES_CLOCK_LOW();
_delay_us(6);
tmp1 >>= 1;
tmp2 >>= 1;
if (!SNES_GET_DATA1()) { tmp1 |= 0x80; }
if (!SNES_GET_DATA2()) { tmp2 |= 0x80; }
SNES_CLOCK_HIGH();
_delay_us(6);
}
MTAP_SELECT_LOW();
_delay_us(6);
for (i=0; i<8; i++)
{
SNES_CLOCK_LOW();
_delay_us(6);
tmp3 <<= 1;
tmp4 <<= 1;
if (!SNES_GET_DATA1()) { tmp3 |= 1; }
if (!SNES_GET_DATA2()) { tmp4 |= 1; }
SNES_CLOCK_HIGH();
_delay_us(6);
}
last_read_controller_bytes[4] = tmp3;
last_read_controller_bytes[6] = tmp4;
for (i=0; i<8; i++)
{
SNES_CLOCK_LOW();
_delay_us(6);
tmp3 >>= 1;
tmp4 >>= 1;
if (!SNES_GET_DATA1()) { tmp3 |= 0x80; }
if (!SNES_GET_DATA2()) { tmp4 |= 0x80; }
SNES_CLOCK_HIGH();
_delay_us(6);
}
}
else // standard mode (not multitap)
{
SNES_LATCH_HIGH();
_delay_us(12);
SNES_LATCH_LOW();
for (i=0; i<8; i++) for (i=0; i<8; i++)
{ {
_delay_us(6); _delay_us(6);
@ -294,6 +444,7 @@ static void fournsnesUpdate(void)
_delay_us(6); _delay_us(6);
SNES_CLOCK_HIGH(); SNES_CLOCK_HIGH();
} }
}
if (live_autodetect) { if (live_autodetect) {

View File

@ -5,7 +5,7 @@
* Tabsize: 4 * Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig.h,v 1.7 2009-05-02 13:55:11 cvs Exp $ * This Revision: $Id: usbconfig.h,v 1.8 2012-04-09 04:05:43 cvs Exp $
*/ */
#ifndef __usbconfig_h_included__ #ifndef __usbconfig_h_included__
@ -143,7 +143,7 @@ rename it to "usbconfig.h". Then edit it accordingly.
#define USB_CFG_DEVICE_ID 0x9d, 0x0a #define USB_CFG_DEVICE_ID 0x9d, 0x0a
#define USB_CFG_DEVICE_VERSION 0x02, 0x01 #define USB_CFG_DEVICE_VERSION 0x03, 0x01
/* Version number of the device: Minor number first, then major number. /* Version number of the device: Minor number first, then major number.
*/ */
#define USB_CFG_VENDOR_NAME 'r', 'a', 'p', 'h', 'n', 'e', 't', '.', 'n', 'e', 't' #define USB_CFG_VENDOR_NAME 'r', 'a', 'p', 'h', 'n', 'e', 't', '.', 'n', 'e', 't'