;FLOPHANA.68K	JUN-15-88	(Also see INFOSTR)
;Amiga 3.5" Disk Handler
; by Loren Blaney
;
;REVISION HISTORY:
;MAR-11-87, Original
;JUN-15-88, Eliminate unnecessary delays waiting for motor spin up and
; initialize CIAB data direction register.
;
;WARNING:
;This currently works only for reading/writing an even number of blocks.
; The local variables should be in a stack structure, not at $D00.
;
;NOTES:
;This is based on a disassembly of the Amiga Kickstart "ROM" Kernel,
; V1.1, trackdisk.device 31.58 (23 Nov 1985).
;For further information see section 8.3 and Appendix A in the Amiga
; Hardware Manual, and see III-3 (Trackdisk) and Appendix L in the ROM
; Kernel Manual.
;
;The register convention used here is different than the convention
; generally used by Apex. The registers D0, D1, A0, and A1 are used
; as scratch. All other registers have their values preserved across
; subroutine calls.
;
;Apex blocks have 256 bytes; Amiga sectors have 512 bytes. "End
; buffering" is used to handle half sectors.
;
;INPUTS:
;	UNIT:	Unit number
;	FADDR:	Address in memory to read/write (long word)
;	BLKNOX:	Block on the disk to start read/write
;	NBLKS:	Number of blocks to read/write
;
;
;DISK FORMAT:
; Double sided, 80 cylinders, 160 tracks
;
;TRACK FORMAT:
; 11 sectors, gap
; (The gap is written as nulls. There are no gaps between sectors)
;
;SECTOR FORMAT:
; All data is MFM encoded (Amiga version).
; 0	   00		(MFM = AAAA)
; 1	   00		(MFM = AAAA)
; 2	   A1		Standard sync byte -- MFM encoded $A1 with a
; 3	   A1		 clock pulse missing to distinguish it from
;			 data (= $4489 each)
; 4	  FF		Format byte (Amiga 1.0)
; 5	  1 byte	Track number
; 6	  1 byte	Sector number
; 7	  1 byte	Number of sectors until end of write (1-11)
; 8	 16 bytes 	Header label -- OS recovery info (not used)
;24	  4 bytes	Header "checksum" (bytes (4-23)
;28	  4 bytes	Data "checksum"
;32	512 bytes	Data
;	(544 bytes total)
;
;"Checksums" are really just a simple exclusive-oring of the unencoded
; 16-bit words (i.e. parity check).

	NOLIST
	INCLUDE	SYSPAG
	LIST

;----------------------------------------------------------------------
;DEFINITIONS:
;
UNTNUM	EQU	0		;Unit number of first floppy

CIAB	EQU	$BFD000		;CIAB base address
CIABB	EQU	$BFD100		;CIAB PRB address (= CIAB + PRB)
				;8520-A OUTPUTS:
				;7 -DSKMOTOR	Motor on (latched)
				;6 -DSKSEL3	Select drive 3
				;5 -DSKSEL2	Select drive 2
				;4 -DSKSEL1	Select drive 1
				;3 -DSKSEL0	Select drive 0
				;2 DSKSIDE	Select lower side
				;1 DSKDIREC	Step outward (toward 0)
				;0 -DSKSTEP	Step head

CIAA	EQU	$BFE001		;Base address of 8520-A
				; INPUTS:
				;5 -DSKRDY	Disk is ready
				;4 -DSKTRACK0	On track zero
				;3 -DSKPROT	Write protected
				;2 -DSKCHANGE	Disk was changed

DDRB	EQU	$300		;Offset to CIA data direction register B

IOBASE	EQU	$DFF000		;Base address of I/O
DMACONR	EQU	$DFF002		;DMA control register (read)
INTREQR	EQU	$DFF01E		;Interrupt request bits (read)
DSKPTH	EQU	$DFF020		;Pointer to disk buffer (high 3 bits)
DSKPTL	EQU	$DFF022		;Pointer to disk buffer (low 16 bits)
DSKLEN	EQU	$DFF024		;Number of words to transfer and
				; bit 15 = DMA enable, bit 14 = write
BLTCON0	EQU	$DFF040		;Blitter control register 0
BLTCON1	EQU	$DFF042		;Blitter control register 1
BLTAFWM	EQU	$DFF044		;Blitter first word mask for source A

BLTBPTH	EQU	$DFF04C		;Blitter pointer for source B
BLTAPTH	EQU	$DFF050		;Blitter pointer for source A
BLTDPTH	EQU	$DFF054		;Blitter pointer for source D

BLTSIZE	EQU	$DFF058		;Blitter start and size register
BLTBMOD	EQU	$DFF062		;Blitter modulo for source B

INTENA	EQU	$DFF09A		;Interrupt enable bits
INTREQ	EQU	$DFF09C		;Interrupt request bits
ADKCON	EQU	$DFF09C		;Audio, disk UART control

;Register A1 points to a standard I/O request block whos format is:
;	IOStdReq	A1
;
;	Message		0	Not used
;	io_Device	20	Device/unit/drive number (0..3)
;	io_Unit		24	Pointer to local variables structure
;	io_Command	28	Read = 2; write = 3
;	io_Flags	30	Not used
;	io_Error	31	Error code (20..35)
;	io_Actual	32	Actual number of bytes read/written
;	io_Length	36	Requested number of bytes to read/write
;	io_Data		40	Pointer to memory data to read/write
;	io_Offset	44	Offset in bytes to track and sector
;
;	ioTD_Count	48	IOExtTD, For extended I/O which is not
;	ioTD_SecLabel	52	 used here
;
;----------------------------------------------------------------------
;
	ORG	$D00		;*** THESE SHOULD NOT BE HERE *** ???

;These variables have replaced the io_Unit structure:
FLAGS	DS.B	1	;38	;Bit array
				;0: Sector label in extended I/O is used
				;1: No disk in drive
				;2: Extended I/O is used
				;3: (set and cleared but not used)
				;4: Disk is write-protected (not used)
CIABBX	DS.B	1	;39	;Copy of CIAB-B port
CNTR	DS.B	1	;40	;Counter
	DS.B	1
IOPTR	DS.L	1	;42	;Pointer to IOStdReq structure
	DS.B	1
SECTOR	DS.B	1	;47	;Current sector number
TRACKD	DS.W	1	;48	;Desired track number

;GLOBAL VARIABLE:
TRACKS	DS.W	4	;50	;Table of track numbers (for each unit)
;*** WARNING, THIS SHOULD BE INITIALIZED TO -1, OR THERE IS A SLIGHT
; CHANCE THAT A DISK WILL GET EATEN. ***
TRACK	DS.W	1		;Track number for current unit

PTR2	DS.L	1	;58	;Pointer into track buffer
PTR1	DS.L	1	;62	;Pointer into track buffer
IODATA	DS.L	1	;66	;Pointer to unencoded I/O data
LBLBUF	DS.L	1	;70	;Pointer to sector label data region

SECBUF	DS.L	128		;Sector buffer, blocks into sectors

	ORG	$70000		;????????
TRKBUF	DS.B	$4004		;MFM Encoded track buffer

;----------------------------------------------------------------------
;
	ORG	MEMTOP - $2800
START	EQU	@		;Address where this handler starts

FLOPHAN	DC.L	DUMMY		;0
	DC.L	DUMMY		;1
	DC.L	READ		;2
	DC.L	WRITE		;3
	DC.L	DUMMY		;4
	DC.L	GETINFO		;5
	DC.L	DUMMY		;6 (Spare)
	DC.L	DUMMY		;7 (Spare)

;-----------------------------------------------------------------------
;Read bytes from floppy and store them into memory.
; This deals with the problem of reading 256-byte blocks from 512-byte
; sectors by doing "end buffering".
;
READ	MOVEM.L	D0-D7/A0-A6,-(SP)	;Save all registers

	BSR	OPEN		;Initialize track buffer, etc.

	MOVE.L	BLKNOX,D2	;Get arguments
	MOVEA.L	FADDR,A2
	MOVE.L	NBLKS,D3

	BTST	#0,D2		;Is BLKNOX an odd number?
	BEQ.S	RD20		;Branch if not
	SUBQ.L	#1,D2		;Backup to previous (even) block
	LEA	SECBUF,A2	;Read 2 blocks into the sector buffer
	MOVEQ	#2,D3
	BSR	DOREAD		;READ(BLKNOX, FADDR, NBLKS)
				;	D2	A2	D3

	LEA	SECBUF,A0	;Move the second block in the sector
	LEA	$100(A0),A0	; buffer into memory at FADDR
	MOVEA.L	FADDR,A2
	MOVEQ	#64-1,D0
RD10	MOVE.L	(A0)+,(A2)+	;Do it 4 bytes at a time
	DBF	D0,RD10

				;(A2  [FADDR] is incremented by 256)
	ADDQ.L	#2,D2		;Net gain of one block (BLKNOX)
	MOVE.L	NBLKS,D3	;One less block to do
	SUBQ.L	#1,D3
RD20
	MOVEQ	#FALSE,D4	;Anticipate an even number of remaining
	BTST	#0,D3		; blocks
	BEQ.S	RD30		;Branch if correct
	MOVEQ	#TRUE,D4	;Set flag to handle last, odd block
	SUBQ.L	#1,D3		;Decrement NBLKS to make them even
	MOVE.L	D3,D0		;FADDR' := FADDR + NBLKS *256
	ASL.L	#8,D0		; A3	  A2	  D3
	LEA	0(A2,D0.L),A3
RD30
	TST.L	D3		;Are there some blocks to read?
	BEQ.S	RD40		;Branch if not
	BSR	DOREAD		;READ(BLKNOX, FADDR, NBLKS)
RD40				;	D2	A2	D3
	TST.W	D4		;Is there a last, odd block?
	BEQ.S	RD60		;Branch if not
	LEA	SECBUF,A2	;Read 2 blocks into the sector buffer
	ADD.L	D3,D2		;BLKNOX:= BLKNOX + NBLKS 
	MOVEQ	#2,D3
	BSR	DOREAD		;READ(BLKNOX, FADDR, NBLKS)
				;	D2	A2	D3
	LEA	SECBUF,A0	;Move the first block in the sector
	MOVEQ	#64-1,D0	; buffer into memory (FADDR')
RD50	MOVE.L	(A0)+,(A3)+	;Do it 4 bytes at a time
	DBF	D0,RD50
RD60
	BSR	MTROFF		;Turn off the motor

	MOVEM.L	(SP)+,D0-D7/A0-A6	;Restore all registers
DUMMY	RTS

;-----------------------------------------------------------------------
;Write bytes from memory onto the floppy.
; This deals with the problem of writing 256-byte blocks on to 512-byte
; sectors by doing "end buffering".
;
WRITE	MOVEM.L	D0-D7/A0-A6,-(SP)	;Save all registers

	BSR	OPEN		;Initialize track buffer, etc.

	MOVE.L	BLKNOX,D2	;Get arguments
	MOVEA.L	FADDR,A2
	MOVE.L	NBLKS,D3

	BTST	#0,D2		;Is BLKNOX an odd number?
	BEQ.S	WR20		;Branch if not
	SUBQ.L	#1,D2		;Backup to previous (even) block
	LEA	SECBUF,A2	;Read 2 blocks into the sector buffer
	MOVEQ	#2,D3
	BSR	DOREAD		;READ(BLKNOX, FADDR, NBLKS)
				;	D2	A2	D3

	MOVEA.L	FADDR,A0
	LEA	SECBUF,A1	;Move memory (at FADDR) into the second
	LEA	$100(A1),A1	; block in the sector buffer
	MOVEQ	#64-1,D0	;Move 64 longs
WR10	MOVE.L	(A0)+,(A1)+	;Do it 4 bytes at a time
	DBF	D0,WR10

	BSR	DOWRITE		;WRITE(BLKNOX, FADDR, NBLKS)
				;	D2	A2	D3
	MOVEA.L	A0,A2		;(A2  [FADDR] is incremented by 256)
	ADDQ.L	#2,D2		;Net gain of one block (BLKNOX)
	MOVE.L	NBLKS,D3	;One less block to do
	SUBQ.L	#1,D3
WR20
	MOVEQ	#FALSE,D4	;Anticipate an even number of remaining
	BTST	#0,D3		; blocks
	BEQ.S	WR30		;Branch if correct
	MOVEQ	#TRUE,D4	;Set flag to handle last, odd block
	SUBQ.L	#1,D3		;Decrement NBLKS to make them even
	MOVE.L	D3,D0		;FADDR' := FADDR + NBLKS *256
	ASL.L	#8,D0		; A3	  A2	  D3
	LEA	0(A2,D0.L),A3
WR30
	TST.L	D3		;Are there some blocks to WRITE?
	BEQ.S	WR40		;Branch if not
	BSR	DOWRITE		;WRITE(BLKNOX, FADDR, NBLKS)
WR40				;	D2	A2	D3
	TST.W	D4		;Is there a last, odd block?
	BEQ.S	WR60		;Branch if not
	LEA	SECBUF,A2	;Read 2 blocks into the sector buffer
	ADD.L	D3,D2		;BLKNOX:= BLKNOX + NBLKS 
	MOVEQ	#2,D3
	BSR	DOREAD		;READ(BLKNOX, FADDR, NBLKS)
				;	D2	A2	D3
	MOVEA.L	A3,A0
	LEA	SECBUF,A1	;Move 256 bytes from memory (at FADDR')
	MOVEQ	#64-1,D0	; into the first half of the sector buf
WR50	MOVE.L	(A0)+,(A1)+	;Do it 4 bytes at a time
	DBF	D0,WR50

	BSR	DOWRITE		;WRITE(BLKNOX, FADDR, NBLKS)
WR60				;	D2	A2	D3
	BSR	CLOSE		;Write anything left in the track buffer

	MOVEM.L	(SP)+,D0-D7/A0-A6	;Restore all registers
	RTS

;-----------------------------------------------------------------------
;Return the address of the information block in D0
;
GETINFO	MOVE.L	#INFO,D0
	RTS

INFO	DC.L	START		;Handler start and end addresses
	DC.L	END
	DC.L	INFOSTR
INFOSTR	ASCII	'FLOPHANA  JUN-15-88  3 1/2", 3520 blocks'
	DC.B	0

;======================================================================
;SUBROUTINES
;-----------------------------------------------------------------------
;Read bytes from floppy and store them into memory.
;
; Inputs:
;	D2 = (BLKNOX_) Starting Apex block number (must be even)
;	A2 = (FADDR_) Starting memory address to read/write
;	D3 = (NBLKS_) Number of Apex blocks (must be even)
;
DOREAD	MOVEM.L	D0-D7/A0-A6,-(SP)	;Save registers

	LINK	A5,#-56		;Reserve space for IOStdReq structure
	MOVEA.L	SP,A1		;Point to base of IOStdReq structure
	BSR	SETUP		;Initialize IOStdReq structure

	MOVE.W	#2,28(A1)	;Indicate "read"
	BSR	CMD_RW		;Call disk I/O handler

	BSR	REPORT		;Report any errors

	UNLK	A5		;Release structure space
	MOVEM.L	(SP)+,D0-D7/A0-A6	;Restore registers
	RTS

;-----------------------------------------------------------------------
;Write bytes from memory onto the floppy
;
; Inputs:
;	D2 = (BLKNOX_) Starting Apex block number (must be even)
;	A2 = (FADDR_) Starting memory address to read/write
;	D3 = (NBLKS_) Number of Apex blocks (must be even)
;
DOWRITE	MOVEM.L	D0-D7/A0-A6,-(SP)	;Save registers

	LINK	A5,#-56		;Reserve space for IOStdReq structure
	MOVEA.L	SP,A1		;Point to base of IOStdReq structure
	BSR	SETUP		;Initialize IOStdReq structure

	MOVE.W	#3,28(A1)	;Indicate "write"
	BSR	CMD_RW		;Call disk I/O handler

	BSR	REPORT		;Report any errors

	UNLK	A5		;Release structure space
	MOVEM.L	(SP)+,D0-D7/A0-A6	;Restore registers
	RTS

;----------------------------------------------------------------------
;Routine to set up the IOStdReq structure.
; Inputs:
;	A1 = Pointer to IOStdReq structure
;	D2 = (BLKNOX_) Starting Apex block number (must be even)
;	A2 = (FADDR_) Starting memory address to read/write
;	D3 = (NBLKS_) Number of Apex blocks (must be even)
;
; Outputs:
;	PTR = Pointer to encoded track buffer
;	FLAGS
;
;Registers D0, D1 and A0 are destroyed.
;
SETUP	MOVE.W	#$8250,$DFF096.L	;Enable blitter and disk DMA
	MOVE.W	#$0400,$DFF09E.L	;Clear WORDSYNC
	MOVE.W	#$8100,$DFF09E.L	;Enable fast mode

	MOVE.B	#0,FLAGS

	MOVEA.L	A1,A0
	MOVEQ	#55,D0		;Clear the structure space
SUP10	CLR.B	(A0)+
	DBF	D0,SUP10

	MOVE.B	UNIT,D0		;Get unit number, 0..3
	ANDI.B	#$03,D0
	MOVE.B	D0,23(A1)	;io_Device

	MOVE.L	D3,D0		;Number of bytes = NBLKS_ * 256
	ASL.L	#8,D0
	MOVE.L	D0,36(A1)	;io_Length

	MOVE.L	A2,40(A1)	;io_Data

	MOVE.L	D2,D0		;Offset = BLKNOX_ * 256
	ASL.L	#8,D0
	MOVE.L	D0,44(A1)	;io_Offset

	RTS

;----------------------------------------------------------------------
;This routine initializes the track buffer and sets PTR1 to it.
; Registers D0, D1, and A0 are destroyed.
;
OPEN	LEA	CIAB.L,A0	;Point to base of 8520-B
	MOVE.B	#$FF,DDRB(A0)	;Set port B to all outputs

	MOVE.B	#$FF,CIABBX	;Indicate that motor is off (bit 7 high)

	LEA	TRKBUF.L,A0	;Track buffer ($4004 bytes)
	MOVE.L	A0,PTR1
	MOVE.W	#-1,0(A0)	;Set invalid track number
	MOVE.W	#0,2(A0)	;Indicate "clean" buffer

	MOVE.L	#$AAAAAAAA,D1	;Fill track buffer with clock bits
	MOVE.W	#$FFF,D0
	LEA	4(A0),A0
OP05	MOVE.L	D1,(A0)+
	DBF	D0,OP05

	RTS

;----------------------------------------------------------------------
;This routine writes the track buffer to disk if there is something
; on it.
;
CLOSE	LEA	TRKBUF.L,A0	;Is the track buffer dirty?
	BTST	#0,2(A0)
	BEQ.S	CL20		;Branch if not
	MOVE.L	A0,PTR2
	BSR	SUBRO		;Write the track buffer (D0=error code)
CL20
	BSR	REPORT		;Report any error
	BRA	MTROFF		;(PBRA) Turn off the motor

;-----------------------------------------------------------------------
;Report the error for the error code in D0.
; D0 and A6 are destroyed.
;
REPORT	TST.B	D0		;Test the error code
	BEQ	ERR00		;Branch if no errors

	BSR	MTROFF		;Turn off the motor
	CMPI.B	#20,D0		;It's an unspecified error if the error
	BLO	ERR20		; code is outside the range 20..35
	CMPI.B	#35,D0
	BHI	ERR20

	SUBI.B	#20,D0		;Subtract offset
	LSL.W	#2,D0		;Times 4 for long entries
	MOVEA.W	D0,A6		;Move index into an address register
	LEA	ERRTBL-@-2(PC,A6),A6 ;Get the address of the table entry
	MOVEA.L	(A6),A6		;Get the address of the error routine
	JMP	(A6)		;Jump to this address

;Table of entry points to error reporting routines
ERRTBL	DC.L	ERR20
	DC.L	ERR21
	DC.L	ERR22
	DC.L	ERR23
	DC.L	ERR24
	DC.L	ERR25
	DC.L	ERR26
	DC.L	ERR27
	DC.L	ERR28
	DC.L	ERR29
	DC.L	ERR30
	DC.L	ERR31
	DC.L	ERR32
	DC.L	ERR33
	DC.L	ERR34
	DC.L	ERR35

;-----------------------------------------------------------------------
;ERROR REPORTING ROUTINES
;
; Normal errors include:
;	door open
;	write protected
;	damaged disk
;	unformatted disk
;	wrong format
;	inserted wrong
;
ERR20	JSR	VERROR
	ASCII	'20 - DISK ERROR (UNSPECIFIED)'
	DC.B	0
ERR00	RTS			;(PJMPs won't work)

ERR21	JSR	VERROR
	ASCII	'21 - NO SECTOR HEADER'
	DC.B	0
	RTS

ERR22	JSR	VERROR
	ASCII	'22 - SECTOR PREAMBLE'
	DC.B	0
	RTS

ERR23	JSR	VERROR
	ASCII	'23 - SECTOR ID'
	DC.B	0
	RTS

ERR24	JSR	VERROR
	ASCII	'24 - HEADER CHECKSUM'
	DC.B	0
	RTS

ERR25	JSR	VERROR
	ASCII	'25 - DATA CHECKSUM'
	DC.B	0
	RTS

ERR26	JSR	VERROR
	ASCII	'26 - TOO FEW SECTORS'
	DC.B	0
	RTS

ERR27	JSR	VERROR
	ASCII	'27 - SECTOR HEADER'
	DC.B	0
	RTS

ERR28	JSR	VERROR
	ASCII	'28 - WRITE PROTECTED'
	DC.B	0
	RTS

ERR29	JSR	VERROR
	ASCII	'29 - DISK CHANGED OR MISSING'
	DC.B	0
	RTS

ERR30	JSR	VERROR
	ASCII	'30 - SEEK'
	DC.B	0
	RTS

ERR31	JSR	VERROR
	ASCII	'31 - DISK MEMORY OVERFLOW'
	DC.B	0
	RTS

ERR32	JSR	VERROR
	ASCII	'32 - DISK UNIT NUMBER'
	DC.B	0
	RTS

ERR33	JSR	VERROR
	ASCII	'33 - DRIVE TYPE'
	DC.B	0
	RTS

ERR34	JSR	VERROR
	ASCII	'34 - DISK IN USE'
	DC.B	0
	RTS

ERR35	JSR	VERROR
	ASCII	'35 - DISK - UNASSIGNED'
	DC.B	0
	RTS

;----------------------------------------------------------------------
;This routine turns off the motor for the current unit.
;
MTROFF	MOVE.L	D0,-(SP)	;Save D0

	MOVE.B	#$FF,CIABBX	;Turn off the motor
	MOVE.B	CIABBX,CIABB.L	; (this also selects the lower head)
	MOVEQ	#3,D0		;Select units 0..3 which correspond to
	ADD.B	UNIT,D0		; bits 3..6
	BCLR	D0,CIABBX
	MOVE.B	CIABBX,CIABB.L
	BSET	D0,CIABBX	;Deselect unit
	MOVE.B	CIABBX,CIABB.L
	MOVE.L	(SP)+,D0	;Restore D0
	RTS

;======================================================================
;FLOPPY DISK ROUTINES MODIFIED FROM ROM KERNEL CODE:
;----------------------------------------------------------------------
;Read or write I/O data from or to the disk.
;
;Inputs:
;	A1	= IOStdReq (the significant entries are shown):
;		io_Command = 2=Read, 3=Write
;		io_Length = Number of bytes to read/write (must be an
;			integer multiple of 256)
;		io_Data = Pointer to (unencoded) I/O data
;		io_Offset = Byte address of disk location (= sector *
;			cylinder * 2 * 512)
;	PTR1	= Pointer to track buffer
;	TRACKS	= Array of current head positons for each unit
;
;Outputs:
;	D0 = Error code
;
;
CMD_RW	MOVEM.L	D2/A2-A4,-(SP)	;Save registers

	MOVEA.L	24(A1),A3	;Get io_Unit structure (this has been
	MOVEA.L	A1,A2		; replaced by variable names)
	MOVE.L	A2,IOPTR	;Save pointer to IOStdReq structure

;Set port control bits (CIABBX) and TRACK for current unit:
	MOVE.B	CIABBX,D0	;Select drive (= unit = device)
	ORI.B	#$7F,D0		;Disable all bits except DSKMOTOR
	MOVE.W	22(A2),D1	;Get io_Device (unit)
	ANDI.W	#$0003,D1	;Mask for safety, legal units: 0..3
	MOVE.L	D1,D2		;Save copy of unit number
	ADDI.W	#$0003,D1	;CIABB bits 3..6 select units 0..3
	BCLR	D1,D0		;Drive select is true low

	ADD.W	D2,D2		;Get track for current unit
	LEA	TRACKS.L,A0	;Index into TRACK table (words)
	MOVE.W	0(A0,D2.W),D1
	MOVE.W	D1,TRACK	;Set track number for current unit
	BTST	#0,D1		;Is this an odd or even track number?
	BEQ.S	LB300		;Branch if even -- use lower side
	BCLR	#2,D0		;Else, select upper head
LB300
	MOVE.B	D0,CIABBX	;Set copy of CIA-B port

	MOVE.L	#0,32(A2)	;Clear io_Actual (amount read/written)
	MOVE.L	40(A2),IODATA	;Get pointer to io_Data
	MOVE.L	44(A2),D0	;Get io_Offset
	MOVE.L	D0,D1
	ANDI.L	#$000001FF,D1	;Must be an integer multiple of 512
	BNE	LB50A		;Branch if not
	CMPI.L	#$000DC000,D0	;Must be < 2 *80 *11 *512
	BHS	LB50A		;Branch if not
	MOVE.L	36(A2),D1	;Get io_Length
	ADD.L	D0,D1		;Add io_Offset
	CMPI.L	#$000DC000,D1	;Must be <= 2 *80 *11 *512
	BHI	LB50A		;Branch if not
	MOVEQ	#9,D1		;Divide by 512 to get "sector" number
	LSR.L	D1,D0
	DIVU	#11,D0		;Divide by 11 to get desired track no.
	MOVE.W	D0,TRACKD
	SWAP	D0		;Get the remainder, the sector number
	MOVE.B	D0,SECTOR

	BTST	#2,FLAGS	;Is extended I/O used?
	BEQ.S	LB36E		;Branch if not

	MOVE.L	52(A2),LBLBUF	;Get pointer to sector label
	BEQ.S	LB36E		;Branch if it is not used
	BSET	#0,FLAGS	;Indicate sector label is not used
LB36E
	BTST	#1,FLAGS	;Is there a disk inserted?
	BEQ.S	LB380		;Branch if yes
	MOVE.B	#29,31(A2)	;io_Error: Disk changed or not present
	BRA	LB500		;Exit
LB380
	MOVE.W	TRACKD,D0	;Get the desired track number
	BSR	SUBRM		;Is this track in the buffer?
	MOVE.L	A0,PTR2		;Set PTR2=A0 if not
	BNE.S	LB3E4		;Branch if it is in the buffer

	MOVEA.L	PTR1,A0
	MOVEA.L	A0,A2
	MOVE.L	A2,PTR2		;PTR2:= A2:= A0:= PTR1

;Read track into buffer, loop for all tracks:
LB398	BTST	#0,2(A2)	;Is the current track "dirty"
	BEQ.S	LB3B4		;Branch if not
	BSR	SUBRO		;Write the current track
	TST.L	D0		;Any errors?
	BEQ.S	LB3B4		;Branch if not
	MOVEA.L	IOPTR,A1
	MOVE.B	D0,31(A1)	;Set io_Error
	BRA	LB500		;Exit
LB3B4
	MOVE.W	TRACKD,0(A2)	;Get desired track number
	BCLR	#0,2(A2)
	CLR.B	CNTR		;Initialize counter
	BSR	SUBRP		;Read in the desired track


	MOVE.B	3(A2),D0
	CMPI.B	#11,D0		;Any bad errors?
	BLO.S	LB3E4		;Branch if not
	MOVE.W	#$FFFF,0(A2)
	MOVEA.L	IOPTR,A1
	MOVE.B	D0,31(A1)	;Set io_Error
	BRA	LB500		;Exit

;Track is in the buffer, loop for all sectors:
LB3E4	MOVEA.L	IOPTR,A2	;Get io_StdReq
	MOVE.W	28(A2),D0	;Get I/O command
	MOVEA.L	PTR2,A0
	CMPI.B	#$03,D0		;Is it a write command?
	BNE	LB470		;Branch if not (must be read)

;Write command:
	BSET	#0,2(A0)
	MOVEQ	#0,D0		;Calculate offset into the track
	MOVE.B	SECTOR,D0	; buffer to the desired sector
	SUB.B	3(A0),D0
	BPL.S	LB40E
	ADDI.B	#11,D0
LB40E
	MULU	#544*2,D0	;Multiply by encoded bytes per sector
	LEA	1664(A0),A4	;Skip the gap
	ADDA.L	D0,A4
	BTST	#0,FLAGS	;Is sector label (header) used?
	BEQ.S	LB446		;Branch if not
	MOVEA.L	LBLBUF,A0	;Point to label (header) data
	MOVE.L	#16,D0		;Encode 16 bytes
	LEA	16(A4),A1	;A1 = location for encoded data
	BSR	ENCODE		;Encode and move the label data

	LEA	8(A4),A0	;Point to encoded data
	MOVE.W	#40,D1		;Number of encoded bytes
	BSR	DOCKSUM		;Compute header checksum (D0)

	LEA	48(A4),A0	;Encode and store header checksum
	BSR	ENCOD_L

LB446	MOVEA.L	IODATA,A0	;Get pointer to io_Data
	MOVE.L	#512,D0		;Number of bytes to encode
	LEA	64(A4),A1	;A1 = location for encoded data
	BSR	ENCODE		;Encode and move sector data

	LEA	64(A4),A0	;Point to start of encoded data
	MOVE.W	#1024,D1	;Number of bytes of encoded data
	BSR	DOCKSUM		;Compute checksum (D0)

	LEA	56(A4),A0	;Encode and store data checksum
	BSR	ENCOD_L
	BRA	LB4B6		;Go to next sector

;Enter here if read command:
LB470	MOVEQ	#0,D0		;sector number (0..10)
	MOVE.B	SECTOR,D0
	SUB.B	3(A0),D0
	BPL.S	LB480
	ADDI.B	#11,D0
LB480
	MULU	#544*2,D0	;calculate offset into track buffer
	LEA	1664(A0),A4	; to desired sector
	ADDA.L	D0,A4
	BTST	#0,FLAGS	;Is extended I/O (header) used?
	BEQ.S	LB4A4		;Branch if not
	LEA	16(A4),A1	;Point to encoded label (header) data
	MOVEA.L	LBLBUF,A0	;Point to place for decoded label data
	MOVE.L	#16,D0		;Number of bytes to decode
	BSR	DECODE		;Decode and move a block of data
LB4A4
	LEA	64(A4),A1	;Point to encoded data
	MOVEA.L	IODATA,A0	;Get pointer to io_Data (destination)
	MOVE.L	#512,D0		;Number of bytes to decode
	BSR	DECODE		;Decode and move a block of data

LB4B6	MOVE.L	#512,D1		;Point to next sector
	ADD.L	D1,IODATA
	MOVE.L	32(A2),D0	;Bump io_Actual
	ADD.L	D1,D0
	MOVE.L	D0,32(A2)
	BTST	#0,FLAGS	;Is extended I/O used?
	BEQ.S	LB4DA		;Branch if not
	ADDI.L	#16,LBLBUF	;Bump label pointer
LB4DA
	CMP.L	36(A2),D0	;Compare io_Actual to io_Length
	BHS.S	LB500		;Branch if we're done -- exit
	MOVEA.L	PTR2,A2
	ADDQ.B	#1,SECTOR	;Next sector
	CMPI.B	#11,SECTOR	;Are we in the next track?
	BLT	LB3E4		;Branch if not -- loop back

	MOVE.B	#0,SECTOR	;Start next track with sector 0
	ADDQ.W	#1,TRACKD	;Next track
	BRA	LB398		;Loop until all sectors are copied

LB500	MOVEA.L	IOPTR,A1
	BRA.S	LB516		;Exit

LB50A	MOVEA.L	IOPTR,A1
	MOVE.B	#$FF,31(A1)	;Set io_Error (-1)
	BRA.S	LB500		;Exit (circuitously)

LB516	MOVEA.L	IOPTR,A1	;Record current track in TRACKS array
	MOVE.W	22(A1),D2	;Get io_Device (unit)
	ANDI.W	#$0003,D2	;Mask for safety, legal units: 0..3
	ADD.W	D2,D2
	LEA	TRACKS.L,A0	;Index into TRACK table (words)
	MOVE.W	TRACK,0(A0,D2.W) ;Set track number for current unit

	MOVEQ	#0,D0		;Return error code in D0
	MOVE.B	31(A1),D0

	MOVEM.L	(SP)+,D2/A2-A4	;Restore registers
	RTS

;----------------------------------------------------------------------
;If the track number (D0) is written in the buffer, then A0 is returned
; set equal to PTR1.
; Inputs: track number (D0) and PTR1.
;
SUBRM	MOVE.L	PTR1,D1		;Get pointer
	TST.L	D1		;Branch if it is zero
	BEQ.S	SUBRM10

	MOVEA.L	D1,A0		;Is the track number in the buffer?
	CMP.W	0(A0),D0	;Return A0 = PTR1
	BEQ.S	SUBRM99
SUBRM10	MOVEQ	#0,D0		;Return D0 = A0 = 0
	MOVEA.L	D0,A0
SUBRM99	RTS

;----------------------------------------------------------------------
;Routine to write a track.
; Inputs:
;	PTR2 = Pointer to encoded buffer
;		0 = track number
;		2 = bit #0 = dirty/clean flag
;		4 = actual buffer space begins here
;
SUBRO	MOVEM.L	D2/A2,-(SP)

	MOVEA.L	PTR2,A2

	MOVEQ	#1,D0		;Turn on the motor
	BSR	DOMOTOR

	MOVEQ	#0,D0
	MOVE.W	0(A2),D0	;Move to track recorded in buffer
	BSR	DOSEEK

	LEA	4(A2),A0	;Location of buffer
	MOVE.W	#13630,D0	;Write 13630 encoded bytes to a track
	BSR	WRITTRK		; (13630 = 544 *2 *11 + 1662)

	MOVEM.L	(SP)+,D2/A2
	RTS

;----------------------------------------------------------------------
;Routine to read a track
; Inputs PTR2 = Pointer to buffer
;
SUBRP	MOVEM.L	A2,-(SP)	;Save A2

	MOVEA.L	PTR2,A2		;Point to buffer for encoded data

	MOVEQ	#1,D0		;Turn on the motor
	BSR	DOMOTOR

SUBRP10	MOVEQ	#0,D0
	MOVE.W	TRACKD,D0	;Move head to desired track
	BSR	DOSEEK

SUBRP20	LEA	$4000-14716(A2),A0	;Fill bottom of 16K buffer
	MOVE.W	#14716,D0	;Number of encoded bytes to read

	BSR	READTRK		;Read a track

	TST.L	D0		;Was there an error?
	BEQ.S	SUBRP30		;Branch if not
	MOVE.B	D0,3(A2)
	BRA	SUBRP90		;Exit
SUBRP30
	BSR	SUBRXQ		;Verify header and checksum
	MOVE.B	D0,3(A2)	;Save error code
	CMPI.B	#11,D0
	BLO.S	SUBRP90		;Exit on certain errors

	ADDQ.B	#1,CNTR		;Bump sector counter
	MOVE.B	CNTR,D0
	CMPI.B	#10,D0		;Are all 11 sectors read in?
	BGT.S	SUBRP90		;Branch if yes -- exit

	ANDI.B	#$03,D0
	BNE.S	SUBRP20		;Loop for 11 sectors

	MOVE.W	#-1,TRACK	;We don't know where we are
	BRA.S	SUBRP10

SUBRP90	MOVEM.L	(SP)+,A2	;Restore A2
	RTS

;======================================================================
;Routine to turn the selected drive motor on or off. It returns the
; initial state of the motor (on or off) in D0.
; On = 1; Off = 0.
;
DOMOTOR	MOVE.L	D2,-(SP)

	TST.L	D0
	SEQ	D1
	ANDI.B	#$0080,D1

	MOVE.B	CIABBX,D2
	ANDI.B	#$0080,D2
	CMP.B	D1,D2
	BEQ	LB0C2
	BCLR	#7,CIABBX	;Turn on motor
	OR.B	D1,CIABBX

	MOVEQ	#-1,D1
	EOR.B	D2,D1
	MOVE.B	D1,CIABB.L
	MOVE.B	CIABBX,CIABB.L

	BTST	#7,CIABBX	;Is motor turned on?
	BNE	LB0C2		;Branch if not
	MOVE.L	#500000,D0	;Wait half a second for motor to
	BSR	DELAY		; come up to speed
LB0C2
	MOVEQ	#0,D0
	BTST	#7,D2
	SEQ	D0

	MOVE.L	(SP)+,D2
	RTS

;----------------------------------------------------------------------
;Routine to seek the track number in D0.
; D0 returns with error code (= 0 if no errors)
;
DOSEEK	MOVEM.L	D2-D3,-(SP)	;Save registers

	MOVE.W	TRACK,D2	;Get current track number
	BPL	LB002		;Branch if it is known
	MOVE.L	D0,D2		;Otherwise we don't know where we are
	BSR	STEPTO0		;Move head to track 0
	TST.L	D0		;Any errors detected?
	BNE	LB06C		;Branch if so -- exit with error code
	EXG	D0,D2		;Get track number back in D0, (D2=0)
LB002
	CMP.W	D0,D2		;Is the head already on our track?
	BEQ	LB06A		;Branch if so -- exit with no errors
	MOVE.L	D0,D3		;Save desired track number in D3

	MOVE.B	CIABBX,D1	;Get copy of the control port
	BSET	#2,D1		;Assume we want the lower head
	BTST	#0,D3		;Is this an odd or even track number?
	BEQ.S	LB020		;Branch if even -- use lower side
	BCLR	#2,D1		;Else, select upper head
LB020
	MOVE.B	D1,CIABBX	;Update port control bits (and the copy)
	MOVE.B	D1,CIABB.L

	MOVE.W	D3,TRACK	;Update current track number
	LSR.W	#1,D2		;D2 = current track, D3 = desired track
	LSR.W	#1,D3		;Divide by 2 to get cylinder number
	SUB.W	D3,D2		;D2 = number of cylinders to move head
	BLO.S	LB03E		; away from center (think backwards)
	BHI.S	LB04A
	BRA.S	LB06A		;We don't need to move the head - exit

LB03E	BCLR	#1,D1		;Seek toward center
	MOVE.B	D1,CIABBX
	NEG.W	D2		;Make number of steps positive
	BRA.S	LB058		;Enter step loop

LB04A	BSET	#1,D1		;Seek toward track 0
	MOVE.B	D1,CIABBX
	BRA.S	LB058		;Enter step loop

LB054	BSR	DOSTEP		;Step head in selected direction
LB058	DBF 	D2,LB054	;loop for D2 steps

	MOVE.L	#15000,D0	;Wait 15 milliseconds for head to settle
	BSR	DELAY

LB06A	MOVEQ	#0,D0		;Indicate no errors

LB06C	MOVEM.L	(SP)+,D2-D3	;Restore registers
	RTS

;----------------------------------------------------------------------
;Routine to step head to track 0.
; D0 returns with error code (0 = no errors)
;
STEPTO0	MOVEM.L	D2,-(SP)

	MOVE.B	CIABBX,D0
	BSET	#2,D0		;Select lower head
	BCLR	#1,D0		;Seek toward the center
	MOVE.B	D0,CIABB.L
	MOVE.B	D0,CIABBX

	MOVEQ	#40,D2		;Set maximum tries
LAF90	BTST	#4,CIAA.L	;On track 0?
	BNE.S	LAFA2		;Branch if not
	BSR	DOSTEP
	DBF 	D2,LAF90

LAFA2	BSET	#1,CIABBX	;Seek toward track 0
	CLR.W	TRACK		;Record that we will be on track 0
	MOVE.W	#100,D2		;Set maximum steps to 100
	BRA.S	LAFB6		;Enter step loop

LAFB2	BSR	DOSTEP		;Step head toward track 0
LAFB6	BTST	#4,CIAA.L	;On track 0?
	DBEQ	D2,LAFB2	;Exit loop if so

	MOVE.B	CIAA.L,D2
	BTST	#4,D2		;On track 0?
	BEQ.S	LAFD6		;Branch if so
	MOVEQ	#30,D0		;Seek error while verifying posn
	BRA.S	LAFE2		;Exit

LAFD6	MOVE.L	#15000,D0	;Wait 15 milliseconds for head to settle
	BSR	DELAY

	MOVEQ	#0,D0		;Indicate no errors
LAFE2	MOVEM.L	(SP)+,D2
	RTS

;----------------------------------------------------------------------
;Routine to step the head
;
DOSTEP	MOVE.B	CIABBX,D0
	LEA	CIABB.L,A1	;Point to output control port
	MOVE.B	D0,D1		;Save copy of original port bits
	BCLR	#0,D0		;Command to step the head
	MOVE.B	D0,(A1)		;Send command
	NOP			;Delay for step pulse width
	NOP
	MOVE.B	D1,(A1)		;terminate pulse

	MOVE.L	#3000,D0	;Wait at least 3 milliseconds for
	BSR	DELAY		; step to occur

	MOVE.B	CIABBX,CIABB.L
	RTS

;======================================================================
;Routine to write a track.
; Inputs D0 = number of encoded bytes to write
;	 A0 = pointer to buffer
; Outputs D0 = error code (0 = no errors)
;
WRITTRK	MOVEM.L	D2/A2,-(SP)

	MOVEA.L	A0,A2
	MOVE.L	D0,D2

	MOVE.B	CIABBX,CIABB.L
	MOVE.W	#$4000,DSKLEN.L	;Make sure DMA is off
	MOVE.L	#1000,D0	;Wait a millisecond
	BSR	DELAY

	LEA	IOBASE.L,A1
	MOVE.L	A2,DSKPTH-IOBASE(A1)	;Set DMA pointer to buffer
	MOVE.W	#$1002,INTREQ-IOBASE(A1) ;Clear disk sync & blk done

;	BTST	#2,CIAA.L	;Has the disk been changed?
;	BNE.S	LB24A		;Branch if not
	BRA.S	LB24A		;Ignore disk changed error

LB244	MOVEQ	#29,D2		;io_Error: Disk changed
	BRA	LB2AE

LB24A	BTST	#3,CIAA.L	;Is disk write protected?
	BEQ	LB2BA		;Branch if it is

;Set precomp at 140 ns when TRACK is beyond half way:
	MOVE.W	#$6000,ADKCON-IOBASE(A1)	;No precomp
	MOVE.W	TRACK,D1
	CMPI.W	#81,D1		;Half way?
	BLT.S	LB26C		;Branch if lower half
	MOVE.W	#$A000,D1	;Precomp 140ns
	BRA.S	LB270
LB26C	MOVE.W	#$8000,D1	
LB270	MOVE.W	D1,ADKCON-IOBASE(A1)	;Set precomp

	LSR.W	#1,D2		;Divide by 2 to get number of words
	ORI.W	#$C000,D2	;Enable write and DMA
	MOVE.W	D2,DSKLEN-IOBASE(A1)	;Start DMA
	MOVE.W	D2,DSKLEN-IOBASE(A1)

WRT30	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for disk block finished
	BTST	#1,D0
	BEQ.S	WRT30

	MOVE.L	#2000,D0	;Wait 2 milliseconds
	BSR	DELAY

	MOVE.W	#$4000,36(A1)	;Leave DMA in a safe state

;	BTST	#2,CIAA.L	;Has the disk been changed?
;	BEQ.S	LB244		;Branch if so
	MOVEQ	#0,D2		;Indicate no errors

LB2AE	MOVE.L	D2,D0		;Return error code in D0

	MOVEM.L	(SP)+,D2/A2
	RTS


LB2BA	MOVEQ	#28,D2		;io_Error: Write protected
	BRA.S	LB2AE		;Exit

;----------------------------------------------------------------------
;Routine to read a track.
; Inputs D0 = number of encoded bytes to read
;	 A0 = pointer to encoded buffer
; Outputs D0 = error code (0 = no errors)
;
READTRK	MOVEM.L	D2/A2,-(SP)	;Save registers

	MOVEA.L	A0,A2
	MOVE.L	D0,D2

	MOVE.B	CIABBX,CIABB.L
	MOVE.W	#$4000,DSKLEN.L	;Turn off DMA
	MOVE.L	#1000,D0	;Wait  a millisecond
	BSR	DELAY

	LEA	IOBASE.L,A1
	MOVE.L	A2,DSKPTH-IOBASE(A1)	;Set pointer to buffer for DMA
	MOVE.W	#$1002,INTREQ-IOBASE(A1) ;Clear disk sync reg & blk done

;	BTST	#2,CIAA.L	;Has the disk been changed?
;	BNE.S	LB1C2		;Branch if not
	BRA.S	LB1C2		;Ignore disk changed

LB1BE	MOVEQ	#29,D2		;Error: Disk changed or not present
	BRA.S	LB1F2		;Exit with error code
LB1C2
	LSR.W	#1,D2		;Divide by 2 to get words
	ORI.W	#$8000,D2	;Set DMA enable bit
	MOVE.W	D2,DSKLEN-IOBASE(A1) ;Command it twice to start DMA
	MOVE.W	D2,DSKLEN-IOBASE(A1)

RDT30	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for disk block finished
	BTST	#1,D0
	BEQ.S	RDT30

	LEA	IOBASE.L,A1
	MOVE.W	#$4000,DSKLEN-IOBASE(A1) ;Prevent accidental writes

;	BTST	#2,CIAA.L	;Has the disk been changed?
;	BEQ.S	LB1BE		;Branch if it has -- exit (circuitously)

	MOVEQ	#0,D2		;Indicate no errors
LB1F2	MOVE.L	D2,D0

	MOVEM.L	(SP)+,D2/A2	;Restore registers
	RTS

;----------------------------------------------------------------------
;This routine aligns raw read data and verifies the header and checksum.
; It returns an error code in D0 (io_Error).
;
SUBRXQ	MOVEM.L	D2-D6/A2,-(SP)	;Save registers
	LINK	A4,#-16		;Allocate space for local variables

	MOVEA.L	PTR2,A2
	LEA	1664(A2),A2	;Skip gap
	MOVEA.L	A2,A0
	ADDQ.L	#2,A0		;Skip rest of gap
	MOVE.L	#1660 +2*544,D0	;Maximum scan is a gap + a sector
	BSR	SUBRXO		;Scan for sync bytes
	CMPA.L	#-1,A0
	BEQ	ERRX21		;Couldn't find sync bytes

	MOVE.L	A0,D5		;Save pointer to sync table
	MOVE.L	D0,D2		;D2 = number of bits to shift

	ADDQ.L	#8,A0
	MOVEQ	#9,D4		;Loop 10 times
	MOVEQ	#0,D6		;Initialize checksum to zero
	TST.L	D2
	BNE.S	LBC34		;Branch if bits are shifted
				;Otherwise bits are aligned
	MOVE.L	(A0),-8(A4)	;Save long word after sync
	MOVE.L	4(A0),-4(A4)	;Save next long word

	MOVE.L	#$55555555,D1	;Compute checksum of header
LBC20	MOVE.L	(A0)+,D0
	AND.L	D1,D0		;Remove clock bits
	EOR.L	D0,D6		;Update "checksum" (really only parity)
	DBF 	D4,LBC20	;Loop 10 times (= 20 encoded bytes)

	MOVE.L	(A0)+,-16(A4)	;Get encoded header checksum from
	MOVE.L	(A0),-12(A4)	; buffer (high, low words)
	BRA.S	LBC68
LBC34
	BSR	SUBRXR		;Decode word
	MOVE.L	D0,-8(A4)
	BSR	SUBRXR
	MOVE.L	D0,-4(A4)
	MOVEA.L	D5,A0
	ADDQ.L	#8,A0

LBC48	BSR	SUBRXR		;Decode word
	ANDI.L	#$55555555,D0
	EOR.L	D0,D6
	DBF 	D4,LBC48

	BSR	SUBRXR
	MOVE.L	D0,-16(A4)
	BSR	SUBRXR
	MOVE.L	D0,-12(A4)
LBC68
	LEA	-16(A4),A0	;Point to header checksum
	BSR	DECOD_L		;Decode 8 bytes to get one long word
	CMP.L	D0,D6		;Compare to value computed above
	BNE	ERRX27		;Branch if header checksum error

	LEA	-8(A4),A0	;Point to format byte, track, sector
	BSR	DECOD_L		;Decode to get 4 bytes of info
	MOVE.L	D0,D3		;Save decoded bytes in D3
	MOVE.L	D3,-(SP)	; and on the stack
	CMPI.B	#$FF,0(SP)	;Check for format byte ($FF)
	BNE	ERRX27		;Unable to read sector header

	MOVE.B	1(SP),D0	;Check track number
	CMP.B	TRACKD+1,D0
	BNE	ERRX27		;Branch if wrong track

	MOVE.L	(SP)+,D3
	MOVEQ	#0,D0
	MOVE.B	D3,D0		;Number of sectors until end of write
	MULU	#544*2,D0	;Compute offset into encoded data
	MOVEA.L	D5,A0		;Point to entry in sync table
	MOVEA.L	A2,A1		;Pointer to maximum scan position
	MOVE.L	D2,D1		;Index into sync table
	MOVE.L	D0,D4		;Offset into encoded data (end of write)
	BSR	SUBRXS		;Let the blitter align the sectors

	MOVEQ	#0,D2
	MOVE.B	D3,D2
	SUBI.L	#11,D2
	NEG.L	D2
	BEQ.S	LBCE6
	ADD.L	D4,D5

	MOVE.L	#1660,D0
	MOVEA.L	D5,A0
	ADDQ.L	#2,A0
	BSR	SUBRXO
	CMPA.L	#-1,A0
	BEQ	ERRX26		;Incorrect number of sectors on track

	MOVE.L	D0,D1		;Get number of bits of shift (should
	MOVEA.L	A2,A1		; be zero)
	ADDA.L	D4,A1
	MOVE.L	D2,D0
	MULU	#544*2,D0
	BSR	SUBRXS
LBCE6	MOVEA.L	A2,A0
	ADDA.L	D4,A0
	BSR	SUBRXK
	LEA	544*2*11(A2),A0
	MOVE.W	#$AAA8,D0
	BTST	#0,-1(A0)
	BEQ	LBD04
	BCLR	#15,D0
LBD04
	MOVE.W	D0,(A0)
	MOVE.W	#$AAAA,(A2)
	MOVEQ	#0,D4
	MOVEQ	#11,D2
	MOVE.W	D3,D5
	LSR.W	#8,D5

LBD12	CMPI.L	#$2AAAAAAA,0(A2,D4.W)	;2 bytes of MFM encoded 00 data
	BEQ.S	LBD28
	CMPI.L	#$AAAAAAAA,0(A2,D4.W)
	BNE	ERRX22		;Error in sector preamble
LBD28
	CMPI.L	#$44894489,4(A2,D4.W)	;sync bytes ($A1 $A1)
	BNE	ERRX22

	LEA	8(A2,D4.W),A0	;Location of encoded bytes
	MOVEQ	#40,D1		;Number of encoded bytes
	BSR	DOCKSUM		;Compute checksum of header
	MOVE.L	D0,D6		;Save checksum in D6

	LEA	48(A2,D4.W),A0
	BSR	DECOD_L		;Decode header checksum
	CMP.L	D6,D0		;Compare checksums
	BNE	ERRX24		;Branch if header field has bad checksum

	LEA	8(A2,D4.W),A0	;Decode format byte, track and sector
	BSR	DECOD_L		; numbers, etc.
	MOVE.L	D0,-(SP)
	CMPI.B	#$FF,0(SP)	;Format byte must be $FF
	BNE	ERRX23		;Error in sector identifier

	MOVE.B	1(SP),D1	;Get track number
	CMP.B	TRACKD+1,D1
	BNE	ERRX23		;Error in sector identifier

	MOVE.B	2(SP),D1	;Get sector number
	CMP.B	D5,D1
	BNE	ERRX23		;Error in sector identifier

	MOVE.B	D2,3(SP)	;Set number of sectors until end
	MOVE.L	(SP)+,D0
	LEA	8(A2,D4.W),A0	;Location to store encoded data
	BSR	ENCOD_L		;Encode and store data

	LEA	8(A2,D4.W),A0	;Location of encoded header data
	MOVEQ	#40,D1		;Number of encoded bytes
	BSR	DOCKSUM		;Compute checksum (D0)

	LEA	48(A2,D4.W),A0	;Encode and store header checksum
	BSR	ENCOD_L		; at A0

	LEA	64(A2,D4.W),A0	;Location of encoded data
	MOVE.W	#1024,D1	;Number of encoded bytes
	BSR	DOCKSUM		;Compute checksum (D0)
	MOVE.L	D0,D6		;Save it in D6

	LEA	56(A2,D4.W),A0	;Decode data checksum that was read in
	BSR	DECOD_L
	CMP.L	D6,D0		;Does it agree with computed checksum?
	BNE	ERRX25		;Branch if not -- bad checksum

	SUBQ.L	#1,D2
	ADDQ.B	#1,D5
	CMPI.B	#11,D5
	BLT.S	LBDC0
	MOVEQ	#0,D5
LBDC0
	ADDI.W	#544*2,D4
	CMPI.W	#544*2*11,D4
	BNE	LBD12
	MOVE.L	D3,D1
	LSR.L	#8,D1
	MOVEQ	#0,D0
	MOVE.B	D1,D0

ERRX90	UNLK	A4
	MOVEM.L	(SP)+,D2-D6/A2
	RTS


ERRX21	MOVEQ	#21,D0		;Couldn't find sector header
	BRA.S	ERRX90

ERRX27	MOVEQ	#27,D0		;Unable to read sector header
	BRA.S	ERRX90

ERRX22	MOVEQ	#22,D0		;Error in sector preamble
	BRA.S	ERRX90

ERRX23	MOVEQ	#23,D0		;Error in sector identifier
	BRA.S	ERRX90

ERRX24	MOVEQ	#24,D0		;Header field has bad checksum
	BRA.S	ERRX90

ERRX25	MOVEQ	#25,D0		;Sector data field has bad checksum
	BRA.S	ERRX90

ERRX26	MOVEQ	#26,D0		;Incorrect number of sectors on track
	BRA.S	ERRX90

;======================================================================
;MFM Encode and store the data in D0. The input data (D0) is four bytes,
; and the stored data is eight bytes.
; Inputs:
;	D0 = Four bytes of data to be encoded and stored
;	A0 = Location to store encoded data (bumped by 8 bytes).
;
ENCOD_L	MOVEM.L	D2-D3,-(SP)
	MOVE.L	D0,D3

	LSR.L	#1,D0		;Encode the odd bits
	BSR	SUBRXH

	MOVE.L	D3,D0		;Encode the even bits
	BSR	SUBRXH

	BSR	SUBRXK

	MOVEM.L	(SP)+,D2-D3
	RTS

;----------------------------------------------------------------------
;MFM Encode the even bits of a long word. The odd bits are used as clock
; bits. A clock bit is set only when the two adjacent data bits are both
; zero.
; Inputs:
;	D0 = Data to be MFM encoded (even bits only)
;	A0 = Location to store encoded data
;
SUBRXH	ANDI.L	#$55555555,D0	;Get the even data bits
	MOVE.L	D0,D2

	EORI.L	#$55555555,D2	;Turn the zeros into ones
	MOVE.L	D2,D1		;Copy inverted data into D1

	LSL.L	#1,D2		;<-- D2
	LSR.L	#1,D1		;    D1 -->
	BSET	#31,D1		;Assume last bit of last data was a one
	AND.L	D2,D1		;Set clock bit if there were two zeros
	OR.L	D1,D0		; in a row. Combine clocks with data
	BTST	#0,-1(A0)	;Check last data bit of preceding data
	BEQ.S	LB982		;Branch if it was a zero -- leave clock
	BCLR	#31,D0		; bit alone, otherwise it's not needed
LB982
	MOVE.L	D0,(A0)+	;Store encoded data in buffer
	RTS

;----------------------------------------------------------------------
;Routine to encode and move a block of data (write a sector).
;Inputs:
;	D0 = Number of bytes to encode
;	A0 = Pointer to bytes to be encoded
;	A1 = Pointer to location to hold encoded bytes
;
ENCODE	LINK	A2,#-30		;Reserve space for blit structure

	MOVE.W	D0,D1
	LSL.W	#2,D1		;Number of bytes *2
	ORI.W	#$0008,D1	; add 8 (BLITSIZE)
	MOVE.W	D1,-10(A2)
	MOVEM.L	D0/A0-A1,-22(A2)
	MOVE.L	A3,-4(A2)


	LEA	IOBASE.L,A1
ENC20	MOVE.W	DMACONR-IOBASE(A1),D0	;Wait for blitter not busy
	BTST	#14,D0
	BNE.S	ENC20

	MOVE.W	#$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit

	MOVEA.L	#$DFF000,A0
	LEA	-30(A2),A1	;Point to blit structure
	BSR	SUBRXZ

	LEA	IOBASE.L,A1
ENC30	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for blitter finished
	BTST	#6,D0
	BEQ.S	ENC30


	LEA	IOBASE.L,A1
ENC40	MOVE.W	DMACONR-IOBASE(A1),D0	;Wait for blitter not busy
	BTST	#14,D0
	BNE.S	ENC40

	MOVE.W	#$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit

	MOVEA.L	#$DFF000,A0
	LEA	-30(A2),A1	;Point to blit structure
	BSR	SUBRY

	LEA	IOBASE.L,A1
ENC50	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for blitter finished
	BTST	#6,D0
	BEQ.S	ENC50


	LEA	IOBASE.L,A1
ENC60	MOVE.W	DMACONR-IOBASE(A1),D0	;Wait for blitter not busy
	BTST	#14,D0
	BNE.S	ENC60

	MOVE.W	#$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit

	MOVEA.L	#$DFF000,A0
	LEA	-30(A2),A1	;Point to blit structure
	BSR	SUBRZ

	LEA	IOBASE.L,A1
ENC70	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for blitter finished
	BTST	#6,D0
	BEQ.S	ENC70


	LEA	IOBASE.L,A1
ENC80	MOVE.W	DMACONR-IOBASE(A1),D0	;Wait for blitter not busy
	BTST	#14,D0
	BNE.S	ENC80

	MOVE.W	#$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit

	MOVEA.L	#$DFF000,A0
	LEA	-30(A2),A1	;Point to blit structure
	BSR	SUBRAA

	LEA	IOBASE.L,A1
ENC90	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for blitter finished
	BTST	#6,D0
	BEQ.S	ENC90

	MOVEM.L	-22(A2),D0/A0-A1
	MOVE.L	D0,D1

	MOVEA.L	A1,A0
	BSR	SUBRXK

	ADDA.L	D1,A0
	BSR	SUBRXK

	ADDA.L	D1,A0
	BSR	SUBRXK

	UNLK	A2		;Release local variables
	RTS

;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
;	A0 = $DFF000
;	A1 = Pointer to blit structure (node)
;
SUBRXZ	MOVE.L	A5,-(SP)

	MOVEA.L	A1,A5
	BSR	SETBLIT
	MOVEA.L	A5,A1

	MOVEM.L	8(A1),D0-D1/A5
	MOVE.L	D1,76(A0)
	MOVE.L	D1,80(A0)
	MOVE.L	A5,84(A0)	;Disk resource
	MOVE.W	#$1DB1,BLTCON0-IOBASE(A0)
	MOVE.W	#$0000,BLTCON1-IOBASE(A0)
	MOVE.W	20(A1),BLTSIZE-IOBASE(A0)

	MOVEA.L	(SP)+,A5
	RTS

;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
;	A0 = $DFF000
;	A1 = Pointer to blit structure (node)
;
;
SUBRY	MOVE.L	A5,-(SP)

	MOVEM.L	8(A1),D0-D1/A5
	MOVE.L	A5,76(A0)
	MOVE.L	D1,80(A0)
	MOVE.L	A5,84(A0)

	MOVE.W	#$2D8C,BLTCON0-IOBASE(A0)
	MOVE.W	20(A1),BLTSIZE-IOBASE(A0)

	MOVEA.L	(SP)+,A5
	RTS

;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
;	A0 = $DFF000
;	A1 = Pointer to blit structure (node)
;
;
SUBRZ	MOVE.L	A5,-(SP)

	MOVEM.L	8(A1),D0-D1/A5
	ADD.L	D0,D1
	SUBQ.L	#2,D1
	ADDA.L	D0,A5
	ADDA.L	D0,A5
	SUBQ.L	#2,A5
	MOVE.L	D1,76(A0)
	MOVE.L	D1,80(A0)
	MOVE.L	A5,84(A0)

	MOVE.W	#$0DB1,BLTCON0-IOBASE(A0)
	MOVE.W	#$1002,BLTCON1-IOBASE(A0)
	MOVE.W	20(A1),BLTSIZE-IOBASE(A0)

	MOVEA.L	(SP)+,A5
	RTS

;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
;	A0 = $DFF000
;	A1 = Pointer to blit structure (node)
;
SUBRAA	MOVE.L	A5,-(SP)	;Save A5

	MOVEM.L	8(A1),D0-D1/A5
	ADDA.L	D0,A5
	MOVE.L	A5,76(A0)
	MOVE.L	D1,80(A0)
	MOVE.L	A5,84(A0)

	MOVE.W	#$1D8C,BLTCON0-IOBASE(A0)
	MOVE.W	#$0000,BLTCON1-IOBASE(A0)
	MOVE.W	20(A1),BLTSIZE-IOBASE(A0) ;Set size and go

	MOVEA.L	(SP)+,A5	;Restore A5
	RTS

;----------------------------------------------------------------------
;
SUBRXK	MOVE.B	(A0),D0
	BTST	#0,-1(A0)
	BNE.S	LB9CC
	BTST	#6,D0
	BNE.S	LB9D2
	BSET	#7,D0
	BRA.S	LB9D0

LB9CC	BCLR	#7,D0
LB9D0	MOVE.B	D0,(A0)
LB9D2	RTS

;======================================================================
;Decode an MFM-encoded, 8-byte block of data pointed to by A0.
; Return the long word result in D0.
;
DECOD_L	MOVE.L	(A0)+,D0	;Get odd bits
	MOVE.L	(A0)+,D1	;Get even bits
	ANDI.L	#$55555555,D0	;Mask off the clock bits
	ANDI.L	#$55555555,D1
	LSL.L	#1,D0		;Shift odd bits into position
	OR.L	D1,D0		;Combine with even bits
	RTS			;Return with result in D0

;----------------------------------------------------------------------
;Routine to decode and move a block of data (i.e. read sector).
; Inputs: D0 = Number of bytes to decode (512, number after decoding)
;	  A0 = Pointer to decoded buffer (io_Data)
;	  A1 = Pointer to encoded track buffer data
;
;This routine uses the blitter. The following is the blit structure
; formerly used by the graphics library routine QBlit:
;
;	A1 -->	-30			Pointer to next blit node
;		-26	SUBREE		(not used)
;		-22	D0		call clean up routine?
;		-21			(dummy)
;		-20			blit size
;		-18	A0		beamsync
;		-16			clean up
;		-14	A1	
;		-10	D0 *4 ! 8
;		-6
;		-4	A3
;	A2 -->	 0
;
;
DECODE	LINK	A2,#-30		;Reserve space for local variables
	MOVEM.L	D0/A0-A1,-22(A2) ;Save registers in this blit structure

	LSL.W	#2,D0		;*4
	ORI.W	#$0008,D0
	MOVE.W	D0,-10(A2)
	MOVE.L	A3,-4(A2)

	LEA	IOBASE.L,A1
DEC20	MOVE.W	DMACONR-IOBASE(A1),D0	;Wait for blitter not busy
	BTST	#14,D0
	BNE.S	DEC20

	MOVE.W	#$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit

	MOVEA.L	#$DFF000,A0
	LEA	-30(A2),A1	;Set pointer to blit structure
	BSR	SUBREE

	LEA	IOBASE.L,A1
DEC30	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for blitter finished
	BTST	#6,D0
	BEQ.S	DEC30

	MOVEM.L	-22(A2),D0/A0-A1	;Restore registers and stack
	UNLK	A2		;Release local variable space
	RTS

;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
;	A0 = $DFF000
;	A1 = Pointer to blit structure (node)
;
SUBREE	MOVE.L	A5,-(SP)

	MOVEA.L	A1,A5
	BSR	SETBLIT

	MOVEA.L	A5,A1
	MOVEM.L	8(A1),D0-D1/A5
	ADDA.L	D0,A5
	SUBQ.L	#1,A5
	MOVE.L	A5,BLTAPTH-IOBASE(A0)
	ADDA.L	D0,A5
	MOVE.L	A5,BLTBPTH-IOBASE(A0)
	ADD.L	D0,D1
	SUBQ.L	#1,D1
	MOVE.L	D1,BLTDPTH-IOBASE(A0)
	MOVE.W	#$1DD8,BLTCON0-IOBASE(A0)
	MOVE.W	#$0002,BLTCON1-IOBASE(A0)
	MOVE.W	20(A1),BLTSIZE-IOBASE(A0) ;Set size and go

	MOVEA.L	(SP)+,A5
	RTS

;======================================================================
;Routine to scan for sync bytes at start of sector. These sync bytes
; (A1A1, encoded as 44894489) may be shifted any number of bit positions
;Inputs:
;	D0 = Maximum number of encoded words to scan (2748/1660).
;	A0 = Pointer to encoded data (at start of header).
;Outputs:
;	D0 = Number of bits of shift.
;	A0 = Points to table entry before the entry that matches the
;		sync byte.
;
SUBRXO	MOVEM.L	D2-D4/A2,-(SP)

	MOVE.W	#$AAAA,D3	;There are two possible patterns for
	MOVE.W	#$5555,D4	; nulls depending on phase lock

	MOVEA.L	A0,A2		;Point A2 to maximum scan position
	ADDA.L	D0,A2

LBB54	MOVE.W	(A0)+,D2	;Get word from buffer
	CMP.W	D3,D2		;Is it $AAAA?
	BEQ.S	LBB90		;Branch if it is
	CMP.W	D4,D2		;Is it $5555?
	BEQ.S	LBB68		;Branch if it is
	CMPA.L	A0,A2		;Loop if we haven't gone too far
	BHI.S	LBB54

LBB62	MOVEQ	#-1,D0		;Indicate error
	MOVEA.L	D0,A0		;A0 = -1
	BRA.S	LBB8A		;Exit with error

;Found a $5555:
LBB68	MOVEQ	#15,D0		;Initialize counter for table entries
	LEA	TBLEVEN.L,A1	;Point to table containing even bits

LBB70	CMPA.L	A0,A2		;Have we scanned too far?
	BLS.S	LBB62		;Branch if we have -- error exit

	MOVE.W	(A0)+,D1	;Scan past nulls
	CMP.W	D2,D1
	BEQ.S	LBB70

	SUBQ.L	#2,A0		;Back up a word
	MOVE.L	(A0),D1		;Fetch long word from this location
LBB7E	CMP.L	(A1)+,D1	;Compare it to entries in the table
	BEQ.S	LBB88		;Exit loop if found
	SUBQ.L	#2,D0		;Decrement counter
	BGE.S	LBB7E		;Loop through table
	BRA.S	LBB54		;Not found -- try again

LBB88	SUBQ.L	#4,A0		;Adjust A0 to point to matching entry

LBB8A	MOVEM.L	(SP)+,D2-D4/A2
	RTS

LBB90	MOVEQ	#14,D0
	LEA	TBLODD.L,A1
	BRA.S	LBB70

;These tables contain sync byte patterns for all possible bit shifts.
TBLEVEN	DC.L	$2244A244
	DC.L	$48912891
	DC.L	$52244A24
	DC.L	$54891289
	DC.L	$552244A2
	DC.L	$55489128
	DC.L	$5552244A
	DC.L	$55548912

TBLODD	DC.L	$91225122
	DC.L	$A4489448
	DC.L	$A9122512
	DC.L	$AA448944
	DC.L	$AA912251
	DC.L	$AAA44894
	DC.L	$AAA91225
	DC.L	$44894489	;Normal unshifted position

;----------------------------------------------------------------------
;Decode word at A0 and return it in D0.
;Inputs:
;	A0 = Pointer to encoded buffer
;	D2 = Number of bits to shift
;
SUBRXR	MOVE.L	(A0)+,D0
	MOVE.W	(A0),D1
	MOVEQ	#16,D3
	SUB.L	D2,D3
	LSL.L	D3,D0		;D0:= D0 *
	LSR.W	D2,D1
	OR.W	D1,D0
	RTS

;----------------------------------------------------------------------
;Routine to call the blitter handling routine.
; Inputs:
;	D0 = Byte offset into encoded buffer
;	D1 = Index into sync table
;	A2 = Pointer to max offset in buffer
;
;	A1 -->	 0  -30
;		 4  -26  SUBRXT (not used)
;		 8  -22  D0
;		12  -18  A0
;		16  -14  A1 = pointer to max posn
;		20  -10  BlitSize
;		22   -8  Index into sync table
;		24   -6  Data (word) at max scan posn
;		26   -4  A3
;	A2 -->	30    0
;
;
SUBRXS	LINK	A2,#-30		;Reserve space for local variables

	MOVE.B	D1,-8(A2)
	TST.L	D1
	BEQ.S	LBE1A		;Branch if no bit shift required
	ADDQ.L	#2,D0		;Adjust byte offset
LBE1A
	MOVE.L	D0,D1
	ADDI.L	#63,D1
	ANDI.W	#$FFC0,D1
	ORI.W	#$0020,D1
	MOVE.W	D1,-10(A2)

	MOVEM.L	D0/A0-A1,-22(A2)
	MOVE.L	A3,-4(A2)

	LEA	IOBASE.L,A1
SXS20	MOVE.W	DMACONR-IOBASE(A1),D0	;Wait for blitter not busy
	BTST	#14,D0
	BNE.S	SXS20

	MOVE.W	#$0040,INTREQ-IOBASE(A1) ;Clear blitter finished bit

	MOVEA.L	#$DFF000,A0
	LEA	-30(A2),A1	;Point to blit structure
	BSR	SUBRXT

	LEA	IOBASE.L,A1
SXS30	MOVE.W	INTREQR-IOBASE(A1),D0	;Wait for blitter finished
	BTST	#6,D0
	BEQ.S	SXS30

	LEA	-30(A2),A1	;Point to blit structure
	TST.B	22(A1)		;Test shift count
	BEQ.S	SXS40		;Branch if no shift
	MOVEA.L	16(A1),A0	;Get pointer to max position
	MOVE.W	24(A1),-2(A0)	;Restore data
SXS40
	UNLK	A2		;Release local variable space
	RTS

;----------------------------------------------------------------------
;Routine to handle the blitter.
; Inputs:
;	A0 = $DFF000
;	A1 = Pointer to blit structure (node)
;
SUBRXT	MOVE.L	A5,-(SP)	;Save A5

	MOVEA.L	A1,A5		;Save pointer to blit structure in A5
	BSR	SETBLIT		;Set blitter registers
	MOVE.B	22(A5),D0	;Get index into sync table
	MOVEQ	#12,D1		;Move shift count into high nybble
	LSL.W	D1,D0

	MOVE.W	#$05CC,BLTCON0-IOBASE(A0) ;No shift, B = D
	MOVE.W	D0,BLTCON1-IOBASE(A0)	;Set shift value for B source

	MOVEM.L	8(A5),D0-D1/A1	;Restore registers from SUBRXS
	MOVE.L	D1,BLTBPTH-IOBASE(A0)

	TST.B	22(A5)		;Get index into sync table
	BEQ.S	LBE94		;Branch if shift count = 0
	SUBQ.L	#2,A1
LBE94
	MOVE.L	A1,BLTDPTH-IOBASE(A0)	;Max scan positon
	MOVE.W	(A1),24(A5)
	MOVE.W	20(A5),BLTSIZE-IOBASE(A0) ;Set size and go

	MOVEA.L	(SP)+,A5	;Restore A5
	RTS

;----------------------------------------------------------------------
;Set up blitter registers.
; Inputs:
;	A0 = $DFF000
;
;
SETBLIT	LEA	BLTAFWM-IOBASE(A0),A1	;Set first word mask to all ones
	MOVE.L	#$FFFFFFFF,(A1)		; i.e. no masking

	MOVEQ	#0,D0
	LEA	BLTBMOD-IOBASE(A0),A1
	MOVE.L	D0,(A1)+	;BLTBMOD, BLTAMOD, modulo := 0
	MOVE.W	D0,(A1)+	;BLTDMOD

	ADDQ.L	#8,A1
	MOVE.W	#$5555,(A1)	;BLTCDAT
	RTS

;======================================================================
;Routine to delay at least D0 microseconds. (Be aware of interrupts and
; DMA slowing things down. Also be aware that this may not work if a
; faster processor is used.) This will work for up to 16 seconds of
; delay.
;
DELAY	MOVE.L	D2,-(SP)	;Save D2

	MOVE.L	D0,D2		;Get copy of the delay time in D2
	LSR.L	#8,D2		;Divide it by 256
DLY10	MOVE.L	#256,D0		;Set D0 to delay 256 us

	LSR.W	#1,D0		;Adjust D0 for delay loop
	MOVE.L	D0,D1
	LSR.W	#1,D1
	ADD.L	D1,D0
	LSR.W	#2,D1
	SUB.L	D1,D0
	MOVE.W	D0,D1
	SWAP	D0

DLY20	DBF 	D1,@		;Kill 10 cycles * D1 / 6.7  (microsec)
	MOVE.W	#$FFFF,D1
	SUBQ.W	#1,D0
	BPL.S	DLY20

	DBF	D2,DLY10	;Loop for each 256 us interval

	MOVE.L	(SP)+,D2	;Restore D2
	RTS

;----------------------------------------------------------------------
;Routine to compute the "checksum" of encoded data.
; Inputs:
;	D1 = Number of bytes of encoded data
;	A0 = Location of the bytes
; Outputs:
;	D0 = The 32-bit "checksum"
;
DOCKSUM	MOVE.L	D2,-(SP)	;Save D2

	LSR.W	#2,D1		;Divide by 4 to get long words
	SUBQ.W	#1,D1		;Compensate for DBF which includes 0
	MOVEQ	#0,D0		;Initialize checksum

DOCK10	MOVE.L	(A0)+,D2	;EOR the bits (parity style)
	EOR.L	D2,D0
	DBF 	D1,DOCK10

	ANDI.L	#$55555555,D0	;Mask off the MFM clock bits

	MOVE.L	(SP)+,D2	;Restore D2
	RTS

END	EQU	@-1		;ADDRESS OF END OF HANDLER

;======================================================================
;Hook this handler into the unit-handler tables
;
	ORG	4 *UNTNUM +MAXTBL
	DC.L	3520
	DC.L	3520

	ORG	4 *UNTNUM +OFFTBL
	DC.L	0
	DC.L	0

	ORG	4 *UNTNUM +UNTTBL
	DC.L	FLOPHAN
	DC.L	FLOPHAN

	END
	3520
	DC.L	3520

	ORG	4 *U