Basically, I put this here in case it would interest anybody but also because I have trouble emulating it correctly since it seems to me the software is handling byte patches incorrectly (more details at the end of this huge post )
There are actually 3 versions of the device:
- Action Replay (not PRO): this is basically a Game Genie, no Trainer like the PRO versions and no ability to handle RAM ($FFxxxx) patches (this need to be confirmed but I couldn't find any infos about this version except that PAR codes were not compatible and software do not seem to handle these patches differently that ROM patches). It can handle up to 4 address patches, through dedicated hardware registers (described further).
- Pro Action Replay: This comes with extra RAM (64K) mapped at $420000-$42FFFF and additional features implemented in software like the ability to find your own codes (Trainer) and to use RAM patches. Like the previous version, it can handle up to 4 address patches, through hardware registers, with some restrictions when mixing ROM and RAM patches (explained later). There is also a 3-position switch that can be used as follow:
--> UP : patches are activated (internal hardware decodes access to cartridge ROM area and return patched value when patch address matches)
--> MIDDLE : patches are disactivated
--> DOWN: trainer mode is activated (basically, it makes internal registers being reseted when !VRESET is asserted, so, when user press RESET button, Action Replay boot software would restart instead of the game, and would detect RAM has already been initialized)
Two ROM dumps actually exist, one being 128K and the other being only 64K (this one is mis-labeled as being Pro Action replay II alt version in goodgen/no-intro list but there is an hidden debug menu if you press C on startup and I've figured it was the same software revision as the Pro Action replay cartridge I own, same date, same revision). The 128K version has an additional debug menu that appears when you hold B + START when exiting the Main Menu, basically it's a memory dump screen.
By analyzing available ROM dumps, it appears that only the first 32K holds valid data, the rest is filled with 0xFF.
The Pro Action Replay cartridge version I have (1/92 SEGA II labeled board) contains:
- 2 x 27C256-15L (32K x 8bit) EPROM
- 2 x HY62256ALJ-70 (32K x 8bit) SRAM, both connected to /UWR (/LWR is not used by the board)
- 1 x LZ95F39 CPLD chip (manufactured by Sharp)
- 1 x 74LS32D + 1 x LS138 as address decoding logic
- Pro Action Replay 2: This one also have additional RAM (64K) mapped at $600000-$60FFFF , same features as the previous version but it can now store an "unlimited" number of cheats in internal RAM, comes with a hard-coded list of cheats for various games and has an enhanced menu with "advanced" features like region bypass (interception of IO version register reads ?) , "slow-motion" (VINT handler patch) or "memory clear" (clear work RAM before starting some game requiring it to be cleared on startup ?).
The Pro Action Replay 2 cartridge version I have (29/9/93 DATEL labeled board) contains:
- 2 x 27C256-15L (32K x 8bit) EPROM
- 2 x HY62256ALJ-10 (32K x 8bit) SRAM, , both connected to /UWR (/LWR is not used by the board)
- 1 x LZ95G64 CPLD chip (DATEL SUPER REPLAY)
- no additional address decoding logic
I didn't really investigated how the Pro Action Replay 2 works (it seems to use a different chip and handle patches differently from the previous versions) so the following would only apply to Action Replay and Pro Action Replay 1 cartridges, which both seem to use the same internal hardware.
There are 13 internal word-registers, mapped between $100000-$100018. Those registers seem to be write-only (software never try to read this area).
Here is the layout of internal registers:
$100000: patch #1 data value
$100002: patch #1 address bits 23-17 (000000000xxxxxxx)
$100004: patch #1 address bits 16-1
$????: Action Replay ROM mapped in $000000-$3FFFFF area
$FFFF: Cartridge ROM mapped in $000000-$3FFFFF area
Registers are cleared on power ON or when pressing the RESET button (if the switch is in low position), so that Action Replay program starts automatically.
When exiting the menu, Action Replay software runs from the extra RAM and enable the game cartridge.
$100008: patch #2 data value
$10000A: patch #2 address bits 23-17 (000000000xxxxxxx)
$10000C: patch #2 address bits 16-1
$10000E: patch #3 data value
$100010: patch #3 address bits 23-17 (000000000xxxxxxx)
$100012: patch #3 address bits 16-1
$100014: patch #4 data value
$100016: patch #4 address bits 23-17 (000000000xxxxxxx)
$100018: patch #4 address bits 16-1
As you can see, patch addresses are WORD-only, i.e A0 is not decoded. For example, entering $100001 or $100000 in the address field of the main menu is handle exactly the same way internally, ROM patches are word patches.
In the Pro Action Replay version, the routine that setups those registers is executed from the extra RAM ($420058, copied from ROM $33B8) and is called when exiting the menu (by hitting START button or A button when all codes are filled). Below is the disassembled routine, the normal Action replay behaves the same (except it executes from genesis RAM and does not check if patches are activated):
Code: Select all
000033B8 MOVE #$2700,SR 000033BC MOVE.W #$0000,$00010000 000033C4 MOVE.W #$0020,$00010002 000033CC MOVE.W #$0000,$00010004 000033D4 MOVE.W #$0000,$00010008 000033DC MOVE.W #$0021,$0001000A 000033E4 MOVE.W #$0000,$0001000C 000033EC MOVE.W #$0000,$0001000E 000033F4 MOVE.W #$0022,$00010010 000033FC MOVE.W #$0000,$00010012 00003404 MOVE.W #$0000,$00010014 0000340C MOVE.W #$0023,$00010016 00003414 MOVE.W #$0000,$00010018 0000341C MOVE.W #$FFFF,$00010006 00003424 MOVE.L #$0042015E,D0 0000342A CMP.L $00000078,D0 ; test if ROM header VINT entry point is patched 00003430 BNE $0000347C(pc) ; if not, continue and exit to game 00003434 MOVE.L #$73800003,$00C00004 0000343E MOVEQ #$00,D2 00003440 LEA $0000345E,A0 00003446 MOVE.W $20(A0,D0.W),D1 0000344A MOVE.W D1,$00C00000 00003450 ADDQ.W #$1,D2 00003452 CMP.W #$000F,D2 00003456 BNE $00003446(pc) 0000345A BRA $0000342A(pc) ; loop until patches are disabled
When the switch is in upper position and the game accesses ROM cartridge area, the ASIC will decode the address and if it matches one of the registered entries, it returns the patched value. The first version (not PRO) may or may not have a switch, in which case it basically acts as a Game Genie (i.e a master code is required to patch checksum verifications).
On a side note, the first version does not make differences between ROM & RAM patches: if the entered patch address is not within cartridge ROM space (> 0x800000), it will be treated exactly the same as usual by writing the registers with expected value. However, it's very unlikely the device can decode RAM access and put the patched word on the bus in place of the main Genesis RAM. Even the Pro version does not do that to handle RAM patches, as explained below.
The PRO Action Replay Software does additional processing when registering a code entry. If the high-nibbles of the address are $FF, then the code appears to be a patch to RAM area ($FF0000-$FFFFFF) and is handled differently.
Basically, the PRO Action Replay would be setup to patch the VINT callback entry point in ROM header so that, when patches are activated, its own callback is called first, which does up to 4 writes to the Main RAM area then jump to the default VINT callback. To patch the VINT entrypoint in ROM header, 2 word addresses need to be patched ($78 and $7C) and therefore, anytime a RAM patch is detected, software will reserve the use of the 2 last entries (registers $10000E-$100018), which is why you can mix ROM & RAM patches but ROM patches must occupy the 2 first entry slots.
PRO Action Replay pre-VINT callback is executed from extra RAM ($42015E) and is initialized with the following code, copied from ROM ($34BE)
Code: Select all
000034BE MOVE SR,-(A7) 000034C0 MOVE.B #$00,$00000000 000034C8 MOVE.B #$00,$00000000 ; apply RAM patches if any 000034D0 MOVE.B #$00,$00000000 000034D8 MOVE.B #$00,$00000000 000034E0 TST.W $00420052 000034E6 BEQ $000034FE(pc) 000034EA MOVE.W #$0001,$00420050 000034F2 MOVE.W D0,-(A7) 000034F4 MOVE.W #$61A8,D0 000034F8 DBRA D0,$000034F8 000034FC MOVE.W (A7)+,D0 000034FE MOVE (A7)+,SR 00003500 JMP $00000000 ; execute game VINT
Code: Select all
0000347C MOVE.W #$8134,$00C00004 00003484 MOVE.L $00000078,$004201A2 ; patch game VINT entrypoint into VINT pre-callback 0000348E BTST #$04,$00420010 00003496 BEQ $000034A0(pc) 0000349A JMP $00FF0000 000034A0 MOVE.L #$00000000,$00A10008 000034AA MOVE.L #$00000000,A0 000034B0 MOVE.L $00000000,A7 000034B6 MOVE.L $00000004,A1 000034BC JMP (A1) ; start game
The 4 MOVE instructions are patched during the code decoding (when user press A or START), using the following code:
Code: Select all
00001FEC move.w ($42002A).l,d5 00001FF2 cmp.b #$FF,d1 00001FF6 bne.w loc_2044 00001FFA mulu.w #8,d5 00001FFE movea.l #$420160,a2 ; VINT pre-callback start address 00002004 move.w d0,($42004E).l 0000200A andi.w #$FF00,d0 ; test DATA MSB 0000200E cmp.w #0,d0 00002012 beq.w loc_202A 00002016 move.w ($42004E).l,d0 0000201C move.w #$33FC,(a2,d5.w) ; this is a WORD patch, change MOVE.B into MOVE.W 00002022 move.w d0,2(a2,d5.w) ; force the MOVE.W immediate source value 00002026 bra.w loc_2034 0000202A move.w ($42004E).l,d0 00002030 move.b d0,2(a2,d5.w) ; this is a BYTE patch, force the MOVE.B immediate source value 00002034 00002034 swap d0 00002036 move.w d0,6(a2,d5.w) ; force the MOVE indirect destination address LSB 0000203A move.w #$FF,4(a2,d5.w) ; force the MOVE indirect destination address MSB (always $FF) 00002040 bra.w loc_205C
D5 holds cheat index (0-3)
D0 holds address low bytes and the data values ($AAAADDDD)
D1 holds address high bytes ($000000AA)
Now, the interesting thing is that software differentiates between BYTE & WORD patches: if the MSB of the DATA is zero then the patch is considered to be a BYTE patch, otherwise it's a WORD patch (the MOVE.B instruction inside the VINT pre-callback is changed into MOVE.W).
Strangely, I did not succeed in making BYTE patches work in my emulator as it seems the MOVE.B instruction in the VINT pre-callback is not patched correctly: for examples, if the data value is $0003 and address is $FF0000, then the instruction becomes 13 FC 03 00 00 FF 00 00
which would actually writes $00 as a byte, not $03
It seems to me the correct instruction when patching the VINT pre-callback should be:
Code: Select all
00002030 move.b d0,3(a2,d5.w)
Code: Select all
00002030 move.b d0,2(a2,d5.w)
For the record, I tested same BYTE patch on a real Pro Action Replay and it appears to work fine.
Either I'm doing something wrong when handling MOVE.B instructions or the ROM dump is bad... Any idea ? This is actually the only thing left I need to figure...
It could be that byte writes to PRO Action Replay internal RAM work like word writes, with the LSB duplicated in MSB (same thing happens when accessing VDP ports using byte writes, it's because the 68k duplicates the value on data bus when doing byte writes to memory), while byte reads return expected value... This would actually explain the above behavior.
Indeed, my cartridge have two 32Kx8bit SRAM chips in parallel but /LWR is not connected to anything (except the additional cartridge port), only /UWR seems to be used internally (most likely it's used by both chips as /WE).
EDIT2: Here is my notesabout Gamtec Magicard. It' s indeed a game genie clone but the code layout is different.