mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-05 02:15:07 +01:00
510 lines
13 KiB
C
510 lines
13 KiB
C
/*
|
|
Copyright (C) 1999, 2000, 2001, 2002, 2003 Charles MacDonald
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
/*
|
|
02/2007: fixed 6BUTTONS gamepad emulation (Eke-Eke)
|
|
06/2007: Added TEAMPLAYER/4WAYPLAY/MENACER emulation (Eke-Eke)
|
|
*/
|
|
|
|
#include "shared.h"
|
|
|
|
t_input input;
|
|
uint8 current_pad = 0;
|
|
uint8 j_cart = 0;
|
|
|
|
void gamepad_reset(uint8 num);
|
|
void gamepad_update(uint8 num);
|
|
void gamepad_raz(uint8 num);
|
|
void lightgun_update(void);
|
|
void lightgun_reset(void);
|
|
void teamplayer_reset(uint8 port);
|
|
|
|
/*****************************************************************************
|
|
* Generic INPUTS Control
|
|
*
|
|
*****************************************************************************/
|
|
void input_reset (int padtype)
|
|
{
|
|
uint8 i;
|
|
|
|
input.max = 0;
|
|
for (i=0; i<MAX_DEVICES; i++)
|
|
{
|
|
input.dev[i] = NO_DEVICE;
|
|
input.pad[i] = 0;
|
|
}
|
|
|
|
switch (input.system[0])
|
|
{
|
|
case SYSTEM_GAMEPAD:
|
|
if (input.max++ < MAX_INPUTS) input.dev[0] = padtype;
|
|
gamepad_reset(0);
|
|
break;
|
|
|
|
case SYSTEM_WAYPLAY:
|
|
for (i=0; i<4; i++)
|
|
{
|
|
if (input.max++ < MAX_INPUTS) input.dev[i] = padtype;
|
|
gamepad_reset(i);
|
|
}
|
|
current_pad = 0;
|
|
break;
|
|
|
|
case SYSTEM_TEAMPLAYER:
|
|
for (i=0; i<4; i++)
|
|
{
|
|
if (input.max++ < MAX_INPUTS) input.dev[i] = padtype;
|
|
}
|
|
teamplayer_reset(0);
|
|
break;
|
|
}
|
|
|
|
switch (input.system[1])
|
|
{
|
|
case SYSTEM_GAMEPAD:
|
|
if (input.max++ < MAX_INPUTS) input.dev[4] = padtype;
|
|
gamepad_reset(4);
|
|
break;
|
|
|
|
case SYSTEM_TEAMPLAYER:
|
|
for (i=4; i<8; i++)
|
|
{
|
|
if (input.max++ < MAX_INPUTS) input.dev[i] = padtype;
|
|
}
|
|
teamplayer_reset(1);
|
|
break;
|
|
|
|
case SYSTEM_MENACER:
|
|
if (input.max++ < MAX_INPUTS) input.dev[4] = DEVICE_LIGHTGUN;
|
|
lightgun_reset();
|
|
break;
|
|
}
|
|
|
|
if (input.max > MAX_INPUTS) input.max = MAX_INPUTS;
|
|
|
|
/* J-CART: add two gamepad inputs */
|
|
if (j_cart)
|
|
{
|
|
input.dev[5] = padtype;
|
|
input.dev[6] = padtype;
|
|
gamepad_reset(5);
|
|
gamepad_reset(6);
|
|
input.max+=2;
|
|
}
|
|
}
|
|
|
|
void input_update()
|
|
{
|
|
uint8 i;
|
|
switch (input.system[0])
|
|
{
|
|
case SYSTEM_GAMEPAD:
|
|
if (input.dev[0] == DEVICE_6BUTTON) gamepad_update(0);
|
|
break;
|
|
|
|
case SYSTEM_WAYPLAY:
|
|
for (i=0; i<4; i++)
|
|
{
|
|
if (input.dev[i] == DEVICE_6BUTTON) gamepad_update(i);
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (input.system[1])
|
|
{
|
|
case SYSTEM_GAMEPAD:
|
|
if (input.dev[4] == DEVICE_6BUTTON) gamepad_update(4);
|
|
break;
|
|
|
|
case SYSTEM_MENACER:
|
|
lightgun_update();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void input_raz()
|
|
{
|
|
uint8 i;
|
|
switch (input.system[0])
|
|
{
|
|
case SYSTEM_GAMEPAD:
|
|
if (input.dev[0] == DEVICE_6BUTTON) gamepad_raz(0);
|
|
break;
|
|
|
|
case SYSTEM_WAYPLAY:
|
|
for (i=0; i<4; i++)
|
|
{
|
|
if (input.dev[i] == DEVICE_6BUTTON) gamepad_raz(i);
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (input.system[1])
|
|
{
|
|
case SYSTEM_GAMEPAD:
|
|
if (input.dev[4] == DEVICE_6BUTTON) gamepad_raz(4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* LIGHTGUN specific functions
|
|
*
|
|
*****************************************************************************/
|
|
struct gun
|
|
{
|
|
int16 x;
|
|
int16 y;
|
|
} lightgun;
|
|
|
|
void lightgun_reset(void)
|
|
{
|
|
lightgun.x = bitmap.viewport.w >> 1;
|
|
lightgun.y = bitmap.viewport.h >> 1;
|
|
}
|
|
|
|
void lightgun_update(void)
|
|
{
|
|
if ((v_counter == lightgun.y))
|
|
{
|
|
if (reg[0x0B] & 8) m68k_set_irq(2);
|
|
if (reg[0x00] & 2)
|
|
{
|
|
hc_latch = lightgun.x + 166;
|
|
if (hc_latch >= bitmap.viewport.w + 52) hc_latch -= (bitmap.viewport.w + 52 + 48);
|
|
hc_latch >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lightgun_set ()
|
|
{
|
|
if ((input.pad[4] & INPUT_RIGHT)) lightgun.x ++;
|
|
if ((input.pad[4] & INPUT_LEFT)) lightgun.x --;
|
|
if ((input.pad[4] & INPUT_UP)) lightgun.y --;
|
|
if ((input.pad[4] & INPUT_DOWN)) lightgun.y ++;
|
|
|
|
if (lightgun.x < 0) lightgun.x = 0;
|
|
else if (lightgun.x > bitmap.viewport.w-34) lightgun.x = bitmap.viewport.w-34;
|
|
|
|
if (lightgun.y < 0) lightgun.y = 0;
|
|
else if (lightgun.y > bitmap.viewport.h-8) lightgun.y = bitmap.viewport.h-8;
|
|
}
|
|
|
|
uint8 lightgun_read (void)
|
|
{
|
|
uint8 retval = 0x00;
|
|
if ((input.pad[4] & INPUT_B)) retval |= 0x01;
|
|
if ((input.pad[4] & INPUT_A)) retval |= 0x02;
|
|
if ((input.pad[4] & INPUT_C)) retval |= 0x04;
|
|
if ((input.pad[4] & INPUT_START)) retval |= 0x08;
|
|
return (retval & 0x7F);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* GAMEPAD specific functions (2PLAYERS/4WAYPLAY)
|
|
*
|
|
*****************************************************************************/
|
|
struct pad
|
|
{
|
|
uint8 State;
|
|
uint8 Counter;
|
|
uint8 Delay;
|
|
} gamepad[MAX_DEVICES];
|
|
|
|
void gamepad_reset(uint8 i)
|
|
{
|
|
gamepad[i].State = 0x40;
|
|
if (input.dev[i] == DEVICE_6BUTTON) gamepad_raz(i);
|
|
}
|
|
|
|
void gamepad_raz(uint8 i)
|
|
{
|
|
gamepad[i].Counter = 0;
|
|
gamepad[i].Delay = 0;
|
|
}
|
|
|
|
void gamepad_update(uint8 i)
|
|
{
|
|
if (gamepad[i].Delay++ > 25) gamepad_raz(i);
|
|
}
|
|
|
|
uint8 gamepad_read (uint8 i)
|
|
{
|
|
int control;
|
|
unsigned char retval = 0xFF;
|
|
|
|
if (input.dev[i] == NO_DEVICE) return 0x7F;
|
|
|
|
control = (gamepad[i].State & 0x40) >> 6; /* current TH state */
|
|
|
|
if (input.dev[i] == DEVICE_6BUTTON)
|
|
{
|
|
control += (gamepad[i].Counter & 3) << 1; /* TH transitions counter */
|
|
}
|
|
|
|
switch (control)
|
|
{
|
|
case 1: /*** First High ***/
|
|
case 3: /*** Second High ***/
|
|
case 5: /*** Third High ***/
|
|
|
|
/* TH = 1 : ?1CBRLDU */
|
|
if (input.pad[i] & INPUT_C) retval &= ~0x20;
|
|
if (input.pad[i] & INPUT_B) retval &= ~0x10;
|
|
if (input.pad[i] & INPUT_UP) retval &= ~0x01;
|
|
if (input.pad[i] & INPUT_DOWN) retval &= ~0x02;
|
|
if (input.pad[i] & INPUT_LEFT) retval &= ~0x04;
|
|
if (input.pad[i] & INPUT_RIGHT) retval &= ~0x08;
|
|
break;
|
|
|
|
case 0: /*** First low ***/
|
|
case 2: /*** Second low ***/
|
|
|
|
/* TH = 0 : ?0SA00DU */
|
|
if (input.pad[i] & INPUT_A) retval &= ~0x10;
|
|
if (input.pad[i] & INPUT_START) retval &= ~0x20;
|
|
if (input.pad[i] & INPUT_UP) retval &= ~0x01;
|
|
if (input.pad[i] & INPUT_DOWN) retval &= ~0x02;
|
|
retval &= 0xB3;
|
|
break;
|
|
|
|
/* 6buttons specific (taken from gen-hw.txt) */
|
|
/* A 6-button gamepad allows the extra buttons to be read based on how */
|
|
/* many times TH is switched from 1 to 0 (and not 0 to 1). Observe the */
|
|
/* following sequence */
|
|
/*
|
|
TH = 1 : ?1CBRLDU 3-button pad return value
|
|
TH = 0 : ?0SA00DU 3-button pad return value
|
|
TH = 1 : ?1CBRLDU 3-button pad return value
|
|
TH = 0 : ?0SA0000 D3-0 are forced to '0'
|
|
TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0
|
|
TH = 0 : ?0SA1111 D3-0 are forced to '1'
|
|
*/
|
|
case 4: /*** Third Low ***/
|
|
|
|
/* TH = 0 : ?0SA0000 D3-0 are forced to '0'*/
|
|
if (input.pad[i] & INPUT_A) retval &= ~0x10;
|
|
if (input.pad[i] & INPUT_START) retval &= ~0x20;
|
|
retval &= 0xB0;
|
|
break;
|
|
|
|
case 6: /*** Fourth Low ***/
|
|
|
|
/* TH = 0 : ?0SA1111 D3-0 are forced to '1'*/
|
|
if (input.pad[i] & INPUT_A) retval &= ~0x10;
|
|
if (input.pad[i] & INPUT_START) retval &= ~0x20;
|
|
retval &= 0xBF;
|
|
break;
|
|
|
|
case 7: /*** Fourth High ***/
|
|
|
|
/* TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0*/
|
|
if (input.pad[i] & INPUT_X) retval &= ~0x04;
|
|
if (input.pad[i] & INPUT_Y) retval &= ~0x02;
|
|
if (input.pad[i] & INPUT_Z) retval &= ~0x01;
|
|
if (input.pad[i] & INPUT_B) retval &= ~0x10;
|
|
if (input.pad[i] & INPUT_C) retval &= ~0x20;
|
|
if (input.pad[i] & INPUT_MODE) retval &= ~0x08;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* bit7 is latched*/
|
|
return (retval&0x7f);
|
|
}
|
|
|
|
void gamepad_write (uint8 i, uint8 data)
|
|
{
|
|
if (input.dev[i] == DEVICE_6BUTTON)
|
|
{
|
|
/* TH=0 to TH=1 transition */
|
|
if (!(gamepad[i].State & 0x40) && (data & 0x40))
|
|
{
|
|
gamepad[i].Counter++;
|
|
gamepad[i].Delay = 0;
|
|
}
|
|
}
|
|
|
|
gamepad[i].State = data;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* TEAMPLAYER specific functions
|
|
*
|
|
*****************************************************************************/
|
|
struct teamplayer
|
|
{
|
|
uint8 State;
|
|
uint8 Counter;
|
|
uint8 Table[12];
|
|
} teamplayer[2];
|
|
|
|
void teamplayer_reset(uint8 port)
|
|
{
|
|
uint8 i;
|
|
uint8 index = 0;
|
|
uint8 pad_input = 0;
|
|
|
|
teamplayer[port].State = 0x60; /* TH = 1, TR = 1 */
|
|
teamplayer[port].Counter = 0;
|
|
|
|
/* this table determines which gamepad input should be returned during acquisition sequence
|
|
index = teamplayer read table index: 0=1st read, 1=2nd read, ...
|
|
pad_input = gamepad input 0-14: 0=P1_DIR, 1=P1_SABC, 2=P1_MXYZ, 4=P2_DIR, 5=P2_SABC, ...
|
|
*/
|
|
for (i=0; i<4; i++)
|
|
{
|
|
if (input.dev[(4*port) + i] == DEVICE_3BUTTON)
|
|
{
|
|
teamplayer[port].Table[index++] = pad_input;
|
|
teamplayer[port].Table[index++] = pad_input + 1;
|
|
}
|
|
else if (input.dev[(4*port) + i] == DEVICE_6BUTTON)
|
|
{
|
|
teamplayer[port].Table[index++] = pad_input;
|
|
teamplayer[port].Table[index++] = pad_input + 1;
|
|
teamplayer[port].Table[index++] = pad_input + 2;
|
|
}
|
|
pad_input += 4;
|
|
}
|
|
}
|
|
|
|
|
|
/* SEGA teamplayer returns successively:
|
|
- PAD1 inputs
|
|
- PAD2 inputs
|
|
- PAD3 inputs
|
|
- PAD4 inputs
|
|
|
|
Each PAD inputs is obtained through 2 or 3 sequential reads:
|
|
1/ DIR buttons
|
|
2/ START,A,C,B buttons
|
|
3/ MODE, X,Y,Z buttons (6Button only !)
|
|
*/
|
|
uint8 teamplayer_read(uint8 port, uint8 index)
|
|
{
|
|
uint8 retval = 0x7F;
|
|
uint8 pad_input = teamplayer[port].Table[index] & 0x03;
|
|
uint8 pad_num = (4 * port) + ((teamplayer[port].Table[index] >> 2) & 0x03);
|
|
|
|
switch (pad_input)
|
|
{
|
|
case 0:
|
|
/* Directions Buttons */
|
|
if (input.pad[pad_num] & INPUT_UP) retval &= ~0x01;
|
|
if (input.pad[pad_num] & INPUT_DOWN) retval &= ~0x02;
|
|
if (input.pad[pad_num] & INPUT_LEFT) retval &= ~0x04;
|
|
if (input.pad[pad_num] & INPUT_RIGHT) retval &= ~0x08;
|
|
break;
|
|
|
|
case 1:
|
|
/* S,A,C,B Buttons */
|
|
if (input.pad[pad_num] & INPUT_B) retval &= ~0x01;
|
|
if (input.pad[pad_num] & INPUT_C) retval &= ~0x02;
|
|
if (input.pad[pad_num] & INPUT_A) retval &= ~0x04;
|
|
if (input.pad[pad_num] & INPUT_START) retval &= ~0x08;
|
|
break;
|
|
|
|
case 2:
|
|
/* M,X,Y,Z Buttons (6-Buttons only)*/
|
|
if (input.pad[pad_num] & INPUT_Z) retval &= ~0x01;
|
|
if (input.pad[pad_num] & INPUT_Y) retval &= ~0x02;
|
|
if (input.pad[pad_num] & INPUT_X) retval &= ~0x04;
|
|
if (input.pad[pad_num] & INPUT_MODE) retval &= ~0x08;
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* MULTITAP generic functions (TEAMPLAYER/4WAYPLAY)
|
|
*
|
|
*****************************************************************************/
|
|
void multitap_write (uint8 port, uint8 data)
|
|
{
|
|
uint8 old_state;
|
|
switch (input.system[port])
|
|
{
|
|
case SYSTEM_WAYPLAY:
|
|
if (!port) gamepad_write(current_pad, data);
|
|
else current_pad = (data >> 4) & 0x07;
|
|
break;
|
|
|
|
case SYSTEM_TEAMPLAYER:
|
|
old_state = teamplayer[port].State;
|
|
teamplayer[port].State = (data & io_reg[port+4]) | (teamplayer[port].State & ~io_reg[port+4]);
|
|
if (old_state != teamplayer[port].State) teamplayer[port].Counter ++;
|
|
if ((data&0x60) == 0x60) teamplayer[port].Counter = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8 multitap_read (uint8 port)
|
|
{
|
|
uint8 retval = 0x7F;
|
|
uint8 padnum;
|
|
|
|
switch (input.system[port])
|
|
{
|
|
case SYSTEM_WAYPLAY:
|
|
if (port == 1) return 0x7F;
|
|
if (current_pad >= 4) return 0x70; /* multitap detection (TH2 = 1) */
|
|
return gamepad_read(current_pad); /* 0x0C = Pad1, 0x1C = Pad2, ... */
|
|
|
|
case SYSTEM_TEAMPLAYER:
|
|
switch (teamplayer[port].Counter) /* acquisition sequence steps */
|
|
{
|
|
case 0: /* initial state: TH = 1, TR = 1 */
|
|
retval = 0x73;
|
|
break;
|
|
|
|
case 1: /* start request: TH = 0, TR = 1 */
|
|
retval = 0x3F;
|
|
break;
|
|
|
|
case 2:
|
|
case 3: /* ack request: TH=0, TR handshake */
|
|
retval = 0x00;
|
|
break;
|
|
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7: /* gamepads type */
|
|
padnum = (4 * port) + teamplayer[port].Counter - 4;
|
|
retval = input.dev[padnum];
|
|
break;
|
|
|
|
default: /* gamepads inputs acquisition */
|
|
retval = teamplayer_read(port, teamplayer[port].Counter - 8);
|
|
break;
|
|
}
|
|
|
|
/* TL must match TR state */
|
|
retval &= ~0x10;
|
|
if (teamplayer[port].State & 0x20) retval |= 0x10;
|
|
return retval;
|
|
}
|
|
return retval;
|
|
}
|