fixed Action Replay hardware emulation (still need Pro Action Replay 1 & 2)

This commit is contained in:
ekeeke31 2009-08-02 20:41:46 +00:00
parent 3031072ee0
commit 9eecfa2c7c
10 changed files with 281 additions and 145 deletions

View File

@ -146,14 +146,29 @@ void cart_hw_reset()
cart_hw.realtec |= 2; cart_hw.realtec |= 2;
} }
/* save default cartridge slot mapping */
default_rom = m68k_memory_map[0].base;
/* SVP chip */ /* SVP chip */
if (svp) svp_reset(); if (svp) svp_reset();
/* Game Genie hardware */ /* Lock-ON */
if (config.lock_on == CART_GG) ggenie_reset(); switch (config.lock_on)
{
case GAME_GENIE:
ggenie_reset();
break;
case ACTION_REPLAY:
datel_reset();
break;
case SONIC_KNUCKLES:
break;
default:
break;
}
/* save default cartridge slot mapping */
default_rom = m68k_memory_map[0].base;
} }
/* cart hardware detection */ /* cart hardware detection */
@ -226,9 +241,24 @@ void cart_hw_init()
} }
/********************************************** /**********************************************
GAME GENIE CARTRIDGE LOCK-ON
***********************************************/ ***********************************************/
if (config.lock_on == CART_GG) ggenie_init(); switch (config.lock_on)
{
case GAME_GENIE:
ggenie_init();
break;
case ACTION_REPLAY:
datel_init();
break;
case SONIC_KNUCKLES:
break;
default:
break;
}
/********************************************** /**********************************************
SVP CHIP SVP CHIP
@ -417,6 +447,7 @@ void cart_hw_init()
} }
/* default write handler for !TIME signal */ /* default write handler for !TIME signal */
/* TODO: handle Sonic & Knuckles + Sonic 2 case to prevent RAM activation */
if (!cart_hw.time_w) cart_hw.time_w = default_time_w; if (!cart_hw.time_w) cart_hw.time_w = default_time_w;
} }

View File

@ -28,10 +28,9 @@
#define _CART_HW_H_ #define _CART_HW_H_
/* Lock-ON cartridge devices */ /* Lock-ON cartridge devices */
#define NO_CART 0 /* no connections */ #define GAME_GENIE 1
#define CART_GG 1 /* game genie */ #define ACTION_REPLAY 2
#define CART_AR 2 /* action replay */ #define SONIC_KNUCKLES 3
#define CART_SK 3 /* Sonic & Knuckles */
/* Hardware description */ /* Hardware description */
typedef struct typedef struct

View File

