Difference between revisions of "Para Protect"

From Software Archive
Jump to navigation Jump to search
 
Line 1: Line 1:
−
Data sector on track 40.
+
= Para Protect =
 +
 
 +
This analysis is based on the state of the wiki from 3 Oct 2026.
 +
 
 +
'''Para Protect''' is a Commodore 64 disk protection found on 47 game and
 +
compilation pages of this wiki, among them several Elite compilations
 +
(''Best of Elite'', ''Hit Pak'', ''6*Pak'', ''Fist 'n' Throttles''). The disks announce it
 +
openly: the boot file is usually called <code>PROTECTED BY PARA…</code>, or
 +
the directory carries "PARA-PROTECT" art.
 +
 
 +
In short: '''one 256-byte block of the program is stored on track 40,
 +
sector 14''' rather than on tracks 1–35. The Para fast loader fetches that
 +
block as an ordinary part of loading the game. A copy made with a
 +
35-track copier doesn't have the block, so the loader waits for it forever.
 +
 
 +
This is not a signature check in the usual sense. Nothing read from
 +
track 40 is compared against a constant, and there is no compare-and-branch
 +
to patch out. The track-40 block is simply missing code or data the game
 +
needs (start-up code, the next loader stage, or a data block), so a copy
 +
can't be fixed without knowing what belongs there.
 +
 
 +
== Disk layout ==
 +
 
 +
<pre>
 +
Track  1 ─────────────────────────────────────── 35
 +
          ordinary CBM DOS layout, the visible file is
 +
          the Para boot (load address $00AE)
 +
 
 +
Track 36-39  unused / unformatted
 +
 
 +
Track 40      normally formatted CBM track:
 +
                $08 headers / $07 data blocks, valid
 +
                checksums, same disk ID as tracks 1-35
 +
              sector 14      program block  <-- read by the loader
 +
              sector 15, 16  further program blocks (multi-load
 +
                              disks only, one per load entry)
 +
              all others    zero-filled
 +
</pre>
 +
 
 +
* The track-40 sectors themselves are completely standard. There is no sync manipulation, bad checksum, or non-standard GCR. The protection relies only on the track lying outside the 35 tracks that copiers and D64 images normally cover.
 +
* Some masters format only the payload sectors: Deliverance and Hellfire Attack have only sectors 14–16 on track 40; P47 Thunderbolt, Xenophobe and ''Six Appeal'' Disk 2 have only sector 14.
 +
* The payload differs from title to title; no two titles share one. Examples:
 +
** Deliverance: start-up code (<code>SEI / LDA #$35 / STA $01 …</code>), loaded to <code>$C000</code> and jumped to.
 +
** American Indoor Soccer: the next loader stage, loaded to <code>$6000</code> during boot.
 +
* Where sector 14 differs between dumps of the same game, the cause is a different build of the game, not disk damage; checksums are valid in all cases:
 +
** Hypaball: 3 bytes differ.
 +
** Empire!: 3 bytes differ.
 +
** After Burner (European version): a 9-byte code change.
 +
* On Elite compilation disks, sectors 14, 15 and (where present) 16 belong to the different games on the disk.
 +
 
 +
== Boot ==
 +
 
 +
The boot file is the recognisable part of the scheme:
 +
 
 +
* It is the only real file on the disk side, and it '''loads to <code>$00AE</code>''', which is the KERNAL's own load-address pointer.
 +
* The first bytes loaded overwrite <code>$AE/$AF</code> while the LOAD is still running. That redirects the rest of the file through zero page, the stack page and the <code>$0300</code> vector table.
 +
* <code>ISTOP</code> is redirected to <code>$00ED</code>, so Para code is already running before the KERNAL <code>LOAD"*",8,1</code> returns, so no <code>RUN</code> is needed.
 +
 
 +
The boot then uploads drive code. From then on the C64 requests blocks by
 +
track/sector over a 2-bit <code>$DD00</code>/<code>$1800</code> protocol
 +
with raster-timed handshakes.
 +
 
 +
== Two families ==
 +
 
 +
Every Para disk examined reads '''track 40, sector 14 first'''. The
 +
differences lie in the loader around that read. There are two code bases:
 +
 
 +
{| class="wikitable"
 +
! Family !! Boot !! Track-40 read !! Titles
 +
|-
 +
| '''A''' || <code>$00AE</code> boot, 508–1584 bytes; per-title tables and data on a common code base || custom reader running as a DOS <code>$E0</code> (EXECUTE) job || all titles except family B (94 sides, including all Elite compilations)
 +
|-
 +
| '''B''' || <code>$00AE</code> boot, always 843 bytes; different code base (about 25% byte similarity to family A) || stock DOS <code>$80</code> (READ) job into buffer 1 (<code>$0400</code>) || Hypaball, Empire!, Bobby Bearing, The Sentinel, Druid (14 sides; the 1986–87 Odin, Firebird and Softek titles)
 +
|}
 +
 
 +
=== Family A: the drive-side reader ===
 +
 
 +
The C64 sends four bytes to the drive program at <code>$0703</code>: block
 +
count, track, sector, and a follow-up command. The drive program puts
 +
these into <code>$C1</code>/<code>$0C</code>/<code>$0D</code> and submits an
 +
'''EXECUTE job for buffer 3'''. The stock ROM job loop therefore does the
 +
head stepping to the requested track. The job code at <code>$0600</code>
 +
then reads the sector with its own loop, not the ROM's.
 +
 
 +
Captured from drive RAM at the moment the head steps to track 40
 +
(Thundercats; <code>$0C</code>=40, <code>$0D</code>=14, <code>$C1</code>=1):
 +
 
 +
<pre>
 +
; ---- drive main loop: receive request, run the job ----
 +
$0716  20 5B 07    JSR $075B        ; receive byte (2-bit $1800 protocol)
 +
$0719  85 C1      STA $C1          ; number of blocks
 +
$071B  20 5B 07    JSR $075B
 +
$071E  85 0C      STA $0C          ; track  (job 3 header table entry)
 +
$0720  20 5B 07    JSR $075B
 +
$0723  85 0D      STA $0D          ; sector
 +
$0725  20 5B 07    JSR $075B
 +
$0728  48          PHA              ; command after the transfer
 +
$0729  58          CLI
 +
$072A  A9 E0      LDA #$E0
 +
$072C  85 03      STA $03          ; job 3 = EXECUTE -> ROM seeks, then JMP $0600
 +
$072E  A5 03      LDA $03
 +
$0730  30 FC      BMI $072E        ; wait for job end
 +
$0732  C9 7F      CMP #$7F
 +
$0734  90 F4      BCC $072A        ; any other status -> simply run it again
 +
$0736  A5 0C      LDA $0C
 +
$0738  C9 24      CMP #$24
 +
$073A  90 0C      BCC $0748        ; ended on a track >= 36 (i.e. track 40):
 +
$073C  A9 12      LDA #$12        ;  put the head back on track 18
 +
$073E  85 0C      STA $0C          ;  with an ordinary DOS READ job
 +
$0740  A9 80      LDA #$80
 +
$0742  85 03      STA $03
 +
...
 +
$0748  68          PLA
 +
$0749  C9 01      CMP #$01        ; 1 = wait for the next request
 +
$074B  F0 BC      BEQ $0709
 +
$074D  C9 02      CMP #$02        ; 2 = park, hold the bus
 +
$074F  F0 03      BEQ $0754
 +
$0751  6C FC FF    JMP ($FFFC)      ; anything else = reset the drive
 +
 
 +
; ---- EXECUTE job, buffer 3 ----
 +
$0600  A9 03      LDA #$03
 +
$0602  85 31      STA $31          ; buffer pointer -> $0300
 +
$0604  A5 12      LDA $12          ; build the wanted header:
 +
$0606  85 16      STA $16          ;  ID1, ID2 (from the disk's own ID)
 +
$0608  A5 13      LDA $13
 +
$060A  85 17      STA $17
 +
$060C  A5 0C      LDA $0C
 +
$060E  85 18      STA $18          ;  track
 +
$0610  85 22      STA $22
 +
$0612  A5 0D      LDA $0D
 +
$0614  85 19      STA $19          ;  sector
 +
$0616  A9 00      LDA #$00
 +
$0618  45 16      EOR $16
 +
$061A  45 17      EOR $17
 +
$061C  45 18      EOR $18
 +
$061E  45 19      EOR $19
 +
$0620  85 1A      STA $1A          ;  header checksum
 +
$0622  20 34 F9    JSR $F934        ; ROM: GCR-encode header -> $24-$2B
 +
$0625  20 A8 06    JSR $06A8        ; wait for sync, Y=0
 +
$0628  50 FE      BVC $0628
 +
$062A  B8          CLV
 +
$062B  AD 01 1C    LDA $1C01
 +
$062E  D9 24 00    CMP $0024,Y      ; compare 8 GCR header bytes
 +
$0631  D0 F2      BNE $0625        ; mismatch -> next sync, NO retry limit
 +
$0633  C8          INY
 +
$0634  C0 08      CPY #$08
 +
$0636  D0 F0      BNE $0628
 +
$0638  20 A8 06    JSR $06A8        ; header found: sync of the data block
 +
...                                ; read 256 + 70 raw GCR bytes
 +
$0654  20 E0 F8    JSR $F8E0        ; ROM: GCR-decode the data block
 +
$0657  B9 00 03    LDA $0300,Y
 +
$065A  20 B4 06    JSR $06B4        ; send byte to C64 (2 bits per $1800 write)
 +
$065D  C8          INY
 +
$065E  D0 F7      BNE $0657
 +
$0660  A5 0C      LDA $0C          ; next sector: interleave 8
 +
$0662  20 4B F2    JSR $F24B        ;  (ROM: speed zone of the track)
 +
$0665  BD FF 06    LDA $06FF,X      ;  sectors per track
 +
...                                ; on wrap: INC $0C and step the head
 +
$068C  A9 96      LDA #$96        ;  itself (timer $96 per half-track)
 +
...
 +
$069C  C6 C1      DEC $C1          ; more blocks?
 +
$069E  D0 05      BNE $06A5
 +
$06A0  A9 7F      LDA #$7F
 +
$06A2  4C 69 F9    JMP $F969        ; job end, status $7F
 +
$06A5  4C 04 06    JMP $0604
 +
 
 +
$06A8  2C 00 1C    BIT $1C00        ; sync wait
 +
$06AB  30 FB      BMI $06A8
 +
</pre>
 +
 
 +
The header search at <code>$0625</code>–<code>$0636</code> has no retry
 +
counter. On a disk without track 40 the head steps out to track 40 and the
 +
job loops through <code>$06A8</code>/<code>$06AB</code> forever, waiting
 +
for a header that doesn't exist. No error is reported, and the C64 sits in
 +
its receive loop. Once the track-40 block has been read, the drive moves
 +
the head back to track 18 with an ordinary DOS READ job.
 +
 
 +
=== Family A: reader variants ===
 +
 
 +
There are five versions of the drive reader, all with the same request
 +
protocol and the same track-40 read. Three are one-byte or one-instruction
 +
edits of the standard reader; two are a re-implementation in buffer 2.
 +
 
 +
{| class="wikitable"
 +
! Variant !! Difference to the standard reader !! Titles
 +
|-
 +
| '''A-std''' || — || American Indoor Soccer, Rick Dangerous 2, Deliverance, Thundercats, Winter Olympiad 88 (Tynesoft), Superstar Ice Hockey, Rimrunner, SDI, Heatseeker, Hellfire Attack, Overlander, Buggy Boy, Gazza II, Pac-Land, Stormlord, Vixen, Track & Field, The Last Ninja, Kinetik, Star Wars, Evening Star (side 1), Galactic Games, Xenophobe, MicroProse Soccer, She-Fox, The Fury, Troll, Beyond the Ice Palace, ''Soccer Stars'' Disk 2, all Elite compilations
 +
|-
 +
| '''A-interleave1''' || 1 byte: sector step <code>ADC #$01</code> instead of <code>ADC #$08</code>, so consecutive sectors are read. The game reads 3 blocks, track 40 sectors 14–16, in one request. || F-16 Combat Pilot
 +
|-
 +
| '''A-fixedT35''' || 1 instruction: the sectors-per-track lookup uses a fixed <code>LDA #$23</code> (track 35) instead of the current track. Makes no difference on track 40. || P47 Thunderbolt, ''Six Appeal'' Disk 2 (= P47)
 +
|-
 +
| '''A-buf2''' || Re-implemented: job in buffer 2 (<code>$0500</code>), parameters in <code>$0A/$0B</code>, header built with ROM <code>$F6D0</code>. Interleave 8 and step timer <code>#$96</code> as in A-std. || Bangkok Knights
 +
|-
 +
| '''A-buf2-interleave1''' || As A-buf2, plus interleave 1 with a hard 21-sector wrap (<code>CMP #$15</code>) and step timer <code>#$94</code> || After Burner
 +
|}
 +
 
 +
=== Family A: when track 40 is read ===
 +
 
 +
Family A disks read the track-40 block at one of two points. This is
 +
independent of which reader variant they use.
 +
 
 +
* '''During boot.''' The zero-page boot loader fetches its next stage from track 40, sector 14 (e.g. American Indoor Soccer, Thundercats). The read happens 2–40 seconds into the boot, so a copy hangs while still loading.
 +
* '''At game start.''' The boot holds a title screen or menu and a table-driven loader (<code>LDA $0335,X / $033C,X / $0343,X / $034A,X</code> = destination page, block count, track, sector). Each menu entry pairs the main game body with '''one start-up block on track 40'''. In Deliverance, entries 1, 2 and 3 load track 40, sectors 14, 15 and 16 to <code>$C000</code>, followed by <code>JMP $C000</code>. The Elite compilations work the same way per game. A copy shows the title screen and menu normally and hangs only after the key press or menu selection (seen on The Last Ninja, Stormlord, Bangkok Knights and ''Best of Elite'' Vol. 1 disk 1A).
 +
 
 +
=== Family B ===
 +
 
 +
Family B also reads track 40, sector 14, but uses an
 +
'''unmodified DOS <code>$80</code> READ job in buffer 1'''. The drive's
 +
job queue at the moment of the seek shows <code>$01 = $80</code> with
 +
header table entry track 40, sector 14. The stock ROM reader with its
 +
normal checksum handling does the read; there is no custom GCR code.
 +
What a copy without track 40 does in family B was not determined. In the
 +
test run, both the original and the 35-track copy ended in a machine reset
 +
in the emulator.
 +
 
 +
=== Elite <code>MENU</code> boot ===
 +
 
 +
Elite's compilation disks (''Best of Elite'' Vol. 1/2, ''Hit Pak Top 10'',
 +
''6*Pak'' Vol. 3 Disk 2, ''Fist 'n' Throttles'') boot a <code>MENU</code>
 +
file (load address <code>$0326</code>, "PARA-PROTECT" directory art)
 +
instead of loading the <code>$00AE</code> boot directly. The selected
 +
game's family-A Para boot is then loaded from the menu, so these disks use
 +
the A-std reader.
 +
 
 +
== Copying ==
 +
 
 +
* Track 40 holds only standard, checksum-valid sectors. There are no sync tricks, unusual densities or deliberate errors, so a copy only has to include track 40.
 +
* A 35-track copy or D64 image loses the block. Family A then hangs with the drive head on track 40 (see the reader above). Depending on the title, that happens during loading or only after the title-screen key press.
 +
* Bangkok Knights' page notes that the check fails on a 1541-II; under VICE it was traced with the drive type set to 1541.
 +
 
 +
== Verification on this wiki ==
 +
 
 +
All 122 disk sides on this wiki listed as Para Protect (90 sources) were
 +
checked. Each side was checked statically from its G64 and by tracing
 +
under VICE with true drive emulation, with a watchpoint on the drive
 +
moving the head to track 40.
 +
 
 +
* '''All 108 bootable Para sides''' have a checksum-valid track 40 with the payload in sector 14 (plus 15/16 where the loader table asks for them).
 +
* All 108 were confirmed to read it:
 +
** 104 by stopping the emulation at the track-40 job, either on that side or on a side with a byte-identical boot file.
 +
** 4 by booting a 35-track copy next to the original: the original loads, and the copy stops in the reader's sync wait for track 40, sector 14.
 +
* The remaining 14 sides are data sides without a boot of their own, loaded by the protected side's loader. They have no track 40 and need none.
 +
* '''Frank Bruno's Boxing''' is a special case:
 +
** Side 1 carries the Para directory art and the same side-2 Para boot as ''Best of Elite'' Vol. 1 disk 2B.
 +
** The game itself boots from side 0, which has no Para code. It loads  side 1 by filename through the KERNAL (<code>"BOXER n"</code>,  <code>"ANIMB n"</code>), so track 40 is never read.
 +
** Neither the Frank Bruno side 1 stream nor the ''Best of Elite'' 2B side contains a track 40.
 +
** The Para label on this release is nominal.
  
 
==== Pages that refer to this protection ====
 
==== Pages that refer to this protection ====
  
 
{{Special:Whatlinkshere/Para Protect}}
 
{{Special:Whatlinkshere/Para Protect}}

Latest revision as of 01:38, 4 October 2026

Para Protect

This analysis is based on the state of the wiki from 3 Oct 2026.

Para Protect is a Commodore 64 disk protection found on 47 game and compilation pages of this wiki, among them several Elite compilations (Best of Elite, Hit Pak, 6*Pak, Fist 'n' Throttles). The disks announce it openly: the boot file is usually called PROTECTED BY PARA…, or the directory carries "PARA-PROTECT" art.

In short: one 256-byte block of the program is stored on track 40, sector 14 rather than on tracks 1–35. The Para fast loader fetches that block as an ordinary part of loading the game. A copy made with a 35-track copier doesn't have the block, so the loader waits for it forever.

This is not a signature check in the usual sense. Nothing read from track 40 is compared against a constant, and there is no compare-and-branch to patch out. The track-40 block is simply missing code or data the game needs (start-up code, the next loader stage, or a data block), so a copy can't be fixed without knowing what belongs there.

Disk layout

 Track  1 ─────────────────────────────────────── 35
          ordinary CBM DOS layout, the visible file is
          the Para boot (load address $00AE)

 Track 36-39   unused / unformatted

 Track 40      normally formatted CBM track:
                 $08 headers / $07 data blocks, valid
                 checksums, same disk ID as tracks 1-35
               sector 14      program block  <-- read by the loader
               sector 15, 16  further program blocks (multi-load
                              disks only, one per load entry)
               all others     zero-filled
  • The track-40 sectors themselves are completely standard. There is no sync manipulation, bad checksum, or non-standard GCR. The protection relies only on the track lying outside the 35 tracks that copiers and D64 images normally cover.
  • Some masters format only the payload sectors: Deliverance and Hellfire Attack have only sectors 14–16 on track 40; P47 Thunderbolt, Xenophobe and Six Appeal Disk 2 have only sector 14.
  • The payload differs from title to title; no two titles share one. Examples:
    • Deliverance: start-up code (SEI / LDA #$35 / STA $01 …), loaded to $C000 and jumped to.
    • American Indoor Soccer: the next loader stage, loaded to $6000 during boot.
  • Where sector 14 differs between dumps of the same game, the cause is a different build of the game, not disk damage; checksums are valid in all cases:
    • Hypaball: 3 bytes differ.
    • Empire!: 3 bytes differ.
    • After Burner (European version): a 9-byte code change.
  • On Elite compilation disks, sectors 14, 15 and (where present) 16 belong to the different games on the disk.

Boot

The boot file is the recognisable part of the scheme:

  • It is the only real file on the disk side, and it loads to $00AE, which is the KERNAL's own load-address pointer.
  • The first bytes loaded overwrite $AE/$AF while the LOAD is still running. That redirects the rest of the file through zero page, the stack page and the $0300 vector table.
  • ISTOP is redirected to $00ED, so Para code is already running before the KERNAL LOAD"*",8,1 returns, so no RUN is needed.

The boot then uploads drive code. From then on the C64 requests blocks by track/sector over a 2-bit $DD00/$1800 protocol with raster-timed handshakes.

Two families

Every Para disk examined reads track 40, sector 14 first. The differences lie in the loader around that read. There are two code bases:

Family Boot Track-40 read Titles
A $00AE boot, 508–1584 bytes; per-title tables and data on a common code base custom reader running as a DOS $E0 (EXECUTE) job all titles except family B (94 sides, including all Elite compilations)
B $00AE boot, always 843 bytes; different code base (about 25% byte similarity to family A) stock DOS $80 (READ) job into buffer 1 ($0400) Hypaball, Empire!, Bobby Bearing, The Sentinel, Druid (14 sides; the 1986–87 Odin, Firebird and Softek titles)

Family A: the drive-side reader

The C64 sends four bytes to the drive program at $0703: block count, track, sector, and a follow-up command. The drive program puts these into $C1/$0C/$0D and submits an EXECUTE job for buffer 3. The stock ROM job loop therefore does the head stepping to the requested track. The job code at $0600 then reads the sector with its own loop, not the ROM's.

Captured from drive RAM at the moment the head steps to track 40 (Thundercats; $0C=40, $0D=14, $C1=1):

; ---- drive main loop: receive request, run the job ----
$0716  20 5B 07    JSR $075B        ; receive byte (2-bit $1800 protocol)
$0719  85 C1       STA $C1          ; number of blocks
$071B  20 5B 07    JSR $075B
$071E  85 0C       STA $0C          ; track   (job 3 header table entry)
$0720  20 5B 07    JSR $075B
$0723  85 0D       STA $0D          ; sector
$0725  20 5B 07    JSR $075B
$0728  48          PHA              ; command after the transfer
$0729  58          CLI
$072A  A9 E0       LDA #$E0
$072C  85 03       STA $03          ; job 3 = EXECUTE -> ROM seeks, then JMP $0600
$072E  A5 03       LDA $03
$0730  30 FC       BMI $072E        ; wait for job end
$0732  C9 7F       CMP #$7F
$0734  90 F4       BCC $072A        ; any other status -> simply run it again
$0736  A5 0C       LDA $0C
$0738  C9 24       CMP #$24
$073A  90 0C       BCC $0748        ; ended on a track >= 36 (i.e. track 40):
$073C  A9 12       LDA #$12         ;   put the head back on track 18
$073E  85 0C       STA $0C          ;   with an ordinary DOS READ job
$0740  A9 80       LDA #$80
$0742  85 03       STA $03
...
$0748  68          PLA
$0749  C9 01       CMP #$01         ; 1 = wait for the next request
$074B  F0 BC       BEQ $0709
$074D  C9 02       CMP #$02         ; 2 = park, hold the bus
$074F  F0 03       BEQ $0754
$0751  6C FC FF    JMP ($FFFC)      ; anything else = reset the drive

; ---- EXECUTE job, buffer 3 ----
$0600  A9 03       LDA #$03
$0602  85 31       STA $31          ; buffer pointer -> $0300
$0604  A5 12       LDA $12          ; build the wanted header:
$0606  85 16       STA $16          ;   ID1, ID2 (from the disk's own ID)
$0608  A5 13       LDA $13
$060A  85 17       STA $17
$060C  A5 0C       LDA $0C
$060E  85 18       STA $18          ;   track
$0610  85 22       STA $22
$0612  A5 0D       LDA $0D
$0614  85 19       STA $19          ;   sector
$0616  A9 00       LDA #$00
$0618  45 16       EOR $16
$061A  45 17       EOR $17
$061C  45 18       EOR $18
$061E  45 19       EOR $19
$0620  85 1A       STA $1A          ;   header checksum
$0622  20 34 F9    JSR $F934        ; ROM: GCR-encode header -> $24-$2B
$0625  20 A8 06    JSR $06A8        ; wait for sync, Y=0
$0628  50 FE       BVC $0628
$062A  B8          CLV
$062B  AD 01 1C    LDA $1C01
$062E  D9 24 00    CMP $0024,Y      ; compare 8 GCR header bytes
$0631  D0 F2       BNE $0625        ; mismatch -> next sync, NO retry limit
$0633  C8          INY
$0634  C0 08       CPY #$08
$0636  D0 F0       BNE $0628
$0638  20 A8 06    JSR $06A8        ; header found: sync of the data block
...                                 ; read 256 + 70 raw GCR bytes
$0654  20 E0 F8    JSR $F8E0        ; ROM: GCR-decode the data block
$0657  B9 00 03    LDA $0300,Y
$065A  20 B4 06    JSR $06B4        ; send byte to C64 (2 bits per $1800 write)
$065D  C8          INY
$065E  D0 F7       BNE $0657
$0660  A5 0C       LDA $0C          ; next sector: interleave 8
$0662  20 4B F2    JSR $F24B        ;   (ROM: speed zone of the track)
$0665  BD FF 06    LDA $06FF,X      ;   sectors per track
...                                 ; on wrap: INC $0C and step the head
$068C  A9 96       LDA #$96         ;   itself (timer $96 per half-track)
...
$069C  C6 C1       DEC $C1          ; more blocks?
$069E  D0 05       BNE $06A5
$06A0  A9 7F       LDA #$7F
$06A2  4C 69 F9    JMP $F969        ; job end, status $7F
$06A5  4C 04 06    JMP $0604

$06A8  2C 00 1C    BIT $1C00        ; sync wait
$06AB  30 FB       BMI $06A8

The header search at $0625–$0636 has no retry counter. On a disk without track 40 the head steps out to track 40 and the job loops through $06A8/$06AB forever, waiting for a header that doesn't exist. No error is reported, and the C64 sits in its receive loop. Once the track-40 block has been read, the drive moves the head back to track 18 with an ordinary DOS READ job.

Family A: reader variants

There are five versions of the drive reader, all with the same request protocol and the same track-40 read. Three are one-byte or one-instruction edits of the standard reader; two are a re-implementation in buffer 2.

Variant Difference to the standard reader Titles
A-std — American Indoor Soccer, Rick Dangerous 2, Deliverance, Thundercats, Winter Olympiad 88 (Tynesoft), Superstar Ice Hockey, Rimrunner, SDI, Heatseeker, Hellfire Attack, Overlander, Buggy Boy, Gazza II, Pac-Land, Stormlord, Vixen, Track & Field, The Last Ninja, Kinetik, Star Wars, Evening Star (side 1), Galactic Games, Xenophobe, MicroProse Soccer, She-Fox, The Fury, Troll, Beyond the Ice Palace, Soccer Stars Disk 2, all Elite compilations
A-interleave1 1 byte: sector step ADC #$01 instead of ADC #$08, so consecutive sectors are read. The game reads 3 blocks, track 40 sectors 14–16, in one request. F-16 Combat Pilot
A-fixedT35 1 instruction: the sectors-per-track lookup uses a fixed LDA #$23 (track 35) instead of the current track. Makes no difference on track 40. P47 Thunderbolt, Six Appeal Disk 2 (= P47)
A-buf2 Re-implemented: job in buffer 2 ($0500), parameters in $0A/$0B, header built with ROM $F6D0. Interleave 8 and step timer #$96 as in A-std. Bangkok Knights
A-buf2-interleave1 As A-buf2, plus interleave 1 with a hard 21-sector wrap (CMP #$15) and step timer #$94 After Burner

Family A: when track 40 is read

Family A disks read the track-40 block at one of two points. This is independent of which reader variant they use.

  • During boot. The zero-page boot loader fetches its next stage from track 40, sector 14 (e.g. American Indoor Soccer, Thundercats). The read happens 2–40 seconds into the boot, so a copy hangs while still loading.
  • At game start. The boot holds a title screen or menu and a table-driven loader (LDA $0335,X / $033C,X / $0343,X / $034A,X = destination page, block count, track, sector). Each menu entry pairs the main game body with one start-up block on track 40. In Deliverance, entries 1, 2 and 3 load track 40, sectors 14, 15 and 16 to $C000, followed by JMP $C000. The Elite compilations work the same way per game. A copy shows the title screen and menu normally and hangs only after the key press or menu selection (seen on The Last Ninja, Stormlord, Bangkok Knights and Best of Elite Vol. 1 disk 1A).

Family B

Family B also reads track 40, sector 14, but uses an unmodified DOS $80 READ job in buffer 1. The drive's job queue at the moment of the seek shows $01 = $80 with header table entry track 40, sector 14. The stock ROM reader with its normal checksum handling does the read; there is no custom GCR code. What a copy without track 40 does in family B was not determined. In the test run, both the original and the 35-track copy ended in a machine reset in the emulator.

Elite MENU boot

Elite's compilation disks (Best of Elite Vol. 1/2, Hit Pak Top 10, 6*Pak Vol. 3 Disk 2, Fist 'n' Throttles) boot a MENU file (load address $0326, "PARA-PROTECT" directory art) instead of loading the $00AE boot directly. The selected game's family-A Para boot is then loaded from the menu, so these disks use the A-std reader.

Copying

  • Track 40 holds only standard, checksum-valid sectors. There are no sync tricks, unusual densities or deliberate errors, so a copy only has to include track 40.
  • A 35-track copy or D64 image loses the block. Family A then hangs with the drive head on track 40 (see the reader above). Depending on the title, that happens during loading or only after the title-screen key press.
  • Bangkok Knights' page notes that the check fails on a 1541-II; under VICE it was traced with the drive type set to 1541.

Verification on this wiki

All 122 disk sides on this wiki listed as Para Protect (90 sources) were checked. Each side was checked statically from its G64 and by tracing under VICE with true drive emulation, with a watchpoint on the drive moving the head to track 40.

  • All 108 bootable Para sides have a checksum-valid track 40 with the payload in sector 14 (plus 15/16 where the loader table asks for them).
  • All 108 were confirmed to read it:
    • 104 by stopping the emulation at the track-40 job, either on that side or on a side with a byte-identical boot file.
    • 4 by booting a 35-track copy next to the original: the original loads, and the copy stops in the reader's sync wait for track 40, sector 14.
  • The remaining 14 sides are data sides without a boot of their own, loaded by the protected side's loader. They have no track 40 and need none.
  • Frank Bruno's Boxing is a special case:
    • Side 1 carries the Para directory art and the same side-2 Para boot as Best of Elite Vol. 1 disk 2B.
    • The game itself boots from side 0, which has no Para code. It loads side 1 by filename through the KERNAL ("BOXER n", "ANIMB n"), so track 40 is never read.
    • Neither the Frank Bruno side 1 stream nor the Best of Elite 2B side contains a track 40.
    • The Para label on this release is nominal.

Pages that refer to this protection