Copy Link
Add to Bookmark
Report

How to HD install All Terrain Racing XMas Coverdisk using WHDLoad

DrWatson's profile picture
Published in 
Amiga hacking
 · 6 years ago

The All Terrain Racing Xmas Coverdisk is a nice easy game to install. I'll talk you through how I patched it for use with the magnificent WHDLoad system (and also works with JST).

A fully text version is also available if you prefer!

You can download some of the tools mentioned in this article from the WHDLoad site www.whdload.org and the Action site zap.to/action

OK, the first thing to do is check if the disk is Dos or Non-Dos. Put the disk in the drive and see if it's a valid disk. In this case, no it isn't. You would see an icon and be able to see files if it was.

Insert the game disk and try and make an image of it. Simply type:

 
DIC


and let it image the disk. If all went well, you will have a file called Disk.1 and no errors reported.

You must rip the bootblock off the game and disassemble it to see where it is loading the data to. I use a program called grab to rip data from any file. The bootblock is the first 1024 bytes ($400) so you can type:

 
grab Disk.1 bootblock first 1024


Now load the bootblock into your disassembler (or cartridge :) I recommend Resource 6.xx as it is very powerful. You will need a lot of memory to use it however!

 
DC.B 'DOS',0
DC.L $2BAE11B5
DC.L $370

_BootStart move.w #$F80,($DFF180).l
movea.l (4).w,a6
move.w #2,($1C,a1)
move.l #$78000,($28,a1)
move.l #$2C00,($24,a1)
move.l #$400,($2C,a1)
jsr (-$1C8,a6)
jsr (-$96,a6)
lea ($7F000).l,sp
move.w #$2700,sr
move.w #$7FFF,($DFF09A).l
move.w #$7FFF,($DFF09C).l
move.w #$7FFF,($DFF096).l
bset #1,($BFE001).l
bsr.w lbC000072
jmp ($78000).l

lbC000072 movea.l (4).w,a6
cmpi.w #$24,($22,a6)
blt.b lbC000086
moveq #0,d0
moveq #-1,d1
jsr (-$288,a6)
lbC000086 rts


In this case, it's a very simple loader. When the bootblock is run, A6 contains Execbase, and A1 points to an IORequest structure which allows you to read more data off the disk.

If you have a fancy disassembling program such as Resource, you can use it to put the labels back into the program. The bootblock would then be as follows:

 
DC.B 'DOS',0
DC.L $2BAE11B5
DC.L $370

_BootStart move.w #$F80,($DFF180).l
movea.l (4).w,a6
move.w #CMD_READ,(IO_COMMAND,a1)
move.l #$78000,(IO_DATA,a1)
move.l #$2C00,(IO_LENGTH,a1)
move.l #$400,(IO_OFFSET,a1)
jsr (_LVODoIO,a6)
jsr (_LVOSuperState,a6)
lea ($7F000).l,sp
move.w #$2700,sr
move.w #$7FFF,($DFF09A).l
move.w #$7FFF,($DFF09C).l
move.w #$7FFF,($DFF096).l
bset #1,($BFE001).l
bsr.w _DisableCache
jmp ($78000).l

_DisableCache movea.l (4).w,a6
cmpi.w #36,(SoftVer,a6)
blt.b _NotWB2
moveq #0,d0
moveq #-1,d1
jsr (_LVOCacheControl,a6)
_NotWB2 rts

Let's step through this one:

move.w #$F80,(_custom+color).l ;Background colour
;to orange.


The next part is the main loading from the bootblock:

 
movea.l (4).w,a6
move.w #CMD_READ,(IO_COMMAND,a1)
move.l #$78000,(IO_DATA,a1)
move.l #$2C00,(IO_LENGTH,a1)
move.l #$400,(IO_OFFSET,a1)
jsr (_LVODoIO,a6)


We are going to fill the IO request structure with a read tracks (CMD_READ) operation. A disk IO request needs a command to perform, and it needs to know what to load, how many sectors to load and where to load the data to.

 
move.l #$78000,(IO_DATA,a1) ;Load file at $78000
move.l #$2C00,(IO_LENGTH,a1) ;Load $2c00 bytes
move.l #$400,(IO_OFFSET,a1) ;Load from $400 on
;the disk.


