Schematic execution flow
The following table shows the program flow when a WHDLoad installed program
will be executed. I hope it helps to understand how WHDLoad works and how
WHDLoad, the Slave and the installed program does cooperate.
- starts the demo or game by clicking a Icon or by starting
WHDLoad via the command line
|The Operating System
- loads the WHDLoad executable and starts it
- checks the Software and Hardware environment
- loads and checks the Slave
- allocates required memory for the installed program
- if Preload/S is enabled it loads
disk images and files into the RAM (as far as free memory is
- switches OS off (disables mutitasking and interrupts, degrades
graphics hardware to OCS, inits all hardware with defined values)
- jumps into the Slave
- loads the main executable of the installed program by calling a
WHDLoad function (e.g. resload_DiskLoad or resload_LoadFile)
- patches the main executable (so that the program will load his data
via the Slave, to fix compatibility problems, to enable an exit
from the program)
- calls the main executable
- will do his stuff
- on loading data from disk it will call the Slave (because the
Slave has patched it this way previously), and the Slave
will call WHDLoad, and WHDLoad will partially enable the OS to
load the data (only if the data is not Preload'ed), then return, return
and the installed program continues
- exits the program by pressing the QuitKey
- re-enables the OS (restores hardware registers, display and memory)
- frees all allocated resources
- returns to the OS
How to install a simple one disk trackloader
This is a very small and short step by step guide on how to create an install
for a NDOS demo/game
using WHDLoad. The guide reflects an ideal simple case. In the real world such a
case will probably never occur. For special cases and problems, read the
chapters that follow this.
- Make a drawer which will hold all files.
- Create a disk image using DIC in this
- Create a #?.info file with "WHDLoad"
as <Default Tool> and a Tooltype "SLAVE=#?" containing the
name of the Slave. (or simply copy the icon from an Example
Install, and disable all tooltypes except "SLAVE=").
- The Slave
To write the Slave we need following information:
To get this information we first analyze the bootblock. Most times
the main executable will be loaded from here via exec.DoIO(). Sometimes
a special trackloader is in the bootblock. We now write a Slave which
will simulate the bootblock and load the main executable from the disk
image. Now we rip the main executable from the image or a memory dump. After that we have to find the
loader in the main exe. A fast way is to search for the pattern
$AAAAAAAA (used by the MFM decoding) with a hex-editor. Then cut the area
(+/- $1000 bytes) found, disassemble it, and search for the start of the
routine. Understand the parameterlist. Now we create code for the Slave
which will patch this loader routine in a way that all calls to the
loader will be redirected to the Slave. The Slave will then adjust the
parameters and call the WHDLoad function resload_DiskLoad.
- Where on disk is the main executable located?
- Where inside the main executable is the disk loader located?
- In the ideal case the install is now complete.
One thing left to do is to create a nice Icon. Rip two pictures using the snoop feature of WHDLoad and SP or
use a freezer or some kind of UAE to extract pictures and build the icon. The
16 color RomIcon
palette is recommended.
Possible problems and special cases
Non standard trackloader
Some programs use their very own disk format. This means that DIC is unable to create the disk images. To create files
or images from such disks the use of RawDIC is
recommended. See the documentation of RawDIC for more information.
If the program uses more than one disk the Slave must redirect the disk
accesses to the appropriate image file. Sometimes this is not easy. Some
programs support more than one drive, so you can use the drive number to
select the disk. Most programs use an ID on every disk to distinguish them.
In this case, use a variable which holds the disk number, and on every access
to the disk ID (determine such an access by analyzing the parameters for the
disk loader) increase the variable (if the last disk is reached, decrease it). So
hopefully the loader will read the ID again and again up until the correct
disk is inserted. Perhaps there is a request from the program that the user
should insert the right disk, disable it.
Use resload_SaveFile to write the appropriate
memory area to the disk. If you like, encrypt it a bit so lamers can't patch
it too easily. It is not recommended to write directly into disk images (using
resload_SaveFileOffset), because if
something goes wrong (e.g. crash) it's possible that the images will be
Savegame handling is the same as with highscores.
Accesses to the operating system At the time the Slave and the
installed program is executed, absolutely no OS exists nor is accessible nor
makes any sense to access! Therefore all accesses attempted by the installed
program must be disabled. If there aren't many of them and they don't
make sense in the WHDLoad environment (like exec.Disable() or
exec.SuperState()) simply NOP ($4e71) them. If the accesses have an
important function (like exec.DoIO()), redirect them to the Slave and emulate
them. If there are loads of them, create a simple exec.library in an unused
memory area (initialize the longword at address $4). You can check the
source for the Oscar.Slave, which emulates exec.AllocMem(). To detect accesses
to the OS, the initial execbase is set to $f0000001 with the intention
that all routines which like to use the execbase will create an "Address Error"
If there is heavy use of OS functions, use one of the kickemu
packages which can be found in the WHDLoad-dev package. There is one package
for Kick 1.3 ('src/sources/whdload/kick13.s')
and one for Kick 3.1 ('src/sources/whdload/kick31.s').
These packages require an original kickstart image and will create an complete
OS enviroment inside the WHDLoad space. Consult also the approriate readme supplied for further
Common compatibility problems
Limited address space on 68000/68010/68ec020
On these processors the address space is limited to 16 MiB
($000000...$ffffff) because these CPUs do have only 24 address lines.
As a result all accesses to higher addresses are performed to the lower 16 MiB
by ignoring the most significant 8 bits. Some programs use these bits to store
data, or simply forget to clear them. On a processor with full 4 GiB address
space like the 68020/680ec30/68030/68040/68060 this does not work, because the
full 32-bit addresses will be accessed.
To solve this you have to patch these accesses and redirect them to the
Sometimes the reason for accesses to strange addresses may be an uninitialized
pointer. In these cases it may help to clear the memory $400 - ws_BaseMemSize which can also be
accomplished by using the flag WHDL_ClearMem.
Different stackframes on each processor The stackframes created by the
processor on interrupts and exceptions are different for the members of the 68k
family. On the 68000 a stackframe is 6 bytes long, except for Bus and Address
Errors. The stackframe contains the saved SR at (a7) and the saved PC at
(2,a7). On all other processors (68010+) the minimal stackframe is 8 bytes and
additionally contains the vector number as a word at (6,a7). This Four-Word
stackframe format $0 is created for "Trap #xx" and Interrupts on
68010-68060. The stackframes on other exceptions are different on each
processor. The RTE instruction works differently on the 68000 compared to 68010+.
On a 68000 it simply restores the SR and PC and continues program execution at
the interrupted address. On the 68010+ it will additionally free the stackframe
depending on the stackframe format.
Some programs push an address (PC) and
an SR and then execute an RTE instruction. This works on a 68000 only, on
68010+ this will have unexpected results.
If a program does so, you'll
have to fix this. Sometimes it may be enough to replace the RTE with a RTR.
MOVEM.x RL,-(An) on 68000/010 and 68020-68060
There is a difference if the register used in predecrement mode (RL) is also
contained in the register list. For the 68020-68060 the value written to memory
is the initial register value decemented by the size of the operation. The
68000 and 68010 write the initial register value (not decremented).
Currently no software is known to have problems because of this.
General guidelines for writing installs
- Don't modify CPU registers present in higher processors like VBR or CACR.
The VBR is always 0 from the Slave programmers point of view, even when it's
moved because Auto Vectors (and Traps if the Flag WHDL_EmulTrap is set) are emulated.
The bits in the CACR are different for each processor. There is only one valid
way to modify the caches, use resload_SetCACR/SetCPU and the bitdefs from the
include files 'exec/execbase.i' and 'whdload.i'. Also all stuff present in the
program to install banging these registers must be disabled or skipped,
otherwise WHDLoad cannot work correctly!
- Never modify disk images. That has the advantage that if anybody wishes to
start the program from a floppy he must only write the images back to a disk
(assumed that the program runs without several fixes).
- Never use original stuff from the program directly in the Slave (copyright
- Enable the Caches only if you are sure it runs on all processors.
- Use as little memory as possible for ws_BaseMemSize. Some people have
resident tags on the end of the Chip memory, so it helps to use only
$1f0000 instead $200000 and WHDLoad can use absolute
Tips & tricks
What's better, using diskimages or files ? Sometimes you will have the
choice to use disk images or real files. Both has his advantages. The use of
disk images is usually the easier and faster way to create the Slave. But real
files are more easily cached (if there is very little memory or the memory is
fragmented). The needed space on harddisk will also be smaller with real files
than with disk images. You should only use disk images if there are a lot of
files (more than 30).