SidecarTridge logo markSidecarTridge Atari ST hard-disk adaptations, from GEMDOS and GEMDRIVE ← Back
C:\FINDINGS.HTM

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 $FA0000 serves the drive through TRAP #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 → bufferwhat it is
65, 81, 263–264 → $B1100–$B5100the root, LEMMINGS and FILES directories
33 → $AF100FAT copy 2
387–396 → $60000, 397–406 → $68000the two logos, PSYGLOGO.IMG and DMALOGO.IMG
813–915 → $A00MAIN (105,220 bytes)
415–496 → $10000INTROPAR (83,362 bytes)
497–575 → $A00CODE (80,639 bytes), over MAIN
677–680LEVELDAT, at the title
407–409 → $41800LEVEL020, 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)$FFFC42800fallback, raw ACSI
ST 1.04 PPDRIVER$12960"PPHD"$12FECprimary
STE 1.62 GEMDRIVE$E01914 (ROM)$FFFC42800fallback, raw ACSI
STE 1.62 PPDRIVER$129A4"PPHD"$13030primary
STE 2.06 GEMDRIVE$E05210 (ROM)$FFFC42800fallback, XBIOS DMAread
STE 2.06 PPDRIVER$14DC4"PPHD"$15450primary

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, $476 and $47E ($B718C, $B71C2, $B75BE), sets $47A to $C1F3E (D15R) and clears pun_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 handwhat happened next
nothingthe error message
sector 0 = the image's PPDRIVER boot sectorthe launcher goes into D15R. TOS returns 0 for Getbpb(2), and the run crashes
+ Getbpb(2) = PPDRIVER's BPBTOS returns -15 (unknown device) for Rwabs, and the run crashes
+ Mediach(2) = 0, Rwabs 65 and 81 from the imageD15R 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:

  1. A resident driver with the PPHD header before hdv_bpb, laid out like PPDRIVER. The launcher patches its copy at +$9, +$3F, +$43B, +$495..+$49D and +$771..+$78B.
  2. Failing that, a boot sector 0 that can be read raw. Even then D15R is not initialised.
  3. BIOS Getbpb(2) returning PPDRIVER's BPB (§4).
  4. BIOS Mediach(2) returning 0 before each read.
  5. BIOS Rwabs on drive 2, serving the whole FAT volume.
  6. Fixed memory: the driver copy at $B7180, its buffers at $AF100–$B5100, D15R at $C0100, LS2DD at $70000, MAIN and CODE at $A00.
  7. TRAP #1 for D15R from the first Rwabs on. 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 PPHD check patched out, the run stalls on lower-case file names: refuted. It stops on Rwabs returning -15 (§4).
  • The loader hooks TRAP #1 only for Malloc and Mshrink: not at the first Rwabs, when D15R's handler owns it (§3).
  • On TOS 2.06 the loader works through whatever driver is installed: refuted. It needs PUN_INFO and 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.TOS only unpacks the rest, and the .FIC files are packed code: consistent with the runs (launcher-formats.md).

7. Evidence and tools

  • The reference: run 20261011-144024-st-104-ppdriver-first-level, script tools/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-gate and the other …-gate runs, from src/probes/gemdrive-failure/gate_probe.py <setup> <disk>. read_probe.py <run> prints a run's chain.
  • The driver copy: runs …-st-104-ppdriver-gate and …-drv.
  • The answers by hand: src/probes/gemdrive-failure/fallback_probe.py and serve_rwabs.py, runs …-gemdrive-fallback and …-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

stagesource
START.TOS's stub, 230 bytessrc/start/start.s
LEMLD2, the launcher, with the block and the loader belowsrc/lemld2/lemld2.s
LS2DD, the game's own loadersrc/ls2dd/ls2dd.s
D15R, which holds GOS (ST and STE build)src/d15r/d15r_h5.s
D15R, Falcon buildsrc/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

  1. start reads the TOS version, the RAM and the machine type. It stops with 512 KB.
  2. main sets phystop to $80000, so the game gets 512 KB and the top of RAM keeps TOS's state.
  3. info_screen and cover show the machine, then the cover from LEMMINGS.PCH.
  4. gamex_setup writes the Gamex header at $FFF40 (fields in GAMEXdoc.html).
  5. load_d15r unpacks D15R_H5.FIC (D15R_F5.FIC on a Falcon) to $C0100. save_low_ram keeps TOS's low RAM at $D7008, for the return to the desktop.
  6. load_ls2dd reads LS2DD.FIC to $81000. set_paths calls Dsetpath("FILES").
  7. check_pp_driver looks for the XBRA id PPHD before hdv_bpb (gemdrive-failure.md §3).
    • With PPDRIVER resident, reuse_pp_driver copies it (7,204 bytes) to $B7180. In the copy, D15R's floppy routines replace the three TOS vectors the driver chains to. set_buffers gives GOS four 8 KB buffers from $AF100.
    • Without it, no_pp_driver reads sector 0 itself: by ACSI DMA on TOS 1, XBIOS DMAread on TOS 2, IDE on a Falcon. If sector 0 is an executable boot sector, boot_driver runs it and keeps the vectors it sets. From a GEMDOS drive this path finds no disk and ends in error_exit.
  8. start_d15r calls D15R's init, copies the block to $80100 and 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, the TRAP #1 handler it gives a booting driver, read the function number at 8(sp). The 68030's exception frame has a format word the 68000's lacks.
  • GXUT260.PRG carries 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.py makes each source from its map (src/<stage>/<stage>.map). tools/dev/all_harness.sh reassembles all five and checks them with a nop inserted.

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:

