GMA92
Contents
Pages that refer to this protection
GMA92
GMA92 is a Commodore 64 disk copy protection scheme, and the last
confirmed generation of the "GMA" protection family with any archived
material — successor to GMA90 (no GMA91 material is archived on the
wiki at all; that page returns a 404). Only one title links to this
generation on the wiki: McDonaldland (Virgin Games). The loader chain
keeps the exact same family convention seen in every earlier generation: a
boot stub relocates itself over the KERNAL's page-2/3 RAM vector table,
then a second-stage loader uploads a signature-measurement program to 1541
drive RAM via M-W/M-E and triggers it on the
drive's own 6502.
GMA92 shows no deviation from GMA88 through GMA90 at all. Both of McDonaldland's independently-archived sources (a "quader" release and a separate "stefan_h/Virgin Games" dump) use the identical mechanism: measurement on track 38, consumed as a C64-side bulk XOR decrypt. This is the smallest generation surveyed in this project (one title, two sources) and, like GMA90, shows no exceptions of any kind — the check is genuine and working in every locally-archived copy.
Mechanism
- The drive seeks to track 38 (
$26), searches for the family's standard byte-aligned marker (69 xx xx xx A9), and measures ten raw sync-to-sync pulse-width samples there, folding them into a single byte via the sameCMP/ROLtechnique used throughout the whole GMA family. - The result is sent to the C64 over a raster-line-synced
$DD00handshake and used as a single repeating-byte XOR key over almost the entire game, decrypted in place in one pass. - On any genuine failure, the drive hangs permanently in an infinite self-loop — the same "hang, never produce wrong output" convention used throughout the whole GMA family.
The critical disk pattern
Tracks 1–35 are a completely ordinary CBM DOS layout; track 38 sits
outside the range any stock-formatted disk ever uses. Read directly off a
g64conv text dump of McDonaldland's own disk (live-confirmed
key $CB) — no live tracing required to get these numbers:
Track 1 ────────────────────────────────────────── 35
ordinary CBM DOS layout ($08 header /
$07 data blocks, correct checksums)
Track 38, raw sync sequence (McDonaldland, measured key = $CB):
15, 40,40,40,40, 36, 111, 176,176, 56,56, 176,56,176,176, ...
└── ordinary syncs ──┘ └─────────── the anomalous region ───────────┘
(10 raw samples the drive's pulse-width
measurement loop actually reads)
Discarding the first sample (36) and taking the second
(111) as the reference, the remaining eight
(176,176,56,56,176,56,176,176) compared against it via
CMP/ROL fold to exactly $CB,
matching this disk's live-confirmed key exactly — the same boot-free
static-derivation technique already validated across GMA89's and GMA90's
own disks reproduces GMA92's key just as cleanly, with the method entirely
unchanged across all three generations. The archiver's own notes for this
disk mention it "could be repaired from data of different rotations
sampled" due to read errors on some sectors — despite that, the signature
track itself reads back cleanly and consistently, and the second,
independently-dumped source reproduces the identical key from an entirely
different raw sample sequence (10 32 4E 4E 19 19 4E 19 4E 4E
vs. 12 35 52 52 19 19 52 19 52 52 — different absolute values,
same fold result), confirming the repair didn't compromise the measurement.
Captured live for McDonaldland via a breakpoint freeze on the drive's own
DRVTRK variable becoming 38 (no resume, immediate static
disassembly of drive RAM) — byte-for-byte identical to the shared
GMA85-90 signature-measurement program documented throughout this project:
; ---- 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
$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 sync-length result: $CB on McDonaldland
; ---- send the result byte to the C64 via VIA1 $1800, nibble-out ----
$035F 2C 00 18 BIT $1800
$0362 10 FB BPL $035F ; wait for IEC bus ready
$0364 A9 10 LDA #$10
$0366 8D 00 18 STA $1800
$0369 2C 00 18 BIT $1800
$036C 30 FB BMI $0369
$036E 8A TXA ; A = result byte
$036F 4A LSR A ; \ send high nibble
$0370 4A LSR A ; |
$0371 4A LSR A ; |
$0372 4A LSR A ; /
$0373 8D 00 18 STA $1800
$0376 0A ASL A
$0377 29 0F AND #$0F
$0379 8D 00 18 STA $1800
$037C 8A TXA
$037D 29 0F AND #$0F ; \ send low nibble
$037F 8D 00 18 STA $1800 ; |
$0382 0A ASL A ; |
$0383 29 0F AND #$0F ; |
$0385 8D 00 18 STA $1800 ; /
$0388 A9 0F LDA #$0F
$038A EA NOP
$038B 8D 00 18 STA $1800 ; final bus state - no further timing-pad
; NOPs on this build (matches
; Ghostbusters II's GMA89 build,
; not Arac/Pipe Mania's 4-NOP variant)
$038E A9 01 LDA #$01
$0390 4C 69 F9 JMP $F969
; ---- job dispatch (M-E entry point): target track 38, submit EXECUTE job ----
$0393 A9 26 LDA #$26 ; $26 = 38 decimal - THE TARGET TRACK
$0395 85 06 STA $06
$0397 A9 01 LDA #$01
$0399 85 07 STA $07
$039B 20 18 C1 JSR $C118
$039E A9 E0 LDA #$E0
$03A0 85 00 STA $00
$03A2 A5 00 LDA $00
$03A4 30 FC BMI $03A2
$03A6 C9 02 CMP #$02
$03A8 90 06 BCC $03B0
$03AA 4C AA 03 JMP $03AA ; failure -> infinite self-loop, family convention
$03AD 20 2C C1 JSR $C12C
$03B0 60 RTS ; plain RTS - no second-stage upload, C64-side only
Note the timing-pad detail this capture settles for GMA92 specifically:
McDonaldland's build sends the final nibble with only a single
NOP before the bus-state write, matching Ghostbusters II's
GMA89 build rather than the four-NOP variant seen in GMA88's
Arac and GMA90's Pipe Mania — a cosmetic, per-build difference with no
effect on the measurement itself, present across every generation surveyed
without correlating to anything else about the disk.
Relationship to GMA90
GMA92 continues GMA88/89/90's architecture completely unbroken: track 38, the identical shared drive-side signature-measurement routine, C64-side bulk-decrypt consumption. With GMA91 unarchived and nothing observed past GMA92 anywhere in this project's survey, this is the last confirmed data point in the GMA family as currently documented on the wiki — closing out a span of eight generations (GMA85 through GMA92, minus the unarchived GMA91) that begins as a simple presence-only check and ends, unchanged for its final four generations running, as a fully consolidated single mechanism.