From 61e4873c070f9d82132f5f7d233ff5d7f1bb931e Mon Sep 17 00:00:00 2001 From: ekeeke31 Date: Mon, 17 May 2010 11:01:52 +0000 Subject: [PATCH] fixed Action Replay & Game Genie emulation --- source/cart_hw/cart_hw.c | 27 +++- source/cart_hw/datel.c | 278 ++++++++++++++++++++++----------------- source/cart_hw/datel.h | 1 + source/cart_hw/ggenie.c | 98 ++++++++------ source/cart_hw/ggenie.h | 3 +- 5 files changed, 237 insertions(+), 170 deletions(-) diff --git a/source/cart_hw/cart_hw.c b/source/cart_hw/cart_hw.c index ae22c96..9d7babd 100644 --- a/source/cart_hw/cart_hw.c +++ b/source/cart_hw/cart_hw.c @@ -175,7 +175,8 @@ void cart_hw_init() /* calculate nearest size with factor of 2 */ int size = 0x10000; - while (cart.romsize > size) size <<= 1; + while (cart.romsize > size) + size <<= 1; /* total ROM size is not a factor of 2 */ /* TODO: handle more possible ROM configurations (using cartridge database ???) */ @@ -408,6 +409,12 @@ void cart_hw_init() /********************************************** LOCK-ON ***********************************************/ + + /* clear all existing patches */ + ggenie_shutdown(); + datel_shutdown(); + + /* initialize extra hardware */ cart.lock_on = 0; switch (config.lock_on) { @@ -426,15 +433,25 @@ void cart_hw_init() /* load Sonic & Knuckles ROM (2 MBytes) */ FILE *f = fopen(SK_ROM,"r+b"); if (!f) break; - fread(cart.rom+0x700000,1,0x200000,f); + int done = 0; + while (done < 0x200000) + { + fread(cart.rom+0x700000+done,4096,1,f); + done += 4096; + } fclose(f); /* load Sonic 2 UPMEM ROM (256 KBytes) */ f = fopen(SK_UPMEM,"r+b"); if (!f) break; - fread(cart.rom+0x900000,1,0x40000,f); + done = 0; + while (done < 0x40000) + { + fread(cart.rom+0x900000+done,4096,1,f); + done += 4096; + } fclose(f); - + #ifdef LSB_FIRST /* Byteswap ROM */ int i; @@ -575,7 +592,7 @@ void cart_hw_reset() case TYPE_SK: if (cart.lock_on) { - /* reset memory map */ + /* disable UPMEM chip at $300000-$3fffff */ for (i=0x30; i<0x40; i++) m68k_memory_map[i].base = cart.rom + ((i<<16) & cart.mask); } diff --git a/source/cart_hw/datel.c b/source/cart_hw/datel.c index 79d29fe..def9cc9 100644 --- a/source/cart_hw/datel.c +++ b/source/cart_hw/datel.c @@ -24,7 +24,7 @@ #define TYPE_PRO1 0x12 #define TYPE_PRO2 0x22 -static struct +struct { uint8 enabled; uint8 rom[0x20000]; @@ -44,37 +44,43 @@ void datel_init(void) { memset(&action_replay,0,sizeof(action_replay)); - /* load Action Replay ROM program */ + /* Open Action Replay ROM */ FILE *f = fopen(AR_ROM,"rb"); - if (!f) return; - int size = fread(action_replay.rom,1,0x20000,f); - fclose(f); + if (!f) + return; - /* 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; + /* ROM size */ + fseek(f, 0, SEEK_END); + int size = ftell(f); - /* default memory map */ - switch (action_replay.enabled) + /* Detect Action Replay type */ + switch (size) { - case TYPE_AR: + case 0x8000: /* ACTION REPLAY (32K) */ { - /* internal registers mapped at $010000-$01ffff */ - m68k_memory_map[0x01].write16 = ar_write_regs; + action_replay.enabled = TYPE_AR; /* $0000-$7fff mirrored into $8000-$ffff */ memcpy(action_replay.rom+0x8000,action_replay.rom,0x8000); break; } - case TYPE_PRO1: + case 0x10000: /* PRO ACTION REPLAY 2 (64K) */ { - /* internal registers mapped at $010000-$01ffff */ - m68k_memory_map[0x01].write16 = ar_write_regs; + action_replay.enabled = TYPE_PRO2; + + /* RAM (64k) mapped at $600000-$60ffff */ + 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; + } + + case 0x20000: /* PRO ACTION REPLAY (128K) */ + { + action_replay.enabled = TYPE_PRO1; /* RAM (64k) mapped at $420000-$42ffff */ m68k_memory_map[0x42].base = action_replay.ram; @@ -85,114 +91,132 @@ void datel_init(void) break; } - case TYPE_PRO2: - { - /* internal registers mapped at $100000-$10ffff */ - m68k_memory_map[0x10].write16 = ar_write_regs_pro2; - - /* RAM (64k) mapped at $600000-$60ffff */ - 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; - } + default: + return; } + if (action_replay.enabled) + { + /* Load ROM */ + fseek(f, 0, SEEK_SET); + int i = 0; + while (i < size) + { + fread(action_replay.rom+i,0x1000,1,f); + i += 0x1000; + } + #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; - } + /* Byteswap ROM */ + uint8 temp; + for(i = 0; i < size; i += 2) + { + temp = action_replay.rom[i]; + action_replay.rom[i] = action_replay.rom[i+1]; + action_replay.rom[i+1] = temp; + } #endif + } + + fclose(f); +} + +void datel_shutdown(void) +{ + if (action_replay.enabled) + { + datel_switch(0); + action_replay.enabled = 0; + } } void datel_reset(int hard_reset) { - if (action_replay.enabled) + /* reset external mapping */ + switch (action_replay.enabled) { - if (hard_reset) - { - /* clear RAM */ - memset(action_replay.ram,0,sizeof(action_replay.ram)); - } + case TYPE_AR: - /* reset codes */ - datel_switch(0); + /* internal registers mapped at $010000-$01ffff */ + m68k_memory_map[0x01].write16 = ar_write_regs; - /* 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)); + /* internal ROM mapped at $000000-$00ffff */ + m68k_memory_map[0].base = action_replay.rom; + break; - /* ROM mapped at $000000-$3fffff */ - switch (action_replay.enabled) - { - case TYPE_AR: /* 32k ROM */ - case TYPE_PRO2: /* 64k ROM */ - { - m68k_memory_map[0].base = action_replay.rom; - break; - } + case TYPE_PRO2: - case TYPE_PRO1: /* 128k ROM */ - { - m68k_memory_map[0].base = action_replay.rom; - m68k_memory_map[1].base = action_replay.rom + 0x10000; - break; - } - } + /* internal registers mapped at $100000-$10ffff */ + m68k_memory_map[0x10].write16 = ar_write_regs_pro2; + + /* internal ROM mapped at $000000-$00ffff */ + m68k_memory_map[0].base = action_replay.rom; + break; + + case TYPE_PRO1: + + /* internal registers mapped at $010000-$01ffff */ + m68k_memory_map[0x01].write16 = ar_write_regs; + + /* internal ROM mapped at $000000-$01ffff */ + m68k_memory_map[0].base = action_replay.rom; + m68k_memory_map[1].base = action_replay.rom + 0x10000; + break; + + default: + return; } + + /* clear existing 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)); + + /* clear RAM on hard reset only */ + if (hard_reset) + memset(action_replay.ram,0,sizeof(action_replay.ram)); } void datel_switch(int enable) { - int i; + int i,use_wram = 0; 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)); + /* store old values & patch new values */ + if (action_replay.addr[i] < 0x400000) + { + action_replay.old[i] = *(uint16 *)(cart.rom + (action_replay.addr[i]&~1)); + *(uint16 *)(cart.rom + (action_replay.addr[i]&~1)) = action_replay.data[i]; + } + else + { + use_wram = 1; + action_replay.old[i] = *(uint16 *)(work_ram + (action_replay.addr[i]&0xfffe)); + if (action_replay.data[i] & 0xff00) + *(uint16 *)(work_ram + (action_replay.addr[i]&0xfffe)) = action_replay.data[i]; + else + WRITE_BYTE(work_ram, (action_replay.addr[i]&0xfffe) + 1, action_replay.data[i] & 0xff); + } } } - /* patch new values */ - for (i=0; i<4; i++) + if (use_wram) { - if (action_replay.data[i]) + /* use specific WRAM write handlers */ + for (i=0xe0; i<0x100; 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]; + m68k_memory_map[i].write8 = wram_write_byte; + m68k_memory_map[i].write16 = wram_write_word; } } - - /* 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 { @@ -203,12 +227,12 @@ void datel_switch(int enable) { 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]; + else + *(uint16 *)(work_ram + (action_replay.addr[i]&0xfffe)) = action_replay.old[i]; } } - /* set default Work RAM write handlers */ + /* default WRAM write handlers */ for (i=0xe0; i<0x100; i++) { m68k_memory_map[i].write8 = NULL; @@ -223,13 +247,16 @@ 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 (action_replay.data[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; + if ((address & 0xfffe) == (action_replay.addr[i] & 0xfffe)) + { + WRITE_BYTE(&action_replay.old[i], address & 1, data); + if (action_replay.data[i] & 0xff00) + return; + if ((address & 0xffff) == ((action_replay.addr[i]&0xfffe) + 1)) + return; + } } } @@ -238,35 +265,40 @@ static void wram_write_byte(uint32 address, uint32 data) static void wram_write_word(uint32 address, uint32 data) { + *(uint16 *)(work_ram + (address & 0xffff)) = data; + int i; for (i=0; i<4; i++) { - if ((address & 0xe0fffe) == (action_replay.addr[i]&0xe0fffe)) + if (action_replay.data[i]) { - action_replay.old[i] = data; - return; + if ((address & 0xffff) == (action_replay.addr[i] & 0xfffe)) + { + action_replay.old[i] = data; + if (action_replay.data[i] & 0xff00) + *(uint16 *)(work_ram + (address & 0xfffe)) = action_replay.data[i]; + else + WRITE_BYTE(work_ram, (action_replay.addr[i] & 0xfffe) + 1, action_replay.data[i]); + } } } - - *(uint16 *)(work_ram + (address & 0xffff)) = data; } static void ar_write_regs(uint32 address, uint32 data) { - if ((address > 0x10018) || (action_replay.regs[3] == 0xffff)) + /* register offset */ + int offset = (address & 0xffff) >> 1; + if (offset > 12) { 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)) + /* exit program */ + if (action_replay.regs[3] == 0xffff) { /* decode patch data */ action_replay.data[0] = action_replay.regs[0]; @@ -280,17 +312,21 @@ static void ar_write_regs(uint32 address, uint32 data) 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; - /* Enable Cartridge ROM */ - /* NOTE: codes should be disabled on startup */ + /* enable Cartridge ROM */ m68k_memory_map[0].base = cart.rom; m68k_memory_map[1].base = cart.rom + ((1<<16) & cart.mask); + + /* disable internal registers (?) */ + m68k_memory_map[0x01].write16 = m68k_unused_16_w; + + /* enable patches */ + datel_switch(1); } } - static void ar_write_regs_pro2(uint32 address, uint32 data) { - /* Enable Cartridge ROM */ + /* enable Cartridge ROM */ if (((address & 0xff) == 0x78) && (data == 0xffff)) m68k_memory_map[0].base = cart.rom; } diff --git a/source/cart_hw/datel.h b/source/cart_hw/datel.h index 7edd3a6..ee8503f 100644 --- a/source/cart_hw/datel.h +++ b/source/cart_hw/datel.h @@ -23,6 +23,7 @@ #define _DATEL_H_ extern void datel_init(void); +extern void datel_shutdown(void); extern void datel_reset(int hard_reset); extern void datel_switch(int enable); diff --git a/source/cart_hw/ggenie.c b/source/cart_hw/ggenie.c index 2d62a10..f8360e0 100644 --- a/source/cart_hw/ggenie.c +++ b/source/cart_hw/ggenie.c @@ -43,20 +43,24 @@ void ggenie_init(void) { memset(&ggenie,0,sizeof(ggenie)); - /* load Game Genie ROM program */ + /* Open Game Genie ROM */ FILE *f = fopen(GG_ROM,"rb"); if (!f) return; - fread(ggenie.rom,1,0x8000,f); - fclose(f); - /* $0000-$7fff mirrored into $8000-$ffff */ - memcpy(ggenie.rom+0x8000,ggenie.rom,0x8000); + /* Load ROM */ + int i = 0; + while (i < 0x8000) + { + fread(ggenie.rom+i,0x1000,1,f); + i += 0x1000; + } + fclose(f); #ifdef LSB_FIRST /* Byteswap ROM */ int i; uint8 temp; - for(i = 0; i < 0x10000; i += 2) + for(i = 0; i < 0x8000; i += 2) { temp = ggenie.rom[i]; ggenie.rom[i] = ggenie.rom[i+1]; @@ -64,36 +68,48 @@ void ggenie_init(void) } #endif - /* enable registers write */ - m68k_memory_map[0].write8 = ggenie_write_byte; - m68k_memory_map[0].write16 = ggenie_write_word; + /* $0000-$7fff mirrored into $8000-$ffff */ + memcpy(ggenie.rom+0x8000,ggenie.rom,0x8000); /* set flag */ ggenie.enabled = 1; } -void ggenie_reset(void) +void ggenie_shutdown(void) { if (ggenie.enabled) { - /* 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; + ggenie.enabled = 0; } } -void ggenie_switch(uint8 enable) +void ggenie_reset(void) +{ + if (!ggenie.enabled) + return; + + /* clear 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)); + + /* Game Genie ROM is mapped at $000000-$007fff */ + m68k_memory_map[0].base = ggenie.rom; + + /* Internal registers are mapped at $000000-$00001f */ + m68k_memory_map[0].write8 = ggenie_write_byte; + m68k_memory_map[0].write16 = ggenie_write_word; + + /* Disable registers reads */ + m68k_memory_map[0].read16 = NULL; +} + +void ggenie_switch(int enable) { int i,j; if (enable) @@ -137,18 +153,10 @@ void ggenie_switch(uint8 enable) } } - /* Byte write handler */ /* Note: 2nd revision of the Game Genie software use byte writes to set register values on exit */ static void ggenie_write_byte(uint32 address, uint32 data) { - /* Lock bit */ - if (ggenie.regs[0] & 0x100) - { - m68k_unused_8_w(address, data); - return; - } - /* Register offset */ uint8 offset = (address >> 1) & 0x1f; @@ -159,13 +167,6 @@ static void ggenie_write_byte(uint32 address, uint32 data) /* Word write handler */ static void ggenie_write_word(uint32 address, uint32 data) { - /* Lock bit */ - if (ggenie.regs[0] & 0x100) - { - m68k_unused_8_w(address, data); - return; - } - /* Register offset */ uint8 offset = (address >> 1) & 0x1f; @@ -194,7 +195,7 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type) ggenie.regs[offset] = data; /* Mode Register */ - if (!offset) + if (offset == 0) { /* by default, registers are write only */ m68k_memory_map[0].read16 = NULL; @@ -204,11 +205,13 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type) { /* $0000-$7ffff reads mapped to Cartridge ROM */ m68k_memory_map[0].base = cart.rom; + m68k_memory_map[0].read16 = NULL; } else { /* $0000-$7ffff reads mapped to Game Genie ROM */ m68k_memory_map[0].base = ggenie.rom; + m68k_memory_map[0].read16 = NULL; if (data & 0x200) { @@ -222,7 +225,7 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type) if (data & 0x100) { /* decode patch address (ROM area only)*/ - /* note: Charles's documment is wrong, first register holds bits 23-16 of patch address */ + /* note: Charles's doc 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]; @@ -238,17 +241,26 @@ static void ggenie_write_regs(uint8 offset, uint32 data, uint8 type) ggenie.data[4] = ggenie.regs[16]; ggenie.data[5] = ggenie.regs[19]; + /* disable internal registers */ + m68k_memory_map[0].write8 = m68k_unused_8_w; + m68k_memory_map[0].write16 = m68k_unused_16_w; + /* 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 */ ggenie_switch(1); } } + + /* RESET register */ + else if (offset == 1) + { + ggenie.regs[1] |= 1; + } } static uint32 ggenie_read_regs(uint32 address) { - if (address < 0x40) return ggenie.regs[address >> 1]; - else return *(uint16 *)(cart.rom + address); /* is that correct ? */ + return ggenie.regs[(address >> 1) & 0x1f]; } diff --git a/source/cart_hw/ggenie.h b/source/cart_hw/ggenie.h index a44dd8a..8c30d93 100644 --- a/source/cart_hw/ggenie.h +++ b/source/cart_hw/ggenie.h @@ -27,7 +27,8 @@ /* Function prototypes */ extern void ggenie_init(void); +extern void ggenie_shutdown(void); extern void ggenie_reset(void); -extern void ggenie_switch(uint8 enable); +extern void ggenie_switch(int enable); #endif