Difference between revisions of "Prism Leisure Protection"

From Software Archive
Jump to navigation Jump to search
Line 13: Line 13:
 
== The signature track ==
 
== The signature track ==
  
−
* Track 41 has no sectors, headers or checksums. It contains the byte
+
* Track 41 has no sectors, headers or checksums. It contains the byte <code>$AC</code> repeated about 7,140 times per revolution.
−
  <code>$AC</code> repeated about 7,140 times per revolution.
+
* In GCR bits <code>$AC</code> is <code>10101100</code>. On the disk this appears as a repeating flux interval pattern of 2-2-1-3 bit cells.
−
* In GCR bits <code>$AC</code> is <code>10101100</code>. On the disk this
+
* The track is written at the bit rate of speed zone 2 (3.50&nbsp;µs per cell). This is not the zone the 1541 DOS would use for track 41.
−
  appears as a repeating flux interval pattern of 2-2-1-3 bit cells.
+
* No sync mark was found in the flux of any analyzed disk. The pattern simply runs once around the track.
−
* The track is written at the bit rate of speed zone 2 (3.50&nbsp;µs per
 
−
  cell). This is not the zone the 1541 DOS would use for track 41.
 
−
* No sync mark was found in the flux of any analyzed disk. The pattern
 
−
  simply runs once around the track.
 
  
 
<pre>
 
<pre>
Line 111: Line 107:
 
In short:
 
In short:
  
−
* The drive reads raw bytes from track 41 without looking for a sync or a
+
* The drive reads raw bytes from track 41 without looking for a sync or a header.
−
  header.
+
* It searches up to 255&nbsp;×&nbsp;256 bytes (about nine revolutions) for an <code>$AC</code>.
−
* It searches up to 255&nbsp;×&nbsp;256 bytes (about nine revolutions) for an
 
−
  <code>$AC</code>.
 
 
* After that, 256 bytes in a row must be <code>$AC</code>.
 
* After that, 256 bytes in a row must be <code>$AC</code>.
−
* The result goes to drive address <code>$05FF</code>: <code>$C2</code>
+
* The result goes to drive address <code>$05FF</code>: <code>$C2</code> means passed, <code>$00</code> means failed.
−
  means passed, <code>$00</code> means failed.
 
 
* A second job returns the head to track 18.
 
* A second job returns the head to track 18.
  
Line 181: Line 174:
  
 
* A 35-track copy or a D64 image loses track 41, and the check fails.
 
* A 35-track copy or a D64 image loses track 41, and the check fails.
−
* A track 41 that decodes as "almost <code>$AC</code>" fails too. The check
+
* A track 41 that decodes as "almost <code>$AC</code>" fails too. The check needs 256 correct bytes in a row, so a single shifted or lost bit in every few bytes is enough to break it.
−
  needs 256 correct bytes in a row, so a single shifted or lost bit in every
 
−
  few bytes is enough to break it.
 
 
* '''Back sides (flippy sides) need care when imaging:'''
 
* '''Back sides (flippy sides) need care when imaging:'''
−
** '''Recording direction.''' In a double-sided KryoFlux capture, head 1
+
** '''Recording direction.''' In a double-sided KryoFlux capture, head 1 reads the back side in reverse time order. This applies to track 41 as well as to tracks 1–35. Read without reversal, the <code>$AC</code> pattern looks like 3-1-2-2 instead of 2-2-1-3.
−
  reads the back side in reverse time order. This applies to track 41 as
+
** '''Signal distortion.''' The track 41 signal on back sides can be heavily peak-shifted. On ''Power-Pack'' source 7, Disk 1 back side, the measured intervals were 1.30 / 1.88 / 2.18 / 2.60 cells instead of 1 / 2 / 2 / 3. It then decodes cleanly only at exactly 3.50&nbsp;µs per cell.
−
  well as to tracks 1–35. Read without reversal, the <code>$AC</code>
 
−
  pattern looks like 3-1-2-2 instead of 2-2-1-3.
 
−
** '''Signal distortion.''' The track 41 signal on back sides can be heavily
 
−
  peak-shifted. On ''Power-Pack'' source 7, Disk 1 back side, the measured
 
−
  intervals were 1.30 / 1.88 / 2.18 / 2.60 cells instead of 1 / 2 / 2 / 3.
 
−
  It then decodes cleanly only at exactly 3.50&nbsp;µs per cell.
 
  
 