From the code, we can tell that the game is loading a file on the disk from $400 to $3000 ($400 + $2c00). At this point, the easiest way to rip the file off the disk is using a grab utility (there are many other ways). Using grab:

 
grab Disk.1 Load1 from $400 length $2c00


You now have the first file from the game called Load1. We'll come back to this in a minute.

Next the game performs the I/O which actually loads the file into memory. Another way to get this file is to rip the file using a monitor or Action Replay cartridge and save from $78000 length $2c00.

 
jsr (_LVODoIO,a6) ;Load the file


The game now wants to take over the computer so changes into Supervisor mode:

 
jsr (_LVOSuperState,a6) ;Supervisor mode
lea ($7F000).l,sp ;Change stack to
;$7f000
move.w #$2700,sr
move.w #$7FFF,($DFF09A).l ;Disable interrupts
move.w #$7FFF,($DFF09C).l
move.w #$7FFF,($DFF096).l
bset #1,($BFE001).l ;Toggle the LED


The next line branches to a routine to disable the cache.

 
bsr.w _DisableCache ;Kill the cache


Here's a breakdown of the routine:

 
_DisableCache movea.l (4).w,a6
cmpi.w #36,(SoftVer,a6) ;Check v36 (WB2.0+)
blt.b _NotWB2 ;Branch ahead if
;the user does not
;have WB2 or higher

moveq #0,d0 ;Disable the cache
moveq #-1,d1
jsr (_LVOCacheControl,a6)

_NotWB2 rts ;Return


The only other line was a jump which jumps to the file we loaded previously.
We have the file called Load1:

 
jmp ($78000).l ;Run the file we
;just loaded.


OK, that's the easy part out of the way. Once you have done a few of these you can work out the above information in about 30 seconds! You just need practice!

OK, we're onto the next file, disassemble it and have a look. Often the game will firstly try to work out where there is memory to play with and sure enough, this one does as well:

 
_Hex78000 movea.l (4).w,a6
move.l #$50000,d0 ;Allocate $50000
move.l #MEMF_CHIP,d1 ;bytes of chip
jsr (_LVOAllocMem,a6) ;memory

movea.l (4).w,a6
move.l #$40000,d0 ;Allocate $40000
clr.l d1 ;bytes of memory
jsr (_LVOAllocMem,a6)


The code above is simply trying to find $80000 bytes of memory above your minimum 512k chip memory. Chances are the game will require 1Mb to play.
You'll need this information later for your HD installer!

After that code, d0 will contain the address where there is $40000 bytes of memory (or 0 if you had none). Now it adds $40000 bytes so the value is guaranteed not to be in your first $80000 (512k) of chip memory.

 
addi.l #$40000,d0 ;Add $40000


Now the and operation rounds the memory to the nearest $80000 (512k) block and stores the memory at $f00 in memory.

 
andi.l #$FFF80000,d0 ;Round memory
move.l d0,($F00).w ;Store expansion
;memory at $f00


The following code is all rather boring, disabling multitasking, going into supervisor mode, clearing the screen etc.

 
move.w #$2700,sr
move.w #$3FFF,($9A).w
move.w #$7CEF,($96).w
move.l #_Supervisor,($20).w
_Supervisor move.w #$2700,sr
lea ($DFF180).l,a0
moveq #$1F,d7
_PaletteBlack clr.w (a0)+
dbra d7,_PaletteBlack
lea (_Hex78000).l,sp
lea ($DFF000).l,a6
move.w #$7FFF,($9A,a6)
move.w #$8610,($96,a6)
move.w #$FFFF,($EC0).w


The following code looks interesting and this could well be the loader we need to patch:

 
move.l #'game',d0
lea ($1000).w,a0
lea ($7CC00).l,a1
bsr.w lbC0000BE ;Possible loader
tst.w (lbW0000BC).l ;Check something
bne.w _FlashScreen ;If not 0, flash!
jmp ($1000).w

