GMA86

From Software Archive
Revision as of 02:51, 28 August 2026 by Enigma (talk | contribs)
Jump to navigation Jump to search

Pages that refer to this protection

GMA86

GMA86 is a Commodore 64 disk copy protection scheme spanning a run of 1986 releases from Firebird, Domark, Alligata, Hewson, Robtek, Thalamus and others, and the middle generation of the "GMA" protection family — successor to GMA85, predecessor to GMA87. It shares that family's naming convention: each disk's BAM label carries a build-date stamp (gma<DDMMYY>, e.g. gma140486 = 14 Apr 1986 for Robin of the Wood), and the loader chain keeps the same file-naming pattern used throughout the family (a boot stub → gm1/gma1 second-stage loader → game data files).

Where GMA85 never derives a key at all, and GMA87's surveyed titles converge on one architecture (a C64-side bulk decrypt, with Eagles as a single confirmed drive-side exception), GMA86 is the generation where a real, disk-specific decryption key first becomes the norm — but it is not architecturally uniform. Of eleven surveyed titles, seven use a C64-side bulk decrypt, three use a drive-side per-sector decrypt otherwise thought unique to Eagles, and one carries the ancestral GMA85 presence-only check forward unchanged, with no key at all. All three mechanisms share the exact same underlying drive-upload plumbing and sync-length measurement primitive; what differs is only what happens to the measured byte afterward, and which out-of-range track it's read from.

Mechanism

Every GMA86 title uploads a short machine-code program to the 1541's own drive RAM over the serial bus (the standard DOS M-W/M-E commands), and has the drive itself seek an out-of-range track, search for the family's standard byte-aligned marker (69 xx xx xx A9, not decoded to the usual 5-bit GCR nibble boundaries), and — for two of the three mechanisms below — measure ten raw sync-to-sync pulse-width samples there, folding them into a single byte via the same CMP/ROL technique documented for GMA87's Cholo and Triaxos. Which out-of-range track is targeted correlates exactly with which of the three mechanisms a given title uses:

  1. Track 38 ($26) — C64-side bulk decrypt. The
 measured byte is sent to the C64 over a raster-synced $DD00
 handshake and used as a single repeating-byte XOR key over almost the
 entire game, decrypted in place in one pass before jumping straight into
 the result. Seven of eleven surveyed titles: Sanxion, Split Personalities,
 Cyrus II Chess, The Sentry, Filemaster 64, Pub Games, and The Graphic
 Adventure Creator.
  1. Track 39 ($27) — drive-side per-sector decrypt.
 The measured byte is instead baked as a literal EOR #imm
 operand into a second, freshly-uploaded drive-resident program, and the
 1541's own 6502 decrypts every subsequent sector before it ever reaches
 the C64, streaming the result out over a bit-banged serial link one byte
 at a time. Three of eleven surveyed titles, all Hewson-published: Firelord,
 Uridium, and Iridis Alpha — matching GMA87's Eagles almost byte-for-byte,
 including the identical $035C/$035D
 key-injection address.
  1. Track 37 ($25) — presence-only, no key. The
 drive reads two raw 256-byte blocks off the track and reports success
 unconditionally; nothing is ever folded, compared, or sent. This is the
 ancestral GMA85 mechanism, carried forward unmodified. One of eleven
 surveyed titles: Robin of the Wood, whose disk-label date
 (gma140486) places it firmly within the GMA86 era despite
 running GMA85-generation drive code.

On any genuine failure — marker not found within the retry budget, or a failed job status — the drive hangs permanently in an infinite self-loop rather than reporting an error the C64 could act on, the same "hang, never produce wrong output" convention used throughout the whole GMA family.

Signature measurement: the shared drive-side routine

