Difference between revisions of "GMA90"
(Created page with "Descriptions needs to be done. ==== Pages that refer to this protection ==== {{Special:Whatlinkshere/GMA90}}") |
|||
| Line 1: | Line 1: | ||
| − | |||
| − | |||
==== Pages that refer to this protection ==== | ==== Pages that refer to this protection ==== | ||
{{Special:Whatlinkshere/GMA90}} | {{Special:Whatlinkshere/GMA90}} | ||
| + | |||
| + | = 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 <code>M-W</code>/<code>M-E</code> 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 == | ||
| + | |||
| + | # The drive seeks to '''track 38''' (<code>$26</code>), searches for the family's standard byte-aligned marker (<code>69 xx xx xx A9</code>), and measures ten raw sync-to-sync pulse-width samples there, folding them into a single byte via the same <code>CMP</code>/<code>ROL</code> technique used throughout the whole GMA family. | ||
| + | # The result is sent to the C64 over a raster-line-synced <code>$DD00</code> handshake 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 | ||
| + | <code>g64conv</code> text dump of a genuine GMA90 disk (Pipe Mania, | ||
| + | live-confirmed key <code>$1B</code>) — no live tracing required to get | ||
| + | these numbers: | ||
| + | |||
| + | <pre> | ||
| + | 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) | ||
| + | </pre> | ||
| + | |||
| + | Discarding the first sample (<code>38</code>) and taking the second | ||
| + | (<code>111</code>) as the reference, the remaining eight | ||
| + | (<code>56,56,56,176,176,56,176,176</code>) compared against it via | ||
| + | <code>CMP</code>/<code>ROL</code> fold to exactly '''<code>$1B</code>''', | ||
| + | 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 <code>a</code>, | ||
| + | <code>b</code>, <code>c</code>, <code>d</code>, <code>gm2</code>, plus a | ||
| + | <code>gmaread.src.mod</code> 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 | ||
| + | <code>DRVTRK</code> 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: | ||
| + | |||
| + | <pre> | ||
| + | ; ---- 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 | ||
| + | </pre> | ||
| + | |||
| + | The routine's structure, the retry budget, the sample-fold algorithm, and | ||
| + | even the exact byte-for-byte timing-pad variant (four extra <code>NOP</code>s | ||
| + | after the nibble-send, matching GMA88's Arac build rather than | ||
| + | Ghostbusters II's zero-<code>NOP</code> 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 == | ||
| + | |||
| + | * [[GMA89]] | ||
| + | * [[GMA92]] | ||
Latest revision as of 01:02, 29 August 2026
Contents
Pages that refer to this protection
- Pipe Mania (← links)
- Descriptions of old disk copy protections (← links)
- GMA89 (← links)
- Ninja Spirit (← links)
- Cloud Kingdoms (← links)
- Galaxy Force (← links)
- GMA92 (← links)
- Hammerfist (← links)
- Weird Dreams (← links)
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
- 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 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.
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.