/**************************************************************************** * 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 ***************************************************************************/ #include "shared.h" #define TYPE_AR 0x01 #define TYPE_PRO1 0x02 #define TYPE_PRO2 0x03 static struct { uint8 enabled; uint8 rom[0x20000]; uint8 ram[0x10000]; uint16 regs[13]; uint16 old[4]; uint16 data[4]; uint32 addr[4]; } action_replay; static void ar_write_regs(uint32 address, uint32 data); static void wram_write_byte(uint32 address, uint32 data); static void wram_write_word(uint32 address, uint32 data); void datel_init(void) { memset(&action_replay,0,sizeof(action_replay)); /* load Action Replay ROM program */ FILE *f = fopen(AR_ROM,"rb"); if (!f) return; int size = fread(action_replay.rom,1,0x20000,f); fclose(f); /* detect Action Replay yype */ if (size < 0x10000) action_replay.enabled = TYPE_AR; else if (size < 0x20000) action_replay.enabled = TYPE_PRO2; else action_replay.enabled = TYPE_PRO1; /* default memory map */ switch (action_replay.enabled) { case TYPE_AR: /* $0000-$7fff mirrored into $8000-$ffff */ memcpy(action_replay.rom+0x8000,action_replay.rom,0x8000); m68k_memory_map[1].write16 = ar_write_regs; break; case TYPE_PRO1: m68k_memory_map[1].write16 = ar_write_regs; break; case TYPE_PRO2: /* todo */ break; } #ifdef LSB_FIRST /* Byteswap ROM */ int i; uint8 temp; for(i = 0; i < 0x20000; i += 2) { temp = action_replay.rom[i]; action_replay.rom[i] = action_replay.rom[i+1]; action_replay.rom[i+1] = temp; } #endif } void datel_reset(void) { if (action_replay.enabled) { /* reset codes */ datel_switch(0); /* 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)); /* reset memory map */ switch (action_replay.enabled) { case TYPE_AR: m68k_memory_map[0].base = action_replay.rom; break; case TYPE_PRO1: m68k_memory_map[0].base = action_replay.rom; m68k_memory_map[1].base = action_replay.rom + 0x10000; m68k_memory_map[0x42].base = action_replay.ram; m68k_memory_map[0x42].read8 = NULL; m68k_memory_map[0x42].read16 = NULL; m68k_memory_map[0x42].write8 = NULL; m68k_memory_map[0x42].write16 = NULL; break; case TYPE_PRO2: m68k_memory_map[0].base = action_replay.rom; m68k_memory_map[0x60].base = action_replay.ram; m68k_memory_map[0x60].read8 = NULL; m68k_memory_map[0x60].read16 = NULL; m68k_memory_map[0x60].write8 = NULL; m68k_memory_map[0x60].write16 = NULL; break; } } } 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 ((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; } } WRITE_BYTE(work_ram, address & 0xffff, data); } static void wram_write_word(uint32 address, uint32 data) { int i; for (i=0; i<4; i++) { if ((address & 0xe0fffe) == (action_replay.addr[i]&0xe0fffe)) { action_replay.old[i] = data; return; } } *(uint16 *)(work_ram + (address & 0xffff)) = data; } static void ar_write_regs(uint32 address, uint32 data) { if ((address > 0x10018) || (action_replay.regs[3] == 0xffff)) { m68k_unused_16_w(address,data); return; } /* register offset */ int offset = (address >> 1) & 0x0F; /* update internal register */ action_replay.regs[offset] = data; /* decode patch value & address on exit */ if ((offset == 3) && (data == 0xffff)) { /* 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]; /* 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; /* reads are mapped to Cartridge ROM */ /* NOTE: codes should be disabled on startup */ m68k_memory_map[0].base = cart.rom; } }