341 lines
6.6 KiB
C++
341 lines
6.6 KiB
C++
/******************************************************************************/
|
|
/* Mednafen NEC PC-FX Emulation Module */
|
|
/******************************************************************************/
|
|
/* input.cpp:
|
|
** Copyright (C) 2006-2016 Mednafen Team
|
|
**
|
|
** 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.,
|
|
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "pcfx.h"
|
|
#include "interrupt.h"
|
|
#include "input.h"
|
|
|
|
#include "input/gamepad.h"
|
|
#include "input/mouse.h"
|
|
|
|
namespace MDFN_IEN_PCFX
|
|
{
|
|
|
|
#define PCFX_PORTS 2
|
|
#define TOTAL_PORTS 8
|
|
|
|
#define TAP_PORTS 4
|
|
|
|
static const int TapMap[2][TAP_PORTS] =
|
|
{
|
|
{0, 2, 3, 4},
|
|
{1, 5, 6, 7},
|
|
};
|
|
|
|
static void RemakeDevices(int which = -1);
|
|
static uint8 MultiTapEnabled;
|
|
|
|
// Mednafen-specific input type numerics
|
|
enum
|
|
{
|
|
FXIT_NONE = 0,
|
|
FXIT_GAMEPAD = 1,
|
|
FXIT_MOUSE = 2,
|
|
};
|
|
|
|
PCFX_Input_Device::~PCFX_Input_Device()
|
|
{
|
|
}
|
|
|
|
uint32 PCFX_Input_Device::ReadTransferTime(void)
|
|
{
|
|
return (1536);
|
|
}
|
|
|
|
uint32 PCFX_Input_Device::WriteTransferTime(void)
|
|
{
|
|
return (1536);
|
|
}
|
|
|
|
uint32 PCFX_Input_Device::Read(void)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
void PCFX_Input_Device::Write(uint32 data)
|
|
{
|
|
}
|
|
|
|
void PCFX_Input_Device::Power(void)
|
|
{
|
|
}
|
|
|
|
void PCFX_Input_Device::Frame(uint32_t data)
|
|
{
|
|
}
|
|
|
|
static PCFX_Input_Device *devices[TOTAL_PORTS] = {NULL};
|
|
|
|
// D0 = TRG, trigger bit
|
|
// D1 = MOD, multi-tap clear mode?
|
|
// D2 = IOS, data direction. 0 = output, 1 = input
|
|
|
|
static uint8 TapCounter[PCFX_PORTS];
|
|
static uint8 control[PCFX_PORTS];
|
|
static bool latched[PCFX_PORTS];
|
|
static int32 LatchPending[PCFX_PORTS];
|
|
|
|
static int InputTypes[TOTAL_PORTS];
|
|
static const uint32_t *data_ptr[TOTAL_PORTS];
|
|
static uint32 data_latch[TOTAL_PORTS];
|
|
|
|
void FXINPUT_Init(void)
|
|
{
|
|
RemakeDevices();
|
|
}
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
uint32 FXINPUT_GetRegister(const unsigned int id, char *special, const uint32 special_len)
|
|
{
|
|
uint32 value = 0xDEADBEEF;
|
|
|
|
switch (id)
|
|
{
|
|
case FXINPUT_GSREG_KPCTRL0:
|
|
case FXINPUT_GSREG_KPCTRL1:
|
|
value = control[id == FXINPUT_GSREG_KPCTRL1];
|
|
if (special)
|
|
{
|
|
trio_snprintf(special, special_len, "Trigger: %d, MOD: %d, IOS: %s", value & 0x1, value & 0x2, (value & 0x4) ? "Input" : "Output");
|
|
}
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void FXINPUT_SetRegister(const unsigned int id, uint32 value)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
static INLINE int32 min(int32 a, int32 b, int32 c)
|
|
{
|
|
int32 ret = a;
|
|
|
|
if (b < ret)
|
|
ret = b;
|
|
if (c < ret)
|
|
ret = c;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static INLINE int32 CalcNextEventTS(const v810_timestamp_t timestamp)
|
|
{
|
|
return (min(LatchPending[0] > 0 ? (timestamp + LatchPending[0]) : PCFX_EVENT_NONONO, LatchPending[1] > 0 ? (timestamp + LatchPending[1]) : PCFX_EVENT_NONONO, PCFX_EVENT_NONONO));
|
|
}
|
|
|
|
static uint32_t Dummy;
|
|
|
|
static void RemakeDevices(int which)
|
|
{
|
|
int s = 0;
|
|
int e = TOTAL_PORTS;
|
|
|
|
if (which != -1)
|
|
{
|
|
s = which;
|
|
e = which + 1;
|
|
}
|
|
|
|
for (int i = s; i < e; i++)
|
|
{
|
|
if (devices[i])
|
|
delete devices[i];
|
|
devices[i] = NULL;
|
|
|
|
switch (InputTypes[i])
|
|
{
|
|
default:
|
|
case FXIT_NONE:
|
|
devices[i] = new PCFX_Input_Device();
|
|
data_ptr[i] = &Dummy;
|
|
break;
|
|
case FXIT_GAMEPAD:
|
|
devices[i] = PCFXINPUT_MakeGamepad();
|
|
break;
|
|
case FXIT_MOUSE:
|
|
devices[i] = PCFXINPUT_MakeMouse(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FXINPUT_SetInput(unsigned port, int type, const uint32_t* ptr)
|
|
{
|
|
data_ptr[port] = ptr;
|
|
InputTypes[port] = type; // FXIT_NONE, FXIT_GAMEPAD, FXIT_MOUSE
|
|
RemakeDevices(port);
|
|
}
|
|
|
|
void FXINPUT_SetMultitap(bool port1, bool port2)
|
|
{
|
|
MultiTapEnabled = port1 | port2 << 1;
|
|
}
|
|
|
|
|
|
uint8 FXINPUT_Read8(uint32 A, const v810_timestamp_t timestamp)
|
|
{
|
|
//printf("Read8: %04x\n", A);
|
|
|
|
return (FXINPUT_Read16(A & ~1, timestamp) >> ((A & 1) * 8));
|
|
}
|
|
|
|
uint16 FXINPUT_Read16(uint32 A, const v810_timestamp_t timestamp)
|
|
{
|
|
FXINPUT_Update(timestamp);
|
|
|
|
uint16 ret = 0;
|
|
|
|
A &= 0xC2;
|
|
|
|
//printf("Read: %04x\n", A);
|
|
|
|
if (A == 0x00 || A == 0x80)
|
|
{
|
|
int w = (A & 0x80) >> 7;
|
|
|
|
if (latched[w])
|
|
ret = 0x8;
|
|
else
|
|
ret = 0x0;
|
|
}
|
|
else
|
|
{
|
|
int which = (A >> 7) & 1;
|
|
|
|
ret = data_latch[which] >> ((A & 2) ? 16 : 0);
|
|
|
|
// Which way is correct? Clear on low reads, or both? Official docs only say low...
|
|
if (!(A & 0x2))
|
|
latched[which] = FALSE;
|
|
}
|
|
|
|
if (!latched[0] && !latched[1])
|
|
PCFXIRQ_Assert(PCFXIRQ_SOURCE_INPUT, FALSE);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
void FXINPUT_Write16(uint32 A, uint16 V, const v810_timestamp_t timestamp)
|
|
{
|
|
FXINPUT_Update(timestamp);
|
|
|
|
//printf("Write16: %04x:%02x, %d\n", A, V, timestamp / 1365);
|
|
|
|
//PCFXIRQ_Assert(PCFXIRQ_SOURCE_INPUT, FALSE);
|
|
//if(V != 7 && V != 5)
|
|
//printf("PAD Write16: %04x %04x %d\n", A, V, timestamp);
|
|
|
|
switch (A & 0xC0)
|
|
{
|
|
case 0x80:
|
|
case 0x00:
|
|
{
|
|
int w = (A & 0x80) >> 7;
|
|
|
|
if ((V & 0x1) && !(control[w] & 0x1))
|
|
{
|
|
//printf("Start: %d\n", w);
|
|
if (MultiTapEnabled & (1 << w))
|
|
{
|
|
if (V & 0x2)
|
|
TapCounter[w] = 0;
|
|
}
|
|
LatchPending[w] = 1536;
|
|
PCFX_SetEvent(PCFX_EVENT_PAD, CalcNextEventTS(timestamp));
|
|
}
|
|
control[w] = V & 0x7;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FXINPUT_Write8(uint32 A, uint8 V, const v810_timestamp_t timestamp)
|
|
{
|
|
FXINPUT_Write16(A, V, timestamp);
|
|
}
|
|
|
|
void FXINPUT_Frame(void)
|
|
{
|
|
for (int i = 0; i < TOTAL_PORTS; i++)
|
|
{
|
|
devices[i]->Frame(data_ptr[i][0]);
|
|
}
|
|
}
|
|
|
|
static v810_timestamp_t lastts;
|
|
|
|
v810_timestamp_t FXINPUT_Update(const v810_timestamp_t timestamp)
|
|
{
|
|
int32 run_time = timestamp - lastts;
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
if (LatchPending[i] > 0)
|
|
{
|
|
LatchPending[i] -= run_time;
|
|
if (LatchPending[i] <= 0)
|
|
{
|
|
//printf("Update: %d, %d\n", i, timestamp / 1365);
|
|
|
|
if (MultiTapEnabled & (1 << i))
|
|
{
|
|
if (TapCounter[i] >= TAP_PORTS)
|
|
data_latch[i] = FX_SIG_TAP << 28;
|
|
else
|
|
{
|
|
data_latch[i] = devices[TapMap[i][TapCounter[i]]]->Read();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data_latch[i] = devices[i]->Read();
|
|
}
|
|
// printf("Moo: %d, %d, %08x\n", i, TapCounter[i], data_latch[i]);
|
|
latched[i] = TRUE;
|
|
control[i] &= ~1;
|
|
PCFXIRQ_Assert(PCFXIRQ_SOURCE_INPUT, TRUE);
|
|
|
|
if (MultiTapEnabled & (1 << i))
|
|
{
|
|
if (TapCounter[i] < TAP_PORTS)
|
|
{
|
|
TapCounter[i]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lastts = timestamp;
|
|
|
|
return CalcNextEventTS(timestamp);
|
|
}
|
|
|
|
void FXINPUT_ResetTS(int32 ts_base)
|
|
{
|
|
lastts = ts_base;
|
|
}
|
|
}
|