FD1094 Game Conversion
BEFORE You Start!
When converting an existing Sega game to a new game using decrypted ROMs, please check to see if the FD1094 or ROMs have been dumped. If you can dump your own ROMs, verify if that particular set is supported in MAME or not.
If you have an undumped game, by all means get in contact with myself, Guru, or the MAME™ team. It would be a shame to have any undumped Sega games converted to another type and be lost forever in the process.
The FD1094 is a custom 68000 made by Hitachi for Sega. Many games use it, notably those on the System 16B, System 18, and System 24 hardware amongst others. It decrypts program code based on information stored in internal battery-backed SRAM. When the battery dies, the table contents are lost and the FD1094 will no longer decrypt correctly.
Using tables of data extracted from the FD1094 CPUs used in various games, it has been possible to create what is thought to be a close (or identical) representation of the SRAM contents. This is used in MAME to decrypt FD1094 games allowing them to be emulated. Even better for arcade game collectors and operators, boards with dead FD1094's can be restored to full working order using decrypted ROM images.
Due to the way the FD1094 works, creating a decrypted ROM image for a particular game is not easy. Program code and data are stored in a interleaved fashion that is game-dependant, complicating the process of decrypting just the encrypted code and leaving the data unmodified.
The program code and data can be combined, through either manual inspection (tracing of the game program), collecting reference data through an emulator to make a 'map' of which addresses are executed from and which are read from, or using an intelligent disassembly tool to automatically recurse through code paths to determine what is code and what is data.
One technique used in bootlegs of FD1089 games is to store the decrypted program code and data in two parts of a double-sized ROM. The high-order address line of the ROM is modified depending on when the 68000 accesses program code or data. The two do not need to be combined and it takes the guesswork out of determining which addresses are code or data.
This same technique can be applied to the FD1094, with one additional complication. Sections of the program code are encrypted differently through a state selection mechanism. It would not be (easily) possible to detect when states are selected during execution, nor have enough ROM storage for multiple copies of the decrypted program code in each of the states that are used. The solution is to manually decrypt and merge the sections of program code back together.
There are two situations that don't have to be bothered with; FD1094 games don't expect to use data at one address in two different states (this would expose information about how the encryption works) nor do they have a random dispersal of state switches - most all games go through several states during initialization and stick with one for the the main game loop.
When the 68000 access memory, it outputs the address space type on the function code pins, FC2-FC0. These pins are only valid when /AS is low. Here is a list of all possible settings from the 68000 user's manual:
The only type of address space we want to differentiate from the others is a program space access. Both supervisor and user accesses will be treated the same, so we can ignore FC2. Beacuse the original data and decrypted code will be split into two parts, we will select the decrypted code half for a program space access and select the data half for everything else. This means the EPROM high-order address input will be set when FC1 is high, FC0 is low, and /AS is low.
We can detect this condition using a simple circuit consisting of a 74HC02 quad NOR gate:
The circuit is small enough that it could be assembled on a small piece of veroboard, or have direct connections between the IC pins themselves, with the IC and related wiring covered in heat-shrink tubing to prevent shorts. Here's another diagram showing the connections with more detail:
I would recommend adding a 0.1uF ceramic disc decoupling capacitor between +5V and GND on the 74HC02. It may be necessary to buffer /AS, FC1, and FC0 (using a 74LS244 for example) to avoid fanout limitations depending on how they are used elsewhere on the PCB.
Creating a decrypted ROM image
For this example, I will go through the steps used to decrypt the Sega System 18 game "Alien Storm". It stores it's program code in two 27C020 (256Kx8) EPROMs. Two 27C040's will be used to hold the original data and decrypted program code.
With the bytes interleaved from both ROMs, the vector table can be examined:
Offset: +00 +04 +08 +0C 000000 B297F23F 55334533 00000408 00000408 000010 00000408 00000408 0000040C 0000040C 000020 00000408 0000040C 0000040C 0000040C 000030 0000040C 0000040C 0000040C 0000040C 000040 0000040C 0000040C 0000040C 0000040C 000050 0000040C 0000040C 0000040C 0000040C 000060 0000040C 0000040C 0000040C 0000040C 000070 00000404 0000040C 0000040C 0000040C : 0003E0 0000040C 0000040C 0000040C 0000040C 0003F0 FA60FA60 FA60FA60 FFFFFFFF 0000040C
The vector table is $400 bytes in size. Some games store junk data or checksum information at offsets $3F0 onwards, which is the case for Alien Storm.
The first two vectors for SP and PC are encrypted, because the 68000 counts them as being located in the program space, even though they are not opcodes. The remainder of the vector table is not encrypted. We can get the real SP and PC values by decrypting the ROM in state $0000, the reset state.
In addition, the vector table only references three unique ISRs, at $404, $408, and $40C. Now we know the following:
SP: $FFFFFF00 (decrypted stack top, offset $3F00 in work RAM) PC: $00000400 (decrypted initial PC, program starts at $400) ISR #1: $00000408 (handler for critical errors) ISR #2: $0000040C (handlers for everything else) Level 4 ISR: $00000404 (handler for V-Blank interrupt)
Let's examine the disassembled code from the ROM, when decrypted using state $0000:
BRA *+$000E [0000040E] ; 000400 60 00 00 0C ST .B D4 ; 000404 50 C4 MOVE.L -(A1),D3 ; 000406 26 21 EOR.B D3,D2 ; 000408 B7 02 SUBX.B D0,D3 ; 00040A 97 00 RTE ; 00040C 4E 73 LEA $FF00,A7 ; 00040E 4F F8 FF 00 MOVE #$2700,SR ; 000412 46 FC 27 00 CMPI.L #$00DFFFFF,D0 ; 000416 0C 80 00 DF FF FFAt offset $400 (initial PC) we have a branch to $40E where the rest of the startup code resumes. Notice that the two locations of the generic ISR's, $408 and $40C seem like unusual instructions. That's because they are actually encrypted in a different state, the interrupt state ($0200) so in state $0000 they are not decrypted correctly. Then we have a state change to $00DF. We decrypt the ROM using this state.
MOVEQ #$00,D0 ; 00041C 70 00 MOVE.L D0,D1 ; 00041E 22 00 MOVE.L D0,D2 ; 000420 24 00 MOVE.L D0,D3 ; 000422 26 00 MOVE.L D0,D4 ; 000424 28 00 MOVE.L D0,D5 ; 000426 2A 00 MOVE.L D0,D6 ; 000428 2C 00 MOVE.L D0,D7 ; 00042A 2E 00 MOVEA.L D0,A0 ; 00042C 20 40 MOVEA.L D0,A1 ; 00042E 22 40 MOVEA.L D0,A2 ; 000430 24 40 MOVEA.L D0,A3 ; 000432 26 40 MOVEA.L D0,A4 ; 000434 28 40 MOVEA.L D0,A5 ; 000436 2A 40 MOVEA.L D0,A6 ; 000438 2C 40 CMPI.L #$0019FFFF,D0 ; 00043A 0C 80 00 19 FF FF
Now state $0019 is selected. We decrypt the ROM again, using this state.
LEA $000027D6,A0 ; 000440 41 F9 00 00 27 D6 MOVEA.L #$00FE0020,A1 ; 000446 22 7C 00 FE 00 20 MOVE.W #$000F,D1 ; 00044C 32 3C 00 0F MOVE.B (A0)+,D0 ; 000450 10 18 MOVE.W D0,(A1)+ ; 000452 32 C0 DBF D1,*-$0004  ; 000454 51 C9 FF FA CMPI.L #$0046FFFF,D0 ; 000458 0C 80 00 46 FF FFThe memory mapping registers are set up. As before, switch to state $0046.
MOVE.B #$00,$00A00007 ; 00045E 13 FC 00 00 00 A0 00 07 MOVE.B #$88,$00A0001F ; 000466 13 FC 00 88 00 A0 00 1F MOVE.B #$00,$00A0001D ; 00046E 13 FC 00 00 00 A0 00 1D MOVE.B #$C0,$00A00007 ; 000476 13 FC 00 C0 00 A0 00 07 CMPI.L #$0058FFFF,D0 ; 00047E 0C 80 00 58 FF FFHere we have I/O chip setup, then a switch to state $0058.
LEA $C000,A0 ; 000484 41 F8 C0 00 MOVE.W #$0EFF,D0 ; 000488 30 3C 0E FF MOVE.L $EC00,D1 ; 00048C 22 38 EC 00 MOVE.L $EC24,D4 ; 000490 28 38 EC 24 CLR.L (A0)+ ; 000494 42 98 DBF D0,*-$0002  ; 000496 51 C8 FF FC MOVE.L D1,$EC00 ; 00049A 21 C1 EC 00 NOP ; 00049E 4E 71 NOP ; 0004A0 4E 71 NOP ; 0004A2 4E 71 LEA $FF00,A7 ; 0004A4 4F F8 FF 00 MOVE #$2700,SR ; 0004A8 46 FC 27 00 JSR $00030EEE ; 0004AC 4E B9 00 03 0E EEThe RAM is cleared, the stack top and operating mode is set again, and the rest of the game code continues. There are no more state-switching instructions from here onwards, so the remainder of the ROM contains code that will decrypt correctly using state $0058, except for the interrupt handlers.
Let's look at our interrupt handlers now. The three addresses of interest were $404, $408, $40C. Let's look at these when they are decrypted in state $0200:
BRA *+$2946 [00002D4A] ; 000404 60 00 29 44 BRA *+$0004 [0000040C] ; 000408 60 00 00 02 RTE ; 00040C 4E 73The ISR at $40C simply terminates in a RTE, and the ISR for $408 does the same. This leaves the level 4 interrupt (V-Blank) ISR which starts at address $2D4A. Here's data from that address, still using state $0200.
NOP ; 002D46 4E 71 NOP ; 002D48 4E 71 TST.B $ECB1 ; 002D4A 4A 38 EC B1 BEQ.S *+$10 [00002D5E] ; 002D4E 67 0E MOVEQ #$18,D1 ; 002D50 72 18 DBF D1,*-$0000 [00002D52] ; 002D52 51 C9 FF FE MOVE.B #$01,$EC2C ; 002D56 11 FC 00 01 EC 2C RTE ; 002D5C 4E 73 MOVEM.L D0-D7/A0-A6,-(A7) ; 002D5E 48 E7 FF FE MOVE.B $00A00009,D0 ; 002D62 10 39 00 A0 00 09 : MOVE.L (A0)+,(A1)+ ; 003842 22 D8 LEA ($0008,A5),A5 ; 003844 4B ED 00 08 BRA.S *-$4E [000037FA] ; 003848 60 B0 RTS ; 00384A 4E 75 NOP ; 00384C 4E 71 NOP ; 00384E 4E 71 NOP ; 003850 4E 71 NOP ; 003852 4E 71 NOP ; 003854 4E 71 NOP ; 003856 4E 71 NOP ; 003858 4E 71 NOP ; 00385A 4E 71 NOP ; 00385C 4E 71 NOP ; 00385E 4E 71 NOP ; 003860 4E 71One common feature in most FD1094 games is that the interrupt service routine and all related subroutines are grouped together, and padded with many NOPs. This is helpful to look for when trying to identifying the end of the interrupt related code.
I typically scan through the main ISR looking for the highest and lowest addresses referenced by branches and jumps to get an idea of how much code is involved. This part always requires some manual tracing of the code to figure out just how big the interrupt code section is.
Now that the various sections of code in their respective states have been determined, these pieces can be joined together to form a single, unified ROM image containing decrypted program code in each state that is used.
System 18 modifications
To replace the two 27C020's with 27C040's, we need to free up pin 31 which is the A18 input. Sockets A5 and A6 have this pin connected to pin 32 (+5V) by a thin trace. This should be cut (remember to check continuity with a multimeter) and pin 31 of both sockets should be tied together, then connected to the output of the 74HC02 circuit described earlier.
I've implemented this method for my Alien Storm PCB, and it works perfectly. I used a breadboard for prototyping the circuit and then constructed the finalized, more compact assembly once it was determined that the game worked as expected.
This technique will work for any FD1094 based game that stores program code in ROMs. System 24 disk-based games do not apply.