== See also ==
 
== See also ==

Revision as of 07:09, 10 October 2026

Prism Leisure Protection

Prism Leisure Protection is a Commodore 64 disk protection that checks for a data duplication track on track 41: a track outside the normal 35-track range that consists of one byte value, $AC, repeated around the whole track. A disk copied with a normal 35-track copier lacks this track, and the check fails.

Disk title in directory is modified to show in green color.

Used by Electric Dreams, Software Studios, Mirrorsoft Ltd., Thalamus

The signature track

  • Track 41 has no sectors, headers or checksums. It contains the byte $AC repeated about 7,140 times per revolution.
  • In GCR bits $AC is 10101100. On the disk this appears as a repeating flux interval pattern of 2-2-1-3 bit cells.
  • The track is written at the bit rate of speed zone 2 (3.50 µs per cell). This is not the zone the 1541 DOS would use for track 41.
  • No sync mark was found in the flux of any analyzed disk. The pattern simply runs once around the track.
 Track  1 ─────────────────────────────── 35   ordinary CBM DOS layout
 Track 36 ─ 40                                  unused
 Track 41   $AC $AC $AC $AC ... (whole track, about 7,140 bytes, zone 2)

The check

The loader uploads drive code. The drive code runs as an EXECUTE job ($E0) in buffer 3, so the DOS job loop first moves the head to track 41. Buffer 3 then starts with JMP $0530.

Captured from drive RAM at the moment the head is stepped to track 41:

; ---- job setup, called once for track 41 and once for track 18 ----
$0580  A9 29       LDA #$29          ; track 41
$0582  8D FD 05    STA $05FD
$0585  A9 30       LDA #$30          ; job code entry -> $0530 (the check)
$0587  8D 1C 05    STA $051C
$058A  20 03 05    JSR $0503
$058D  A9 12       LDA #$12          ; afterwards: track 18
$058F  8D FD 05    STA $05FD
$0592  A9 7A       LDA #$7A          ; job code entry -> $057A (no check,
$0594  8D 1C 05    STA $051C         ;   just puts the head back on 18)
$0597  20 03 05    JSR $0503
$059A  60          RTS

$0503  20 00 C1    JSR $C100         ; LED on
...
$050A  AD FD 05    LDA $05FD
$050D  85 80       STA $80           ; current track
$050F  85 51       STA $51
...
$0516  A9 4C       LDA #$4C          ; buffer 3: JMP $05xx
$0518  8D 00 06    STA $0600
$051B  A9 30       LDA #$30          ; (operand patched to $30 / $7A above)
$051D  8D 01 06    STA $0601
$0520  A9 05       LDA #$05
$0522  8D 02 06    STA $0602
$0525  A5 7F       LDA $7F
$0527  09 E0       ORA #$E0          ; EXECUTE job for buffer 3
$0529  85 03       STA $03
$052B  A5 03       LDA $03
$052D  30 FC       BMI $052B         ; wait for the job to finish
$052F  60          RTS

; ---- the check (runs on track 41) ----
$0530  AD 0C 1C    LDA $1C0C
$0533  09 E0       ORA #$E0          ; head to read mode
$0535  8D 0C 1C    STA $1C0C
$0538  A9 00       LDA #$00
$053A  8D 03 1C    STA $1C03         ; data port = input
$053D  A9 FF       LDA #$FF
$053F  8D FE 05    STA $05FE         ; search budget: 255 x 256 bytes
$0542  A0 00       LDY #$00
$0544  50 FE       BVC $0544         ; wait for byte ready
$0546  B8          CLV
$0547  AD 01 1C    LDA $1C01
$054A  C9 AC       CMP #$AC          ; search for the first $AC
$054C  F0 10       BEQ $055E
$054E  88          DEY
$054F  D0 F3       BNE $0544
$0551  CE FE 05    DEC $05FE
$0554  D0 EC       BNE $0542
$0556  A9 00       LDA #$00          ; FAILED: result $00
$0558  8D FF 05    STA $05FF
$055B  4C 96 FD    JMP $FD96

