GMA90

From Software Archive
Revision as of 01:02, 29 August 2026 by Enigma (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Pages that refer to this protection

GMA90

GMA90 is a Commodore 64 disk copy protection scheme used across a small run of 1990 releases from Empire, Activision, Millennium, and other publishers, and the sixth confirmed generation of the "GMA" protection family — successor to GMA89, predecessor to GMA92 (no GMA91 material is archived on the wiki). The loader chain keeps the same family convention: 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.

GMA90 shows no deviation from GMA88/89's architecture at all — and, for the six titles surveyed here, no exceptions of any kind. Every title uses the identical mechanism: measurement on track 38, consumed as a C64-side bulk XOR decrypt. Unlike GMA89, which turned up two titles with no functioning check whatsoever, every GMA90 title traced in this survey has a genuine, working, live-confirmed protection check.

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.
  2. 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.
  3. 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 a genuine GMA90 disk (Pipe Mania, live-confirmed key $1B) — 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 (Pipe Mania, measured key = $1B):

     36, 40,40,40,40,   38, 111, 56,56,56, 176,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 (38) and taking the second (111) as the reference, the remaining eight (56,56,56,176,176,56,176,176) compared against it via CMP/ROL fold to exactly $1B, matching this disk's live-confirmed key exactly — the same boot-free static-derivation technique validated across GMA89's own disks reproduces GMA90's keys just as cleanly, with no changes needed anywhere in the method.

A note on directory appearance, since it can mislead: Pipe Mania's own directory is fully legible (individually-named files a, b, c, d, gm2, plus a gmaread.src.mod entry) rather than the near-empty, obfuscated style seen on most earlier-generation GMA disks — the same surface appearance GMA89's confirmed Hollywood Collection crack has. On its own, directory legibility is not a reliable signal of a cracked disk: this specific image was independently live-traced and produces a genuine, correctly-folding measurement on track 38 (confirmed above), so whatever this build's directory convention is, the protection check itself is fully intact and functioning. Directory obfuscation style appears to vary by release/distribution group, not by protection status.

Signature measurement: the shared drive-side routine

Captured live for Pipe Mania 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-89 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: $1B on Pipe Mania

; ---- 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
$038E  EA          NOP                 ; Pipe Mania has 4 further NOPs here
$038F  EA          NOP                 ;   (build-specific timing variant,
$0390  EA          NOP                 ;   same as Arac's GMA88 build)
$0391  EA          NOP
$0392  A9 01       LDA #$01
$0394  4C 69 F9    JMP $F969

; ---- job dispatch (M-E entry point): target track 38, submit EXECUTE job ----
$0397  A9 26       LDA #$26            ; $26 = 38 decimal - THE TARGET TRACK
$0399  85 06       STA $06
$039B  A9 01       LDA #$01
$039D  85 07       STA $07
$039F  20 18 C1    JSR $C118
$03A2  A9 E0       LDA #$E0
$03A4  85 00       STA $00
$03A6  A5 00       LDA $00
$03A8  30 FC       BMI $03A6
$03AA  C9 02       CMP #$02
$03AC  90 06       BCC $03B4
$03AE  4C AE 03    JMP $03AE           ; failure -> infinite self-loop, family convention
$03B1  20 2C C1    JSR $C12C
$03B4  60          RTS                 ; plain RTS - no second-stage upload, C64-side only

The routine's structure, the retry budget, the sample-fold algorithm, and even the exact byte-for-byte timing-pad variant (four extra NOPs after the nibble-send, matching GMA88's Arac build rather than Ghostbusters II's zero-NOP build) are unchanged from every earlier generation surveyed in this project.

Relationship to GMA89 and GMA92

GMA90 continues GMA88/89's architecture completely unbroken: track 38, the identical shared drive-side signature-measurement routine, C64-side bulk-decrypt consumption. Where GMA89 introduced the first confirmed cases of a wiki-tagged GMA title with no functioning check at all, GMA90's six surveyed titles show no such exceptions — every one has a genuine, working, independently-verified key. GMA92, the last generation with any archived material, continues the identical mechanism for its own single surveyed title.

See also