Finally an interesting protection!
The boot sector is executable and simply loads the code into memory plus reading the boot sector (moving the head back to track 0). After loading the code the fun begins (skip further down…)
TEXT
BRA.S L0000
DC.B $00,$00,$00,$00,$00,$00,$00,$00
DC.B $00,$00,$02,$02,$01,$00,$02,$70
DC.B $00,$20,$03,$00,$05,$00,$0A,$00
DC.B $01,$00,$00,$00,$00,$00
L0000:MOVE.L #$230,D0 ;track 56, sector 1
MOVE.L #101,D1 ;$CA00 bytes
MOVEA.L $432.L,A0 ;_membot = Start of TPA (user memory)
BSR LoadSectors
BNE.S L0000
LSL.L #8,D1
ADD.L D1,D1 ;buf += 512 * sectors
ADDA.L D1,A0
MOVEQ #1,D1 ;$200 bytes
BSR LoadSectors ;Track 1, Sector 1
MOVE.L $432.L,-(A7) ;_membot = Start of TPA (user memory)
RTS
LoadSectors:
MOVEM.L A0-A2/D1-D5,-(A7)
MOVE.L D0,D3 ;startsector
MOVE.L D1,D4 ;number of sectors
MOVEQ #-1,D0
LoadSectorsLoop:
MOVE.L D3,D0
DIVU #10,D0 ;10 sectors per track
SWAP D0
MOVEQ #0,D1
MOVE.W D0,D1
ADDQ.W #1,D1 ;sectno
SWAP D0
ANDI.L #$FF,D0 ;trackno
MOVE.L D0,D5
MOVEQ #11,D2
SUB.L D1,D2 ;number of sectors
CMP.L D2,D4
BGE.S LoadSectorsR ;< remaining sectors
MOVE.L D4,D2 ;then read the remaining sectors
LoadSectorsR:
BSR Floprd
BNE.S LoadSectorsError
ADD.L D2,D3 ;sector += read sectors
SUB.L D2,D4 ;number of sectors -= read sectors
LSL.L #8,D2
LSL.L #1,D2 ;buf += read sectors * 512
ADDA.L D2,A0
TST.L D4 ;remaining sectors left?
BGT.S LoadSectorsLoop
MOVEQ #0,D0
LoadSectorsError:
MOVEM.L (A7)+,A0-A2/D1-D5
TST.L D0
RTS
Floprd:MOVEM.L A0/D1-D2,-(A7)
MOVE.W D2,-(A7) ;count
CLR.W -(A7) ;sideno
MOVE.W D0,-(A7) ;trackno
MOVE.W D1,-(A7) ;sectno
CLR.W -(A7) ;devno = 'A'
CLR.L -(A7) ;filler
MOVE.L A0,-(A7) ;buf
MOVE.W #8,-(A7) ;FLOPRD
TRAP #14 ;Floprd( void *buf, int32_t filler, int16_t devno, int16_t sectno, int16_t trackno, int16_t sideno, int16_t count )
LEA 20(A7),A7
TST.L D0
MOVEM.L (A7)+,A0/D1-D2
RTS
DS.W 3
DC.B 'Copylock ST (c)1988-90 Rob Northen Computing, U.K. All Rights Reserved.',0
DS.W 123
DC.W $027F
END
Ok, the code starts by entering supervisor mode via an illegal instruction, then pushing a trace exception handler onto the stack, which decrypts code on the fly. A *lot* of code. All this code is just there to annoy people trying to trace it by hand. Later it reaches the actual protection, which I'll publish here. It again uses trace to decrypt the next opcode (actually 8 bytes) and encrypting the last one again. As a little bonus it executes a subroutine if the "EXG D7,D7" opcode is executed. Fun, but useless: if you made it that far, this is not doing anything... Another thing: LINE-A, all interrupts and traps have to point to ROM, so does GPI7 and printer code (used to enter the debugger). If not, they point to a memory erase routine and a call into the reset of the ROM.
But now to the actual thing: the protection. 3 Parts:
- Via "read track" the first 450 bytes are read and the first sector address header is searched. It has to be a 512 byte sector on the first side and the first sector has to be equal to "(11 + (track % 5) * -2) % 10". If the routine fails, it returns -1, otherwise the current track. Funny thing: on track 0, sector 1 is just fine and because the boot sector was read at the end of the loading code, that's where the floppy head is located. 3 tries to get it done.
- Via Read Sector it reads sector 5 and sector 6 of track 0. It simply times the time it takes to read the sector and expects sector 6 to be at least >1% slower than sector 5. On the floppy it seems to be more than 3%, so 1% is safe.
- The first 16 bytes of sector 6 have to contain the string "Rob Northen Comp". A checksum over this string plus the following 8 bytes results in the serial number of the disk, which is specific for the application.
Yes, that's it for the protection. It continues with trace decoding and then launches the app. The following code is the heart of the protection, cleaned up and decoded.
018CAE 48E7C0C0 MOVEM.L A0-A1/D0-D1,-(A7)
018CB2 43F90000015C LEA $15C.L,A1
018CB8 233C00044ED0 MOVE.L #$44ED0,-(A1)
018CBE 233CFFFA2078 MOVE.L #$FFFA2078,-(A1)
018CC4 233CFFFF51C8 MOVE.L #$FFFF51C8,-(A1)
018CCA 233C3FF948E0 MOVE.L #$3FF948E0,-(A1)
018CD0 233C0000303C MOVE.L #$303C,-(A1)
018CD6 233C41F90010 MOVE.L #$41F90010,-(A1)
018CDC 233C46FC2700 MOVE.L #$46FC2700,-(A1)
;erase all memory and call reset
000140: 46fc 2700 MOVE #$2700,SR
000144: 41f9 0010 0000 LEA $100000,A0
00014a: 303c 3ff9 MOVE.W #$3FF9,D0
00014e: 48e0 ffff MOVEM.L D0-D7/A0-A7,-(A0)
000152: 51c8 fffa DBF D0,*-$4 [$14E]
000156: 2078 0004 MOVEA.L $4.w,A0
00015a: 4ed0 JMP (A0)
018CE2 23C90000042A MOVE.L A1,$42A.L
018CE8 23FC3141592600000426 MOVE.L #$31415926,$426.L
018CF2 41F900000028 LEA $28.L,A0 ;Line 1010 Emulator (LineA)
018CF8 61000038 BSR 56(PC) resetIfNotInROM
018CFC 41F900000060 LEA $60.L,A0 ;all interrupts and traps!
018D02 6100002E L0000:BSR 46(PC) resetIfNotInROM
018D06 B1FC000000C0 CMPA.L #$C0,A0
018D0C 66F4 BNE.S -12(PC) L0000
018D0E 41F90000013C LEA $13C.L,A0 ;GPI7 - Monochrome Detect
018D14 6100001C BSR 28(PC) resetIfNotInROM
018D18 41F90000050A LEA $50A.L,A0 ;prv_lst
018D1E 61000012 BSR 18(PC) resetIfNotInROM
018D22 41F900000512 LEA $512.L,A0 ;prv_aux
018D28 61000008 BSR 8(PC) resetIfNotInROM
018D2C 4CDF0303 MOVEM.L (A7)+,A0-A1/D0-D1
018D30 6018 BRA.S 24(PC) L0003
resetIfNotInROM
018D32 2018 MOVE.L (A0)+,D0
018D34 6712 BEQ.S 18(PC) L0002
018D36 028000FFFFFF ANDI.L #$FFFFFF,D0
018D3C B0BC00400000 CMP.L #$400000,D0
018D42 6C04 BGE.S 4(PC) L0002
018D44 2149FFFC MOVE.L A1,-4(A0)
018D48 4E75 L0002:RTS
018D4A 7000 L0003:MOVEQ #0,D0
018D4C 7201 MOVEQ #1,D1
018D4E 48E73FF0 MOVEM.L A0-A3/D2-D7,-(A7)
018D52 7400 MOVEQ #0,D2
018D54 E690 ROXR.L #3,D0
018D56 E292 ROXR.L #1,D2
018D58 E790 ROXL.L #3,D0
018D5A 1400 MOVE.B D0,D2
018D5C 2601 MOVE.L D1,D3
018D5E 7E01 MOVEQ #1,D7
018D60 7C00 MOVEQ #0,D6
018D62 47FA03A6 LEA 934(PC),A3 buffer512Bytes
018D66 3F390000043E MOVE.W $43E.L,-(A7)
018D6C 50F90000043E ST $43E.L
018D72 610000A0 L0004:BSR 160(PC) doCheckDiskInterleave
018D76 6B0C BMI.S 12(PC) L0005
018D78 61000036 BSR 54(PC) checkSectorsReturnMagic
018D7C 4A86 TST.L D6 ;fail? 2nd try!
018D7E 6604 BNE.S 4(PC) L0005
018D80 6100002E BSR 46(PC) checkSectorsReturnMagic
018D84 610000A0 L0005:BSR 160(PC) seekToPreviousTrack
018D88 4A86 TST.L D6
018D8A 660E BNE.S 14(PC) L0006
018D8C 4A82 TST.L D2
018D8E 6A0A BPL.S 10(PC) L0006
018D90 5202 ADDQ.B #1,D2
018D92 02020001 ANDI.B #1,D2
018D96 51CFFFDA DBF D7,-38(PC) L0004
018D9A 6100005E L0006:BSR 94(PC) eraseBuffer
018D9E 33DF0000043E MOVE.W (A7)+,$43E.L
018DA4 3202 MOVE.W D2,D1 ;d1.w = drive no. key disk was found in
018DA6 2006 MOVE.L D6,D0 ;d0.l = serial no. 0=key disk not found
018DA8 4CDF0FFC MOVEM.L (A7)+,A0-A3/D2-D7
018DAC 60000560 BRA 1376(PC) L002D
checkSectorsReturnMagic:
018DB0 598F SUBQ.L #4,A7
018DB2 303C0005 MOVE.W #5,D0 ;Sector 5 in Track 0 (normal sector)
018DB6 610000AC BSR 172(PC) fdcMeasureSectorTiming
018DBA 2E80 MOVE.L D0,(A7)
018DBC 303C0006 MOVE.W #6,D0 ;Sector 6 in Track 0 (slower sector, ~3%)
018DC0 610000A2 BSR 162(PC) fdcMeasureSectorTiming
018DC4 2217 MOVE.L (A7),D1
018DC6 9081 SUB.L D1,D0 ;delta between 5 and 6
018DC8 6B2C BMI.S 44(PC) L000A
018DCA C0FC0064 MULU #100,D0
018DCE 80C1 DIVU D1,D0 ;convert delta into percent
018DD0 B03C0001 CMP.B #1,D0 ;1 percent or less is not good enough!
018DD4 6D20 BLT.S 32(PC) L000A
018DD6 7000 MOVEQ #0,D0
018DD8 7203 MOVEQ #3,D1
018DDA 204B MOVEA.L A3,A0
018DDC 9098 L0008:SUB.L (A0)+,D0 ;checksum over first 16 bytes of sector 6 = "Rob Northen Comp"
018DDE 51C9FFFC DBF D1,-4(PC) L0008
018DE2 B0BCB34C4FDC CMP.L #$B34C4FDC,D0 ;has to be a specific value!
018DE8 660C BNE.S 12(PC) L000A
018DEA 2C00 MOVE.L D0,D6
018DEC 7201 MOVEQ #1,D1
018DEE DC98 L0009:ADD.L (A0)+,D6 ;secret ID over the next 8 bytes
018DF0 4846 SWAP D6
018DF2 51C9FFFA DBF D1,-6(PC) L0009
018DF6 588F L000A:ADDQ.L #4,A7
018DF8 4E75 RTS
eraseBuffer
018DFA 204B MOVEA.L A3,A0
018DFC 323C00FF MOVE.W #$FF,D1
018E00 203C00D4C742 MOVE.L #$D4C742,D0
018E06 C0FC0011 L000C:MULU #$11,D0
018E0A 5280 ADDQ.L #1,D0
018E0C 30C0 MOVE.W D0,(A0)+
018E0E 51C9FFF6 DBF D1,-10(PC) L000C
018E12 4E75 RTS
doCheckDiskInterleave:
018E14 61000028 BSR 40(PC) selectFloppy
018E18 610000D0 BSR 208(PC) checkDiskSectorInterleave
018E1C 3A00 MOVE.W D0,D5 => current track number
018E1E 6B04 BMI.S 4(PC) L000E
018E20 61000234 BSR 564(PC) fdcRestore
018E24 4E75 L000E:RTS
seekToPreviousTrack:
018E26 3005 MOVE.W D5,D0 current track number set?
018E28 6B0E BMI.S 14(PC) L0010
018E2A 61000210 BSR 528(PC) fdcSeekD0
018E2E 4A86 TST.L D6
018E30 6722 BEQ.S 34(PC) deselectFloppy
018E32 4A03 TST.B D3
018E34 671E BEQ.S 30(PC) deselectFloppy
018E36 4E75 RTS
018E38 61000250 L0010:BSR 592(PC) fdcFloppyDeselect
018E3C 4E75 RTS
selectFloppy:
018E3E 1002 MOVE.B D2,D0
018E40 5200 ADDQ.B #1,D0
018E42 E308 LSL.B #1,D0
018E44 0A000007 EORI.B #7,D0
018E48 02000007 ANDI.B #7,D0
018E4C 7201 MOVEQ #1,D1
018E4E 61000244 BSR 580(PC) fdcFloppySelect
018E52 4E75 RTS
deselectFloppy:
018E54 223C00005949 MOVE.L #$5949,D1
018E5A 103C0007 MOVE.B #7,D0
018E5E 61000234 BSR 564(PC) fdcFloppySelect
018E62 4E75 RTS
fdcMeasureSectorTiming:
018E64 48E72000 MOVEM.L D2,-(A7)
018E68 33FC008400FF8606 MOVE.W #$84,$FF8606.L
018E70 6100027C BSR 636(PC) fdcWriteD0
018E74 610000EE BSR 238(PC) fdcDMAReadAddress
018E78 303C0001 MOVE.W #1,D0
018E7C 61000270 BSR 624(PC) fdcWriteD0
018E80 33FC008000FF8606 MOVE.W #$80,$FF8606.L
018E88 43F900FF860B LEA $FF860B.L,A1
018E8E 7200 MOVEQ #0,D1
018E90 240B MOVE.L A3,D2
018E92 2F3C66F64E75 MOVE.L #$66F64E75,-(A7)
018E98 2F3C00FFFA01 MOVE.L #$FFFA01,-(A7)
018E9E 2F3C08390005 MOVE.L #$8390005,-(A7)
018EA4 2F3CB44066F6 MOVE.L #$B44066F6,-(A7)
018EAA 2F3C01090000 MOVE.L #$1090000,-(A7)
018EB0 2F3C02005281 MOVE.L #$2005281,-(A7)
018EB6 2F3C06820000 MOVE.L #$6820000,-(A7)
018EBC 2F3CB44067F8 MOVE.L #$B44067F8,-(A7)
018EC2 2F3C01090000 MOVE.L #$1090000,-(A7)
018EC8 2F3C00FF8604 MOVE.L #$FF8604,-(A7)
018ECE 2F3C33FC0080 MOVE.L #$33FC0080,-(A7)
018ED4 2F3C007C0700 MOVE.L #$7C0700,-(A7)
018EDA 244F MOVEA.L A7,A2
018EDC CF47 EXG D7,D7
A2 =>
05BC8C 007C0700 ORI.W #$700,SR
05BC90 33FC008000FF8604 MOVE.W #$80,$FF8604.L
05BC98 01090000 L0000:MOVEP.W 0(A1),D0
05BC9C B440 CMP.W D0,D2
05BC9E 67F8 BEQ.S -8(PC) L0000
05BCA0 068200000200 ADDI.L #$200,D2
05BCA6 5281 L0001:ADDQ.L #1,D1
05BCA8 01090000 MOVEP.W 0(A1),D0
05BCAC B440 CMP.W D0,D2
05BCAE 66F6 BNE.S -10(PC) L0001
05BCB0 0839000500FFFA01 L0002:BTST #5,$FFFA01.L
05BCB8 66F6 BNE.S -10(PC) L0002
05BCBA 4E75 RTS
018EDE 4FEF0030 LEA 48(A7),A7
018EE2 2001 MOVE.L D1,D0
018EE4 4CDF0004 MOVEM.L (A7)+,D2
018EE8 4E75 RTS
checkDiskSectorInterleave:
018EEA 61000044 BSR 68(PC) L0016
018EEE 6B3C BMI.S 60(PC) L0015
018EF0 B03C0002 CMP.B #2,D0 ;512 bytes sector?
018EF4 6636 BNE.S 54(PC) L0015 ;no => error
018EF6 2200 MOVE.L D0,D1
018EF8 E199 ROL.L #8,D1
018EFA 0281000000FF ANDI.L #$FF,D1 ;track number
018F00 E088 LSR.L #8,D0 ;sector number
018F02 82FC0005 DIVU #5,D1
018F06 4241 CLR.W D1
018F08 4841 SWAP D1
018F0A C2FCFFFE MULU #$FFFE,D1
018F0E 02810000FFFF ANDI.L #$FFFF,D1
018F14 0641000B ADDI.W #11,D1
018F18 82FC000A DIVU #10,D1
018F1C 4841 SWAP D1 ;d1 = (11 + (track % 5) * -2) % 10
018F1E B200 CMP.B D0,D1 ;sector number == track
018F20 660A BNE.S 10(PC) L0015
018F22 E088 LSR.L #8,D0 ;side
018F24 4A00 TST.B D0
018F26 6604 BNE.S 4(PC) L0015 ;Side 2? => error
018F28 E088 LSR.L #8,D0 ;return the track number
018F2A 4E75 RTS
018F2C 70FF L0015:MOVEQ #-1,D0
018F2E 4E75 RTS
; read track 0, load 4 bytes of the address block of first sector into D0 (Track,Side,Sector,Length)
; == 0x00000002
018F30 7202 L0016:MOVEQ #2,D1 ; 3 tries
018F32 61000062 L0017:BSR 98(PC) fdcReadTrack
018F36 6B2A BMI.S 42(PC) L001B
018F38 204B MOVEA.L A3,A0
018F3A 303C01C1 MOVE.W #450-1,D0
018F3E 0C1800FE L0018:CMPI.B #$FE,(A0)+
018F42 6614 BNE.S 20(PC) L001A
018F44 0C2800A1FFFE CMPI.B #$A1,-2(A0)
018F4A 660C BNE.S 12(PC) L001A
018F4C 7203 MOVEQ #3,D1
018F4E E188 L0019:LSL.L #8,D0
018F50 1018 MOVE.B (A0)+,D0
018F52 51C9FFFA DBF D1,-6(PC) L0019
018F56 4E75 RTS
018F58 51C8FFE4 L001A:DBF D0,-28(PC) L0018
018F5C 51C9FFD4 DBF D1,-44(PC) L0017
018F60 70FF MOVEQ #-1,D0
018F62 4E75 L001B:RTS
fdcDMAReadAddress:
018F64 200B MOVE.L A3,D0
018F66 13C000FF860D MOVE.B D0,$FF860D.L
018F6C E088 LSR.L #8,D0
018F6E 13C000FF860B MOVE.B D0,$FF860B.L
018F74 E088 LSR.L #8,D0
018F76 13C000FF8609 MOVE.B D0,$FF8609.L
018F7C 33FC009000FF8606 MOVE.W #$90,$FF8606.L
018F84 33FC019000FF8606 MOVE.W #$190,$FF8606.L
018F8C 33FC009000FF8606 MOVE.W #$90,$FF8606.L
018F94 4E75 RTS
fdcReadTrack:
018F96 48A74000 MOVEM.W D1,-(A7)
018F9A 6100FFC8 BSR -56(PC) fdcDMAReadAddress
018F9E 303C001F MOVE.W #$1F,D0
018FA2 6100014A BSR 330(PC) fdcWriteD0
018FA6 33FC008000FF8606 MOVE.W #$80,$FF8606.L
018FAE 223C00040000 MOVE.L #$40000,D1
018FB4 43EB01C2 LEA 450(A3),A1
018FB8 40E7 MOVE SR,-(A7)
018FBA 007C0700 ORI.W #$700,SR
018FBE 2F3C584F4E75 MOVE.L #$584F4E75,-(A7)
018FC4 2F3C00FF8604 MOVE.L #$FF8604,-(A7)
018FCA 2F3C33FC00D0 MOVE.L #$33FC00D0,-(A7)
018FD0 2F3C00FF8606 MOVE.L #$FF8606,-(A7)
018FD6 2F3C33FC0080 MOVE.L #$33FC0080,-(A7)
018FDC 2F3CB3D76ED6 MOVE.L #$B3D76ED6,-(A7)
018FE2 2F3C860D0003 MOVE.L #$860D0003,-(A7)
018FE8 2F3C1F7900FF MOVE.L #$1F7900FF,-(A7)
018FEE 2F3C860B0002 MOVE.L #$860B0002,-(A7)
018FF4 2F3C1F7900FF MOVE.L #$1F7900FF,-(A7)
018FFA 2F3C86090001 MOVE.L #$86090001,-(A7)
019000 2F3C1F7900FF MOVE.L #$1F7900FF,-(A7)
019006 2F3C53816B1C MOVE.L #$53816B1C,-(A7)
01900C 2F3CFA016720 MOVE.L #$FA016720,-(A7)
019012 2F3C000500FF MOVE.L #$500FF,-(A7)
019018 2F3C42A70839 MOVE.L #$42A70839,-(A7)
01901E 2F3C00FF8604 MOVE.L #$FF8604,-(A7)
019024 2F3C33FC00E0 MOVE.L #$33FC00E0,-(A7)
01902A 244F MOVEA.L A7,A2
01902C CF47 EXG D7,D7
A2 =>
05BC8C 33FC00E000FF8604 MOVE.W #$E0,$FF8604.L ; Read Track
05BC94 42A7 CLR.L -(A7)
05BC96 0839000500FFFA01 L0000:BTST #5,$FFFA01.L
05BC9E 6720 BEQ.S 32(PC) L0001
05BCA0 5381 SUBQ.L #1,D1
05BCA2 6B1C BMI.S 28(PC) L0001
05BCA4 1F7900FF86090001 MOVE.B $FF8609.L,1(A7)
05BCAC 1F7900FF860B0002 MOVE.B $FF860B.L,2(A7)
05BCB4 1F7900FF860D0003 MOVE.B $FF860D.L,3(A7)
05BCBC B3D7 CMPA.L (A7),A1 ; end address reached?
05BCBE 6ED6 BGT.S -42(PC) L0000
05BCC0 33FC008000FF8606 L0001:MOVE.W #$80,$FF8606.L
05BCC8 33FC00D000FF8604 MOVE.W #$D0,$FF8604.L ; Force Interrupt
05BCD0 584F ADDQ.W #4,A7
05BCD2 4E75 RTS
01902E 4FEF0048 LEA 72(A7),A7
019032 46DF MOVE (A7)+,SR
019034 2001 MOVE.L D1,D0
019036 4C9F0002 MOVEM.W (A7)+,D1
01903A 4E75 RTS
fdcSeekD0:
01903C 33FC008600FF8606 MOVE.W #$86,$FF8606.L
019044 610000A8 BSR 168(PC) fdcWriteD0
019048 303C0014 MOVE.W #$14,D0
01904C 6100000A BSR 10(PC) fdcCommand
019050 6B02 BMI.S 2(PC) L001F
019052 7000 MOVEQ #0,D0
019054 4E75 L001F:RTS
fdcRestore:
019056 7004 MOVEQ #4,D0
fdcCommand:
019058 B03C0080 CMP.B #$80,D0 ;Type I command?
01905C 6404 BCC.S 4(PC) L0022
01905E 00000003 ORI.B #3,D0 ;=> seek rate = 3ms
019062 33FC008000FF8606 L0022:MOVE.W #$80,$FF8606.L
01906A 61000082 BSR 130(PC) fdcWriteD0
01906E 203C00004000 MOVE.L #$4000,D0
019074 0839000500FFFA01 L0023:BTST #5,$FFFA01.L
01907C 6756 BEQ.S 86(PC) fdcReadStatus
01907E 5380 SUBQ.L #1,D0
019080 66F2 BNE.S -14(PC) L0023
019082 61000038 BSR 56(PC) fdcForceInterrupt
019086 70FF MOVEQ #-1,D0
019088 4E75 RTS
fdcFloppyDeselect:
01908A 223C0000004B MOVE.L #$4B,D1
019090 103C0007 MOVE.B #7,D0
fdcFloppySelect:
019094 5381 SUBQ.L #1,D1
019096 66FC BNE.S -4(PC) fdcFloppySelect
019098 40E7 MOVE SR,-(A7)
01909A 007C0700 ORI.W #$700,SR
01909E 13FC000E00FF8800 MOVE.B #$E,$FF8800.L
0190A6 123900FF8800 MOVE.B $FF8800.L,D1
0190AC 020100F8 ANDI.B #$F8,D1
0190B0 8200 OR.B D0,D1
0190B2 13C100FF8802 MOVE.B D1,$FF8802.L
0190B8 46DF MOVE (A7)+,SR
0190BA 4E75 RTS
fdcForceInterrupt:
0190BC 33FC008000FF8606 MOVE.W #$80,$FF8606.L
0190C4 303C00D0 MOVE.W #$D0,D0
0190C8 61000024 BSR 36(PC) fdcWriteD0
0190CC 303C000F MOVE.W #$F,D0
0190D0 51C8FFFE L0027:DBF D0,-2(PC) L0027
fdcReadStatus
0190D4 33FC008000FF8606 MOVE.W #$80,$FF8606.L
0190DC 6100001A BSR 26(PC) fdcDelay
0190E0 303900FF8604 MOVE.W $FF8604.L,D0
0190E6 02800000001F ANDI.L #$1F,D0
0190EC 600A BRA.S 10(PC) fdcDelay
fdcWriteD0
0190EE 61000008 BSR 8(PC) fdcDelay
0190F2 33C000FF8604 MOVE.W D0,$FF8604.L
fdcDelay:
0190F8 40E7 MOVE SR,-(A7)
0190FA 3F00 MOVE.W D0,-(A7)
0190FC 303C0001 MOVE.W #1,D0
019100 51C8FFFE L002B:DBF D0,-2(PC) L002B
019104 301F MOVE.W (A7)+,D0
019106 46DF MOVE (A7)+,SR
019108 4E75 RTS
buffer512Bytes: DS.B 512