@ -1,11 +1,9 @@
/**************************************************************************** /****************************************************************************
* Genesis Plus * Genesis Plus
* Action Replay / Pro Action Replay emulation * DATEL Action Replay / Pro Action Replay emulation
* *
* Copyright (C) 2009 Eke-Eke (GCN/Wii port) * Copyright (C) 2009 Eke-Eke (GCN/Wii port)
* *
* Based on reverse-engineering done on DATEL softwares
*
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
@ -23,7 +21,6 @@
#include "shared.h" #include "shared.h"
#ifdef TEST_AR
static struct static struct
{ {
uint8 enabled; uint8 enabled;
@ -34,16 +31,16 @@ static struct
uint32 addr[4]; uint32 addr[4];
} action_replay; } action_replay;
static void ar_write_byte(uint32 address, uint32 data);
static void ar_write_regs(uint32 address, uint32 data); static void ar_write_regs(uint32 address, uint32 data);
static void ar_write_word(uint32 address, uint32 data); static void wram_write_byte(uint32 address, uint32 data);
static void wram_write_word(uint32 address, uint32 data);
void ar_init(void) void datel_init(void)
{ {
memset(&action_replay,0,sizeof(action_replay)); memset(&action_replay,0,sizeof(action_replay));
/* load Game Genie ROM program */ /* load Game Genie ROM program */
FILE *f = fopen("./areplay.bin","rb"); FILE *f = fopen(AR_ROM,"rb");
if (!f) return; if (!f) return;
fread(action_replay.rom,1,0x8000,f); fread(action_replay.rom,1,0x8000,f);
fclose(f); fclose(f);
@ -70,41 +67,115 @@ void ar_init(void)
action_replay.enabled = 1; action_replay.enabled = 1;
} }
void ar_reset(void) void datel_reset(void)
{ {
if (!action_replay.enabled) return; if (action_replay.enabled)
{
/* reset codes */
datel_switch(0);
/* restore patched ROM */ /* reset internal state */
memset(action_replay.regs,0,sizeof(action_replay.regs));
memset(action_replay.old,0,sizeof(action_replay.old));
memset(action_replay.data,0,sizeof(action_replay.data));
memset(action_replay.addr,0,sizeof(action_replay.addr));
/* slot 0 is mapped to Action replay ROM */
m68k_memory_map[0].base = action_replay.rom;
}
}
void datel_switch(uint8 enable)
{
int i;
if (enable)
{
int offset;
/* store old values */
for (i=0; i<4; i++)
{
if (action_replay.data[i])
{
offset = action_replay.addr[i] >> 16;
if (offset < 0x40) /* cartridge ROM */
action_replay.old[i] = *(uint16 *)(cart_rom + action_replay.addr[i]);
else if (offset >= 0xe0) /* Work RAM */
action_replay.old[i] = *(uint16 *)(work_ram + (action_replay.addr[i]&0xffff));
}
}
/* patch new values */
for (i=0; i<4; i++)
{
if (action_replay.data[i])
{
offset = action_replay.addr[i] >> 16;
if (offset < 0x40) /* cartridge ROM */
*(uint16 *)(cart_rom + action_replay.addr[i]) = action_replay.data[i];
else if (offset >= 0xe0) /* Work RAM */
*(uint16 *)(work_ram + (action_replay.addr[i]&0xffff)) = action_replay.data[i];
}
}
/* set RAM write handlers */
for (i=0xe0; i<0x100; i++)
{
m68k_memory_map[i].write8 = wram_write_byte;
m68k_memory_map[i].write16 = wram_write_word;
}
}
else
{
/* restore original data */
for (i=0; i<4; i++)
{
if (action_replay.data[i])
{
if (action_replay.addr[i] < 0x400000)
*(uint16 *)(cart_rom + action_replay.addr[i]) = action_replay.old[i];
else if (action_replay.addr[i] >= 0xe00000)
*(uint16 *)(work_ram + (action_replay.addr[i]&0xffff)) = action_replay.old[i];
}
}
}
}
static void wram_write_byte(uint32 address, uint32 data)
{
int i; int i;
for (i=0; i<4; i++) for (i=0; i<4; i++)
{ {
if (action_replay.addr[i] < 0x400000) if ((address & 0xe0fffe) == (action_replay.addr[i]&0xe0fffe))
*(uint16 *)(cart_rom + action_replay.addr[i]) = action_replay.old[i]; {
if (address & 1) /* lower byte write */
action_replay.old[i] = (action_replay.old[i] & 0xff00) | (data & 0xff);
else /* upper byte write */
action_replay.old[i] = (action_replay.old[i] & 0x00ff) | (data << 8);
return;
}
} }
/* reset internal state */ WRITE_BYTE(work_ram, address & 0xffff, data);
memset(action_replay.regs,0,sizeof(action_replay.regs)); }
memset(action_replay.old,0,sizeof(action_replay.old));
memset(action_replay.data,0,sizeof(action_replay.data));
memset(action_replay.addr,0,sizeof(action_replay.addr));
/* slot 0 is mapped to Action replay ROM */ static void wram_write_word(uint32 address, uint32 data)
m68k_memory_map[0].base = action_replay.rom; {
int i;
/* reset RAM handlers */ for (i=0; i<4; i++)
for (i=0xe0; i<0x100; i++)
{ {
m68k_memory_map[i].write8 = NULL; if ((address & 0xe0fffe) == (action_replay.addr[i]&0xe0fffe))
m68k_memory_map[i].write16 = NULL; {
action_replay.old[i] = data;
return;
}
} }
}
static void ar_write_byte(uint32 address, uint32 data) *(uint16 *)(work_ram + (address & 0xffff)) = data;
{
}
static void ar_write_word(uint32 address, uint32 data)
{
} }
static void ar_write_regs(uint32 address, uint32 data) static void ar_write_regs(uint32 address, uint32 data)
@ -124,55 +195,20 @@ static void ar_write_regs(uint32 address, uint32 data)
/* decode patch value & address on exit */ /* decode patch value & address on exit */
if ((offset == 3) && (data == 0xffff)) if ((offset == 3) && (data == 0xffff))
{ {
/* patch data */ /* decode patch data */
action_replay.data[0] = action_replay.regs[0]; action_replay.data[0] = action_replay.regs[0];
action_replay.data[1] = action_replay.regs[4]; action_replay.data[1] = action_replay.regs[4];
action_replay.data[2] = action_replay.regs[7]; action_replay.data[2] = action_replay.regs[7];
action_replay.data[3] = action_replay.regs[10]; action_replay.data[3] = action_replay.regs[10];
/* patch address */ /* decode patch address */
action_replay.addr[0] = (action_replay.regs[1] | ((action_replay.regs[2] & 0x7f00) << 8)) << 1; action_replay.addr[0] = (action_replay.regs[1] | ((action_replay.regs[2] & 0x7f00) << 8)) << 1;
action_replay.addr[1] = (action_replay.regs[5] | ((action_replay.regs[6] & 0x7f00) << 8)) << 1; action_replay.addr[1] = (action_replay.regs[5] | ((action_replay.regs[6] & 0x7f00) << 8)) << 1;
action_replay.addr[2] = (action_replay.regs[8] | ((action_replay.regs[9] & 0x7f00) << 8)) << 1; action_replay.addr[2] = (action_replay.regs[8] | ((action_replay.regs[9] & 0x7f00) << 8)) << 1;
action_replay.addr[3] = (action_replay.regs[11] | ((action_replay.regs[12] & 0x7f00) << 8)) << 1; action_replay.addr[3] = (action_replay.regs[11] | ((action_replay.regs[12] & 0x7f00) << 8)) << 1;
int i;
for (i=0; i<4; i++)
{
offset = action_replay.addr[i] >> 16;
/* ROM area */
if (offset < 0x40)
{
/* store old ROM value */
action_replay.old[i] = *(uint16 *)(cart_rom + action_replay.addr[i]);
}
/* Work RAM area */
else if (offset >= 0xe0)
{
/* patch RAM */
*(uint16 *)(work_ram + (action_replay.addr[i] & 0xffff)) = action_replay.data[i];
/* setup handlers */
m68k_memory_map[offset].write8 = ar_write_byte;
m68k_memory_map[offset].write16 = ar_write_word;
}
}
for (i=0; i<4; i++)
{
offset = action_replay.addr[i] >> 16;
/* ROM area */
if (offset < 0x40)
{
/* patch ROM */
*(uint16 *)(cart_rom + action_replay.addr[i]) = action_replay.data[i];;
}
}
/* reads are mapped to Cartridge ROM */ /* reads are mapped to Cartridge ROM */
/* NOTE: codes should be disabled on startup */
m68k_memory_map[0].base = cart_rom; m68k_memory_map[0].base = cart_rom;
} }
} }
#endif

