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;
}
/* save default cartridge slot mapping */
default_rom = m68k_memory_map[0].base;
/* SVP chip */
if (svp) svp_reset();
/* Game Genie hardware */
if (config.lock_on == CART_GG) ggenie_reset();
/* Lock-ON */
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 */
@ -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
@ -417,6 +447,7 @@ void cart_hw_init()
}
/* 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;
}

View File

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

View File

@ -1,11 +1,9 @@
/****************************************************************************
* Genesis Plus
* Action Replay / Pro Action Replay emulation
* DATEL Action Replay / Pro Action Replay emulation
*
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -23,7 +21,6 @@
#include "shared.h"
#ifdef TEST_AR
static struct
{
uint8 enabled;
@ -34,16 +31,16 @@ static struct
uint32 addr[4];
} action_replay;
static void ar_write_byte(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));
/* load Game Genie ROM program */
FILE *f = fopen("./areplay.bin","rb");
FILE *f = fopen(AR_ROM,"rb");
if (!f) return;
fread(action_replay.rom,1,0x8000,f);
fclose(f);
@ -70,41 +67,115 @@ void ar_init(void)
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;
for (i=0; i<4; i++)
{
if (action_replay.addr[i] < 0x400000)
*(uint16 *)(cart_rom + action_replay.addr[i]) = action_replay.old[i];
if ((address & 0xe0fffe) == (action_replay.addr[i]&0xe0fffe))
{
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 */
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));
WRITE_BYTE(work_ram, address & 0xffff, data);
}
/* slot 0 is mapped to Action replay ROM */
m68k_memory_map[0].base = action_replay.rom;
/* reset RAM handlers */
for (i=0xe0; i<0x100; i++)
static void wram_write_word(uint32 address, uint32 data)
{
int i;
for (i=0; i<4; i++)
{
m68k_memory_map[i].write8 = NULL;
m68k_memory_map[i].write16 = NULL;
if ((address & 0xe0fffe) == (action_replay.addr[i]&0xe0fffe))
{
action_replay.old[i] = data;
return;
}
}
}
static void ar_write_byte(uint32 address, uint32 data)
{
}
static void ar_write_word(uint32 address, uint32 data)
{
*(uint16 *)(work_ram + (address & 0xffff)) = 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 */
if ((offset == 3) && (data == 0xffff))
{
/* patch data */
/* decode patch data */
action_replay.data[0] = action_replay.regs[0];
action_replay.data[1] = action_replay.regs[4];
action_replay.data[2] = action_replay.regs[7];
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[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[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 */
/* NOTE: codes should be disabled on startup */
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)
{
if (!ggenie.enabled) return;
/* restore patched ROM */
int i;
for (i=0; i<6; i++)
if (ggenie.enabled)
{
if (ggenie.regs[0] & (1 << i))
*(uint16 *)(cart_rom + ggenie.addr[i]) = ggenie.old[i];
/* reset codes */
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 */
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));
void ggenie_switch(uint8 enable)
{
int i,j;
if (enable)
{
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 */
m68k_memory_map[0].base = ggenie.rom;
m68k_memory_map[0].read16 = NULL;
/* save old value and patch ROM if enabled */
if (ggenie.regs[0] & (1 << i))
{
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)
{
/* Lock bit */
if (ggenie.regs[0] & 0x100) return;
if (ggenie.regs[0] & 0x100)
{
m68k_unused_8_w(address, data);
return;
}
/* Register offset */
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)
{
/* Lock bit */
if (ggenie.regs[0] & 0x100) return;
if (ggenie.regs[0] & 0x100)
{
m68k_unused_8_w(address, data);
return;
}
/* Register offset */
uint8 offset = (address >> 1) & 0x1f;
@ -140,6 +190,9 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type)
break;
}
/* update internal register */
ggenie.regs[offset] = data;
/* Mode Register */
if (!offset)
{
@ -166,47 +219,31 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type)
}
/* 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) */
/* this is done here to handle patched program reads faster & more easily */
/* on real HW, address decoding would be done on each reads */
int i,j;
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];
}
}
}
ggenie_switch(1);
}
}
/* update internal register */
ggenie.regs[offset] = data;
}
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
***************************************************************************/
/* global variables */
extern int gamegenie;
#ifndef _GGENIE_H_
#define _GGENIE_H_
/* Function prototypes */
extern void ggenie_init(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.addr_error = 1;
config.bios_enabled = 0;
config.lock_on = NO_CART;
config.lock_on = 0;
/* video options */
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[2].text, "68k Address Error: %s", config.addr_error ? "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");
if (svp)
@ -920,24 +921,22 @@ static void systemmenu ()
case 3: /*** BIOS support ***/
config.bios_enabled ^= 1;
sprintf (items[3].text, "System BIOS: %s", (config.bios_enabled & 1) ? "ON":"OFF");
if (genromsize || (config.bios_enabled == 3))
if (genromsize)
{
system_init ();
audio_init(48000);
system_reset ();
}
break;
case 4: /*** Cart Lock-On ***/
config.lock_on++;
if (config.lock_on > CART_GG) config.lock_on = NO_CART;
if (config.lock_on == CART_GG) sprintf (items[4].text, "Lock-On: GAME GENIE");
else sprintf (items[4].text, "Lock-On: OFF");
if (genromsize || (config.bios_enabled == 3))
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");
if (genromsize)
{
system_reset (); /* clear any GG patches */
system_reset (); /* clear any patches first */
system_init ();
audio_init(48000);
system_reset ();
}
break;

View File

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

View File

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