bytescountwhat
the ROM's own54,640 (59.7%)copied unchanged
relocated9,804 (10.7%)a ROM address moved from $FC0000 to around $C0100, or a RAM address plus $D0528
other27,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:

entryoffsetdoes
swap_low_ram$0010swaps $8-$7FF with the image: TOS's vectors in, the game's out, or back
tos_paths$0034sets the drive and path to LEMLD2's
file_io$0070loads or saves a whole file by name, for the launcher's loader
init$01BCcalled once by LEMLD2
file_io_2$01CAfile_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:

  1. The XBRA id PPHD 8 bytes before the routine hdv_bpb points to.
  2. 7,204 bytes from the XBRA header that still work when copied to $B7180.
  3. hdv_rw and hdv_mediach in that block, found by their distance from hdv_bpb.
  4. Three slots holding TOS's own disk routines, which LEMLD2 replaces with D15R's floppy routines.
  5. GEMDOS buffer headers where LEMLD2 expects them. It builds four 8 KB buffers from $AF100.
  6. pun_ptr and 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):

filewhenrecords the driver reads
"FORM…"not a file name: the loader skips itnone
IntroPar, to $10000the intro263, 415 × 80, 495, 496
CODE, to $200the game497, 498, 499 × 76, 575
leveldatthe title screen263, 264, 677 × 2, 679, 680
level020after 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>] and disk_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 cmeaning
$00end of stream
$01-$7Fc literal bytes follow
$80-$BFone offset byte d: copy (c & $3F) + 3 bytes from d bytes back
$C0-$FFoffset 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.

offsetSTART.TOS payloadLS2DD.FICD15R_H5.FICD15R_F5.FIC
0"C3" 00 00samesamesame
+2 long, packed size8,8114,89746,79347,213
+6 long, unpacked size35,2307,16891,46091,512
+10 long$1B046, LEMLD2's BSS$7A007C00 (open)00
+16, nameLEMLD2.TOSLS2DDG558GF558

What each file unpacks to

  • START.TOS is 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.TOS is 35,230 bytes: text 34,910, BSS $1B046, 177 relocations. Relocated, it equals Hatari's memory at its entry: $17A88 on ST TOS 1.04, $17A68 on STE TOS 1.62, $1D2E4 on STE TOS 2.06.
  • LS2DD.FIC is the game's own loader, 7,168 bytes, run at $70000. The launcher patches 9 bytes before it starts (launch-chain.md). At $70A8E the jump to the game goes to the launcher first. At $70E38 the floppy loader becomes jmp $85000, the launcher's loader.
  • D15R_H5.FIC and D15R_F5.FIC are 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 $6E9 on. F5 is the Falcon build. LEMLD2 and GEMDOS write 32 bytes into D15R_H5 before it starts: the drive ($C0735), the path \LEMMINGS\FILES ($C0760), the driver copy's entries for hdv_bpb, hdv_rw and hdv_mediach ($D099A, $D099E, $D09A6), its buffers in _bufl ($D09DA, $D09DE), and drives A, B, C in _drvbits ($D09ED).
  • LEMMINGS.PCH is 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.

piecepatternfound in
START.TOS's unpacking stub4BFA FFFE 204D D1FC ?? ?? ?? ?? 2C4D DDFC ?? ?? ?? ?? 284E 243C ?? ?? ?? ?? 2D20 5382 66FA 264C 43FA 000CSTART.TOS +$1C
packed-file header (launcher-formats.md)4333 0000, then the packed and unpacked sizesSTART.TOS +$102, LS2DD.FIC +0, D15R_H5.FIC +0
the unpack routine4280 7806 7A3F 4240 1019 6B0E 670A 5340 18D9 51C8 FFFC 60EELEMLD2.TOS +$B4E, GXUT260.PRG +$2314; the stub's form, reading through a6, at START.TOS +$58
the PPDRIVER check2278 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 table0010 0034 0070 01BC 01CAD15R_H5.FIC +$21 (packed), both D15Rs unpacked +0
GOS's GEMDOS entry302F 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 D15R41F9 000C 0100 7000 3028 0004 D1C0 4E90LEMLD2.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:

modepatternfound 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 modulestarts 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 checks2278 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 buildspacked-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 RAMheader 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:

accesspatternwhat it is
GEMDOS file3F3C 003D 4E41, 3F3C 003F 4E41, 3F3C 004B 4E41; 703D 3F00 4E41, 703F 3F00 4E41Fopen, Fread, Pexec through TRAP #1
BIOS Rwabs3F3C 0004 4E4Dsectors through the driver
XBIOS Floprd3F3C 0008 4E4Efloppy sectors
XBIOS DMAread3F3C 002A 4E4EACSI sectors (TOS 2.x)
DMA and floppy-controller registersFFFF 8604-FFFF 860D; 41F8…4BF8 8604/8606; 31FC xxxx 8604/8606the hardware itself: ACSI or the floppy controller