GMA92

From Software Archive
Revision as of 01:06, 29 August 2026 by Enigma (talk | contribs)
Jump to navigation Jump to search

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

  1. 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 same CMP/ROL
 technique used throughout the whole GMA family.
  1. The result is sent to the C64 over a raster-line-synced $DD00
 handshake and used as a single repeating-byte XOR key over almost the
 entire game, decrypted in place in one pass.
  1. 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.

Signature measurement: the shared drive-side routine

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.

See also