_FlashScreen move.w #14,($DFF180).l ;Infinite loop
move.w #$E80,($DFF180).l ;flashing the
bra.b _FlashScreen ;screen


The routine is a dead giveaway for a loader. It is passing in some variables, d0, a0 and a1 and branching to a subroutine. It then checks a value and if it's not 0, it goes into an infinite loop flashing the screen. This is probably because the disk was corrupt, not in the drive or some other error.

The routine labelled lbC0000BE needs investigation to work out how it works.

 
lbC0000BE move.l d0,(lbW0000B0).l ;Store something
bsr.w lbC00017A ;Another routine


Now check the routine lbC00017A to see what it is doing.

 
lbC00017A move.l a0,-(sp)
moveq #0,d0
move.w #$6DE,d1
move.w #2,d2
move.w #0,d3
move.l #0,d4
lea (lbL0001A2,pc),a0
bsr.w lbC0007A2
move.w d0,(lbW0000BC).l
movea.l (sp)+,a0
rts


Once you've been in the patching business for a while, this is a dead giveaway as a loader. It's passing various values through to another routine at lbC0007A2:

 
lbC0007A2 movem.l d1-d7/a0-a5,-(sp)
link.w a6,#-$24


These 2 lines are probably the most well known loader ever written on the Amiga, everybody knows a Rob Northen loader when they see it!

You can search in binary for $4e56ff to find Rob Northen loaders quickly!

Here you must patch the code so instead of loading from disk, it will load from your hard drive installer. All you need to remember at this point is the address, $7a2 bytes into the file is a Rob Northen loader which you must patch!

Now back to the routine which called this, we can guess that a0 is the loading address as it passes it into the loading routine and then a few lines down, does a jmp $1000 to start the game. However, we do not want the game to simply start as there is usually something else to patch in the main game.

 
move.l #'game',d0
lea ($1000).w,a0
lea ($7CC00).l,a1
bsr.w _Loader ;Loader
tst.w (lbW0000BC).l ;Check something
_Hex78096 bne.w _FlashScreen ;If not 0, flash!
_Hex7809a jmp ($1000).w


We will change the jmp ($1000).w to go to our code so we can alter the main game before it does anything.
At this point we can start writing our WHDLoad slave! Firstly you save _resload (all slaves do this):

 
lea _resload(pc),a1
move.l a0,(a1) ;save for later use


Now load the first file via WHDLoad's resload_DiskLoad routine:

 
_restart move.l #$400,d0 ;offset ($3c00)
move.l #$2c00,d1 ;size
moveq #1,d2 ;diskno
lea $78000,a0 ;destination address
move.l a0,a5
move.l _resload(pc),a2
jsr resload_DiskLoad(a2) ;Load the first file


Now we must change the loader so it loads from our slave rather than from the original Rob Northen disk loader. In the code above, a5 is the address which the game loaded to. You could also use $787a2 instead of $7a2(a5).

 
lea $7a2(a5),a0 ;Patch Rob Northen loader
move.w #$4ef9,(a0)+ ;Insert jmp
pea _RobNorthenLoader(pc)
move.l (sp)+,(a0)+


$4ef9 is the instruction for JMP, so we are replacing the code at $7a2 with a jmp _loader instruction!

Before we start the main game, we want to have the game return to us so we can do some more fixing:

 
lea $96(a5),a0 ;Don't start the game,
move.w #$4ef9,(a0)+ ;jump back to our patch!
pea _Patch(pc)
move.l (sp)+,(a0)+


We've change the instruction at $78096 (which is another way of writing $96(a5)) to a jmp _Patch instruction.

The initial game worked out where the extra memory was and then stored it in d0. We'll bypass all that code since we know where the memory is as WHDLoad has already worked it out.

 
move.l _expmem(pc),d0
jmp $30(a5) ;Start game


The code above will start the game passing through the expansion memory location in d0 and has altered the loader. Now we're onto the next stage.

