Findings
How it started
I was annoyed when I found out that I not only had to use an ACSI hard disk device to play Lemmings, but also a very specific hard disk driver. Why was that? I started digging into Lemmings because I loved this game, and what I found was not very surprising.
Testing and refactoring all the games to support GEMDRIVE in my SidecarTridge Multidevice was simply impossible a few years ago. But now, with the help of AI and agentic development, it was time to let the agents perform a full test of all the games in Hatari's GEMDOS drive (the distant cousin of my GEMDRIVE) and see what happens.
Why Lemmings fails in Hatari's GEMDRIVE mode
Lemmings' hard-disk adaptation does not start from a GEMDOS drive. A GEMDOS drive, such as Hatari's GEMDRIVE mode, is a host folder served file by file, by name, with no disk sectors behind it. The Gamex launcher needs a resident hard-disk driver, PPDRIVER, and reads the game by sectors through it. Without that driver it prints This prg. requires PP, AHDI or Hddriver hard disk driver … and exits.
Found in Hatari 2.6.1 runs on the original files. Run folders are in tools/dev/logs/. text+ offsets count from the launcher's inner program, LEMLD2. The GOS5 builds work around the cause, for Lemmings too (gos5-gemdrive.md).
The cause in short
The launcher does not read the game through GEMDOS, the part of TOS that opens files by name. It looks for PPDRIVER, the hard-disk driver from the 8bitchip.info website, by its XBRA header "XBRA" "PPHD". The header sits 12 bytes before the routine that hdv_bpb ($472) points to. The launcher copies the driver (7,204 bytes) to $B7180 and rewires it. Then it starts D15R (D15R_H5.FIC, at $C0100), the loader that holds GOS. GOS is TOS 1.04's GEMDOS, running from RAM.
GOS reads the FAT volume by sectors of 1,024 bytes, with BIOS calls on drive C. Getbpb returns the BPB, the table that describes the volume. Mediach asks whether the disk changed. Rwabs reads sectors. Hatari's GEMDRIVE has no driver, no BPB and no sectors. So the driver check fails, the fallback read of sector 0 fails, and the launcher exits.
1. The two setups
tools/dev/make_disks.sh builds both from a copy of LEMMINGS/. Each runs on an ST with TOS 1.04 and STEs with TOS 1.62 and 2.06, all with 1 MB.
- GEMDRIVE:
hatari --harddrive <folder>. A cartridge program at$FA0000serves the drive throughTRAP #1. BIOS and XBIOS calls go to the TOS ROM. - PPDRIVER, the reference: a 32 MB ACSI image from
../atari-hd --ppdriver-bootable. Its boot code loads the driver, which stays resident.
2. What the reference does
Run 20261011-144024-st-104-ppdriver-first-level, ST TOS 1.04. The launcher reads its own files with GEMDOS. After Dsetpath("FILES"), every disk access is a BIOS call from D15R. Record numbers are 1,024-byte logical sectors.
| Rwabs records → buffer | what it is |
|---|---|
65, 81, 263–264 → $B1100–$B5100 | the root, LEMMINGS and FILES directories |
33 → $AF100 | FAT copy 2 |
387–396 → $60000, 397–406 → $68000 | the two logos, PSYGLOGO.IMG and DMALOGO.IMG |
813–915 → $A00 | MAIN (105,220 bytes) |
415–496 → $10000 | INTROPAR (83,362 bytes) |
497–575 → $A00 | CODE (80,639 bytes), over MAIN |
| 677–680 | LEVELDAT, at the title |
407–409 → $41800 | LEVEL020, the first level |
Between reads GOS calls Mediach(2) 7 to 16 times, and PPDRIVER answers 0.
Open issue. The reference does not reach the first level in Hatari. Right after the level loads, the game raises an address error at $2B10. The loader is not to blame: the 3,048 bytes at $41800 equal FILES/LEVEL020 (run …-click-exc3).
3. Where the GEMDRIVE runs part from the reference
All three GEMDRIVE runs stop at the same check, at text+$324:
movea.l $472.w,a1 ; hdv_bpb
cmpi.l #$50504844,-8(a1) ; "PPHD"
bne.w text+$482 ; no PPDRIVER: the fallback
| setup | $472 (hdv_bpb) | long at hdv_bpb−8 | $516 (pun_ptr) | path |
|---|---|---|---|---|
| ST 1.04 GEMDRIVE | $FC173C (ROM) | $FFFC4280 | 0 | fallback, raw ACSI |
| ST 1.04 PPDRIVER | $12960 | "PPHD" | $12FEC | primary |
| STE 1.62 GEMDRIVE | $E01914 (ROM) | $FFFC4280 | 0 | fallback, raw ACSI |
| STE 1.62 PPDRIVER | $129A4 | "PPHD" | $13030 | primary |
| STE 2.06 GEMDRIVE | $E05210 (ROM) | $FFFC4280 | 0 | fallback, XBIOS DMAread |
| STE 2.06 PPDRIVER | $14DC4 | "PPHD" | $15450 | primary |
With a GEMDOS drive, hdv_bpb points to TOS's routine in ROM. With the image, it points to PPDRIVER.
The fallback. On TOS 1.x the launcher reads sector 0 itself, through the ACSI DMA registers. With no ACSI device the read times out, and the error exit prints the message. On TOS 2.x it calls XBIOS DMAread on the device named in PUN_INFO, the table a hard-disk driver installs at pun_ptr. With no driver, pun_ptr is 0, DMAread returns -1 and the same exit follows.
What the launcher does with PPDRIVER. It copies 7,204 bytes from the XBRA header ($12954) to $B7180: the driver's code, data and variables. Then it:
- points the copy's three "old vector" slots and its vector table at D15R (copy offsets
+$9,+$3F,+$43B,+$495..+$49D), so the driver chains to D15R instead of TOS; - rewrites the driver's BPB and buffer pointers (
+$771..+$78B), so its buffers sit at$AF100–$B5100; - installs the copy's entries in
$472,$476and$47E($B718C,$B71C2,$B75BE), sets$47Ato$C1F3E(D15R) and clearspun_ptr.
By the first Rwabs, TRAP #1 points to D15R's handler at $C84A0.
4. The embedded driver
The launcher carries no driver of its own. It requires PPDRIVER, resident, and reuses it: copied, relocated and patched at fixed offsets.
Answering the disk by hand from the debugger only reveals the next missing piece (ST TOS 1.04, GEMDRIVE):
| answered by hand | what happened next |
|---|---|
| nothing | the error message |
| sector 0 = the image's PPDRIVER boot sector | the launcher goes into D15R. TOS returns 0 for Getbpb(2), and the run crashes |
+ Getbpb(2) = PPDRIVER's BPB | TOS returns -15 (unknown device) for Rwabs, and the run crashes |
+ Mediach(2) = 0, Rwabs 65 and 81 from the image | D15R restores a context it never saved, and Hatari halts |
The fallback never initialises D15R, so nothing past the last stop works without the real driver.
PPDRIVER's BPB for the image, as Getbpb(2) returns it on the reference: recsiz 1024, clsiz 2, clsizb 2048, rdlen 16, fsiz 32, fatrec 33, datrec 81, numcl 16343, bflags 1.
5. Everything GEMDRIVE lacks
In the order the launcher needs them:
- A resident driver with the
PPHDheader beforehdv_bpb, laid out like PPDRIVER. The launcher patches its copy at+$9,+$3F,+$43B,+$495..+$49Dand+$771..+$78B. - Failing that, a boot sector 0 that can be read raw. Even then D15R is not initialised.
- BIOS
Getbpb(2)returning PPDRIVER's BPB (§4). - BIOS
Mediach(2)returning 0 before each read. - BIOS
Rwabson drive 2, serving the whole FAT volume. - Fixed memory: the driver copy at
$B7180, its buffers at$AF100–$B5100, D15R at$C0100, LS2DD at$70000,MAINandCODEat$A00. TRAP #1for D15R from the firstRwabson. Hatari's GEMDOS drive hooks it too, so a fix must live with both, as the GOS5 builds do.
The case of file names plays no part. GOS walks the directories itself, and Hatari ignores case on a GEMDOS drive.
6. The first look's guesses
A first look at the launcher, in April 2024, left guesses about the cause.
- Without a resident PPDRIVER, the loader reads sector 0 itself: confirmed (§3).
- With the
PPHDcheck patched out, the run stalls on lower-case file names: refuted. It stops onRwabsreturning -15 (§4). - The loader hooks
TRAP #1only forMallocandMshrink: not at the firstRwabs, when D15R's handler owns it (§3). - On TOS 2.06 the loader works through whatever driver is installed: refuted. It needs
PUN_INFOand an ACSI device (§3). - The copy of PPDRIVER embedded in the launcher causes the failure (the starting guess): confirmed in substance. The driver is required and resident, not embedded (§4).
START.TOSonly unpacks the rest, and the.FICfiles are packed code: consistent with the runs (launcher-formats.md).
7. Evidence and tools
- The reference: run
20261011-144024-st-104-ppdriver-first-level, scripttools/dev/expect/reference-first-level.exp, BPB in run…-ppdriver-bpb. - The check and the fallbacks: runs
20261011-141008-st-104-gemdrive-gate,20261011-141116-ste-206-gemdrive-gateand the other…-gateruns, fromsrc/probes/gemdrive-failure/gate_probe.py <setup> <disk>.read_probe.py <run>prints a run's chain. - The driver copy: runs
…-st-104-ppdriver-gateand…-drv. - The answers by hand:
src/probes/gemdrive-failure/fallback_probe.pyandserve_rwabs.py, runs…-gemdrive-fallbackand…-gemdrive-serve-02.
Lemmings' launcher, from START.TOS to the game
This page follows Lemmings' Gamex launcher, LEMLD2, from START.TOS to the game. The launcher loads GOS, a copy of TOS 1.04's GEMDOS that runs from RAM, and hands over to the game. The game then asks a small loader for each file by name, and GOS reads it by sectors through the PPDRIVER hard-disk driver. That is why the game needs a hard-disk driver (gemdrive-failure.md).
Read from the sources in src/, which reassemble to the original bytes, and checked against runs on the PPDRIVER reference disk. Names are the sources' labels. The file formats are in launcher-formats.md.
The stages and their sources
| stage | source |
|---|---|
START.TOS's stub, 230 bytes | src/start/start.s |
| LEMLD2, the launcher, with the block and the loader below | src/lemld2/lemld2.s |
| LS2DD, the game's own loader | src/ls2dd/ls2dd.s |
| D15R, which holds GOS (ST and STE build) | src/d15r/d15r_h5.s |
| D15R, Falcon build | src/d15r/d15r_f5.s |
The sources reassemble to the original files, so they are not in the repository: only their maps are (src/<stage>/<stage>.map, the names, comments and addresses). tools/make_sources.sh makes the sources from the maps and your own copy of the Lemmings download, unpacked into LEMMINGS/.
LEMLD2: the launch
startreads the TOS version, the RAM and the machine type. It stops with 512 KB.mainsetsphystopto$80000, so the game gets 512 KB and the top of RAM keeps TOS's state.info_screenandcovershow the machine, then the cover fromLEMMINGS.PCH.gamex_setupwrites the Gamex header at$FFF40(fields inGAMEXdoc.html).load_d15runpacksD15R_H5.FIC(D15R_F5.FICon a Falcon) to$C0100.save_low_ramkeeps TOS's low RAM at$D7008, for the return to the desktop.load_ls2ddreadsLS2DD.FICto$81000.set_pathscallsDsetpath("FILES").check_pp_driverlooks for the XBRA idPPHDbeforehdv_bpb(gemdrive-failure.md§3).- With PPDRIVER resident,
reuse_pp_drivercopies it (7,204 bytes) to$B7180. In the copy, D15R's floppy routines replace the three TOS vectors the driver chains to.set_buffersgives GOS four 8 KB buffers from$AF100. - Without it,
no_pp_driverreads sector 0 itself: by ACSI DMA on TOS 1, XBIOSDMAreadon TOS 2, IDE on a Falcon. If sector 0 is an executable boot sector,boot_driverruns it and keeps the vectors it sets. From a GEMDOS drive this path finds no disk and ends inerror_exit.
- With PPDRIVER resident,
start_d15rcalls D15R's init, copies the block to$80100and jumps to it.
The block at $80100: the hand-over
The block unpacks LS2DD to $70000 and copies the loader to $85000. It patches LS2DD, the intro and the game's code (MAIN, CODE), so that their floppy loading goes to the loader. CODE gets its own copy of the loader, at $E892. The block starts LS2DD, and start_game ($8014C) starts the game with jmp $400. patch_code ($801D0) also adds the level entry ("map 001-120") and other fixes.
The loader at $85000: how the game reads a file
The game calls the loader where it would read its floppy, with the file name at a0 and the destination at a1. The loader skips calls that are not files (disk-layer.md §4). It copies the 8.3 name into D15R's parameters at $C0752 and asks for 105,220 bytes, the size of MAIN, the largest file. Then it calls D15R's file_io ($C0170) with interrupts off.
D15R: how GOS reads the file
D15R is TOS 1.04 in RAM: its GEMDOS, BIOS and XBIOS, its floppy routines, and an image of TOS's low RAM ($8-$7FF). LEMLD2 replaces that image's hdv_* vectors, _bufl and _drvbits with the driver copy's.
file_io swaps the game's low RAM out and TOS's image in. It calls Fopen, Fseek, Fread (or Fwrite) and Fclose, then swaps back. GOS reads the FAT volume by sectors with BIOS Rwabs, through the driver copy (gemdrive-failure.md §2). The TOS in ROM takes no part. Neither does Hatari's GEMDOS-drive hook, which sits on the ROM's TRAP #1, not in TOS's image.
Other findings
- LS2DD is unchanged except for the patches above. It holds a protection, an ICE depacker and a floppy loader. The source keeps the code it decrypts at run time as stored bytes.
- On the Falcon, D15R_F5 and LEMLD2's
trap1_stub, theTRAP #1handler it gives a booting driver, read the function number at8(sp). The 68030's exception frame has a format word the 68000's lacks. GXUT260.PRGcarries LEMLD2's unpack routine and its PPDRIVER check (launcher-signatures.md).
Evidence and tools
src/probes/stages/chain_probe.py <setup>: a dump of every stage at its entry.src/probes/stages/profile_probe.py,profile_addrs.py: the addresses Hatari executed.tools/dev/mksource.pymakes each source from its map (src/<stage>/<stage>.map).tools/dev/all_harness.shreassembles all five and checks them with anopinserted.
The disk layer: D15R and the driver it uses
This page shows what sits between Lemmings and the disk when the game runs from a hard disk with PPDRIVER, the hard-disk driver the Gamex launcher requires. The game asks for files by name. D15R, the launcher's build of GOS, is a copy of TOS 1.04 that runs from RAM. Its GEMDOS reads the files by sectors through a copy of PPDRIVER. A GEMDOS drive, such as Hatari's or the Multidevice's GEMDRIVE, has no sectors, so this chain cannot work on one as it is.
Read from the sources (src/d15r/d15r_h5.s, src/lemld2/lemld2.s) and runs on ST TOS 1.04. LEMLD2 is the launcher's main program, which sets all this up (launch-chain.md).
1. D15R is TOS 1.04 in RAM
Aligned with the TOS 1.04 UK ROM, the 91,460 bytes of D15R_H5 sort into three kinds:
| bytes | count | what |
|---|---|---|
| the ROM's own | 54,640 (59.7%) | copied unchanged |
| relocated | 9,804 (10.7%) | a ROM address moved from $FC0000 to around $C0100, or a RAM address plus $D0528 |
| other | 27,016 (29.5%) | D15R's own code, its image of TOS's RAM, its edits to TOS |
So D15R is GOS as GAMEXdoc.html describes it, and more. It holds TOS 1.04's GEMDOS and also its BIOS, XBIOS, floppy driver, start-up code and data. Its own 1,852 bytes at $C0100 hold the entry table and file_io. From $D0528 it keeps an image of TOS's RAM: each variable sits at its TOS address plus $D0528. The Falcon build, D15R_F5, is the same, except that its GEMDOS entry, trap1, reads the function number past the 68030's format word.
2. D15R's entry points
D15R starts with a table of word offsets from $C0100:
| entry | offset | does |
|---|---|---|
swap_low_ram | $0010 | swaps $8-$7FF with the image: TOS's vectors in, the game's out, or back |
tos_paths | $0034 | sets the drive and path to LEMLD2's |
file_io | $0070 | loads or saves a whole file by name, for the launcher's loader |
init | $01BC | called once by LEMLD2 |
file_io_2 | $01CA | file_io for a second parameter set, never seen called (§4) |
file_io takes the file name, path, offset, length and buffer at $C0730. It swaps TOS's low RAM in, calls Fopen, Fseek, Fread or Fwrite, and Fclose through TRAP #1, and swaps the game's low RAM back.
D15R's GEMDOS reaches the disk through BIOS calls. Rwabs reads sectors. Getbpb returns the BPB, the block that describes a volume's layout. Mediach asks whether the disk changed. They go to the vectors hdv_rw, hdv_bpb and hdv_mediach, which LEMLD2 points at its copy of the driver.
3. What the launcher needs from the resident PPDRIVER
From reuse_pp_driver and set_buffers in src/lemld2/lemld2.s, checked against the memory dumps in gemdrive-failure.md §3:
- The XBRA id
PPHD8 bytes before the routinehdv_bpbpoints to. - 7,204 bytes from the XBRA header that still work when copied to
$B7180. hdv_rwandhdv_mediachin that block, found by their distance fromhdv_bpb.- Three slots holding TOS's own disk routines, which LEMLD2 replaces with D15R's floppy routines.
- GEMDOS buffer headers where LEMLD2 expects them. It builds four 8 KB buffers from
$AF100. pun_ptrand the cookie jar pointer, saved into the image as found.
The game then calls hdv_rw for every read, in 1,024-byte records on drive C, and hdv_mediach before each access. A replacement driver would have to provide all of this, laid out the same way.
4. Every call the game makes, from the hand-over to the level load
Run 20261011-182600-st-104-ppdriver-disk-calls on ST TOS 1.04 (PASS, 13/13):
| file | when | records the driver reads |
|---|---|---|
"FORM…" | not a file name: the loader skips it | none |
IntroPar, to $10000 | the intro | 263, 415 × 80, 495, 496 |
CODE, to $200 | the game | 497, 498, 499 × 76, 575 |
leveldat | the title screen | 263, 264, 677 × 2, 679, 680 |
level020 | after the click on "1 PLAYER" | 263, 407 × 2, 409 |
In all: 3 calls to the loader at $85000, 5 to file_io, none to file_io_2. The driver copy gets 16 calls to hdv_rw, 139 to hdv_mediach and none to hdv_bpb. Names arrive in mixed case, and D15R's GEMDOS matches them regardless, as TOS does.
5. For the solution
The game's requests reach GEMDOS as Fopen and Fread by name. Sectors come in only because that GEMDOS is D15R's own. A GEMDOS drive answers Fopen and Fread by name too, at the ROM's TRAP #1 ($FA002A). The GOS5 builds use this: they send GOS's file calls to the system's GEMDOS (gos5-gemdrive.md). The other options are weighed in solutions.md §4.
Tools
src/probes/stages/d15r_vs_tos.py [H5|F5]: the comparison in §1.src/probes/stages/disk_calls_probe.py [<setup>]anddisk_calls_report.py <run>: the calls in §4.
The launcher's file formats
The Gamex launcher's files for Lemmings use one packer, with a 32-byte header in front of each packed file. tools/unpack.py unpacks them all. Each result matched Hatari's memory at the start of that stage (runs tools/dev/logs/*-ppdriver-chain), apart from the few bytes written into it before it starts. LEMLD2 is the launcher's main program. text+ offsets are in its text section.
The packer
One routine, found in START.TOS's stub (text+$32) and in LEMLD2 (text+$B32). It reads a byte stream:
| control byte c | meaning |
|---|---|
$00 | end of stream |
$01-$7F | c literal bytes follow |
$80-$BF | one offset byte d: copy (c & $3F) + 3 bytes from d bytes back |
$C0-$FF | offset bytes hi, lo: copy (c & $3F) + 4 bytes from hi×256+lo back |
It is not ICE. Only the game's own FILES/CODE and FILES/MAIN are ICE-packed.
The packed-file header
The unpacker never reads these 32 bytes: the launcher skips them and knows the sizes in advance.
| offset | START.TOS payload | LS2DD.FIC | D15R_H5.FIC | D15R_F5.FIC |
|---|---|---|---|---|
| 0 | "C3" 00 00 | same | same | same |
| +2 long, packed size | 8,811 | 4,897 | 46,793 | 47,213 |
| +6 long, unpacked size | 35,230 | 7,168 | 91,460 | 91,512 |
| +10 long | $1B046, LEMLD2's BSS | $7A007C00 (open) | 0 | 0 |
| +16, name | LEMLD2.TOS | LS2DD | G558 | GF558 |
What each file unpacks to
START.TOSis a GEMDOS program: a 230-byte text (the stub), an 8,844-byte data section (the header above and the packed LEMLD2), BSS$11A14, no relocations. The stub (tools/dev/builds/stages/start_text.lst) copies the packed stream down to text+$8D40, copies its unpacker after it and jumps there. The unpacker writes LEMLD2 to text−28, so LEMLD2's 28-byte GEMDOS header lands in the last 28 bytes of the basepage and its text on the stub's own text. It then relocates LEMLD2 at that text base with the standard GEMDOS table, fills in the basepage's text, data and BSS fields, clears memory from the relocation table up to itself, and jumps to the text base.LEMLD2.TOSis 35,230 bytes: text 34,910, BSS$1B046, 177 relocations. Relocated, it equals Hatari's memory at its entry:$17A88on ST TOS 1.04,$17A68on STE TOS 1.62,$1D2E4on STE TOS 2.06.LS2DD.FICis the game's own loader, 7,168 bytes, run at$70000. The launcher patches 9 bytes before it starts (launch-chain.md). At$70A8Ethe jump to the game goes to the launcher first. At$70E38the floppy loader becomesjmp $85000, the launcher's loader.D15R_H5.FICandD15R_F5.FICare GOS, a copy of TOS 1.04 that runs from RAM (disk-layer.md). They are 91,460 and 91,512 bytes, run at$C0100, and differ from byte$6E9on.F5is the Falcon build. LEMLD2 and GEMDOS write 32 bytes intoD15R_H5before it starts: the drive ($C0735), the path\LEMMINGS\FILES($C0760), the driver copy's entries forhdv_bpb,hdv_rwandhdv_mediach($D099A,$D099E,$D09A6), its buffers in_bufl($D09DA,$D09DE), and drives A, B, C in_drvbits($D09ED).LEMMINGS.PCHis not packed. It is the picture LEMLD2 shows while it loads. A 6-byte header ($0140 $00C8, 320 × 200, then two flag bytes) is followed by two frames of a 32,000-byte screen and 19,232 bytes of colour data: 102,470 bytes in all.
SHA-256 of the unpacked files
dae36fff1a766766de0477e9d22e84794c1ff844e1caaaf0cc56a7d63d05d0b6 LEMLD2.TOS
aea82756c309e65d9cd5fb4ac71538c36d12bb5ec64eabc57e13f7ab916c85cf LS2DD.bin
53ef0628cc158898d288048356a4562b63ea44d4fcaaed3fbc96922d26b06fbc D15R_H5.bin
5f0c5b4ef69d3ee918e6b0f882e0d8e0c6c9f73c919f983c74df20ec4a6529c1 D15R_F5.bin
Signatures of the Gamex launcher, for the survey of the adaptations
A signature is a byte pattern that marks one piece of a program. This page lists the signatures of Lemmings' launcher, of the other modes, and of the ways a program reaches the disk. The survey of the adaptations looks for them in every game's files (recognising-modes.md). A hit is a place to look, not a proof: data can hold the same bytes, and a routine can be present and never run.
The pieces of Lemmings' launcher
Checked against Lemmings' files. Patterns are hex, ?? is a byte that varies, and offsets are in the file as stored. "Unpacked" means after tools/unpack.py. Several patterns survive the packer and can be found in the packed files directly.
| piece | pattern | found in |
|---|---|---|
START.TOS's unpacking stub | 4BFA FFFE 204D D1FC ?? ?? ?? ?? 2C4D DDFC ?? ?? ?? ?? 284E 243C ?? ?? ?? ?? 2D20 5382 66FA 264C 43FA 000C | START.TOS +$1C |
packed-file header (launcher-formats.md) | 4333 0000, then the packed and unpacked sizes | START.TOS +$102, LS2DD.FIC +0, D15R_H5.FIC +0 |
| the unpack routine | 4280 7806 7A3F 4240 1019 6B0E 670A 5340 18D9 51C8 FFFC 60EE | LEMLD2.TOS +$B4E, GXUT260.PRG +$2314; the stub's form, reading through a6, at START.TOS +$58 |
the PPDRIVER check | 2278 0472 0CA9 5050 4844 FFF8 (movea.l $472.w,a1; cmpi.l #'PPHD',-8(a1)) | START.TOS +$3F4 (packed), LEMLD2.TOS +$340, GXUT260.PRG +$14E6 |
| the requirement message | "This prg. requires PP" | START.TOS +$838, LEMLD2.TOS +$872 |
| the Gamex id | "PPGX" (5050 4758) | START.TOS +$D53, LEMLD2.TOS +$E50, GXUT260.PRG +$256 |
| D15R's entry table | 0010 0034 0070 01BC 01CA | D15R_H5.FIC +$21 (packed), both D15Rs unpacked +0 |
| GOS's GEMDOS entry | 302F 0006 B07C 000B 6E ?? B07C 0001 65 ?? 7000 4E73 (Falcon: 0008 for 0006) | D15R_H5 unpacked +$83A0, D15R_F5 +$83B8 |
| TOS's GEMDOS message | "*** OUT OF INTERNAL MEMORY" | D15R_H5.FIC +$AAB4 (packed), both D15Rs unpacked |
| the loader's call into D15R | 41F9 000C 0100 7000 3028 0004 D1C0 4E90 | LEMLD2.TOS +$99E |
PPDRIVER is the hard-disk driver the launcher requires. D15R is the launcher's build of GOS, a copy of TOS 1.04 that runs from RAM (disk-layer.md). LS2DD, the game's own loader, has no signature: it changes from game to game.
The survey reads them this way. The PPDRIVER check, with the message or the DMAread fallback, marks a launcher that cannot run from a GEMDOS drive as it is. The GOS patterns mark a game whose files GOS reads by sectors through the driver. The header and the stub mark files to unpack first.
Other modes
Checked (modes.md) on the files named, as stored or unpacked:
| mode | pattern | found in |
|---|---|---|
| HAGA family launcher | "C:\GAMEX\HAG" + A/E/2, and "Starting GOS ..." | RUNME.TOS of Wormhole, Quest for Galaxia, Breakout ACC, Vector Invaders |
| HAGA family module | starts 0100 then 15 more word offsets; "GSAU.TOS"; "FILES\D15H5.FIC" | HAGA, HAGE, HAG2 |
| HAGE / HAG2 | "T14AG.FIC" / "T14GG.FIC" | HAGE, HAG2 |
| GOS4/GOS5 launcher, driver checks | 2278 0472 0CA9 5050 4844 FFF8 (PPHD); 2278 0516 … 3F3C 002A 4E4E (pun_ptr, DMAread); 263C 444D 4172 ('DMAr') | unpacked START.TOS of Lemmings, Magnetik Tank, Clever & Smart, Bubble Dizzy; not in Garfield's or Castle Master's |
| GOS builds | packed-file header names: D15R_NG1.PRG, D15R_F12.PRG (GOS 1.00); D15R_H62.PRG, D15R_F69.TOS (GOS4); G552, GF552, G158, GF158 (GOS5); GH5R4, GF5R4 (5H/5F); D15RH.TOS, D15RF.TOS, D15RU.TOS (HAGA); D15I81.TOS (iTOS) | the most common version of each file |
| TOS in RAM | header names T14AGHnn.TOS (with GEM), T14Gnn.TOS (without) | T14AG.FIC, T14GG.FIC |
The PPHD check alone does not mark a game that fails. The HAGA modules have it too, and their games run from a GEMDOS drive. The launchers that fail have all three driver checks and load a GOS4 or GOS5 file.
Disk access
tools/survey/classify.py sorts the games by strategy with these patterns (recognising-modes.md). It looks at even offsets in a program's text section, after unpacking:
| access | pattern | what it is |
|---|---|---|
| GEMDOS file | 3F3C 003D 4E41, 3F3C 003F 4E41, 3F3C 004B 4E41; 703D 3F00 4E41, 703F 3F00 4E41 | Fopen, Fread, Pexec through TRAP #1 |
BIOS Rwabs | 3F3C 0004 4E4D | sectors through the driver |
XBIOS Floprd | 3F3C 0008 4E4E | floppy sectors |
XBIOS DMAread | 3F3C 002A 4E4E | ACSI sectors (TOS 2.x) |
| DMA and floppy-controller registers | FFFF 8604-FFFF 860D; 41F8…4BF8 8604/8606; 31FC xxxx 8604/8606 | the hardware itself: ACSI or the floppy controller |