Pro Action Replay reverse-engineering + MagiCard notes

For anything related to cart (SRAM, SF2 mapper, audio, CD mode 1, ...)

Moderator: BigEvilCorporation

Post Reply
Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Pro Action Replay reverse-engineering + MagiCard notes

Post by Eke » Fri Oct 22, 2010 4:51 pm

I've been doing some disassembly & analysis of the various available Action Replay ROM dumps, essentially to figure how it could be emulated (for what it's worth) but also for curiosity about how it internally works.

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 )

INTRODUCTION

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.



ROM PATCHES:


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

$100006:
$????: 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 user hits Button A, software will decode the current address/data and patches the register setup routine directly in RAM. By default, the routine is setup to patch addresses $40, $42, $44 and $46 which are hopefully unassigned in the 68k exception header area.

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.


RAM PATCHES:

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
The JUMP address is basically the game VINT entrypoint, it is read & patched here after the register setup procedure during exit, once the game as been remapped.

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
I've analysed it like this:

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)      
instead of

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... :?


EDIT:
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.
Last edited by Eke on Wed Jun 15, 2011 4:18 pm, edited 6 times in total.

Charles MacDonald
Very interested
Posts: 292
Joined: Sat Apr 21, 2007 1:14 am

Re: Pro Action Replay reverse-engineering

Post by Charles MacDonald » Mon Oct 25, 2010 6:29 pm

Eke wrote:I've been doing some disassembly & analysis of the various available Action Replay ROM dumps, essentially to figure how it could be emulated (for what it's worth) but also for curiosity about how it internally works.
Wow! This is great stuff! Thanks for doing all the hard work and sharing your findings. Now that we have the Action Replay variants and Game Genie documented, I think the only thing left is the Magic Card but it is just a Genie clone. :D

Didn't Datel or somebody make a multi-region cartridge for the Sega CD to boot any game? It had no cartridge connector on the top. Do we know how that works?
"advanced" features like region bypass (basically, it's a ROM header patch) or slow-motion (VINT handler patch).
Quite some time ago I remember testing the region bypass on a PAR2 and being surprised that it modified the data read from $A10001 but not $A10000, so in theory you could get the correct region from reading that address. It didn't change the header byte.

But I don't understand how the chip could intercept reads from that area safely. It's possible I misinterpreted the results. What do you think?

My PAR2 has a bunch of wire links from the cartridge edge to the connector on top, does yours?

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Re: Pro Action Replay reverse-engineering

Post by Eke » Tue Oct 26, 2010 6:50 am


Quite some time ago I remember testing the region bypass on a PAR2 and being surprised that it modified the data read from $A10001 but not $A10000, so in theory you could get the correct region from reading that address. It didn't change the header byte.

But I don't understand how the chip could intercept reads from that area safely. It's possible I misinterpreted the results. What do you think?
Well, I thought about that also. Read interception (i.e address decoding) is possible but I have no idea how an external device could drive the data bus at the same time as the console, maybe by playing with DTACK/ but this signal is not used by the PAR2.

However, you seems to be right: in my PAR2 this feature is labeled as "CARTRIDGE TYPE" and, in the user manual, they say you must set it to the region of the cartridge you want to play, NOT the region of your console. So they are definitively patching reads from the I/O version register. I have updated my initial post.

My PAR2 has a bunch of wire links from the cartridge edge to the connector on top, does yours?
Yes, it does. They are used to connect the following signals to the cartridge connector:

B14 (/HSYNC)
B19 (VCLK)
B20 (/DTACK)
B26 (/ASEL)
B31 (/TIME)

It seems they added those connections "on last minute" to keep compatibility with cartridges using extra hardware, like Virtua Racing for example.

On the PAR1 however, the board does not use any wire links and those signals are not connected. I cannot try it but I think this game won't work with a PAR1 because of the lack of the VCLK and /TIME signals, while it would work on top of the PAR2.

On the PAR2, there is also a wire on the back connecting A2 pins (Vcc), it seems more like a design choice because of the complexity of the board layout.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Tue Oct 26, 2010 9:45 am

The data lines are all intercepted, and the PAR reads all address lines... so, when the ID register is read, nothing is done on the MD side, but on cart side... at least that is how all the region change carts work. Other solutions are to cause a bus conflict and win it, or mess with DTACK
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Charles MacDonald
Very interested
Posts: 292
Joined: Sat Apr 21, 2007 1:14 am

Post by Charles MacDonald » Wed Oct 27, 2010 3:29 am

TmEE co.(TM) wrote:The data lines are all intercepted, and the PAR reads all address lines... so, when the ID register is read, nothing is done on the MD side, but on cart side... at least that is how all the region change carts work. Other solutions are to cause a bus conflict and win it, or mess with DTACK
I don't think I follow, consider this sequence:

move.b $A10001, d0

The 68000 asserts address $A10001 which the PAR2 can see, but the I/O chip will respond too, drive D0-D7, and the bus controller chip asserts /DTACK to terminate the cycle. In this case I think the only way the PAR2 could change the values is to drive D7,D6 harder than the I/O chip can like you suggested, but such conflicts are problematic to rely on.

Maybe the PAR2 can snoop the bus activity quickly enough to drive the data bus and assert DTACK before the Genesis chipset can, which would end the memory cycle before the chips could respond. Assuming they designed the chips to give up an in-progress memory access if DTACK is asserted by something else.

It would be funny if it could somehow patch a sequence like $00A1 $0001 read from ROM to point to a different address with the right value, but that would break lots of things and be circumventable if you ran the code out of RAM.

Has anyone looked in the region change carts to see what kind of hardware they have? I'm sure it's glop-top, but maybe some variant has off-the-shelf parts and would shed some light onto how it is done.

BTW: Virtua Racing works on my PAR2 but runs at half the speed. I guess the interrupt handling/redirection is problematic?

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Wed Oct 27, 2010 7:39 am

I was thinking something different, and wrong too xD

I guess its doing a bus fight, and winning it... at least this is waht my "Game module & Rrotector" does :P
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Post Reply