29
source/cart_hw/datel.h Normal file
View File

@ -0,0 +1,29 @@
/****************************************************************************
* Genesis Plus
* DATEL Action Replay / Pro Action Replay emulation
*
* Copyright (C) 2009 Eke-Eke (GCN/Wii port)
*
* 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
***************************************************************************/
#ifndef _DATEL_H_
#define _DATEL_H_
extern void datel_init(void);
extern void datel_reset(void);
extern void datel_switch(uint8 enable);
#endif

View File

@ -74,25 +74,67 @@ void ggenie_init(void)
void ggenie_reset(void) void ggenie_reset(void)
{ {
if (!ggenie.enabled) return; if (ggenie.enabled)
/* restore patched ROM */
int i;
for (i=0; i<6; i++)
{ {
if (ggenie.regs[0] & (1 << i)) /* reset codes */
*(uint16 *)(cart_rom + ggenie.addr[i]) = ggenie.old[i]; ggenie_switch(0);
/* reset internal state */
memset(ggenie.regs,0,sizeof(ggenie.regs));
memset(ggenie.old,0,sizeof(ggenie.old));
memset(ggenie.data,0,sizeof(ggenie.data));
memset(ggenie.addr,0,sizeof(ggenie.addr));
/* slot 0 is mapped to Game Genie ROM */
m68k_memory_map[0].base = ggenie.rom;
/* Internal registers are write only */
m68k_memory_map[0].read16 = NULL;
} }
}
/* reset internal state */ void ggenie_switch(uint8 enable)
memset(ggenie.regs,0,sizeof(ggenie.regs)); {
memset(ggenie.old,0,sizeof(ggenie.old)); int i,j;
memset(ggenie.data,0,sizeof(ggenie.data)); if (enable)
memset(ggenie.addr,0,sizeof(ggenie.addr)); {
for (i=0; i<6; i++)
{
/* patch is enabled ? */
if (ggenie.regs[0] & (1 << i))
{
/* first look if address has not already been patched */
for (j=0;j<i;j++)
{
if (ggenie.addr[i] == ggenie.addr[j])
{
/* disable code for later initialization */
ggenie.regs[0] &= ~(1 << i);
j = i;
}
}
/* slot 0 is mapped to Game Genie ROM */ /* save old value and patch ROM if enabled */
m68k_memory_map[0].base = ggenie.rom; if (ggenie.regs[0] & (1 << i))
m68k_memory_map[0].read16 = NULL; {
ggenie.old[i] = *(uint16 *)(cart_rom + ggenie.addr[i]);
*(uint16 *)(cart_rom + ggenie.addr[i]) = ggenie.data[i];
}
}
}
}
else
{
/* restore old values */
for (i=0; i<6; i++)
{
/* patch is enabled ? */
if (ggenie.regs[0] & (1 << i))
{
*(uint16 *)(cart_rom + ggenie.addr[i]) = ggenie.old[i];
}
}
}
} }
@ -101,7 +143,11 @@ void ggenie_reset(void)
static void ggenie_write_byte(uint32 address, uint32 data) static void ggenie_write_byte(uint32 address, uint32 data)
{ {
/* Lock bit */ /* Lock bit */
if (ggenie.regs[0] & 0x100) return; if (ggenie.regs[0] & 0x100)
{
m68k_unused_8_w(address, data);
return;
}
/* Register offset */ /* Register offset */
uint8 offset = (address >> 1) & 0x1f; uint8 offset = (address >> 1) & 0x1f;
@ -114,7 +160,11 @@ static void ggenie_write_byte(uint32 address, uint32 data)
static void ggenie_write_word(uint32 address, uint32 data) static void ggenie_write_word(uint32 address, uint32 data)
{ {
/* Lock bit */ /* Lock bit */
if (ggenie.regs[0] & 0x100) return; if (ggenie.regs[0] & 0x100)
{
m68k_unused_8_w(address, data);
return;
}
/* Register offset */ /* Register offset */
uint8 offset = (address >> 1) & 0x1f; uint8 offset = (address >> 1) & 0x1f;
@ -140,6 +190,9 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type)
break; break;
} }
/* update internal register */
ggenie.regs[offset] = data;
/* Mode Register */ /* Mode Register */
if (!offset) if (!offset)
{ {
@ -166,47 +219,31 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type)
} }
/* LOCK bit */ /* LOCK bit */
if ((data & 0x100) && !(ggenie.regs[0] & 0x100)) if (data & 0x100)
{ {
/* decode patch address (ROM area only)*/
/* note: Charles's documment is wrong, first register holds bits 23-16 of patch address */
ggenie.addr[0] = ((ggenie.regs[2] & 0x3f) << 16) | ggenie.regs[3];
ggenie.addr[1] = ((ggenie.regs[5] & 0x3f) << 16) | ggenie.regs[6];
ggenie.addr[2] = ((ggenie.regs[8] & 0x3f) << 16) | ggenie.regs[9];
ggenie.addr[3] = ((ggenie.regs[11] & 0x3f) << 16) | ggenie.regs[12];
ggenie.addr[4] = ((ggenie.regs[14] & 0x3f) << 16) | ggenie.regs[15];
ggenie.addr[5] = ((ggenie.regs[17] & 0x3f) << 16) | ggenie.regs[18];
/* decode patch data */
ggenie.data[0] = ggenie.regs[4];
ggenie.data[1] = ggenie.regs[7];
ggenie.data[2] = ggenie.regs[10];
ggenie.data[3] = ggenie.regs[13];
ggenie.data[4] = ggenie.regs[16];
ggenie.data[5] = ggenie.regs[19];
/* patch ROM when GG program exits (LOCK bit set) */ /* patch ROM when GG program exits (LOCK bit set) */
/* this is done here to handle patched program reads faster & more easily */ /* this is done here to handle patched program reads faster & more easily */
/* on real HW, address decoding would be done on each reads */ /* on real HW, address decoding would be done on each reads */
int i,j; ggenie_switch(1);
for (i=0; i<6; i++)
{
/* decode patch address */
/* note: Charles's documment is wrong, first register holds bits 23-16 of patch address */
ggenie.addr[i] = (ggenie.regs[2+3*i] << 16) | ggenie.regs[3+3*i];
/* decode patch data */
ggenie.data[i] = ggenie.regs[4+3*i];
/* patch is enabled ? */
if (data & (1 << i))
{
/* first look if address has not already been patched */
for (j=0;j<i;j++)
{
if (ggenie.addr[i] == ggenie.addr[j])
{
/* disable code for later initialization */
data &= ~(1 << i);
}
}
/* ave old value and patch ROM if enabled */
if (data & (1 << i))
{
ggenie.old[i] = *(uint16 *)(cart_rom + ggenie.addr[i]);
*(uint16 *)(cart_rom + ggenie.addr[i]) = ggenie.data[i];
}
}
}
} }
} }
/* update internal register */
ggenie.regs[offset] = data;
} }
static uint32 ggenie_read_regs(uint32 address) static uint32 ggenie_read_regs(uint32 address)