Confirmed byte-for-byte identical (Firelord, Uridium, Iridis Alpha) or structurally identical down to the exact same offsets (the C64-side titles, confirmed point-by-point against Cholo's own upload routine) to the mechanism already documented in detail for GMA87. The only things that vary between tracks 38 and 39 are the literal target-track constant and, for the drive-side variant, what happens to the result afterward (see below):

; ---- sync-marker search, 90-attempt retry budget ----
$0300  AD 00 1C    LDA $1C00
$0303  29 9F       AND #$9F
$0305  8D 00 1C    STA $1C00
$0308  A0 5A       LDY #$5A            ; Y = 90 - sync-retry budget
$030A  88          DEY                 ; <-- retry entry point
$030B  D0 05       BNE $0312
$030D  A9 02       LDA #$02            ; error $02 = HEADER NOT FOUND
$030F  4C 69 F9    JMP $F969           ; ERRR - retries exhausted, report failure
$0312  2C 00 1C    BIT $1C00
$0315  30 FB       BMI $0312           ; sync-wait poll
$0317  AD 01 1C    LDA $1C01           ; discard first raw byte after sync
$031A  B8          CLV
$031B  A2 04       LDX #$04
$031D  50 FE       BVC $031D           ; CLV/BVC byte-ready wait (classic 1541 trick)
$031F  B8          CLV
$0320  AD 01 1C    LDA $1C01           ; read raw GCR byte
$0323  9D 00 05    STA $0500,X         ; store into $0500-$0504 (5 bytes)
$0326  CA          DEX
$0327  10 F4       BPL $031D
$0329  C9 A9       CMP #$A9            ; last byte read must be $A9
$032B  D0 DD       BNE $030A           ; mismatch -> retry
$032D  AD 04 05    LDA $0504           ; first byte read (2nd overall)
$0330  C9 69       CMP #$69            ; must be $69
$0332  D0 D6       BNE $030A           ; mismatch -> retry
                                        ; signature "69 xx xx xx A9" confirmed

; ---- 10-sample raw pulse-width (sync-length) measurement ----
$0334  A0 00       LDY #$00
$0336  2C 00 1C    BIT $1C00
$0339  30 FB       BMI $0336           ; wait for next sync
$033B  A2 00       LDX #$00
$033D  E8          INX                 ; <-- pulse-width measurement loop
$033E  2C 00 1C    BIT $1C00
$0341  10 FA       BPL $033D           ; count iterations (X) while bit7=0
$0343  8A          TXA
$0344  99 00 05    STA $0500,Y         ; store sample[Y]
$0347  C8          INY
$0348  C0 0A       CPY #$0A            ; 10 samples total
$034A  D0 EA       BNE $0336

; ---- fold 10 samples into an 8-bit result via CMP/ROL ----
$034C  A2 02       LDX #$02            ; sample[0] discarded, sample[1] = reference
$034E  BD 00 05    LDA $0500,X
$0351  CD 01 05    CMP $0501           ; compare sample[X] to reference
$0354  2E 0A 05    ROL $050A           ; roll carry (>=ref->1, <ref->0) into result byte
$0357  E8          INX
$0358  E0 0A       CPX #$0A            ; samples[2..9], 8 comparisons -> 8-bit result
$035A  D0 F2       BNE $034E
$035C  AE 0A 05    LDX $050A           ; X = computed 8-bit sync-length result

; ---- job setup: target track, submit EXECUTE job, check result ----
        LDA #<track>                   ; $26 (38) for the C64-side variant,
        STA $06                        ;   $27 (39) for the drive-side variant
        ...
        JMP <infinite self-loop>       ; on failure status - hang forever

The remainder — how the result byte is used — is where the three GMA86 mechanisms diverge.

Variant A: C64-side bulk decrypt

The measured byte is sent to the C64 the same way GMA87's Cholo/Triaxos do it: a raster-line-synced read of CIA2's $DD00, decoded through a small set of fixed lookup tables, landing in A at the return of a hijacked GETIN vector. The main loader stashes it (PHA) immediately after the upload-and-execute call, recovers it later (PLA/TAX), and feeds it into a self-modifying XOR loop that walks upward through memory decrypting the game in place before jumping straight into the result — confirmed byte-for-byte structurally identical (just different target addresses and end-page constants) across all seven C64-side titles.

The Graphic Adventure Creator's build (target track $26 = 38, confirmed directly) is the cleanest captured example:

$401F  JSR $C800     ; the fixed drive-upload-and-execute trigger
$4022  PHA           ; stash the measured key
$4023  LDA #$00 / JSR $42AB
$4028  PLA           ; recover it
$4029  TAX           ; X = $C3 (this disk's measured key)
$402A  LDA $01 / PHA / AND #$FE / STA $01   ; bank out BASIC ROM
$4031  TXA / EOR $C900 / STA $C900          ; self-modifying decrypt
$4038  INC $4033 / INC $4036 / BNE $4031
$4040  INC $4034 / INC $4037
       LDA $4034 / CMP #$C9 / BNE $4031
$404D  PLA / STA $01                        ; restore memory config
$4050  ...                                  ; standard KERNAL cold-start-ish sequence

Variant B: drive-side per-sector decrypt

Firelord, Uridium, and Iridis Alpha instead move the decrypt onto the 1541 itself, matching GMA87's Eagles almost line-for-line: once the signature measurement above completes, a second program is uploaded into drive RAM, overwriting the first. It contains a GCR sector-read loop, the standard DOS ROM's own GCRBIN/CHKBLK header-verification calls, and — at the identical $035C/$035D address in every confirmed example — the measured byte baked directly in as an EOR #imm immediate operand, decrypting each data byte as it's read and streaming the already-decrypted result to the C64 over the bit-banged serial link. Confirmed live for Uridium (measured key $97):

$0359  BD 00 06    LDA $0600,Y   ; raw (still-encrypted) byte off the sector buffer
$035C  49 97       EOR #$97      ; the measured sync-length byte, baked in as a
                                  ;   literal immediate operand at upload time
$035E  AA          TAX           ; decrypted byte ready to send/store
       ...

Because the key is injected directly into freshly-uploaded drive code rather than sent as data, a hunt for EOR #<key> (49 xx) across drive RAM after the upload reliably locates this instruction — the same technique used to confirm all three drive-side titles here and Eagles in GMA87. All three drive-side GMA86 titles are Hewson-published, matching Eagles' own publisher; the C64-side titles found in this survey are Domark, Alligata, Firebird, Ariolasoft, Robtek, and Thalamus releases — a suggestive publisher-level correlation, though not established as a rule (Firelord's own publisher wasn't independently confirmed, and no non-Hewson drive-side counterexample was specifically sought).

Variant C: presence-only, no key

Robin of the Wood's disk-label date places it within the GMA86 era, but its drive-side program is confirmed byte-for-byte identical to the GMA85 generation's: target track 37, two raw 256-byte blocks read and discarded, success reported unconditionally. Confirmed experimentally by corrupting the track's surrounding "salt" bytes while preserving the marker — no change in behavior. No sync-length measurement of any kind occurs on this title's out-of-range track. See GMA85 for the full annotated disassembly of this mechanism, including the C64-side loader's $02/EOR #$97 check some (but not all) titles of that generation carry — a plausible code-integrity tripwire against a patched-out upload call, not a disk-content validation, and not specific to Robin of the Wood; it belongs to the inherited GMA85 loader, not to anything introduced in GMA86.

Relationship to GMA85 and GMA87

GMA86 sits directly between the two: it inherits GMA85's presence-only mechanism wholesale (still shipping on at least one GMA86-labeled title), and it establishes — a full year before GMA87 — both real-key architectures GMA87 titles use, including the drive-side variant later found in Eagles and, until this survey, thought to be an isolated architectural outlier within the whole GMA family. It isn't: the drive-side decrypt is an established technique in this author's toolkit that predates Eagles by at least a year, reused essentially unchanged.

See also