mirror of
https://github.com/raphnet/4nes4snes
synced 2024-11-14 13:05:03 -05:00
SNES multitap support
This commit is contained in:
parent
fd19b71d38
commit
efe9d049d3
319
fournsnes.c
319
fournsnes.c
@ -35,6 +35,11 @@
|
||||
#define SNES_DATA_BIT3 (1<<1) /* controller 3 */
|
||||
#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 **********/
|
||||
#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)
|
||||
@ -46,6 +51,9 @@
|
||||
#define SNES_GET_DATA3() (SNES_DATA_PIN & SNES_DATA_BIT3)
|
||||
#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 *************/
|
||||
static void fournsnesInit(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
|
||||
static unsigned char nesMode=0; /* Bit0: controller 1, Bit1: controller 2...*/
|
||||
static unsigned char fourscore_mode = 0;
|
||||
static unsigned char multitap_mode = 0; // SNES
|
||||
static unsigned char live_autodetect = 1;
|
||||
|
||||
void disableLiveAutodetect(void)
|
||||
@ -69,6 +78,45 @@ void disableLiveAutodetect(void)
|
||||
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)
|
||||
{
|
||||
unsigned char dat18th_low = 0;
|
||||
@ -129,6 +177,11 @@ static void fournsnesInit(void)
|
||||
// LATCH is Active HIGH
|
||||
SNES_LATCH_PORT &= ~(SNES_LATCH_BIT);
|
||||
|
||||
|
||||
MULTITAP_SELECT_DDR |= MULTITAP_SELECT_BIT;
|
||||
MULTITAP_SELECT_PORT |= MULTITAP_SELECT_BIT;
|
||||
|
||||
|
||||
nesMode = 0;
|
||||
fournsnesUpdate();
|
||||
|
||||
@ -160,11 +213,75 @@ static void fournsnesInit(void)
|
||||
}
|
||||
|
||||
autoDetectFourScore();
|
||||
autoDetectSNESMultiTap();
|
||||
|
||||
SREG = sreg;
|
||||
}
|
||||
|
||||
|
||||
static void fournsnesUpdate_fourscore(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();
|
||||
|
||||
/* Nes controller buttons are sent in this order:
|
||||
* One byte: A B SEL START UP DOWN LEFT RIGHT */
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_LOW();
|
||||
|
||||
// FourScore to be connected to ports 1 and 2
|
||||
tmp1 <<= 1;
|
||||
tmp2 <<= 1;
|
||||
if (!SNES_GET_DATA1()) { tmp1 |= 1; }
|
||||
if (!SNES_GET_DATA2()) { tmp2 |= 1; }
|
||||
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
}
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_LOW();
|
||||
|
||||
// FourScore to be connected to ports 1 and 2
|
||||
tmp3 <<= 1;
|
||||
tmp4 <<= 1;
|
||||
if (!SNES_GET_DATA1()) { tmp3 |= 1; }
|
||||
if (!SNES_GET_DATA2()) { tmp4 |= 1; }
|
||||
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
}
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_LOW();
|
||||
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
}
|
||||
last_read_controller_bytes[0] = tmp1;
|
||||
last_read_controller_bytes[1] = tmp2;
|
||||
last_read_controller_bytes[2] = tmp3;
|
||||
last_read_controller_bytes[3] = tmp4;
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
Clock Cycle Button Reported
|
||||
@ -196,103 +313,137 @@ static void fournsnesUpdate(void)
|
||||
unsigned char tmp3=0;
|
||||
unsigned char tmp4=0;
|
||||
|
||||
SNES_LATCH_HIGH();
|
||||
_delay_us(12);
|
||||
SNES_LATCH_LOW();
|
||||
|
||||
if (fourscore_mode)
|
||||
{
|
||||
/* Nes controller buttons are sent in this order:
|
||||
* One byte: A B SEL START UP DOWN LEFT RIGHT */
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_LOW();
|
||||
|
||||
// FourScore to be connected to ports 1 and 2
|
||||
tmp1 <<= 1;
|
||||
tmp2 <<= 1;
|
||||
if (!SNES_GET_DATA1()) { tmp1 |= 1; }
|
||||
if (!SNES_GET_DATA2()) { tmp2 |= 1; }
|
||||
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
}
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_LOW();
|
||||
|
||||
// FourScore to be connected to ports 1 and 2
|
||||
tmp3 <<= 1;
|
||||
tmp4 <<= 1;
|
||||
if (!SNES_GET_DATA1()) { tmp3 |= 1; }
|
||||
if (!SNES_GET_DATA2()) { tmp4 |= 1; }
|
||||
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
}
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_LOW();
|
||||
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
}
|
||||
last_read_controller_bytes[0] = tmp1;
|
||||
last_read_controller_bytes[1] = tmp2;
|
||||
last_read_controller_bytes[2] = tmp3;
|
||||
last_read_controller_bytes[3] = tmp4;
|
||||
|
||||
fournsnesUpdate_fourscore();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
if (multitap_mode)
|
||||
{
|
||||
_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; }
|
||||
SNES_LATCH_HIGH();
|
||||
_delay_us(12);
|
||||
SNES_LATCH_LOW();
|
||||
_delay_us(12);
|
||||
|
||||
MTAP_SELECT_HIGH();
|
||||
_delay_us(6);
|
||||
SNES_CLOCK_HIGH();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
last_read_controller_bytes[0] = tmp1;
|
||||
last_read_controller_bytes[2] = tmp2;
|
||||
last_read_controller_bytes[4] = tmp3;
|
||||
last_read_controller_bytes[6] = tmp4;
|
||||
else // standard mode (not multitap)
|
||||
{
|
||||
SNES_LATCH_HIGH();
|
||||
_delay_us(12);
|
||||
SNES_LATCH_LOW();
|
||||
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
_delay_us(6);
|
||||
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; }
|
||||
|
||||
SNES_CLOCK_LOW();
|
||||
_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;
|
||||
|
||||
// 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();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Tabsize: 4
|
||||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
|
||||
* 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__
|
||||
@ -143,7 +143,7 @@ rename it to "usbconfig.h". Then edit it accordingly.
|
||||
#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.
|
||||
*/
|
||||
#define USB_CFG_VENDOR_NAME 'r', 'a', 'p', 'h', 'n', 'e', 't', '.', 'n', 'e', 't'
|
||||
|
Loading…
Reference in New Issue
Block a user