The game is now patched up until the main file which loads at $1000 and then we have modified it to jump to our patch code labelled _Patch. At this point we can fix anything else (while preserving the state of the game prior to out patch) and then jmp $1000 to begin!

If you run the game and break into it, you will see the level 2 interrupt points to $13aa. There are several ways you can work this out, play the game and break into it, follow the code through until it writes to $68, search for the value $bfec01 which is where the keypress is read from etc. At that address is the following code. You should always check the level 2 interrupt as it contains the keyboard routine so we can add a quit key and/or trainer key:

 
_Hex13aa move.l a0,-(sp)
move.l d0,-(sp)
moveq #0,d0
lea ($BFE001).l,a0
move.b ($D00,a0),d0
btst #0,d0
bne.b lbC001402
btst #3,d0
beq.b lbC001408
move.b ($C00,a0),d0 ;Keypress
not.b d0
ror.b #1,d0 ;d0 = Raw key
move.b d0,($131D).w
_Hex13d2 bset #6,($E00,a0)
move.b #1,($500,a0)
lea (lbL00131E).l,a0
tst.b d0
bmi.w lbC0013F4
move.b #1,(a0,d0.w)
bra.w lbC001408

lbC0013F4 bclr #7,d0
move.b #0,(a0,d0.w)
bra.w lbC001408

lbC001402 bclr #6,($E00,a0)
lbC001408 move.l (sp)+,d0
movea.l (sp)+,a0
move.w #8,($DFF09C).l
rte


Somewhere around the part where we work out the key that was pressed, we want to alter the code to go to our routine and if it's the quit key in WHDLoad, completely exit the game.

In this case, I'll choose _Hex13d2 as the instruction is 6 bytes. 6 is a magic number in patching as we have to jump to our routine somewhere in memory. The instruction will need 2 bytes, plus the address requires 4 bytes so we need 6 in total.

We'll replace that with a subroutine which checks the key is the exit key, and if so, quit the game. If it's not the quit key, we'll do the instruction it used to do, and continue on as if nothing has happened!

The original code was this:

 
_Hex13d2 bset #6,($E00,a0)


We'll change it by having this code in our WHDLoad slave:

 
lea $13d2,a0 ;Quit key
move.w #$4eb9,(a0)+ ;Insert a jsr
pea _keybd(pc)
move.l (sp)+,(a0)+


$4eb9 is the instruction for jsr, which jumps to a subroutine. In our case, it will do jsr _keybd. We do not know where in memory the _keybd routine is, so we'll push it onto the stack, and then move it into the game at $13d4.

In our slave we add the following keyboard routine:

 
_keybd cmp.b _keyexit(pc),d0 ;Check exit key matches
beq _exit ;If so, exit the game

bset #6,($e00,a0) ;Original stolen 6 bytes
rts


The original 6 bytes of code that we overwrote (or stole) is done just prior to us returning control back to the game.

Also in the game is another Rob Northen loader at $5368. We'll need to redirect that to our slave aswell. You can find this by doing a search through the code for magic $4e56ff value.

 
lea $5368,a0 ;Patch Rob Northen loader
move.w #$4ef9,(a0)+ ;Insert jmp
pea _RobNorthenLoader(pc)
move.l (sp)+,(a0)+


That should be enough to get the game going for now. Our WHDLoad slave looks like this:

 
INCDIR include:
INCLUDE whdload.i
INCLUDE whdmacros.i

IFD BARFLY
OUTPUT "ATRXmas.slave"
BOPT O+ ;enable optimizing
BOPT OG+ ;enable optimizing
BOPT ODd- ;disable mul optimizing
BOPT ODe- ;disable mul optimizing
BOPT w4- ;disable 64k warnings
BOPT wo- ;disable warnings
SUPER ;disable supervisor warnings
ENDC

;======================================================================