$055E  A2 00       LDX #$00          ; found: now 256 more bytes ...
$0560  50 FE       BVC $0560
$0562  B8          CLV
$0563  AD 01 1C    LDA $1C01
$0566  C9 AC       CMP #$AC          ; ... must all be $AC
$0568  D0 E4       BNE $054E         ; any other byte -> keep searching
$056A  88          DEY
$056B  D0 05       BNE $0572
$056D  CE FE 05    DEC $05FE
$0570  F0 E4       BEQ $0556         ; budget used up -> FAILED
$0572  CA          DEX
$0573  D0 EB       BNE $0560
$0575  A9 C2       LDA #$C2          ; PASSED: result $C2
$0577  8D FF 05    STA $05FF
$057A  4C 96 FD    JMP $FD96

In short:

  • The drive reads raw bytes from track 41 without looking for a sync or a header.
  • It searches up to 255 × 256 bytes (about nine revolutions) for an $AC.
  • After that, 256 bytes in a row must be $AC.
  • The result goes to drive address $05FF: $C2 means passed, $00 means failed.
  • A second job returns the head to track 18.

The C64 side

The C64 part is in a directory entry with an empty name, loaded to $F000 (1,280 bytes). It is copied to $C000 and entered with JMP $C0E0 (from $C475):

$C0E0  20 03 C0    JSR $C003         ; upload the drive code
$C0E3  20 B8 C0    JSR $C0B8         ; start it: "M-E" $0500
$C0E6  20 70 C0    JSR $C070         ; read the result: "M-R" $05FF -> $C0FF
$C0E9  A9 A6       LDA #$A6          ; build the expected value without
$C0EB  49 17       EOR #$17          ;   ever writing it as a constant:
$C0ED  49 98       EOR #$98          ;   $A6^$17^$98^$B6^$AD^$F0 = $C2
$C0EF  49 B6       EOR #$B6
$C0F1  49 AD       EOR #$AD
$C0F3  49 F0       EOR #$F0
$C0F5  CD FF C0    CMP $C0FF         ; compare with the byte read from the drive
$C0F8  F0 03       BEQ $C0FD
$C0FA  6C FC FF    JMP ($FFFC)       ; FAILED: reset the C64
$C0FD  4C 00 00    JMP $xx00         ; the high byte of this JMP IS $C0FF
  • Upload ($C003): the drive code is stored in C64 memory
 at $C100–$C1FF, XOR-encrypted with
 $05. That's the high byte of its drive address, which the
 upload loop uses as the key. It is sent with M-W in 32-byte
 chunks to drive $0500–$05FF and decrypted on the
 fly.
  • Read-back ($C070): the C64 opens the command channel,
 sends M-R $FF $05, and stores the single byte it
 gets back at $C0FF.
  • Decision: if the byte is not $C2, the loader jumps
 through the reset vector, and the machine restarts. This is the reset seen
 when the check fails.

The result byte is used three times, so patching only the BEQ or the reset jump doesn't crack the protection:

  1. as the comparison value at $C0F5;
  2. as the high byte of the jump target. $C0FF is also the
 operand of JMP $xx00 at $C0FD, so only the
 correct value $C2 continues at $C200;
  1. as the decryption key. The code at $C200 XORs page
 $C300 with $C0FF and then jumps to
 $C300:
$C200  78          SEI
$C201  A2 00       LDX #$00
$C203  BD 00 C3    LDA $C300,X
$C206  4D FF C0    EOR $C0FF         ; key = result byte ($C2)
$C209  9D 00 C3    STA $C300,X
$C20C  CA          DEX
$C20D  D0 F4       BNE $C203
$C20F  58          CLI
$C210  4C 00 C3    JMP $C300

Copying and imaging

  • A 35-track copy or a D64 image loses track 41, and the check fails.
  • A track 41 that decodes as "almost $AC" fails too. The check needs 256 correct bytes in a row, so a single shifted or lost bit in every few bytes is enough to break it.
  • Back sides (flippy sides) need care when imaging:
    • Recording direction. In a double-sided KryoFlux capture, head 1 reads the back side in reverse time order. This applies to track 41 as well as to tracks 1–35. Read without reversal, the $AC pattern looks like 3-1-2-2 instead of 2-2-1-3.
    • Signal distortion. The track 41 signal on back sides can be heavily peak-shifted. On Power-Pack source 7, Disk 1 back side, the measured intervals were 1.30 / 1.88 / 2.18 / 2.60 cells instead of 1 / 2 / 2 / 3. It then decodes cleanly only at exactly 3.50 µs per cell.

See also

Pages that refer to this protection