Protection Turrican

The boot sector starts quite interesting: copying code to address 8 and executing it there. The format of the disk is different, but not special: sector 1 in the track is 512 bytes long, sector 2-6 are each 1024 bytes long. 5632 bytes per track out of possible 6250 bytes, pretty good capacity on a double sided disk. The trick is that the last 1024 byte sector starts at the end of the track and extends for more than 500 bytes over the index marker. The first sector is packed right after the sector at the beginning. The tracks 75..79 are not used. Strangely on the back side Track 79 contains an empty 512 sector, which does not seem to be part of the protection.

Track 7 to 10 contain the actual protection. The test starts in track 7 and tests the protection, if it fails it continous till track 10 to find a valid protection. If it fails on all 4 tracks, it erases the memory till it crashes…

The usual 6 sectors exist in these tracks, but they also contain sector 0 and 16 in different positions (the position is not tested). They are supposed to be each 1024 bytes long, but they only have an address mark plus the sector header and can not be read without a CRC error. The reason for this is interesting and István Fábián figured out what is going on the the actual disk medium! http://www.atari-forum.com/viewtopic.php?f=47&t=19948&start=25

Track  7: sector order: 5,3,6, 0,16, 1,4,2
Track  8: sector order:  0,16, 1,4,2,5,3,6
Track  9: sector order:  5,3,6, 0,16, 1,4,2
Track 10: sector order:  0,16, 1,4,2,5,3,6

Sector 0 starts with the following data, as you can see it clearly contains the sector header of sector 16! You can also see four weird bytes: 0x14, 0x14, 0x14, 0x00. These bytes are actually 3 sync markers 0xa1 and another sector header 0xfe, but shifted by one bit and the FDC will re-sync to them when trying to read sector 16. And because data bits and clock bits are interleaved, the re-sync will now actually read the clock bits instead of the data bits! The protection therefore reads the data bits via sector 0 (re-sync is disabled after a 0xFE is found for 1024 bytes plus CRC) and then the clock bits via sector 16.

0000   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
0010   A1 A1 A1 FE 07 00 10 03 BB 21 4E 4E 4E 4E 4E 4E    .........!NNNNNN
0020   4E 4E 4E 4E 4E 4E 4E 4E 4E 4E 4E 4E 4E 4E 4E 4E    NNNNNNNNNNNNNNNN
0030   FF FF FF FF FF FF FF FF FF FF FF FE 14 14 14 00    ................
0040   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0050   80 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00    ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................

The DMA read code ignores the first 64 bytes, which is a gap and the re-sync for the sector 16. The following 16 bytes have to contain 0xFF bytes and other 32 bytes have to have more than 204 cleared bits (out of 256 possible bits).

Sector 16 (the clock bits) starts with the following data:

0000   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
0010   40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    @...............
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................

The first 16 bytes have to contain 0x00 bytes and other 32 bytes have to have more than 204 cleared bits (out of 256 possible bits), because of clock drift the FDC probably can randomly read a 1 instead of a 0.

copyProtection:
      MOVEM.L   A0-A6/D0-D7,-(A7)
      MOVE.L    #$100000,D0
L056B:SUBQ.L    #1,D0           ;delay
      BNE.S     L056B
      MOVE      SR,-(A7)
      BTST      #5,(A7)
      BNE.S     L056C
      CLR.L     -(A7)
      MOVE.W    #$20,-(A7) 	;SUPER
      TRAP      #1
      MOVE.L    D0,2(A7)
L056C:MOVE      #$2700,SR

      BSR       clearKeyboard

      LEA       $FFFF8000.W,A4  ;ffff8000
      LEA       1549(A4),A3     ;ffff860d
      LEA       1542(A4),A2     ;ffff8606
      LEA       1540(A4),A1     ;ffff8604
      LEA       31233(A4),A0    ;fffffa01

      MOVEQ     #5,D0
      BSR       selectFloppy

      MOVE.W    #$82,(A2)       ;track register
      BSR       fdcDelay
      MOVE.W    (A1),D0
      ANDI.W    #$FF,D0
      MOVE.W    D0,-(A7)        ;current track

      MOVE.W    #$80,(A2)
      MOVEQ     #$01,D2
      BSR       fdcCommand      ;Restore

      MOVEQ     #7,D7           ;start with track 7
L056D:MOVE.W    D7,D2
      BSR       fdcSeek
      BSR       checkSectors
      TST.W     D6
      BPL.S     L056F
      ADDQ.W    #1,D7
      CMPI.W    #10,D7          ;up to track 10
      BLE.S     L056D

      LEA       $80000,A7
L056E:CLR.L     -(A7)           ;erase everything and crash...
      BRA.S     L056E

L056F:MOVE.W    (A7)+,D2        ;current track
      BSR       fdcSeek
L0570:MOVE.W    (A1),D0         ;wait for motor off
      TST.B     D0
      BMI.S     L0570
      MOVEQ     #7,D0
      BSR       selectFloppy    ;deselect drive
      BSR.S     clearKeyboard
      MOVE.W    (A7)+,D0
      CMPI.W    #$20,D0         ;supervisor mode active?
      BNE.S     L0571
      MOVEA.L   (A7)+,A0
      MOVE.W    (A7)+,D0
      MOVE      A7,USP          ;back to user mode
      MOVEA.L   A0,A7