_base SLAVE_HEADER ;ws_Security + ws_ID
dc.w 10 ;ws_Version
dc.w WHDLF_NoError|WHDLF_EmulTrap ;ws_flags
dc.l $80000 ;ws_BaseMemSize
dc.l 0 ;ws_ExecInstall
dc.w _Start-_base ;ws_GameLoader
dc.w 0 ;ws_CurrentDir
dc.w 0 ;ws_DontCache
_keydebug dc.b 0 ;ws_keydebug
_keyexit dc.b $5d ;ws_keyexit = PrtSc
_expmem dc.l $80000 ;ws_ExpMem
dc.w _name-_base ;ws_name
dc.w _copy-_base ;ws_copy
dc.w _info-_base ;ws_info

;============================================================================
IFND .passchk
DOSCMD "WDate >T:date"
.passchk
ENDC

_name dc.b "ATR Xmas Demo",0
_copy dc.b "1991 Team 17",0
_info dc.b "Installed by Codetapper/Action!",10
dc.b "Version 1.0 "
INCBIN "T:date"
dc.b -1,"Thanks to Riempie for the original!",0
EVEN

;======================================================================
_Start ; A0 = resident loader
;======================================================================

lea _resload(pc),a1
move.l a0,(a1) ;save for later use

_restart move.l #$400,d0 ;offset ($3c00)
move.l #$2c00,d1 ;size
moveq #1,d2 ;diskno
lea $78000,a0 ;destination address
move.l a0,a5
move.l _resload(pc),a2
jsr resload_DiskLoad(a2)

lea $7a2(a5),a0 ;Patch Rob Northen loader
move.w #$4ef9,(a0)+
pea _RobNorthenLoader(pc)
move.l (sp)+,(a0)+

lea $96(a5),a0
move.w #$4ef9,(a0)+
pea _Patch(pc)
move.l (sp)+,(a0)+

move.l _expmem(pc),d0

jmp $30(a5) ;Start game

_Patch move.l a0,-(sp)

lea $13d2,a0 ;Quit key
move.w #$4eb9,(a0)+
pea _keybd(pc)
move.l (sp)+,(a0)+

lea $5368,a0 ;Patch Rob Northen loader
move.w #$4ef9,(a0)+
pea _RobNorthenLoader(pc)
move.l (sp)+,(a0)+

move.l (sp)+,a0
jmp $1000

;======================================================================

_RobNorthenLoader
movem.l d1-d7/a0-a6,-(sp)
move.l _resload(pc),a2 ;a0 = dest address
mulu #$200,d1 ;offset (sectors)
mulu #$200,d2 ;length (sectors)
exg.l d1,d0 ;d0 = offset (bytes)
exg.l d2,d1 ;d1 = length (bytes)
addq #1,d2 ;d2 = disk
jsr resload_DiskLoad(a2) ;a0 = destination
movem.l (sp)+,d1-d7/a0-a6
moveq #0,d0
rts

;======================================================================

_keybd cmp.b _keyexit(pc),d0
beq _exit

bset #6,($e00,a0) ;Stolen code
rts

;======================================================================
_resload dc.l 0 ;address of resident loader
;======================================================================

_exit pea TDREASON_OK
bra _end
_debug pea TDREASON_DEBUG
bra _end
_wrongver pea TDREASON_WRONGVER
_end move.l (_resload),-(a7)
add.l #resload_Abort,(a7)
rts

;======================================================================


If this seems very difficult, relax! You have to serve your apprenticeship and become familiar with the Amiga, how memory is organised, interrupts, the stack, assembler etc. You cannot sit down and expect to be able to patch a game if you do not know hexidecimal and assembler pretty well.

Once you have patched a few games, you will be able to knock up slaves for easy ones like this in about 30 minutes. Later you will find difficult games with copy protection, blitter problems, stack frame problems and lots of other things.

Start off patching easy PD games and coverdisk demos. Generally they have easy loaders, no copy protection and are either a single load or only a few files.

After a while, you will become an expert at reusing another install as the basis for a game and patching it. Loaders will be the same, memory allocation the same etc. It just takes plenty of practice and time.

Once the game in installed, you can rip some images from the game and make a nice icon, add a ReadMe file, an install script and find a few beta-testers to test your work on their high-powered Amigas :)

Good luck!

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT