megadrive: add EA "4-way play" adapter support

This commit is contained in:
dinkc64 2018-02-26 01:49:13 +00:00
parent 6da44a8789
commit fcf2c8680d
2 changed files with 143 additions and 73 deletions

View File

@ -572,6 +572,7 @@ void IpsApplyPatches(UINT8* base, char* rom_name);
#define HARDWARE_SEGA_MEGADRIVE_PCB_MULAN (42) #define HARDWARE_SEGA_MEGADRIVE_PCB_MULAN (42)
#define HARDWARE_SEGA_MEGADRIVE_TEAMPLAYER (43) #define HARDWARE_SEGA_MEGADRIVE_TEAMPLAYER (43)
#define HARDWARE_SEGA_MEGADRIVE_TEAMPLAYER_PORT2 (44) #define HARDWARE_SEGA_MEGADRIVE_TEAMPLAYER_PORT2 (44)
#define HARDWARE_SEGA_MEGADRIVE_FOURWAYPLAY (45)
#define HARDWARE_SEGA_MEGADRIVE_SRAM_00400 (0x0100) #define HARDWARE_SEGA_MEGADRIVE_SRAM_00400 (0x0100)
#define HARDWARE_SEGA_MEGADRIVE_SRAM_00800 (0x0200) #define HARDWARE_SEGA_MEGADRIVE_SRAM_00800 (0x0200)

View File

@ -42,6 +42,12 @@
#define MAX_CARTRIDGE_SIZE 0xc00000 #define MAX_CARTRIDGE_SIZE 0xc00000
#define MAX_SRAM_SIZE 0x010000 #define MAX_SRAM_SIZE 0x010000
#if defined (__GNUC__) && defined (__LIBRETRO__)
#define OPTIMIZE_ATTR __attribute__((optimize("O2")))
#else
#define OPTIMIZE_ATTR
#endif
// PicoDrive Sek interface // PicoDrive Sek interface
static UINT64 SekCycleCnt, SekCycleAim, SekCycleCntDELTA, line_base_cycles; static UINT64 SekCycleCnt, SekCycleAim, SekCycleCntDELTA, line_base_cycles;
@ -149,10 +155,21 @@ struct TileStrip
INT32 cells; // cells (tiles) to draw (32 col mode doesn't need to update whole 320) INT32 cells; // cells (tiles) to draw (32 col mode doesn't need to update whole 320)
}; };
struct TeamPlayer {
UINT32 state;
UINT32 counter;
UINT32 table[12];
};
struct MegadriveJoyPad { struct MegadriveJoyPad {
UINT16 pad[8]; UINT16 pad[8];
UINT8 padTHPhase[8]; UINT32 padTHPhase[8];
UINT8 padDelay[8]; UINT32 padDelay[8];
UINT32 fourwaylatch; // EA "4 way play" adapter
UINT8 fourway[8];
TeamPlayer teamplayer[2]; // Sega "team player" 4 port adapter
}; };
static UINT8 *Mem = NULL, *MemEnd = NULL; static UINT8 *Mem = NULL, *MemEnd = NULL;
@ -195,6 +212,7 @@ UINT8 MegadriveJoy1[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
UINT8 MegadriveJoy2[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; UINT8 MegadriveJoy2[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
UINT8 MegadriveJoy3[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; UINT8 MegadriveJoy3[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
UINT8 MegadriveJoy4[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; UINT8 MegadriveJoy4[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
UINT8 MegadriveJoy5[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
UINT8 MegadriveDIP[2] = {0, 0}; UINT8 MegadriveDIP[2] = {0, 0};
static UINT32 RomNum = 0; static UINT32 RomNum = 0;
@ -221,6 +239,7 @@ static UINT8 bNoDebug = 0;
static INT32 bForce3Button = 0; static INT32 bForce3Button = 0;
INT32 psolarmode = 0; // pier solar INT32 psolarmode = 0; // pier solar
static INT32 TeamPlayerMode = 0; static INT32 TeamPlayerMode = 0;
static INT32 FourWayPlayMode = 0;
void MegadriveCheckHardware() void MegadriveCheckHardware()
{ {
@ -377,7 +396,9 @@ static INT32 MemIndex()
RamSVid = (UINT16 *) Next; Next += 0x000040 * sizeof(UINT16); // VSRam RamSVid = (UINT16 *) Next; Next += 0x000040 * sizeof(UINT16); // VSRam
RamVid = (UINT16 *) Next; Next += 0x008000 * sizeof(UINT16); // Video Ram RamVid = (UINT16 *) Next; Next += 0x008000 * sizeof(UINT16); // Video Ram
RamVReg = (struct PicoVideo *)Next; Next += sizeof(struct PicoVideo); RamVReg = (struct PicoVideo *)Next; Next += sizeof(struct PicoVideo);
JoyPad = (struct MegadriveJoyPad *) Next; Next += sizeof(struct MegadriveJoyPad);
RamEnd = Next; RamEnd = Next;
// Keep RamMisc out of the Ram section to keep from getting cleared on reset. // Keep RamMisc out of the Ram section to keep from getting cleared on reset.
@ -395,8 +416,6 @@ static INT32 MemIndex()
HighPreSpr = (INT32 *) Next; Next += (80*2+1) * sizeof(INT32); // slightly preprocessed sprites HighPreSpr = (INT32 *) Next; Next += (80*2+1) * sizeof(INT32); // slightly preprocessed sprites
HighSprZ = (INT8*) Next; Next += (320+8+8); // Z-buffer for accurate sprites and shadow/hilight mode HighSprZ = (INT8*) Next; Next += (320+8+8); // Z-buffer for accurate sprites and shadow/hilight mode
JoyPad = (struct MegadriveJoyPad *) Next; Next += sizeof(struct MegadriveJoyPad);
MemEnd = Next; MemEnd = Next;
return 0; return 0;
} }
@ -1150,7 +1169,7 @@ static INT32 PadRead(INT32 i)
{ {
INT32 pad=0,value=0,TH; INT32 pad=0,value=0,TH;
pad = ~(JoyPad->pad[i]); // Get inverse of pad MXYZ SACB RLDU pad = ~(JoyPad->pad[i]); // Get inverse of pad MXYZ SACB RLDU
TH = RamIO[i+1] & 0x40; TH = (FourWayPlayMode) ? JoyPad->fourway[i & 0x03] : RamIO[i+1] & 0x40;
if (!bForce3Button) { // 6 button gamepad enabled if (!bForce3Button) { // 6 button gamepad enabled
INT32 phase = JoyPad->padTHPhase[i]; INT32 phase = JoyPad->padTHPhase[i];
@ -1173,68 +1192,69 @@ static INT32 PadRead(INT32 i)
end: end:
// or the bits, which are set as output // or the bits, which are set as output
value |= RamIO[i+1] & RamIO[i+4]; if (!FourWayPlayMode)
value |= RamIO[i+1] & RamIO[i+4];
return value; // will mirror later return value; // will mirror later
} }
static struct { static void PadWrite(INT32 port, UINT8 data, UINT8 *ior)
UINT8 State;
UINT8 Counter;
UINT8 Table[12];
} teamplayer[2];
static void teamplayer_init()
{ {
INT32 index = 0; JoyPad->padDelay[port] = 0;
UINT8 port = TeamPlayerMode - 1; if(!(ior[0] & 0x40) && (data & 0x40))
JoyPad->padTHPhase[port] ++;
memset(&teamplayer[port], 0, sizeof(teamplayer[port])); ior[0] = data;
for (INT32 i = 0; i < 4; i++)
{
INT32 padnum = ((port << 2) + i) << 4;
teamplayer[port].Table[index++] = padnum;
teamplayer[port].Table[index++] = padnum | 4;
if (!bForce3Button)
{
teamplayer[port].Table[index++] = padnum | 8;
}
}
} }
static void teamplayer_reset() static void teamplayer_reset()
{ {
if (!TeamPlayerMode) return; if (!TeamPlayerMode) return;
teamplayer[TeamPlayerMode - 1].State = 0x60;
teamplayer[TeamPlayerMode - 1].Counter = 0; INT32 index = 0;
UINT8 port = TeamPlayerMode - 1;
memset(&JoyPad->teamplayer[port], 0, sizeof(JoyPad->teamplayer[port]));
for (INT32 i = 0; i < 4; i++)
{
INT32 padnum = ((port << 2) + i) << 4;
JoyPad->teamplayer[port].table[index++] = padnum;
JoyPad->teamplayer[port].table[index++] = padnum | 4;
if (!bForce3Button)
{
JoyPad->teamplayer[port].table[index++] = padnum | 8;
}
}
JoyPad->teamplayer[TeamPlayerMode - 1].state = 0x60;
JoyPad->teamplayer[TeamPlayerMode - 1].counter = 0;
} }
static UINT8 teamplayer_read() static UINT8 teamplayer_read()
{ {
UINT8 port = TeamPlayerMode - 1; UINT8 port = TeamPlayerMode - 1;
switch (teamplayer[port].Counter) switch (JoyPad->teamplayer[port].counter)
{ {
case 0: return ((teamplayer[port].State & 0x20) >> 1) | 0x03; case 0: return ((JoyPad->teamplayer[port].state & 0x20) >> 1) | 0x03;
case 1: return ((teamplayer[port].State & 0x20) >> 1) | 0x0F; case 1: return ((JoyPad->teamplayer[port].state & 0x20) >> 1) | 0x0F;
case 2: case 2:
case 3: return ((teamplayer[port].State & 0x20) >> 1); case 3: return ((JoyPad->teamplayer[port].state & 0x20) >> 1);
case 4: case 4:
case 5: case 5:
case 6: case 6:
case 7: return (((teamplayer[port].State & 0x20) >> 1) | ((bForce3Button) ? 0 : 1)); case 7: return (((JoyPad->teamplayer[port].state & 0x20) >> 1) | ((bForce3Button) ? 0 : 1));
default: { default: {
UINT8 padnum = teamplayer[port].Table[teamplayer[port].Counter - 8] >> 4; UINT8 padnum = JoyPad->teamplayer[port].table[JoyPad->teamplayer[port].counter - 8] >> 4;
if (TeamPlayerMode == 2) padnum -= 3; if (TeamPlayerMode == 2) padnum -= 3;
UINT8 retval = 0xf & ~(JoyPad->pad[padnum] >> (teamplayer[port].Table[teamplayer[port].Counter - 8] & 0xf)); UINT8 retval = 0xf & ~(JoyPad->pad[padnum] >> (JoyPad->teamplayer[port].table[JoyPad->teamplayer[port].counter - 8] & 0xf));
return (((teamplayer[port].State & 0x20) >> 1) | retval); return (((JoyPad->teamplayer[port].state & 0x20) >> 1) | retval);
} }
} }
} }
@ -1242,16 +1262,43 @@ static UINT8 teamplayer_read()
static void teamplayer_write(UINT8 data, UINT8 mask) static void teamplayer_write(UINT8 data, UINT8 mask)
{ {
UINT8 port = TeamPlayerMode - 1; UINT8 port = TeamPlayerMode - 1;
UINT8 state = (teamplayer[port].State & ~mask) | (data & mask); UINT8 state = (JoyPad->teamplayer[port].state & ~mask) | (data & mask);
if (state & 0x40) { if (state & 0x40) {
teamplayer[port].Counter = 0; JoyPad->teamplayer[port].counter = 0;
} }
else if ((teamplayer[port].State ^ state) & 0x60) { else if ((JoyPad->teamplayer[port].state ^ state) & 0x60) {
teamplayer[port].Counter++; JoyPad->teamplayer[port].counter++;
} }
teamplayer[port].State = state; JoyPad->teamplayer[port].state = state;
}
static UINT8 fourwayplay_read(UINT8 port)
{
switch (port) {
case 0:
if (JoyPad->fourwaylatch & 0x04) return 0x7c;
return PadRead(JoyPad->fourwaylatch & 0x03);
break;
case 1:
return 0x7f;
break;
}
return 0;
}
static void fourwayplay_write(UINT8 port, UINT8 data, UINT8 mask)
{
switch (port) {
case 0:
PadWrite(JoyPad->fourwaylatch & 0x03, data, &JoyPad->fourway[JoyPad->fourwaylatch & 0x03]);
break;
case 1:
JoyPad->fourwaylatch = ((data & mask) >> 4) & 0x07;
break;
}
} }
UINT8 __fastcall MegadriveIOReadByte(UINT32 sekAddress) UINT8 __fastcall MegadriveIOReadByte(UINT32 sekAddress)
@ -1260,7 +1307,7 @@ UINT8 __fastcall MegadriveIOReadByte(UINT32 sekAddress)
bprintf(PRINT_NORMAL, _T("IO Attempt to read byte value of location %x\n"), sekAddress); bprintf(PRINT_NORMAL, _T("IO Attempt to read byte value of location %x\n"), sekAddress);
INT32 offset = (sekAddress >> 1) & 0xf; INT32 offset = (sekAddress >> 1) & 0xf;
if (!TeamPlayerMode) { if (!TeamPlayerMode && !FourWayPlayMode) {
// 6-Button Support // 6-Button Support
switch (offset) { switch (offset) {
case 0: // Get Hardware case 0: // Get Hardware
@ -1273,8 +1320,8 @@ UINT8 __fastcall MegadriveIOReadByte(UINT32 sekAddress)
//bprintf(PRINT_NORMAL, _T("IO Attempt to read byte value of location %x\n"), sekAddress); //bprintf(PRINT_NORMAL, _T("IO Attempt to read byte value of location %x\n"), sekAddress);
return RamIO[offset]; return RamIO[offset];
} }
} else { } else if (TeamPlayerMode || FourWayPlayMode) {
// Sega Team Player Support // Sega Team Player & Four Way Play Support
switch (offset) { switch (offset) {
case 0: // Get Hardware case 0: // Get Hardware
return Hardware; return Hardware;
@ -1284,9 +1331,19 @@ UINT8 __fastcall MegadriveIOReadByte(UINT32 sekAddress)
UINT8 mask = 0x80 | RamIO[offset + 3]; UINT8 mask = 0x80 | RamIO[offset + 3];
UINT8 data = 0x7f; UINT8 data = 0x7f;
if (offset < 3) { if (offset < 3) {
switch (TeamPlayerMode) { if (TeamPlayerMode) {
case 1: data = (offset==1) ? teamplayer_read() : 0x7f; break; // teamplayer port 1, nothing port 2 switch (TeamPlayerMode) {
case 2: data = (offset==2) ? teamplayer_read() : PadRead(0); break; // teamplayer port2, gamepad port 1 case 1: data = (offset==1) ? teamplayer_read() : 0x7f; break; // teamplayer port 1, nothing port 2
case 2: data = (offset==2) ? teamplayer_read() : PadRead(0); break; // teamplayer port2, gamepad port 1
}
}
if (FourWayPlayMode) {
switch (offset) {
case 1:
case 2:
data = fourwayplay_read(offset - 1);
break;
}
} }
} }
return (RamIO[offset] & mask) | (data & ~mask); return (RamIO[offset] & mask) | (data & ~mask);
@ -1315,28 +1372,28 @@ void __fastcall MegadriveIOWriteByte(UINT32 sekAddress, UINT8 byteValue)
INT32 offset = (sekAddress >> 1) & 0xf; INT32 offset = (sekAddress >> 1) & 0xf;
if (!TeamPlayerMode) { if (!TeamPlayerMode && !FourWayPlayMode) {
// 6-Button Support // 6-Button Support
switch( offset ) { switch( offset ) {
case 1: case 1:
JoyPad->padDelay[0] = 0; case 2:
if(!(RamIO[1] & 0x40) && (byteValue&0x40)) PadWrite(offset - 1, byteValue, &RamIO[offset]);
JoyPad->padTHPhase[0] ++;
break;
case 2:
JoyPad->padDelay[1] = 0;
if(!(RamIO[2] & 0x40) && (byteValue&0x40))
JoyPad->padTHPhase[1] ++;
break; break;
} }
} else { } else if (FourWayPlayMode) {
// EA Four Way Play support
switch (offset) {
case 1:
case 2:
fourwayplay_write(offset-1, byteValue, RamIO[offset + 3]);
break;
}
} else if (TeamPlayerMode) {
// Sega Team Player Support // Sega Team Player Support
switch (offset) { switch (offset) {
case 1: case 1:
if (TeamPlayerMode == 2) { // teamplayer port 2, gamepad port 1 if (TeamPlayerMode == 2) { // teamplayer port 2, gamepad port 1
JoyPad->padDelay[0] = 0; PadWrite(offset - 1, byteValue, &RamIO[offset]);
if(!(RamIO[1] & 0x40) && (byteValue&0x40))
JoyPad->padTHPhase[0] ++;
} else { } else {
teamplayer_write(byteValue, RamIO[offset + 3]); teamplayer_write(byteValue, RamIO[offset + 3]);
} }
@ -1360,9 +1417,9 @@ void __fastcall MegadriveIOWriteByte(UINT32 sekAddress, UINT8 byteValue)
void __fastcall MegadriveIOWriteWord(UINT32 sekAddress, UINT16 wordValue) void __fastcall MegadriveIOWriteWord(UINT32 sekAddress, UINT16 wordValue)
{ {
//if (sekAddress > 0xA1001F) //if (sekAddress > 0xA1001F)
// bprintf(PRINT_NORMAL, _T("IO Attempt to write word value %x to location %x\n"), wordValue, sekAddress); // bprintf(PRINT_NORMAL, _T("IO Attempt to write word value %x to location %x\n"), wordValue, sekAddress);
MegadriveIOWriteByte(sekAddress, wordValue & 0xff); MegadriveIOWriteByte(sekAddress, wordValue & 0xff);
} }
@ -2744,6 +2801,9 @@ static void SetupCustomCartridgeMappers()
} }
switch ((BurnDrvGetHardwareCode() & 0xff)) { switch ((BurnDrvGetHardwareCode() & 0xff)) {
case HARDWARE_SEGA_MEGADRIVE_FOURWAYPLAY:
FourWayPlayMode = 1;
break;
case HARDWARE_SEGA_MEGADRIVE_TEAMPLAYER: case HARDWARE_SEGA_MEGADRIVE_TEAMPLAYER:
TeamPlayerMode = 1; TeamPlayerMode = 1;
break; break;
@ -2752,12 +2812,16 @@ static void SetupCustomCartridgeMappers()
break; break;
default: default:
TeamPlayerMode = 0; TeamPlayerMode = 0;
FourWayPlayMode = 0;
break; break;
} }
if (TeamPlayerMode) { if (TeamPlayerMode) {
bprintf(0, _T("Game supports Sega TeamPlayer 4x Pad in Port %d.\n"), TeamPlayerMode); bprintf(0, _T("Game supports Sega TeamPlayer 4x Pad in Port %d.\n"), TeamPlayerMode);
teamplayer_init(); }
if (FourWayPlayMode) {
bprintf(0, _T("Game supports EA 4-WayPlay 4x Pad in Port 1 & 2.\n"));
} }
} }
@ -3358,6 +3422,7 @@ INT32 MegadriveExit()
bNoDebug = 0; bNoDebug = 0;
bForce3Button = 0; bForce3Button = 0;
TeamPlayerMode = 0; TeamPlayerMode = 0;
FourWayPlayMode = 0;
psolarmode = 0; psolarmode = 0;
@ -4132,7 +4197,7 @@ static void DrawSpritesFromCache(INT32 *hc, INT32 sh)
// Index + 0 : hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size // Index + 0 : hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size
// Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8 // Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8
static void PrepareSprites(INT32 full) static void OPTIMIZE_ATTR PrepareSprites(INT32 full)
{ {
INT32 u=0,link=0,sblocks=0; INT32 u=0,link=0,sblocks=0;
INT32 table=0; INT32 table=0;
@ -4503,7 +4568,7 @@ INT32 MegadriveDraw()
#define CYCLES_M68K_VINT_LAG 68 #define CYCLES_M68K_VINT_LAG 68
#define CYCLES_M68K_ASD 148 #define CYCLES_M68K_ASD 148
INT32 MegadriveFrame() INT32 OPTIMIZE_ATTR MegadriveFrame()
{ {
if (MegadriveReset) { if (MegadriveReset) {
MegadriveResetDo(); MegadriveResetDo();
@ -4511,12 +4576,13 @@ INT32 MegadriveFrame()
return 0xdead; // prevent crash because of a call to Reinitialise() in MegadriveResetDo(); return 0xdead; // prevent crash because of a call to Reinitialise() in MegadriveResetDo();
} }
JoyPad->pad[0] = JoyPad->pad[1] = JoyPad->pad[2] = JoyPad->pad[3] = 0; JoyPad->pad[0] = JoyPad->pad[1] = JoyPad->pad[2] = JoyPad->pad[3] = JoyPad->pad[4] = 0;
for (INT32 i = 0; i < 12; i++) { for (INT32 i = 0; i < 12; i++) {
JoyPad->pad[0] |= (MegadriveJoy1[i] & 1) << i; JoyPad->pad[0] |= (MegadriveJoy1[i] & 1) << i;
JoyPad->pad[1] |= (MegadriveJoy2[i] & 1) << i; JoyPad->pad[1] |= (MegadriveJoy2[i] & 1) << i;
JoyPad->pad[2] |= (MegadriveJoy3[i] & 1) << i; JoyPad->pad[2] |= (MegadriveJoy3[i] & 1) << i;
JoyPad->pad[3] |= (MegadriveJoy4[i] & 1) << i; JoyPad->pad[3] |= (MegadriveJoy4[i] & 1) << i;
JoyPad->pad[4] |= (MegadriveJoy5[i] & 1) << i;
} }
SekCyclesNewFrame(); // for sound sync SekCyclesNewFrame(); // for sound sync
@ -4588,6 +4654,11 @@ INT32 MegadriveFrame()
// pad delay (for 6 button pads) // pad delay (for 6 button pads)
if(JoyPad->padDelay[0]++ > 25) JoyPad->padTHPhase[0] = 0; if(JoyPad->padDelay[0]++ > 25) JoyPad->padTHPhase[0] = 0;
if(JoyPad->padDelay[1]++ > 25) JoyPad->padTHPhase[1] = 0; if(JoyPad->padDelay[1]++ > 25) JoyPad->padTHPhase[1] = 0;
if (FourWayPlayMode) {
if(JoyPad->padDelay[2]++ > 25) JoyPad->padTHPhase[2] = 0; // fourwayplay
if(JoyPad->padDelay[3]++ > 25) JoyPad->padTHPhase[3] = 0; // "
}
} }
// H-Interrupts: // H-Interrupts:
@ -4734,8 +4805,6 @@ INT32 MegadriveScan(INT32 nAction, INT32 *pnMin)
SCAN_VAR(z80_cycle_aim); SCAN_VAR(z80_cycle_aim);
SCAN_VAR(last_z80_sync); SCAN_VAR(last_z80_sync);
SCAN_VAR(teamplayer);
BurnRandomScan(nAction); BurnRandomScan(nAction);
} }