L0571:MOVE      D0,SR
      MOVEM.L   (A7)+,A0-A6/D0-D7
      BRA       copyProtectionReturn

clearKeyboard
      MOVEQ     #$FF,D0
L0573:BTST      #0,$FFFFFC00.W
      BEQ.S     L0574
      MOVE.B    $FFFFFC02.W,D0
      BRA.S     clearKeyboard
L0574:DBF       D0,L0573
      RTS

checkSectors:
      MOVEQ     #4,D6           ;5 tries
checkSectorsTries:
      MOVEQ     #0,D1           ;sector 0
      MOVEQ     #4,D5           ;skip 5 DMA buffer (one empty at the beginning)
      LEA       -128(A7),A5     ;base address
      BSR       readSector
      MOVEQ     #15,D0
L0577:CMPI.B    #$FF,(A5)+      ;16 0xFF bytes have to be at the beginning of the buffer
      BNE.S     checkSectorsFailed
      DBF       D0,L0577
      BSR       countClearedBits
      CMPI.W    #$CC,D0         ;at least 204 cleared bits have to be in the buffer
      BLT.S     checkSectorsFailed

      MOVEQ     #16,D1          ;sector 16
      MOVEQ     #0,D5           ;skip 1 DMA buffer (which is empty anyway)
      LEA       -64(A7),A5      ;base address
      BSR       readSector
      MOVEQ     #15,D0
L0578:TST.B     (A5)+           ;16 0x00 bytes have to be at the beginning of the buffer
      BNE.S     checkSectorsFailed
      DBF       D0,L0578
      BSR.S     countClearedBits
      CMPI.W    #$CC,D0         ;at least 204 cleared bits have to be in the buffer
      BGE.S     checkSectorsSuccess
checkSectorsFailed:
      DBF       D6,checkSectorsTries
checkSectorsSuccess:
      RTS

;count the number of cleared bits in 32 bytes
countClearedBits:
      MOVEQ     #0,D0
      MOVEQ     #31,D1
L057C:MOVEQ     #7,D2
L057D:BTST      D2,(A5)
      BNE.S     L057E
      ADDQ.L    #1,D0
L057E:DBF       D2,L057D
      ADDQ.L    #1,A5
      DBF       D1,L057C
      RTS

readSector:
      MOVE.L    A5,D0
      MOVE.B    D0,(A3)
      LSR.W     #8,D0
      MOVE.B    D0,1547(A4)     ;set the DMA address
      SWAP      D0
      MOVE.B    D0,1545(A4)
      MOVE.W    #$90,(A2)
      MOVE.W    #$190,(A2)      ;read
      MOVE.W    #$90,(A2)
      MOVEQ     #$10,D2
      BSR       fdcWriteD2      ;16*512 byte
      MOVE.W    #$84,(A2)
      MOVE.W    D1,D2           ;sector number
      BSR       fdcWriteD2
      MOVE.W    #$80,(A2)
      BSR       fdcDelay
      MOVE.W    #$80,(A1)       ;read sector

;5+3 DMA buffer = 128 bytes

L0580:MOVEM.L   (A5),D0-D3      ;read original bytes from the buffer
      SUBQ.W    #1,D5           ;5 DMA buffers
      BMI.S     L0582
      MOVE.W    A5,D4
L0581:CMP.B     (A3),D4         ;DMA low unchanged?
      BEQ.S     L0581           ;wait!
      MOVEM.L   D0-D3,(A5)
      LEA       16(A5),A5
      BRA.S     L0580

L0582:MOVEQ     #2,D1
L0583:MOVE.B    (A3),D0
L0584:CMP.B     (A3),D0         ;wait for DMA low to change 3 times
      BEQ.S     L0584
      DBF       D1,L0583

      MOVE.W    #$50,(A2)
      MOVE.W    #$150,(A2)      ;just weird...
      MOVE.W    #$50,(A2)
      BRA       fdcWait

      RTS

selectFloppy:
      MOVE.B    #$E,2048(A4)
      MOVEQ     #$F8,D1
      AND.B     2048(A4),D1
      OR.B      D0,D1
      MOVE.B    D1,2050(A4)
      RTS

fdcSeek:
      MOVE.W    #$86,(A2)
      BSR.S     fdcWriteD2
      MOVE.W    #$80,(A2)
      MOVEQ     #$11,D2
fdcCommand:
      BSR.S     fdcWriteD2
fdcWait:
      BTST      #5,(A0)
      BNE.S     fdcWait
      RTS

fdcWriteD2:
      BSR.S     fdcDelay
      MOVE.W    D2,(A1)

fdcDelay:
      MOVEQ     #$7F,D3
L058B:DBF       D3,L058B
      RTS

copyProtectionReturn:
      RTS
 
atari/protection_turrican.txt · Last modified: 2011/07/22 14:53 by mfritze
 
Recent changes RSS feed