View File

@ -22,9 +22,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***************************************************************************/ ***************************************************************************/
/* global variables */ #ifndef _GGENIE_H_
extern int gamegenie; #define _GGENIE_H_
/* Function prototypes */ /* Function prototypes */
extern void ggenie_init(void); extern void ggenie_init(void);
extern void ggenie_reset(void); extern void ggenie_reset(void);
extern void ggenie_switch(uint8 enable);
#endif

View File

@ -82,7 +82,7 @@ void config_default(void)
config.force_dtack = 0; config.force_dtack = 0;
config.addr_error = 1; config.addr_error = 1;
config.bios_enabled = 0; config.bios_enabled = 0;
config.lock_on = NO_CART; config.lock_on = 0;
/* video options */ /* video options */
config.xshift = 0; config.xshift = 0;

View File

@ -850,7 +850,8 @@ static void systemmenu ()
sprintf (items[1].text, "System Lockups: %s", config.force_dtack ? "OFF" : "ON"); sprintf (items[1].text, "System Lockups: %s", config.force_dtack ? "OFF" : "ON");
sprintf (items[2].text, "68k Address Error: %s", config.addr_error ? "ON" : "OFF"); sprintf (items[2].text, "68k Address Error: %s", config.addr_error ? "ON" : "OFF");
sprintf (items[3].text, "System BIOS: %s", (config.bios_enabled & 1) ? "ON":"OFF"); sprintf (items[3].text, "System BIOS: %s", (config.bios_enabled & 1) ? "ON":"OFF");
if (config.lock_on == CART_GG) sprintf (items[4].text, "Lock-On: GAME GENIE"); if (config.lock_on == GAME_GENIE) sprintf (items[4].text, "Lock-On: GAME GENIE");
else if (config.lock_on == ACTION_REPLAY) sprintf (items[4].text, "Lock-On: ACTION REPLAY");
else sprintf (items[4].text, "Lock-On: OFF"); else sprintf (items[4].text, "Lock-On: OFF");
if (svp) if (svp)
@ -920,24 +921,22 @@ static void systemmenu ()
case 3: /*** BIOS support ***/ case 3: /*** BIOS support ***/
config.bios_enabled ^= 1; config.bios_enabled ^= 1;
sprintf (items[3].text, "System BIOS: %s", (config.bios_enabled & 1) ? "ON":"OFF"); sprintf (items[3].text, "System BIOS: %s", (config.bios_enabled & 1) ? "ON":"OFF");
if (genromsize || (config.bios_enabled == 3)) if (genromsize)
{ {
system_init (); system_init ();
audio_init(48000);
system_reset (); system_reset ();
} }
break; break;
case 4: /*** Cart Lock-On ***/ case 4: /*** Cart Lock-On ***/
config.lock_on++; config.lock_on++;
if (config.lock_on > CART_GG) config.lock_on = NO_CART; if (config.lock_on == GAME_GENIE) sprintf (items[4].text, "Lock-On: GAME GENIE");
if (config.lock_on == CART_GG) sprintf (items[4].text, "Lock-On: GAME GENIE"); else if (config.lock_on == ACTION_REPLAY) sprintf (items[4].text, "Lock-On: ACTION REPLAY");
else sprintf (items[4].text, "Lock-On: OFF"); else sprintf (items[4].text, "Lock-On: OFF");
if (genromsize || (config.bios_enabled == 3)) if (genromsize)
{ {
system_reset (); /* clear any GG patches */ system_reset (); /* clear any patches first */
system_init (); system_init ();
audio_init(48000);
system_reset (); system_reset ();
} }
break; break;

View File

@ -27,6 +27,7 @@
#define DEFAULT_PATH "/genplus" #define DEFAULT_PATH "/genplus"
#define GG_ROM "/genplus/ggenie.bin" #define GG_ROM "/genplus/ggenie.bin"
#define AR_ROM "/genplus/areplay.bin"
#define OS_ROM "/genplus/bios.bin" #define OS_ROM "/genplus/bios.bin"
#ifdef HW_RVL #ifdef HW_RVL

View File

@ -27,6 +27,7 @@
#include "eeprom.h" #include "eeprom.h"
#include "sram.h" #include "sram.h"
#include "ggenie.h" #include "ggenie.h"
#include "datel.h"
#include "svp.h" #include "svp.h"
#include "osd.h" #include "osd.h"