Rainbow Arts Track 37 Protection

From Software Archive
Revision as of 21:56, 9 November 2026 by Enigma (talk | contribs) (Description of the Rainbow Arts Track 37 Protection (Turrican))
Jump to navigation Jump to search

A disk protection used by Rainbow Arts on Turrican (1990). The drive code checks a signature on track 37: a track with a single sync, followed by a long run of bytes that read as $55. The check only passes because the first byte comes from the input latch of the drive's VIA instead of from the disk. Emulators without this latch behaviour (e.g. VICE) fail the check.

Disk signature

  • Track 37 is recorded at the bit rate of speed zone 2 (as on tracks 18-24). The 1541 DOS also reads tracks 36 and above at zone 2.
  • The track has exactly one sync. Because there is no other sync, the byte boundaries are fixed for the whole track: after the sync the drive reads the alternating bit pattern as $55 bytes.
  • On the original disk the sync is followed by 4095 bytes $55 and then by GCR coded $00 bytes (52 94 A5 29 4A ...) up to the sync. This was measured in the KryoFlux stream of Turrican source 5 (identical in all revolutions); the NBZ of source 1 shows the same layout.
  • The data blocks of the program files mostly start with the marker $02 instead of $07, and their checksum is the XOR of the data bytes EOR $69 (also for some blocks with the marker $07). Only the custom fast loader reads these blocks; a normal DOS read reports them as errors (22 or 23).

Loader

The boot file SCHLUMPF loads to $0326 and starts itself through the KERNAL output vector. It loads the fast loader and uploads the drive code.

  1. The drive code copies a prepared page to the zero page and jumps to $0000. It does not use JMP or BRK: it pushes $FF twice (read from $FFFB) and executes RTS, so the return address $FFFF + 1 wraps to $0000.
  2. The code in the zero page starts VIA timer 2 ($1C08/$1C09) and runs several self-modifying delay loops. Then it decrypts $0300-$07FF: each byte is combined by EOR with the current timer value ($1C08 and $1C09) and with the zero page bytes, i.e. with its own code. The decryption therefore only works with cycle exact timing. It also checks three bytes of the drive ROM ($D2B7, $E5B8, $E5B9).
  3. The decrypted code issues a SEEK job on track 18 and then an EXECUTE job for buffer $0400 on track 37, which runs the check.

Check

0419  BIT $1C00          wait until SYNC is active
      BMI $0419          (BIT also copies bit 6 of $1C00 to the V flag)
      LDX #$10           4096 bytes
      LDY #$00
0422  BVC $0422          wait for BYTE READY
      CLV
      LDA $1C01          read a byte
      CMP #$55
      BNE fail
      DEY / BNE $0422 / DEX / BNE $0422
      JMP $F418          job finished OK

The first byte is not read from the disk:

  • Bit 6 of $1C00 is a speed zone bit. On track 37 (zone 2) it is 1, so after the wait loop the V flag is always set. The first BVC does not wait, and the first LDA $1C01 happens while the sync is still being read, without a new byte.
  • The 6522 VIA of the 1541 latches port A on every BYTE READY (input latching is enabled), but only while the interrupt flag of CA1 is clear; reading $1C01 clears it. So the latch still holds the first byte that arrived after the last read of $1C01. That last read was in the SEEK job on track 18: the 8th GCR byte of a sector header. The next byte, the 9th GCR byte of the header, is always $55 (the header ends with $0F $0F).
  • The first comparison therefore uses the latched $55 from track 18, and the following 4095 comparisons use the $55 bytes of track 37. This is exactly the length of the $55 run on the original disk. A drive or emulator that delivered a real disk byte on the first read would read 4096 bytes from track 37, and the last one would not be $55.

If the check fails

The drive code writes $02 (an illegal opcode that halts the 6502) over the JMP instruction at the end of a cleanup routine, copies that routine to $0110, erases the drive code at $0300-$07FF and runs into the halt. The drive stops and the C64 waits forever on the "LOADING" screen. Nothing is written to the disk.

Emulation

  • VICE (tested with 3.10) does not emulate the input latch of the VIA: LDA $1C01 always returns the newest byte. The first byte of the check is then $BF or $AF (the end of the bit pattern before the sync), the check fails and the game stops at "LOADING". In a test where only this first byte was set to $55, the game loaded to the title screen.
  • Denise 1.1.1 and later emulates the latching; the game runs there.
  • G64 images must contain track 37 with the single sync and the $55 run. A G64 converted from the NIB/NBZ with nibconv (default settings) keeps track 37 as on the disk.

Pages that refer to this protection