Bionic Commando

Hits: 1642

Category: TutorialsAmigaCrackingMFM
Author: xyzzy
Submitted by: xyzzy
Date: 2012-08-29 03:14
No tags
Bionic Commando

Bionic Commando


Software Creations (Capcom) 1988


Required items


1 Bionic_Commando_(1988)(Capcom)(US)[1074]

2 An Amiga or WinUAE (Im using WinUAE)

3 X-Copy or your favorite copier.

4 Action Replay Cartridge or your favorite monitor.

5 ASM-One

6 StoneCracker

7 Pencil and paper

8 Blanks, if you use a real Amiga.


Given  that  this  game  was  cracked  over  twenty  years  ago  and the  cracks/SPS  originals  are  readily available on the Internet, we are not doing a disservice to anyone by analyzing the code.   This is for educational purposes only and at your own risk and volition.


Make a copy of the disk using X-Copy to see what we are facing.



This looks interesting.  From the error codes below, it looks like checksum errors.


The list of X-Copy Errors from the X-Copy Shrine WebSite (


Booting the disk results in the game loading to the title screen and then freezing.


Directory the disk and see that it’s Amiga DOS. Type the startup-sequence.



Load the file boot (it’s in the C directory) and disassemble.  One thing to note is that the addresses are offsets from the end of Amiga DOS header(e.g. LEA 74,A1 is actually loading the start address + 20, which is 30020+74 when the file is loaded to 30000, into A1).


Check the ASCII



LEA $74,A1 points to the library name dos.library



CLR.L D0 sets the version to 0 (any version) MOVE $4,A6 sets the pointer to Execbase

JSR -228(A6) Opens the library and returns the library base in D0

MOVEA.L D0,56 – Saves library base pointer

MOVE.L #4E,D1 – points to file bionic

MOVE.L 56,A6 set the pointer to dos.library

JSR -96(A6) LoadSeg scatter loads a program into memory

MOVE.L DO,D3 Saves the segment list pointer

MOVE.L #4E,D1 – set pointer to file bionic (call is CreateProc(name,pri,seglist,stacksize) (D1,D2,D3,D4) CLR.L D2 sets the priority

MOVE.L #7D0,D4 – set stack size

JSR -8A(A6) CreateProc call

It closes the dos.library (JSR -19E(A6)). Let’s disassemble bionic and see what’s happening.



This is all standard open library, allocate memory etc. The reference manuals (Amiga Machine Language book has library listings in the appendix) show the details of the library calls. After it allocates memory (JSR –C6(a6)), it then stores the memory location returned in D0 for the allocated memory in an address e.g. B48 (move.l d0, B48). Search for uses of that address as the program may load a file there. You can also search for the Amiga DOS open and read commands (e.g. F 4E AE FF D6 which corresponds to JSR -




The last address is the most interesting as the code at 3D07A reads the file.


The read call (JSR -2A(A6) has file(D1), buffer(D2) and length(D3) as parameters) is shown below



We could play the game and rip the files each time they load or write a program to read each file and write it out again. The routines are as follows (the open file (JSR -$1E(A6) is at 3D05C)) and this is called by a number of routines which pass the file name to be opened.


Load charset.dat at 3CEB0


Load level?.pi1 at 3CED8 where ? is the level number. This is derived from the base in A0 and the offset for the level in d0. This points to the address of the file name



The first address at 3d142(30020+D122) gives D136 which is 3d156 (30020+d136) and points to level1.pi1.


Load map?.dat at 3CF04 where ? is the level number. Uses the same method as level to point to the correct level.


Load mansp.spr at 3CF28


Load bigsol.spr at 3CF48


Load Level345.spr at 3CF6E


Load level1.spr at 3CF9(This also loads level2.spr, level3.spr and heli.spr [note there are two pointers to heli.spr] using the offset method)


Load panel.pi1 at 3CFD2


Load spic.pi1 at 3CFF2


Load Level2.spr at 3D030

So we could use the games own code to read the files and add our code to write them straight out again to another disk. However, given it’s Amiga DOS, there’s a good chance that a file copy program or rudimentary disk copier will work.  You can copy the files with the Amiga DOS copy command (copy df0: df1: all). Both the built in dcopy in the Action Replay and the standard utility diskcopy will copy the disk and correct the errors. Also, you can copy the files using Directory Opus.



The left panel is the IPF and the right panel is the ADF.  The copy is identical.



Boot the copy


The game boots and plays. Let’s train the game and see if theres any more protection.  Also, search for any loader related addresses (DFF024, DFF07E, BFE001, BFD100).  There doesn’t look like there are any custom loaders.


Load bionic into memory at 30000 and disassemble.



After scanning the code down to 30268, you’ll see a lot of moves.  From the game, the life counter starts

at 6 and the time counter starts at 200.  So 30268 could be the initialization of the life counter and

302b6 could be the initialization of the timer.  Search for the two address initialized


Disassemble just in front of the address. The JSR CCAE call a routine which does a subtract (SBCD – subtract binary coded decimal). The subtract can be replaced with a NOP For a trainer, the offset from the DOS header is CCBA.



Search for the timer address



The code at 3CC9C decrements the timer. NOP out the two SBCD.B at 3CCAE and 3CCB0.  For a trainer, the offset for the CC8E.


The test for the time at 3CC9A is the countdown of the time during play. The test at 3CD54 is the routine to add the remaining time to the player’s score.  This can be discerned by the following. If the two SBCDs are replaced at 3CCAE the in game timer stops decrementing. Disassemble back from the address 3cd54 by disassembling until the d command passes off the top of the screen and then hit the up cursor. Keep scrolling up and the start of the routine appears to be 3CCDE (NOTE: remember to subtract 30020 from the address when searching using faq)



Disassemble from 3c996




By comparing the two sets of code at cdee and ce0e it looks like a level setting.  When you finish the first level it runs the code at 3CE0E and sets the addresses 8d36 and ba0 to 1. From our previous work with the files it is most likely that the address at BA0 is the level point for loading the file. The starting code

for level 1 is at 3CDF0 (offset CDD0) and searching for CDD0 reveals the two calls.

The first one is the setup when starting up the game. The second one is when continuing the game. By changing the branches to this address to point to another level start address e.g.

CDEE the game will start at that level.  This is done by adding a move.w #$CE,(A0) after setting A0 to point to the base load address plus $1F2 (30212 is where the word $CDD0 is held and the difference between $30212 and $30020 is $1F2). The level offsets are at 3C99C and are as follows

Level 2 $CDEE, Level 3 $CE0E, Level 4 $CE2E and Level 5 $CE4E.

There doesnt appear to be any code working with the invalid checksums when I played through to the end.





Let’s add a trainer menu. Get the trainer source from the Chuck Rock 2 trainer tutorial. Set the number of lines to three as the start level, lives and time will be trained. Set the lines, levelsel and levels as follows.


LINES=3                      ; COUNT OF ON/OFF LINES







In the section ENDE, add the line


MOVE.L A1,$80 – this moves the patch address into the TRAP #0 vector. Set the PATCHADR to an unused piece of memory such as 200.









/Save the old trap #0 register



/load our patch into the trap #0 register




Add the following code in the patch section. The MOVE.L A(A7),A0 puts the return address in the game code in A0 which allow the position of the life counter and time counter to be calculated.




MOVEM.L A0/A1,-(A7)                /save A0 and A1 on the stack before we start

MOVE.L A(A7),A0                         /Save the return address

SUB.L    #2,A0                                 /subtract 2 from the address to get the start of the code

MOVE.L A0,A(A7)                          /write the return address back to the stack

MOVE.W #$23CF,(A0)+                /overwrite our TRAP #0


LIVES:                  CMPI.B #$01,$c0.W                      /Check whether the user selected infinite lives BNE TIME                                        /if not then branch to the check for infinite time MOVE.L A(A7),A0                          /move the base of the bionic code to A0

ADD.L #$CCAC,A0                        /add the offset for the subtract from the life counter

MOVE.W #$4E71,(A0)                  /NOP out the subtract

TIME:                  CMPI.B #$01,$c1.W                      / Check whether the user selected infinite lives

BNE LEVEL1                                    / if not then branch to the return to the level set code


ADD.L     #$CC80,A0                     / add the offset for the subtract from the time counter

MOVE.L  #$4E714E71,(A0)         /NOP out the subtracts


LEVEL1:              CMPI.B #$01,$c2.W                      /Check the level selected by the user

BEQ RETURN LEVEL2:              CMPI.B #$02,$c2.W


MOVE.L $A(A7),A0

ADD.L #$CDEE,A0                         /load the JSR address for the level

MOVE.L $A(A7),A1

ADD.L #$1f0,A1                            /load the location of the address part of the JSR inst. MOVE.L A0,(A1)                             /patch the JSR to point to the selected level

BRA RETURN LEVEL3:              CMPI.B #$03,$c2.W


MOVE.L $A(A7),A0


MOVE.L $A(A7),A1

ADD.L #$1F0,A1


LEVEL4:              CMPI.B #$04,$c2.W BNE LEVEL5

MOVE.L $A(A7),A0


MOVE.L $A(A7),A1

ADD.L #$1F0,A1

MOVE.L A0,(A1)

BRA RETURN LEVEL5:              CMPI.B #$05,$c2.W



MOVE.L $A(A7),A1

ADD.L #$1F0,A1

MOVE.L A0,(A1)




RETURN:            MOVE.L A(A7),A0

ADD.L     #$24A,A0                       / add the offset for the instruction that sets life counter

MOVE.W  #$0009,(A0)                /change 0006 to 0009 so we have 9 lives


ADD.L     #$298,A0                       / add offset for the instruction that sets time counter MOVE.W #$0999,(A0)                 /change 0200 to 0999 so we have 999 in the time MOVE.L   OLD80,$80                   /restore the TRAP #0 vector

MOVEM.L (A7)+,A0/A1                /restore A0 and A1

RTE                                                   /return to the exception we caused with trap #0

OLD80                dc.l        0                                         /location for the old TRAP #0 vector



In the life section, the MOVE.W #$4E71,(A0) just NOPs out the subtract.  In the time section, the similar command removes the two subtracts. Compile with ASM-One (v1.20 as v1.48 exe does not work in WinUAE) and crunch with StoneCracker using the default settings.


Load the file bionic into memory at 30000.  Replace the MOVE.L A7,B5C with TRAP #0. You could also use JSR $200 and then RTS back in the patch.



When the game runs the first line of the code will restored removing our TRAP instruction. The return address is changed so that the game runs the MOVE.L A7,B5C code.

FileDownload: Bionic Commando
Filesize: 0KB, downloaded 33 times
Powered by the best online Amiga mod player: FLOD

Some more you may like:
The Silents - Bionic CommandoThe Goonies - Bionic CommandoQuadlite - Bionic Commando DemoBionic Commandos - Xor


Leave a Comment!

: Use this calculator
Your comment will be available for editing for 10 minutes
2012-08-29 11:13

1. WayneK writes

So the original has loads of checksum errors to make it "uncopyable", but a file copy of the disk gives you a working game?! *facepalm*
Nice training tho, and good to see a tutorial on Flashtro again - it's been a while since the last one!
2012-08-29 12:09

2. aLpHa oNe writes

This is really a strange "protection". ;-)
2012-08-30 01:26

3. -TCB!- writes

Back in the day, that protection would have been enough to stop 97% of the non-creative Amiga users that went to bed and woke up with the "X-Copy Boooooing!".
2016-12-13 15:37

4. morpa writes

Just a note that the link for the download doesn't seem to work anymore.