{  File:  NewFloppyTask.asm
		Last edited by:

		JMM   7-Oct-85 13:14:16  Added Mitch's patches and restored FloppyDefs name.
		Last change by MPL 24-Sep-85 13:48:05 timeouts in WaitFDC
			       Changes located around lines beginning with  ## Mitch ##
			       and ending with lines ## EndMitch ##
		Last change by Mitch Lichtenberg  8-Jan-84 23:58:02 no more 58x MP errors!
		Last change by Dennis Grundler 25-May-83 16:50:35: remove dependency on BC register pair
		Last change by Jim Frandeen July 27, 1982  10:24 AM: new IO Page format
		Last change by Jim Frandeen May 24, 1982  10:29 AM: Simplify resource control and task management, change
		 format of Dma Control Block
		- Have Disk pointers wrap around if ends of buffer will extend over end of ring buffer area.  (May 29, 19
		81  2:14 PM)
		- Use DisableRST and EnableRST subs  (February 24, 1981  12:31 PM)
		- Renamed to FloppyTask  (February 4, 1981  3:50 PM)
		- use interrupts in an attempt to transfer runs of sectors at full speed (January 23, 1981  11:01 AM)
		- Consolidate Read and Write transfers into one main routine - Added Format track (January 8, 1981  3:27 
		PM) (December 30, 1980  4:24 PM)
		- Interleave sectors during sector command  (December 28, 1980  6:27 PM)
		- 10 Step-Ins moved from Initialize command to Recalibrate command (December 26, 1980  5:17 PM)
		- WaitFDCCompletion changed back to test Interrupt bit in IntReq register.  
		- reset the virtual IOCB address in the CSB as a completion signal instead of clearing the valid flag.  T
		he valid flag is being discarded because of atomicity problems. (December 20, 1980  2:50 PM)
		- test busy flag in 1797 directly in "WaitFDCCompletion", code seems to stall  waiting for the interrupt 
		even though busy has gone false (December 19, 1980 7:35 PM)
		- attempt to get rid of Data Lost on Writes by starting CP DMA transfers before disk DMA transfers (Decem
		ber 19, 1980  6:14 PM)
		- All Yields except Block made null (***debug) (October 12, 1980  3:26 PM)

		Written by Roy OgusAugust 26, 1980  1:22 P.M.
		}

		;  DEFINITION FILES:

			get	"SysDefs.asm"
			END	SysDefs

			get	"CommonDefs.asm"
			END	ExecDefs

			get	"FloppyDefs.asm"
			END	FloppyDefs



		;  IMPORTS/EXPORTS:

			IMP	ByteToWord	; From CPSubs
			IMP	CheckCPDMAComplete	; From CPSubs
			IMP	ClearDmaChannel		; From DmaSubs
			IMP	Copy		; From CPSubs
			IMP	DoNakedNotify	; From CPSubs
			IMP	DisableRST	; From Common
			IMP	EnableRST	; From Common
			IMP	EndFDBuffer	; From Buffer
			IMP	ErrorReport	; From MPTask
			IMP	FloppyActiveSwitch1	; From DmaSubs
			IMP	FloppyActiveSwitch2	; From DmaSubs
			IMP	FloppyActiveSwitch3	; From DmaSubs
			IMP	FloppyActiveSwitch4	; From RS232CGet
			IMP	FloppyIocbAddr	; From Common
			IMP	FloppyIocbAddrHi	; From Common
			IMP	PortBusyFlag	; From CPSubs
			IMP	ReadCPbuffer	; From CPSubs
			IMP	ReadDmaCompletion	; From DmaSubs
			IMP	StartCPReadDMA	; From CPSubs
			IMP	StartCPWriteDMA	; From CPSubs
			IMP	StartFloppyChannel	; From DmaSubs
			IMP	StartFDBuffer	; From Buffer
			IMP	WordToByte	; From CPSubs
			IMP	WriteCPbuffer	; From CPSubs
			IMP	WriteDDTrack	; From DirectFormat
			IMP	WriteSDTrack	; From DirectFormat
			IMP	ZeroCommand	; From Common

			EXP	CPXferCnt	; ** Debugging
			EXP	Cylinder	;  For DirectFormat
			EXP	DiskHead	;  For DirectFormat
			EXP	DiskStatus	;  For DirectFormat
			EXP	DiskStatusHi	;  For DirectFormat
			EXP	EncSectorLen	;  For DirectFormat
			EXP	FloppyIOCB
			EXP	FloppyIntr	;  for Common
			EXP	Gap3Len		;  For DirectFormat
			EXP	QuarterSectorLen;  For DirectFormat
			EXP	Sector		;  For DirectFormat
			EXP	SectorCnt	;  For DirectFormat
		; 
0000    C3		DB	opJMP
		FloppyTaskResumeAddress:
0001    0000		DW	StartFloppyTask
		;	JMP	WaitForPortToReadIOCB
		;	JMP	WaitForPortForBufService
		;	JMP	WaitCPDmaCompletion
		;	JMP	TestDiskSectorDone

		;  Floppy task internal storage.

		FloppyCSB:			;14021	
0003  			DS	1*2


		;  IOCB:
		;  Note: words 11 and 12 are only used by the Format Track command.
		FloppyIOCB:
0005  			ds	2		;  Word 0: Buffer Pointer (low 16 bits)
0007  			ds	2		;  Word 1: Buffer Pointer (high)
0009  			ds	2		;  Word 2: Buffer size (words)
000B  			ds	2		;  Word 3: Sector Length (words)
000D  			ds	2		;  Word 4: Context (bit 11-Troy/IBM', bit 12-Double/Single'
000F  			ds	2		;  Word 5: Cylinder
0011  			ds	2		;  Word 6: Head (bits 0:7), Sector (bits 8:15)
0013  			ds	2		;  Word 7: Sector count
0015  			ds	2		;  Word 8: IOCB result
0017  			ds	2		;  Word 9: IOCB command
0019  			ds	2		;  Word 10: Escape Command
001B  			ds	2		;  Word 11: SectorLen/4 (bits 0:7),
						;	    Encoded Sector len (bits 8:15)
001D  			ds	2		;  Word 12: SectorsPerTrack (bits 0:7)
						;	    NumFillCharsInGap3 (bits 8:15)
001F  			ds	6		;  Word 13-15: (Reserved)


		;  DATA BUFFER  (it should be at least 2k bytes = 4 standard sectors):
		;  (In Buffer.asm)

		;  CONTROL Blocks:

		DiskDmaFunction:
0025  			ds	1		;  Function field: DmaRead or DmaWrite
		;  Dma control block for Disk operation:
		DiskDmaCB:
0026  			ds	2		;  Size of data buffer (bytes)
		DiskBufPtr:
0028  			ds	2		;  Start of data buffer

		 
		;  CP Port Control Blocks:
		;  Items that are not fixed are marked with an '*'

		;  PCB for IOCB Fetch.
		FDIocbPCB:
002A  			ds	2		;  *CP buffer pointer (low): IOCB pointer from CSB
002C    0000		DW	CPIOCBHi	;  CP buffer pointer (high)
002E    0020		dw	32		;  CP buffer count (bytes):  IOCB size
0030    0005		dw	FloppyIOCB	;  Pointer to IOP buffer:  Local IOCB

		;  PCB for data transfer.
		FDDataPCB:
0032  			ds	2		;  *CP buffer pointer (low): Data buffer address
0034    0001		DW	1		;  *CP buffer pointer (high): Data buffer address
						;  govern the transfer.  Swapped + Virtual.
0036  			ds	2		;  *CP buffer count (bytes):  Data buffer size
		CPBufPtr:
0038  			ds	2		;  *Pointer to IOP buffer:  Local Data buffer

		;  PCB for IOCB result update.
		FDResultPCB:
003A  			ds	2		;  *CP buffer pointer (low): IOCB.sectorCount
003C    0000		DW	CPIOCBHi	;  CP buffer pointer (high)
003E    0004		dw	4		;  CP buffer count (bytes):  Sector count, result
0040    0013		dw	FloppyIOCB+IocbSectorCnt	;  Pointer to IOP buffer:  Local IOCB.sectorCount

		;  PCB to reset FloppyIocbAddr.
		ResetFloppyIocbAddr:
0042    FF13		dw	FloppyIocbLoc	;  CP buffer pointer (low): IOCB pointer
0044    0000		dw	CPIOPageHi	;  CP buffer pointer (high)
0046    0002		dw	2		;  CP buffer count (bytes):  IOCB size
0048    0000		DW	ZeroCommand	;  Pointer to IOP buffer:

		;  PCB to read Floppy CSB.
		ReadFloppyCSB:
004A    FF21		dw	FloppyCSBLoc	;  CP buffer pointer (low): IOCB pointer
004C    0000		dw	CPIOPageHi	;  CP buffer pointer (high)
004E    0002		dw	FloppyCSBSize	;  CP buffer count (bytes):  IOCB size
0050    0003		DW	FloppyCSB	;  Pointer to IOP buffer:



		;  Miscellaneous storage:

		FDCStateVal:
0052  			ds	1		;  constant for FDC state register
		DefaultFDCState:
0053    85		db	DefFDCStateVal	;  constant for FDC state register
		DiskStatus:
0054  			ds	2		;  Disk status after operation (Low: internal, Hi: external)
		DiskStatusHi	equ	DiskStatus+1
		RemainSize:
0056  			ds	2		;  Remaining size of IOCB buffer
		RemainSizeHi	equ	RemainSize+1
		RestoreCmd:
0058  			ds	1		;  Restore Command
		SeekCmd:
0059  			ds	1		;  Seek Command
		ReadSectorCmd:
005A  			ds	1		;  Read Sector Command
		WriteSectorCmd:
005B  			ds	1		;  Write Sector Command
		SectorCmd:
005C  			ds	1		;  Sector Command storage for the sector command

		;  Disk address data.
		DiskHead:
005D  			ds	1		;  Current side
		Cylinder:
005E  			ds	1		;  Current cylinder
		DCylinder:
005F  			ds	1		;  Desired cylinder in Seek

		;  Note:  Sector, NewIntCycle, ErrorSectorCnt MUST be adjacent bytes!!.
		Sector:
0060  			ds	1		;  Current sector
		F1797:
0061    0008		dw	8H		;  F1797 commands:  8H => 1797, 0 => 1793
		S800:
0063    0000		dw	0H		;  SA800 drive:  2H => SA800, 0 => SA850

		;  Variables used to format a track
		SectorCnt:
0065  			ds	1		;  number of sectors left in track being
						;  formatted
		Gap3Len:
0066  			ds	1		;  number of fill chars in Gap 3 of Diskette
		QuarterSectorLen:
0067  			ds	1		;  size of data record/4.
		EncSectorLen:
0068  			ds	1		;  encoded length of sector (00=>128 bytes,
						;  01=>256, 10=>512, 11=> 1024 bytes)

		;  Disk and CP transfer counts
		CPXferCnt:
0069  			ds	1		;  # or CP transfers to do in disk operation
		;  Error mask used in transfer
		DkErrorMask:
006A  			ds	1		;  one mask for reads, one for writes

		;  Boolean used to decide if disk is running
		DiskActive:
006B  			ds	1		;  0FF=> disk is doing a data transfer,
						;  00 => no data transfer
		;  Boolean used to decide if CP<->IOP transfer should occur
		DiskSectorDone:
006C  			ds	1		;  00 => no transfer needed
						; 0FF => need a transfer
		;  **Debugging variables:
		NoYield:
006D    00		db	0		;  1 = Yield only at end of Command
		;
		;  -------------------------------------------------------------------

		{ Check CSB-IOCB pointer. Get high byte of virtual address. If it is non-zero, start the floppy.  No IOCB
		 will ever be put in virtual page zero.}

		StartFloppyTask:
006E  3A  0000		lda	FloppyIocbAddrHi	
0071  B7		ora	a		
0072  CA  0000		JZ	FloppyTaskYield

0075  3A  0000		LDA	PortBusyFlag
0078  B7		ORA	A
0079  C2  0000		JNZ	FloppyTaskYield

007C  21  004A		LXI	H,ReadFloppyCSB
007F  CD  0000		CALL	ReadCPBuffer	;Read Floppy CSB

		{Transfer the IOCB into local memory.}
0082  CD  0000		call	Disable75	;  turn off all FDC interrupts so commands
						;  that don't use them won't be bothered
0085  2A  0000		LHLD	FloppyIocbAddr	;  Fetch IOCB pointer in main memory (low 16 bits)
0088  22  002A		shld	FDIocbPCB+CPAddr3	;  Store in PCB CP address slot
		;  Setup the FDResultPCB with the pointer to the Sector count in the IOCB.
008B  11  0007		lxi	D,CPIocbSectorCnt	;  DE ← SectorCnt index
008E  19		dad	D		;  H,L ← Pointer to SectorCnt location in IOCB
008F  22  003A		shld	FDResultPCB+CPAddr3	;  Store back (Note: assume no 64K crossing)

0092  21  002A		lxi	h,FDIocbPCB	;  Pointer to FDIocbPCB CPport control block
0095  CD  0000		call	ReadCPbuffer	;  Read CP main memory
		;  IOCB is in local memory.  Clear the Result slot.
0098  21  0000		lxi	h,0		;  Result cleared
009B  22  0015		shld	FloppyIOCB+IocbResult

		;  Dispatch on command.
		FDCommandDisp:
009E  3A  0017		lda	FloppyIOCB+IocbCommand	;  Get command (low 8 bits)
00A1  21  0000		lxi	h,FDCommandTable	;  Point to command table
00A4  C3  0000		jmp	CommandDispatch

		;  Command table is a table of starting address of the command code.

		FDCommandTable:
00A7    0000	C0:	dw	DoNOPCmd		;  NOP (command=0)
00A9    0000	C1:	dw	DoReadSectorCmd	;  Read Sector (command=1)
00AB    0000	C2:	dw	DoWriteSectorCmd	;  Write Sector (command=2)
00AD    0000	C3:	dw	DoWriteDelSectorCmd	;  Write Deleted Sector (command=3)
00AF    0000	C4:	dw	DoReadIDCmd		;  ReadID (command=4)
00B1    0000	C5:	dw	DoFormatTrackCmd	;  FormatTrack (command=5)
00B3    0000	C6:	dw	DoRecalibrateCmd	;  Recalibrate (command=6)
00B5    0000	C7:	dw	DoInitializeCmd		;  Initialize (command=7)
00B7    0000	C8:	dw	DoEscapeCmd		;  Escape (command=8)
		EndCommandTable:
00B9  00		nop
		MaxFloppyCommand	equ	(EndCommandTable-FDCommandTable)/2	;  Max. number +1

		;  Check for valid command, and dispatch on the command.
		;    Jump to Contents [CommandTable + 2*(Command)].
		CommandDispatch:
00BA  FE  09		cpi	MaxFloppyCommand	; Check for largest command
00BC  D2  0000		jnc	NoValidCommand	; nc => command (in A) >= MaxCommand
00BF  87		add	a		;  A ← 2*(Command), assume less than 128 commands
00C0  5F		mov	e,a		;  DE ← 2*Command
00C1  16  00		mvi	d,0
00C3  19		dad	d		; HL ← CommandTable+2*(Command)
		;  Command Found.  H,L points to appropriate slot
00C4  5E		mov	e,m		;  Address to D,E
00C5  23		inx	h
00C6  56		mov	d,m
00C7  D5		push	d		;  stack ← starting addr of command's routine

		{When the floppy and RS232C are both running, we must disable interrupts whenever DmaActive is being upda
		ted.}
00C8  3E  CA		MVI	A,opJZ
00CA  32  0000		STA	FloppyActiveSwitch1
00CD  32  0000		STA	FloppyActiveSwitch2
00D0  32  0000		STA	FloppyActiveSwitch3
00D3  3E  F3		MVI	A,opDI
00D5  32  0000		STA	FloppyActiveSwitch4
00D8  C9		ret				;  Jump to command

		;  The command was not valid.  Mark IOCB with error result.
		NoValidCommand:
00D9  21  0244		lxi	h,ErrorNoValidCommand		;  ERROR:  Invalid command
00DC  CD  0000		call	MakeBadIOCBResult
00DF  C3  0000		jmp	FinishCommand		; return to normal mode
		;

		;  COMMAND processing.

		;  All commands assume that the current context is up to date, and that the controller
		;   is enabled and ready for a command.

		;  Command 0:  NOP command.

		{ Issue a Force Interrupt command, to obtain a type 1 status.}
		DoNOPCmd:
00E2  3E  D0		mvi	a,ForceInt0Cmd		;  Issue command with immediate interrupt
00E4  D3  84		out	FDCCommand		;  Issue command
		;  Wait for completion (should be immediate).
00E6  CD  0000		call	WaitFDCBusy		;  Call end of FDCReset subroutine
			
		;--new			7-Oct-85 12:03:23
00E9  D2  0000		jnc	FDCNoopOkay
00EC  21  0255		lxi	h,597 ; FDCTimeoutInNOP
00EF  CD  0000		call	MakeBadIOCBResult
00F2  C3  0000		jmp	FinishCommand
		;--endnew		7-Oct-85 12:03:23

		FDCNoopOkay:
00F5  CD  0000		call	ReadFDCStatus	;  Call end of WaitFDCCompletion for status
		;  Command complete.  Form IOCB result from status in B,C.
00F8  CD  0000		call	MakeIOCBResultType1	;  Convert B,C into an IOCB.result, store in IOCB

		;  Finish up the command.  This code used by all commands.
		FinishCommand:
		;  Update the Sector Count and Result fields of the IOCB in main memory.
		;  The fields are already updated in local IOCB.
		;  Then clear the IOCB pointer in the CSB, and do a naked notify.
		;  Call the subroutine at NotifyCPResultNoAcq if the CP port is already acquired.

		WaitForPortToNotifyResult:
00FB  3A  0000		LDA	PortBusyFlag
00FE  B7		ORA	A
00FF  CA  0000		JZ	NotifyCPResult
0102  21  00FB		LXI	H,WaitForPortToNotifyResult
0105  C3  0000		JMP	SaveFloppyTaskResumeAddressAndYield
		NotifyCPResult:
0108  21  003A		lxi	h,FDResultPCB	;  Pointer to FDResultPCB CPport control block
010B  CD  0000		call	WriteCPbuffer	;  Write IOCB in main memory
		;  Clear FloppyIocbAddr.
010E  21  0042		lxi	h,ResetFloppyIocbAddr
0111  CD  0000		call	WriteCPbuffer	;  Write IOCB in main memory
		;  Do the naked notify.
0114  2A  0003		lhld	FloppyCSB	;  Mask is in first word
0117  CD  0000		call	DoNakedNotify

		FinishCommand1:
		{When the floppy and RS232C are both running, we must disable interrupts whenever DmaActive is being upda
		ted. Put things back so that we don't disable interrupts. This is important for fast RS232C.}
011A  3E  C3		MVI	A,opJMP
011C  32  0000		STA	FloppyActiveSwitch1
011F  32  0000		STA	FloppyActiveSwitch2
0122  32  0000		STA	FloppyActiveSwitch3
0125  AF		XRA	A		;A ← NOP
0126  32  0000		STA	FloppyActiveSwitch4
0129  21  006E		LXI	H,StartFloppyTask
012C  C3  0000		JMP	SaveFloppyTaskResumeAddressAndYield


		;  Command 1:  Read Sector.

		{  The Read Sector command can operate on a run of sectors on the same track.
		To overlap disk operations with transfers to main memory, double buffering is used.
		}

		DoReadSectorCmd:
012F  3A  005A		lda	ReadSectorCmd	;  Set sector command
0132  32  005C		sta	SectorCmd
		DoReadAddrCmd:
0135  3E  9D		mvi	a,ReadErrorMask		;  Set the read error mask for
0137  32  006A		sta	DkErrorMask		;  Do Transfer
013A  AF		xra	a			;  Disk has done nothing yet
013B  32  006C		sta	DiskSectorDone		;  so start with a disk op.
013E  3E  40		mvi	a,InDmaFunc	;  Set up Dma CB for Reads, memory writes
0140  C3  0000		jmp	DoTransfer		;  Do the rest of the command



		;  Command 2:  Write Sector.

		{  The Write Sector command can operate on a run of sectors on the same track.
		To overlap disk operations with transfers to main memory, double buffering is used.
		}

		DoWriteSectorCmd:
0143  3A  005B		lda	WriteSectorCmd	;  Fix write sector command
0146  E6  FE		ani	nDeletedDataMask	;  Clear the Deleted data bit
0148  32  005C		sta	SectorCmd		;  Store in command location
		DoWriteSectorStart:
014B  3E  DD		mvi	a,WriteErrorMask	;  Set proper error mask used to
014D  32  006A		sta	DkErrorMask		;  detect FDC errors during writes
0150  3E  FF		mvi	a,0FFH			;  say disk has "written" all
0152  32  006C		sta	DiskSectorDone		;  sectors so we will start by
							;  filling all from the CP
0155  3E  80		mvi	a,OutDmaFunc		;  Set up Dma CB for Writes, memory reads
0157  C3  0000		jmp	DoTransfer		;  go finish the Write command



		;  Command 3:  Write Deleted Sector.

		DoWriteDelSectorCmd:
015A  3A  005B		lda	WriteSectorCmd	;  Fix write sector command
015D  F6  01		ori	DeletedDataMask	;  Set the Deleted data bit
015F  32  005C		sta	SectorCmd
0162  C3  014B		jmp	DoWriteSectorStart



		;  Command 4:  Read ID.

		{  Read the first Disk ID field.  Set up Sector command, change "sector length",
		and jump into Read Sector command.}

		DoReadIDCmd:
0165  3E  C0		mvi	a,RAddrCmd	;  Set ReadID command
0167  32  005C		sta	SectorCmd
016A  21  0003		lxi	h,RAddrDataLen	;  Fake sector length
016D  22  000B		shld	FloppyIOCB+IocbSectorLen
0170  C3  0135		jmp	DoReadAddrCmd


		;  Command 5:  Format Track.

		{  The Format Track can format a run of track on one side of the disk.  The recording density is specifie
		d in bit 12 of the IocbMiscContext word of the IOCB.  The number of sectors per track is in IocbSectorCnt
		.  The desired Cylinder is in IocbCylinder and the side in IocbHead.  The encoded sector length is in Ioc
		bEncSectorLen, the Sector Length/4 (always fits in a byte) is in IocbQuarterSectorLen, the number of fill
		 characters in Gap Three is in IocbGap3Len and the number of track to be formatted is in IocbTrackCnt.  A
		ll parameters needed by DirectFormat must be put in individual variables.  DirectFormat cannot access the
		m in FloppyIOCB since it cannot have an imported variable (FloppyIOCB) in expressions.  This command loop
		s seeking to a track, then formatting it.
		}

		DoFormatTrackCmd:
		;  First, was there a disk change?  If so, don't format the new disk
0173  CD  0000		call	CheckDiskChange
0176  B7		ora	a		; 0=> no disk change, nz=> disk changed
0177  C2  00FB		jnz	FinishCommand	;  If disk changed, quit now
		;  Set the context for operation and update FDCState register. (Density, head, format)
017A  CD  0000		call	SetIOCBContext		;  Set up the current context
		;  Check whether we need to do a seek.  Compare desired cylinder with current value.
017D  21  005E		lxi	h,Cylinder		;  Point to current value
0180  3A  000F		lda	FloppyIOCB+IocbCylinder	;  Assumes high part of cylinder is 0
0183  BE		cmp	m
0184  CA  0000		jz	FormatTrackStart	;  z => same, don't need a seek
		FormatTrackSeek:
0187  32  005F		sta	DCylinder
018A  57		mov	d,a			;  Desired cylinder to D
018B  3E  1A		mvi	a,SkCmdNoV		;  do a seek with no verify since
							;  there is probably trash there
018D  5F		mov	e,a			;  Command to E
018E  CD  0000		call	DoSeek
		;  Wait for disk completion.  Yield if not complete.
0191  CD  0000		call	WaitFDCCompletion
		;  Command is complete.  D has internal status, E has external status.
		;  Check for error completion in D  (Not Ready, Seek error, CRC error, Busy)
		FormatTrkSeekComplete:
0194  7A		mov	a,D	
0195  E6  99		ani	Type1ErrorMask	;  Check for error status
0197  C2  0000		jnz	FormatTrackStepError	;  nz => there was an error, abort
		;  Check and update the Cylinder location.
019A  CD  0000		call	UpdateCylinder
		;  Successful seek.  Start the Format.
		;  Decide whether to do single or double density and go do the appropriate
		;  routine
		FormatTrackStart:
019D  3E  00		mvi	a,0		;  load number of first sector-1 (code does
						;  increment before storing
019F  32  0060		sta	Sector
01A2  3A  001B		lda	FloppyIOCB+IocbEncSectorLen	; set the encoded sector
						;  length
01A5  32  0068		sta	EncSectorLen
01A8  3A  001E		lda	FloppyIOCB+IocbSectorsPerTrack	;  get number of sectors
						;  per track
01AB  32  0065		sta	SectorCnt
01AE  3A  001C		lda	FloppyIOCB+IocbQuarterSectorLen	; set the data record len/4
01B1  32  0067		sta	QuarterSectorLen
01B4  3A  001D		lda	FloppyIOCB+IocbGap3Len	;  Set the number of fill chars
						;  in Gap 3
01B7  32  0066		sta	Gap3Len
01BA  3A  0052		lda	FDCStateVal
01BD  E6  08		ani	DDenMask	; is the double density bit on?
01BF  C2  0000		jnz	DoDoubleDensityFormat	;  yes, format for double density
01C2  CD  0000		call	WriteSDTrack	;  no, format a single density track
01C5  C3  0000		jmp	CheckFormatResult	;  and see if done with run of tracks
		DoDoubleDensityFormat:
01C8  CD  0000		call	WriteDDTrack	;  format a double density track
		CheckFormatResult:
		{Direct format used to return the disk status in the BC register pair as well as in DiskStatus and DiskSt
		atusHi.  Now we don't take any chances messing with BC while interrupts can occur.}
01CB  3A  0055		LDA	DiskStatusHi
01CE  5F		MOV	E,A
01CF  3A  0054		LDA	DiskStatus
01D2  57		MOV	D,A
01D3  E6  DD		ani	WriteErrorMask		;  Check for error status
01D5  C2  0000		jnz	FormatTrackError	;  nz => there was an error, abort the IOCB
		;  Successful Format.  Store completion in IOCB, in case it's the last
		;  written.
01D8  CD  0000		call	MakeIOCBResultType2	;  Convert DE into an IOCB.result,
							;  store in IOCB
		;  See if there are any more tracks to format
01DB  3A  0013		lda	FloppyIOCB+IocbSectorCnt	;  This holds the Track count
01DE  3D		dcr	a			;  have done one more track
01DF  32  0013		sta	FloppyIOCB+IocbSectorCnt
01E2  CA  0000		jz	EndFormatTrk		;  quit if no more Tracks to do
01E5  3A  005E		lda	Cylinder		;  Else, calculate next cylinder num
01E8  3C		inr	a
01E9  C3  0187		jmp	FormatTrackSeek		;  and go format that track on this
							;  side
		;  Exits:
		FormatTrackStepError:
01EC  CD  0000		call	SetErrorResult		;  Set the Error bit
01EF  CD  0000		call	MakeIOCBResultType1	;  Update Result location (from B,C)
01F2  C3  0000		jmp	EndFormatTrk

		;  Completion error in the Format Track command.
		;  Set the error bits in IOCB.Result
		FormatTrackError:
01F5  CD  0000		call	SetErrorResult		;  Set the Error bit
01F8  CD  0000		call	MakeIOCBResultType2	;  Convert DE into an IOCB.result
		EndFormatTrk:
01FB  C3  00FB		jmp	FinishCommand




		;  Command 6:  Recalibrate.

		{  Do a Restore command, and check for Track 0.  Set Cylinder to 0.  Before the Restore 
		is done, do 10 step-ins to ensure that the subsequent restore will work}

		DoRecalibrateCmd:
01FE  16  0A		mvi	d,10		;  Issue 10 Stepin commands
0200  1E  5A		mvi	e,StepInCmdNoV	;  Send StepIn commands (no verify)
		InitStepLoop:
0202  7B		mov	a,e
0203  D3  84		out	FDCCommand	;  Issue command
		;  Wait for completion.
0205  EB		XCHG	
0206  22  0000		SHLD	SaveDEOverYield
0209  CD  0000		call	WaitFDCCompletion	;  A = completion
020C    11		DB	opLXID		;DE ← SaveDEOverYield
		SaveDEOverYield:
020D    0000		DW	0
		;  Don't care what the status is.  Any errors will be caught by the Recalibrate.
020F  15		dcr	d		;  More Step-ins?
0210  C2  0202		jnz	InitStepLoop
		;  We should now be beyond (in) from Track 00.  Do the Restore.
		DoRestore:
0213  3A  0058		lda	RestoreCmd
0216  D3  84		out	FDCCommand	;  Start the command
		;  Wait for disk completion.  Yield if not complete.
0218  CD  0000		call	WaitFDCCompletion
		;  Command is complete.  D has internal status, E has external status.
		;  Check for error completion in D  (Not Ready, Seek error, CRC error, Busy)
021B  7A		mov	a,D	
021C  E6  99		ani	Type1ErrorMask	;  Check for error status
021E  C2  0000		jnz	RestoreError	;  nz => there was an error, abort
		;  Check for Track 0 bit.
0221  7A		mov	a,D
0222  E6  04		ani	FDCTk0Mask
0224  CA  0000		jz	RecalibrateError	;  z => NO track 00 bit, error
		;  Set completion.
0227  CD  0000		call	MakeIOCBResultType1	;  Convert B,C into an IOCB.result, store in IOCB
		;  Check and update the Cylinder location.
022A  AF		xra	a			;  Desired cyclinder is 0
022B  32  005F		sta	DCylinder
022E  CD  0000		call	UpdateCylinder
		;  Complete command.
		EndRecalibrate:
0231  C3  00FB		jmp	FinishCommand		;  Finish up the command

		;  Error exits.
		;  Error on the Restore command.
		RestoreError:
0234  CD  0000		call	SetErrorResult		;  Set the Error bit
0237  CD  0000		call	MakeIOCBResultType1	;  Update Result location (from B,C)
023A  C3  0231		jmp	EndRecalibrate

		;  No track 0 bit.  Set recalibrate error.
		RecalibrateError:
023D  3E  08		mvi	a,ErrorMask		;  Set the Error bit in Result
023F  F6  02		ori	RecalibrateErrorMask		;  Set the RecalibrateError bit
0241  CD  0000		call	SetErrorResult1	;  Store the Error bits
0244  CD  0000		call	MakeIOCBResultType1	;  Update Result location (from B,C)
0247  C3  0231		jmp	EndRecalibrate


		;  Command 7:  Initialize.

		{  This command is the first command that should be issued to the FloppyTask.
		It will initialize the floppy controller, set default values of single density, head=0,
		no Precomp, and IBM format in the FDC State register.  Setup the values of the various
		disk commands.  The DiskChange bit in the drive is cleared.}

		DoInitializeCmd:
024A  CD  0000		call	FDCReset		;  Reset the controller
			
		;new 	  		7-Oct-85 12:03:23
024D  D2  0000		jnc	FDCResetOkay2
0250  21  0256		lxi	h,598 ; FDCTimeoutInInit
0253  CD  0000		call	MakeBadIOCBResult
0256  C3  00FB		jmp	FinishCommand
		;endnew 		 7-Oct-85 12:03:23

		;  Initialize various command values.

		FDCResetOkay2:
0259  3A  0061		lda	F1797			;  Form ReadSector command
025C  C6  80		adi	RSectorCmd
025E  32  005A		sta	ReadSectorCmd
0261  3A  0061		lda	F1797			;  Form WriteSector command
0264  C6  A0		adi	WSectorCmd
0266  32  005B		sta	WriteSectorCmd
0269  3A  0063		lda	S800			;  Form Restore command
026C  C6  08		adi	ResCmd
026E  32  0058		sta	RestoreCmd
0271  3A  0063		lda	S800			;  Form Seek command
0274  C6  1C		adi	SkCmd
0276  32  0059		sta	SeekCmd
		;  Disable interrupts from the FDC chip
0279  CD  0000		call	Disable75
		;  Do a NOP command to get the disk status
027C  C3  00E2		jmp	DoNOPCmd



		;  Command 8:  Escape.

		{  Look at the next word in the IOCB to determine the Escape command.
		Currently, 1 => Clear Disk Change, #1 => invalid.
		}

		DoEscapeCmd:
027F  3A  0019		lda	FloppyIOCB+IocbEscCommand	;  Get escape command
0282  FE  01		cpi	ClrDiskChgCmd		;  Is it a Disk Change Clear?
0284  CA  0000		jz	DoDiskChgClr		;  z => Disk change clear
		InvalidEscapeCmd:
0287  21  0246		lxi	h,ErrorInvalidEscapeCmd	;  ERROR:  Invalid Escape command
028A  CD  0000		call	MakeBadIOCBResult
028D  C3  00FB		jmp	FinishCommand

		;  Clear disk change by disabling drive and then enabling the drive with drive select.
		DoDiskChgClr:
0290  3A  0052		lda	FDCStateVal		;  Get current value for FDCState
0293  E6  FE		ani	nDriveSelectMask	;  Clear Drive Select
0295  D3  E8		out	FDCState
0297  F6  01		ori	DriveSelectMask	;  Set Drive Select again
0299  D3  E8		out	FDCState
029B  C3  00E2		jmp	DoNOPCmd		;  Do a NOP command to get the status




		;
		;  FLOPPY DISK SUBROUTINES.

		;  Subroutine:   Disable75
		;  Disable the RST 7.5 interrupt, clear the RST 7.5 FF.

		Disable75:
029E  3E  14		mvi	a,ResetRst75+Rst75DisableMsk	;  RST 7.5, clear 7.5 FF
02A0  CD  0000		call	DisableRST		;  Do the disable
02A3  C9		ret

		;new - 			7-Oct-85 12:03:23
		;  Subroutine:   FDCReset.
		;  Reset the FDC, and abort the automatic Restore with a ForceInterupt.
		;  Set the Context in FDCState and FDCStateVal to the default values.
		;  The diskChange bit in the drive is also cleared.
		;  Returns status in A, after command completes.

		FDCReset:
02A4  2E  03		mvi	l,3		; count = we will try three times to reset

		FDCResetLoop:
02A6  3E  80	        mvi     a,DisableFDC    ;  Clear state register, reset FDC, enable Waits
02A8  D3  E8	        out     FDCState
02AA  3E  09	        mvi     a,9             ;  Wait for more than 50 usec (MR pulse)
02AC  CD  0000	        call    DelayV
02AF  3A  0053	        lda     DefaultFDCState ;  Get default values
02B2  32  0052	        sta     FDCStateVal     ;  Get control byte for FDC
02B5  D3  E8	        out     FDCState
02B7  CD  029E	        call    Disable75       ;  Disable the Floppy interrupt
02BA  3E  D0	        mvi     a,ForceInt0Cmd  ;  Issue Force Interrupt command
02BC  D3  84	        out     FDCCommand
02BE  CD  0000	        call    Delay           ; Busy status not available for 12 usec

02C1  CD  0000		call	WaitFDCBusy	; wait for FDC busy
02C4  D2  0000		jnc	FDCResetOkay

02C7  2D		dcr	l		; couldn't reset FDC
02C8  C2  02A6		jnz	FDCResetLoop
02CB  37		stc
02CC  C9		ret

		FDCResetOkay:
02CD  37		stc
02CE  3F		cmc
02CF  C9		ret

		;  Wait for Busy to reset.  Status returned in A and D.

		; The loop here is where the DLion hangs occasionally when doing rapid floppy
		; operations.

		WaitFDCBusy:
02D0  E5		push	h
02D1  21  1388		lxi	h,5000		; or other count-down value.  ***

		WaitFDCBusyLoop:
02D4  DB  84	        in      FDCStatus       ;  Check for restore command completion
02D6  57	        mov     D,a
02D7  E6  01	        ani     FDCBusyMask
02D9  CA  0000	        jz    	WaitFDCBusyExit

		; count down for timeout
02DC  2D		dcr	l
02DD  C2  02D4		jnz	WaitFDCBusyLoop
02E0  25		dcr	h
02E1  C2  02D4		jnz	WaitFDCBusyLoop

		; timeout occured.
02E4  E1		pop	h
02E5  37		stc
02E6  C9		ret
		; normal exit.
		WaitFDCBusyExit:
02E7  7A	        mov     a,D             ;  Return status in A too
02E8  E1		pop	h
02E9  37		stc
02EA  3F		cmc
02EB  C9	        ret

		;endnew - 		7-Oct-85 12:03:23

		;  Subroutine:   WaitFDCCompletion.
		;  Wait for the completion of the FDC command.
		;  If the command is not complete, the task will Yield control.
		;  Note the use of this routine and interrupt processing are
		;  mutually exclusive.
		;  On exit:
		;	  D = internal status
		;	  E = external status
		;	  DiskStatus = full status

		WaitFDCCompletion:
		;  We save the return address because the stack is not saved over a Yield
02EC  E1		POP	H		;HL ← return address
02ED  22  0000		SHLD	WaitFDCCompletionRet
		WaitFDC:
02F0  3A  006D		lda	NoYield
02F3  B7		ora	a
02F4  C2  0000		JNZ	CheckIntReqCompletion	;  nz => Don't do Yield
02F7  21  0000		LXI	H,CheckIntReqCompletion
02FA  C3  0000		JMP	SaveFloppyTaskResumeAddressAndYield
		CheckIntReqCompletion:
02FD  DB  E9		in	IntReq		;  Check for completion in IntReq register
02FF  EE  FF		xri	IntReqMask	;  Complement active low signals
0301  E6  80		ani	FDCIntMask
0303  CA  02F0		jz	WaitFDC		;  z => controller still busy	
		;  Command has completed.  Get status.
0306  DB  84		in	FDCStatus	;  Read internal status
0308  57		mov	D,a		;  Save in reg D
0309  32  0054		sta	DiskStatus	;  Store status from chip
030C  DB  E8		in	FDCStatusReg	;  Get external status register.
030E  32  0055		sta	DiskStatusHi	;  Store
0311  5F		mov	E,a		;  Move to E
0312    C3		DB	opJMP		;JMP WaitFDCCompletionRet
		WaitFDCCompletionRet:
0313    0000		DW	0		;  Return

		ReadFDCStatus:
		{This subroutine is just like the end of WaitFDCCompletion except that it uses the stack to return.}
0315  DB  84		in	FDCStatus	;  Read internal status
0317  57		mov	D,a		;  Save in reg D
0318  32  0054		sta	DiskStatus	;  Store status from chip
		ReadFDCStatusHi:
031B  DB  E8		in	FDCStatusReg	;  Get external status register.
031D  32  0055		sta	DiskStatusHi	;  Store
0320  5F		mov	E,a		;  Move to E
0321  C9		RET

		;  Subroutine:   UpdateDataPCB.
		;  Update the CP address, and IOP address in the DataPCB.
		;  Update IOP address in DataPCB;
		;  Note that the address update adds the count to the 3-byte address.
		;  On Exit:
		;     H,L = Address of Next IOP buffer

		UpdateDataPCB:
0322  3A  0018		lda	FloppyIOCB+IocbCommandHi	;  Check Command [0]
0325  B7		ora	a
0326  FA  0000		jm	UpdateIOPPtr		;  m => High bit is 1, no adjustment of Size
0329  2A  0036		lhld	FDDataPCB+CPCnt	;  H,L ← count in the DataPCB
032C  CD  0000		CALL	ByteToWord	;  H,L ← count in words
032F  EB		xchg				;  D,E ← count in words
0330  2A  0032		lhld	FDDataPCB+CPAddr3	;  H,L ← low address word in the DataPCB
0333  19		dad	d			;  H,L ← Low CPAddress + TransferCount
0334  22  0032		shld	FDDataPCB+CPAddr3	;  Store back low address word in the DataPCB
0337  3A  0034		lda	FDDataPCB+CPAddr1	;  A ← High address byte
033A  CE  00		aci	0			;  Add in any possible carry from above addition
033C  32  0034		sta	FDDataPCB+CPAddr1	;  Store back in PCB

		;  Update IOP buffer pointer.
		UpdateIOPPtr:
033F  2A  0038		lhld	CPBufPtr		;  get CP's pointer to IOP buffer
0342  CD  0000		call	IncrIOPPtr		;  increment it in ring buff fashion
0345  22  0038		shld	CPBufPtr		;  and store it away
0348  C9		ret


		;  Subroutine:   IncrIOPPtr.
		;  Set up an IOP data buffer pointer based on the value in HL.
		;  On entry:
		;	H,L = original contents of buffer pointer.
		;  On exit:
		;	H,L = New buffer pointer contents
		;  Only A and HL are changed.
		;  Note that the ring buffer wraps around if the END of the NEXT buffer lies
		;  after the end of the ring buffer, not only if the beginning of the next buffer
		;  does.  This protects us from sectors that are too large and from not being on
		;  a proper buffer boundry.  The second case will happen when we switch from
		;  transferring small sectors to transferring large ones.
		IncrIOPPtr:
0349  D5		push	d		;  save regs
034A  E5		push	h
034B  EB		xchg			;  DE ← orig buf ptr
034C  2A  0026		lhld	DiskDmaCB+DmaBufCnt	; HL ← length of buffer
034F  29		dad	h		;  HL ← length of two buffers
0350  2B		dcx	h		;  HL ← distance to end of next buffer
0351  19		dad	d		;  HL ← pointer to end of next buffer area
0352  11  0000		lxi	d,EndFDBuffer	;  get end of total buffer area
0355  CD  0000		call	HLcmpDE		;  Compare end of next buffer-End of Buffer area
0358  DA  0000		jc	FinIncrIOP	;  if end of next buffer<= End of buff area, incr this
						;  buffer pointer
035B  21  0000		lxi	h,StartFDBuffer	;  else, start at beginning of buf area
035E  D1		pop	d		; toss old pointer
035F  D1		pop	d		; restore DE
0360  C9		ret
		; point to the next disk buffer area in the ring buffer
		FinIncrIOP:
0361  D1		pop	d		;  get original buffer pointer
0362  2A  0026		lhld	DiskDmaCB+DmaBufCnt	; HL ← length of disk buffer
0365  19		dad	d		;  HL ← ptr to next disk buffer
0366  D1		pop	d		;  restore DE
0367  C9		ret 



		;  Subroutine   HLcmpDE.
		;  Set the conditions flags according to the double register compare HL-DE.
		;  Only the A reg and the flags are affected.

		HLcmpDE:
0368  7C		mov	a,h		;  compare the hi bytes first
0369  BA		cmp	d
036A  C0		rnz			;  only compare the lo bytes if the hi
036B  7D		mov	a,l		;  bytes are equal
036C  BB		cmp	e
036D  C9		ret



		;  Subroutines to insert the result bits from the disk operation into the IOCB.
		;  The subroutines assume that the Result word was cleared at the start of the operation.
		;  (In parenthesis is indicated what is done after a Type1, and Type2 command)

		; Bit 0:	diskChange (copy, copy) 
		; Bit 1:	0 (0, 0)
		; Bit 2:	twoSided  (copy, copy)
		; Bit 3:	DiskID  (copy, copy)
		; Bit 4:	error  (error, error) - inserted externally to subroutine
		; Bit 5:	0 (0, 0)
		; Bit 6:	recalibrateError (RestoreError/0, 0)
		; Bit 7:	dataLost (0, dataLost from bit 13)

		; Bit 8:	notReady (copy, copy)
		; Bit 9:	writeProtect  (copy, copy)
		; Bit 10:	deletedData  (0, copy)
		; Bit 11:	recordNotFound  (copy, copy)
		; Bit 12:	crcError   (copy, copy)
		; Bit 13:	track00 (copy, from Seek)
		; Bit 14:	index (copy, from Seek)
		; Bit 15:	busy  (copy, copy)


		;  Subroutine:  SetErrorResult.
		;  Set the Error bit in the IOCBResult.
		;  Call at SetErrorResult1 with different error mask in A.

		SetErrorResult:
036E  3E  08		mvi	a,ErrorMask		;  Set the Error bit in Result
		SetErrorResult1:
0370  21  0016		lxi	h,FloppyIOCB+IocbResultHi
0373  B6		ora	m
0374  77		mov	m,a
0375  C9		ret


		;  Subroutine:   MakeIOCBResultType1.
		;  Insert the Result bits in D and E into the IOCB result.
		;  D has low bits (internal status), E has high bits (external status).
		MakeIOCBResultType1:
		;  Process low part of Result.
0376  21  0015		lxi	h,FloppyIOCB+IocbResult	;  Point to Low Result in IOCB
0379  3E  DF		mvi	a,nType1ZeroMaskLo	;  Clear values that should be zero
037B  A2		ana	D
037C  B6		ora	m
037D  77		mov	m,a 			;  Store back in IOCB 
		;  Process high part of Result.
037E  23		inx	h			;  Point to High Result in IOCB
037F  3E  B0		mvi	a,nType1ZeroMaskHi	;  Clear values that should be zero
0381  A3		ana	E
0382  B6		ora	m
0383  77		mov	m,a 			;  Store back in IOCB 
0384  C9		ret

		; Subroutine: MakeBadIOCBResult - make the IOCB bad result flags
		; HL -> would-be MP code.
		MakeBadIOCBResult:
0385  22  0015		shld	FloppyIOCB+IocbResult	; store MP code in result slot
0388  3A  0016		lda	FloppyIOCB+IocbResult+1	; load Hi byte
038B  F6  40		ori	nIocbErrorMaskOr		; set error bit
038D  32  0016		sta	FloppyIOCB+IocbResult+1
0390  C9		ret
			

		;  Subroutine:   MakeIOCBResultType2.
		;  Insert the Result bits in D and E into the IOCB result.
		;  D has low bits (internal status), E has high bits (external status).
		MakeIOCBResultType2:
		;  Process low part of Result.
0391  21  0015		lxi	h,FloppyIOCB+IocbResult	;  Point to Low Result in IOCB
0394  3E  F9		mvi	a,nType2ZeroMaskLo	;  Clear values that should be zero
0396  A2		ana	D
0397  B6		ora	m
0398  77		mov	m,a 			;  Store back in IOCB 
		;  Process high part of Result.
0399  23		inx	h			;  Point to High Result in IOCB
039A  3E  B0		mvi	a,nType2ZeroMaskHi	;  Clear values that should be zero
039C  A3		ana	E
039D  B6		ora	m
039E  77		mov	m,a  			;  Store back in IOCB
		;  Fix up DataLost.  Move bit 5 in DiskStatus to bit 7 in Result. 
039F  3A  0054		lda	DiskStatus		;  Get original low status
03A2  E6  04		ani	FDCLostData		;  Isolate the DataLost bit
03A4  0F		rrc				;  Align appropriately
03A5  0F		rrc
03A6  B6		ora	m
03A7  77		mov	m,a  			;  Store back in IOCB
03A8  C9		ret

		;  Subroutine:  CheckDiskChange.
		;  Check if the DiskChange bit is True.
		;  If so, set the Error and DiskChange bits in IOCB.result.
		;  On exit:
		;	A = 0 :  No disk change
		;	A # 0 :  Disk change,  IOCB result updated

		CheckDiskChange:
		;  Check the DiskChange bit.  If true, abort this IOCB, with Error, DiskChange in Result.
03A9  CD  031B		call	ReadFDCStatusHi	;  Read external status (returned in C)
03AC  7B		mov	a,E
03AD  E6  80		ani	DiskChangeMask	;  Mask DiskChange bit
03AF  C8		rz				;  z =>  no DiskChange, proceed
		;  There was a DiskChange.
03B0  16  00		mvi	D,0			;  Fake a DE status
03B2  CD  036E		call	SetErrorResult		;  Set Error bit
03B5  CD  0376		call	MakeIOCBResultType1	;  Update Result location (from DE)
03B8  3E  FF		mvi	a,-1			;  Set A nonzero
03BA  C9		ret

			
		;  Subroutine:   SetIOCBContext.
		;  Set the controller context from the values in the IOCB.
		;  The parameters changed are Double/Single' density, Troy/IBM', Side select.
		;  Both FDCState and FDCStateVal are changed.

		SetIOCBContext:
03BB  21  0052		lxi	h,FDCStateVal	;  Point to FDCStateVal
03BE  3E  E7		mvi	a,nMiscContextMask	;  Clear out old context bits in FDCStateVal
03C0  A6		ana	m
03C1  77		mov	m,a		;  Store back in FDCStateVal
03C2  3A  000D		lda	FloppyIOCB+IocbMiscContext
03C5  E6  18		ani	MiscContextMask	;  Mask out other bits
03C7  B6		ora	m		;  OR in Double/Single', Troy/IBM' bits
03C8  77		mov	m,a		;  Store back in FDCStateVal
03C9  3A  0012		lda	FloppyIOCB+IocbHead
03CC  E6  01		ani	HeadMask
03CE  32  005D		sta	DiskHead
03D1  E5		push	h		;  Save H,L temporarilly
03D2  CA  0000		jz	ContextSide0	;  Side 0 specified

		;  Side 1 specified.
03D5  3E  20		mvi	a,Side1Mask
03D7  B6		ora	m		;  OR in Side 1
03D8  57		mov	D,a		;  Save FDCState value in D
		;  Fix up Read/Write commands.
03D9  21  005C		lxi	h,SectorCmd	;  Set S bit in  current SectorCmd
03DC  3E  02		mvi	a,Type2SMask
03DE  B6		ora	m
03DF  77		mov	m,a
03E0  C3  0000		jmp	SetContext
		;  Side 0 specified.
		ContextSide0:
03E3  3E  DF		mvi	a,nSide1Mask
03E5  A6		ana	m		;  Clear Side 1 bit
03E6  57		mov	D,a		;  Save FDCState value in D
		;  Fix up Read/Write commands.
03E7  21  005C		lxi	h,SectorCmd	;  Clear S bit in current SectorCmd
03EA  3E  FD		mvi	a,nType2SMask
03EC  A6		ana	m
03ED  77		mov	m,a
		SetContext:
03EE  E1		pop	h		;  Restore H,L to point to FDCStateVal
03EF  7A		mov	a,D		;  Restore saved value from D
03F0  77		mov	m,a		;  Store back saved value in FDCStateVal
03F1  D3  E8		out	FDCState	;  Output to controller
03F3  C9		ret



		;  Subroutine:	DoTransfer

		{  This routine is used in all read and write sector commands.  The common code reduces the total size of
		 Domino while providing a relatively clean procedure.
		}
		;  On Entry:
		;   SectorCmd:   holds command word
		;   DiskSectorDone: holds Boolean telling whether to start disk or CP
		;		    transfer first
		;   A:		    holds DMA function mask
		;  Algorithm:
		;  Set up the Dma control blocks
		;  IF DiskChange, set ERROR and quit
		;  seek to proper cylinder if necessary
		;  WHILE DiskStatus=0 AND FloppyIOCB+IocbSectorCnt>0 DO
		;    WHILE DiskActive DO
		;      IF DiskSectorDone THEN
		;        IF CPXferCnt>0 THEN
		;          call DoCPBufService to transfer sectors between CP and IOP until
		;          no IOP Vacancies exist or CPXferCnt=0;
		;      call Yield;
		;      ENDLOOP;
		;    {Disk is now stopped}
		;    IF DiskStatus=0 AND FloppyIOCB+IocbSectorCnt>0 THEN
		;      DoSectorCmd to start or restart the disk
		;    ENDLOOP;
		;  Post disk status and quit
		DoTransfer:
03F4  32  0025		sta	DiskDmaFunction	;  Store function in Dma CB
03F7  21  0000		lxi	h,StartFDBuffer		;  Setup the IOP buffer pointer
03FA  22  0038		shld	CPBufPtr		;  in DataPCB
03FD  22  0028		shld	DiskBufPtr		;  and in DiskDmaCB
0400  AF		xra	a		;  
0401  32  006B		sta	DiskActive	;  the disk is not transferring data now,
		;  Initialize the DataPCB with CP buffer address, and count, and IOP buffer.
		;  CP address from IOCB buffer address, transfer count from sector length in IOCB.
0404  21  0005		lxi	h,FloppyIOCB	;  Source
0407  11  0032		lxi	d,FDDataPCB	;  Destination
040A  3E  03		mvi	A,3		;  No. of bytes (long address).  Note the flags in the MS byte are not
							;  changed.
040C  CD  0000		call	Copy		;  This copies the 3 bytes of address leaving flags untouched.
040F  2A  000B		lhld	FloppyIOCB+IocbSectorLen	;  Transfer count=sector length (words)
0412  CD  0000		CALL	WordToByte
0415  22  0036		shld	FDDataPCB+CPCnt	;  Store in PCB count field
0418  22  0026		shld	DiskDmaCB+DmaBufCnt	;  Store in Dma CB count field
		;  Set the context for operation and update FDCState register. (Density, head, format)
041B  CD  03BB		call	SetIOCBContext
		;  Check the DiskChange bit.
041E  CD  03A9		call	CheckDiskChange		;  is there a disk change?
0421  B7		ora	a
0422  C2  00FB		jnz	FinishCommand		;  nz =>  had a DiskChange, quit
		;  Check whether we need to do a seek.  Compare desired cylinder with current value.
0425  21  005E		lxi	h,Cylinder		;  Point to current value
0428  3A  000F		lda	FloppyIOCB+IocbCylinder	;  Assumes high part of cylinder is 0
042B  BE		cmp	m
042C  CA  0000		jz	FinXferSetup		;  z => same, don't need a seek
042F  32  005F		sta	DCylinder
0432  57		mov	d,a			;  Desired cylinder to D
0433  3A  0059		lda	SeekCmd
0436  5F		mov	e,a			;  Command to E
0437  CD  0000		call	DoSeek
		;  Nothing else to do, so wait for disk completion.  Yield if not complete.
043A  CD  02EC		call	WaitFDCCompletion
		;  Command is complete.  D has internal status, E has external status.
		;  Check for error completion in D  (Not Ready, Seek error, CRC error, Busy)
043D  7A		mov	a,D	
043E  E6  99		ani	Type1ErrorMask		;  Check for error status
0440  C2  0000		jnz	HadXferSeekError	;  nz => there was an error, abort
		;  Check and update the Cylinder location.
0443  CD  0000		call	UpdateCylinder
			
		;  Successful seek.  Start the Transfer.
		FinXferSetup:
0446  AF		xra	a			;  clear the Disk status
0447  32  0054		sta	DiskStatus		;  there have been no errors so far.
044A  3A  0013		lda	FloppyIOCB+IocbSectorCnt	; Set the length of the
044D  32  0069		sta	CPXferCnt		;  CP transfer
0450  3A  0011		lda	FloppyIOCB+IocbSector	;  Sector ← IOCB.FirstSector
0453  32  0060		sta	Sector			;  set initial sector number
0456  C3  0000		jmp	WaitForDisk		;  Choose whether to start the
							;  CP or disk transfer first

		;  We either got behind the disk or it hasn't started yet.  Start it in either
		;  case
		StartDisk:
0459  CD  0000		call	DoSectorCmd		;  restart the disk
		WaitForDisk:
045C  FB		ei				;  make sure Disk can finish
045D  3A  006D		lda	NoYield
0460  B7		ora	a
0461  C2  0000		JNZ	TestDiskSectorDone	;  nz => Don't do Yield
0464  21  0000		LXI	H,TestDiskSectorDone
0467  C3  0000		JMP	SaveFloppyTaskResumeAddressAndYield
		TestDiskSectorDone:
046A  3A  006C		lda	DiskSectorDone		;  should the CP service the IOP
046D  B7		ora	a			;  buffer? (this is set when a write op
							;  begins, reset when a read op begins)
046E  CA  0000		jz	CheckDiskActive		;  no, see if the transfer is still
							;  going
0471  3A  0069		lda	CPXferCnt		;  yes, are there any more sectors
0474  B7		ora	a			;  to transfer?
0475  C4  0000		cnz	DoCPBufService		;  yes, fill the IOP buffer
		;  The IOP buffer needs no service from the CP.  Is the disk still running?
		CheckDiskActive:
0478  F3		di				;  make sure the disk status does
							;  not change while it is being
							;  investigated
0479  3A  006B		lda	DiskActive
047C  B7		ora	a			;  nz => yes
047D  C2  045C		jnz	WaitForDisk		;  so wait for it
0480  3A  0054		lda	DiskStatus		;  no, did it stop because of an
							;  error?
0483  B7		ora	a			;  nz => there was an error
0484  C2  0000		jnz	PostDkStatus		;  so stop now
0487  3A  0013		lda	FloppyIOCB+IocbSectorCnt	;  no, did it finish the
048A  FB		ei				;  (it's ok for things to happen
							;  again)
048B  B7		ora	a			;  transfer?
048C  C2  0459		jnz	StartDisk		;  transfer faltered, so start or 
							;  restart the disk.
		;  Post the final Disk Status and release the CP port
		PostDkStatus:
048F  FB		ei			;  just in case we got here via an error
						;  posted in DiskStatus
0490  3A  0069		lda	CPXferCnt	;  Are there any sectors to finish up?
0493  B7		ora	a		;  nz => disk just stopped, either ok or
0494  C4  0000		cnz	DoCPBufService	;  because of error.  Transfer all remaining
						;  sectors.
0497  3A  0055		lda	DiskStatusHi	;  get final external status
049A  5F		mov	E,a		;  pass status word in DE
049B  3A  0054		lda	DiskStatus	;  get final internal status
049E  57		mov	D,a
		;  See if an error result should be posted
049F  B7		ora	a
04A0  C4  036E		cnz	SetErrorResult		;  no, set the Error bit
04A3  CD  0391		call	MakeIOCBResultType2	;  Update Result location (from B,C)
04A6  C3  00FB		jmp	FinishCommand	;  Finish up the command

		;  Error branches:

		;  Error in the Seek command.
		HadXferSeekError:
04A9  CD  036E		call	SetErrorResult		;  Set the Error bit
04AC  CD  0376		call	MakeIOCBResultType1	;  Update Result location (from DE)
04AF  C3  00FB		jmp	FinishCommand		;  quit now




		;  Subroutine:   DoCPBufService
		{This routine is used to transfer data between the CP and IOP memories.  There is one large buffer area f
		or the Floppy disk.  It is divided into multiple buffers each holding one sector.  The CP pointer to this
		 buffer is in CPBufPtr.  The disk pointer is in DiskBufPtr.  This routine keeps transferring data, advanc
		ing CPBufPtr and decrmenting CPXferCnt until a) CPXfercnt reaches 0 or b) CPBufPtr=DiskBufPtr.  If it lea
		ves the buffer full, it resets DiskSectorDone. }

		DoCPBufService:
04B2  E1		POP	H		;HL ← return address
04B3  22  0000		SHLD	DoCPBufServiceReturn
		WaitForPortForBufService:
04B6  3A  0000		LDA	PortBusyFlag
04B9  B7		ORA	A
04BA  CA  0000		JZ	StartBufTransfer
04BD  21  04B6		LXI	H,WaitForPortForBufService
04C0  C3  0000		JMP	SaveFloppyTaskResumeAddressAndYield

		;  Transfer the data for the first sector
		;  Is this a read or write operation?  The commands are 001=ReadSector,
		;  010=WriteSector, 011=WriteDelectedSector and 100=ReadID.  Hence if bit
		;  0000 0010 is off, it is a Read op so we should write to the CP.
		StartBufTransfer:
04C3  FB		ei			;  let the interrupt routine in during
						;  CP transfers
04C4  21  0032		lxi	h,FDDataPCB	;  Pointer to FDDataPCB CPport control block
04C7  3A  0017		lda	FloppyIOCB+IocbCommand	; get the command
04CA  E6  02		ani	2		;  Is this a read from the disk?
04CC  C2  0000		jnz	ReadFromCP	;  no, so read from CP, write to disk
04CF  CD  0000		call	StartCPWriteDMA	;  yes, start the Dma write to CP main memory
04D2  C3  0000		jmp	WaitCPDmaCompletion
		ReadFromCP:
04D5  CD  0000		call	StartCPReadDMA	;  data going to Disk from CP
		;  Wait for the transfer to complete
		WaitCPDmaCompletion:
04D8  CD  0000		call	CheckCPDMAComplete
04DB  C2  0000		JNZ	CPComplete
04DE  21  04D8		LXI	H,WaitCPDmaCompletion
04E1  C3  0000		JMP	SaveFloppyTaskResumeAddressAndYield
		CPComplete:
04E4  CD  0322		call	UpdateDataPCB		;  Update CP address, IOP ptr in PCB
		;  Was that the last CP transfer?
04E7  F3		di			;  don't let the interrupt routine change
						;  things now
04E8  3A  0069		lda	CPXferCnt
04EB  3D		dcr	a
04EC  32  0069		sta	CPXferCnt
04EF  CA  0000		jz	FinCPBufSrvc	;  yes, just finish up the buffer service
		;  More sectors to transfer, is the IOP buffer area full?
04F2  EB		xchg			;  DE ← addr of next buffer to be transferred
						;  by CP
04F3  2A  0028		lhld	DiskBufPtr	;  HL ← addr of buff being used by disk now
04F6  CD  0368		call	HLcmpDE		;  is the buffer area full (z=>yes)
04F9  C2  04C3		jnz	StartBufTransfer	;  yes, finish the buffer service
		;  Buffer is now full, reset DiskSectorDone, release CP port and finish
		FinCPBufSrvc:
04FC  AF		xra	a		;  turn off DiskSectorDone
04FD  32  006C		sta	DiskSectorDone	;  note interrupts are disabled now
0500  FB		ei			;  let the disk finish now
0501    C3		DB	opJMP		;JMP DoCPBufServiceReturn
		DoCPBufServiceReturn:
0502    0000		DW	0		;RET



		;  Interrupt Routine:   FloppyIntr
		;  This routine is called whenever the disk finishes a transfer of one
		;  sector.  It checks the status, the count of remaining sectors and the
		;  buffer pointers.  Another sectors is started if and only if there was no
		;  error on the last one, there are more sectors to do and there is either
		;  data available to write or space available for data to be read into.

		FloppyIntr:
0504  F5		push	psw		;  Save the Regs
0505  D5		push	d
0506  E5		push	h
0507  FB		ei			;  ok for other processes (RS232) to interrupt
						;  this one.
0508  DB  E8		in	FDCStatusReg	;  Get external status register.
050A  32  0055		sta	DiskStatusHi	;  save it
050D  5F		mov	E,a		;  Save for DMA test below
050E  DB  84		in	FDCStatus	;  Read internal status
0510  57		mov	D,a		;  prepare to test disk status
0511  3A  006A		lda	DkErrorMask	;  get proper error mask for this operation
0514  A2		ana	D		;  look for errors in that operation
0515  32  0054		sta	DiskStatus	;  Store status from chip
0518  C2  0000		jnz	StopDiskService	;  if error, quit now
		;  No disk error, check DMA completion, and clear the disk DMA channel.
		;  If both DMA indicators agree the chip completed, no errors.  If they
		;  disagree, we jump to ErrorReport showing which one failed.  If both
		;  say DMA didn't complete, we post LostData and return.
051B  21  0000		lxi	h,0		;  initialize offset into DMA error table
		;  Check that DMA terminated.
051E  7B		mov	a,E		;  Get external disk status
051F  E6  40		ani	FDCEndCountMask
0521  C2  0000		jnz	GoodDmaEndCount1
0524  2E  04		mvi	l,4
		GoodDmaEndCount1:
0526  1E  01		mvi	E,FloppyChannelMask
0528  CD  0000		call	ReadDMACompletion	;  Get completion
052B  C2  0000		jnz	EndDmaCheck	;  nz => channel completed, look at both
						;  indicators
052E  3E  08		mvi	a,8		;  else set the error flag
0530  B5		ora	l
0531  6F		mov	l,a
		EndDmaCheck:
0532  11  0000		lxi	d,DmaCheckTable	;  load base of instruction table used to
						;  check the DMA completion
0535  19		dad	d		;  calculate the address of the code used to
						;  deal with the DMA completion or lack of it
0536  E9		pchl			;  go deal with the DMA
		DmaCheckTable:
0537  C3  0000		jmp	SectorCmdDone	;  Both DMAs ok
053A  00		nop			;  each table entry is 4 bytes
053B  C3  0000		jmp	NoDmaEndCount1	;  only the External DMA End Count failed
053E  00		nop
053F  C3  0000		jmp	NoDmaEndCount2	;  only internal DMA End Count failed
0542  00		nop
		; both DMA endcounts agree the DMA did not finish.  Probably this means the
		; sector length was wrong, so say LostData and quit
0543  3E  04		mvi	a,FDCLostData	;  set the Lost Data bit
0545  32  0054		sta	DiskStatus
0548  C3  0000		jmp	StopDiskService	;  stop the Transfer now
		;  Only the External DMA end count indicator failed
		NoDmaEndCount1:
054B  21  024A		lxi	h,ErrorNoDmaEndCount1	;  ERROR:  External Dma End Count not set
054E  C3  0000		jmp	ErrorReport	;  Loop forever
		;  Only the Internal DMA end count indicator failed
		NoDmaEndCount2:
0551  21  024B		lxi	h,ErrorNoDmaEndCount2	;  ERROR:  Internal Dma End Count
						;  not set
0554  C3  0000		jmp	ErrorReport	;  Loop forever
		;  Decrement the Disk count for the operation that just
		;  completed successfully
		SectorCmdDone:
0557  3A  0060		lda	Sector		;  increment the Sector num so the CP
055A  3C		inr	a		;  routine will know where to retry
055B  32  0060		sta	Sector
055E  3A  0013		lda	FloppyIOCB+IocbSectorCnt	;  get the num of sectors left
0561  3D		dcr	a		;  update it
0562  32  0013		sta	FloppyIOCB+IocbSectorCnt
		;  Update the IOP buffer address. Note this must be done even if that
		;  was the last sector to allow DoCPBufService to transfer all sectors.
0565  2A  0028		lhld	DiskBufPtr
0568  CD  0349		call	IncrIOPPtr	;  Point to the next buffer location
056B  22  0028		shld	DiskBufPtr	;  Update mem pointer
056E  EB		xchg			;  has the disk caught up to the CP transfer?
056F  2A  0038		lhld	CPBufPtr	;  Compare the two ring buf ptrs
0572  CD  0368		call	HLcmpDE
0575  CA  0000		jz	StopDiskService	;  yes, can't do any more transfers
0578  3A  0013		lda	FloppyIOCB+IocbSectorCnt	;  get the num of sectors left
057B  B7		ora	a		;  is it zero?
057C  CA  0000		jz	StopDiskService	;  quit if that was the last sector to
						;  be transferred
		;  There were no errors, there are more sectors to do and there is buffer
		;  space to use, do start another Sector
057F  CD  0000		call	DoSectorCmd
0582  C3  0000		jmp	FinDiskService	;  and finish up
		;  There is some reason to stop now.  Disable the interrupts and stop
		StopDiskService:
0585  AF		xra	a		;  turn off DiskActive
0586  32  006B		sta	DiskActive
0589  CD  029E		call	Disable75	;  turn off the Floppy interrupts
058C  3E  01		mvi	a,FloppyChannelMask	;  turn off the DMA, just to make sure
058E  CD  0000		call	ClearDmaChannel
		FinDiskService:
0591  3E  FF		mvi	a,0FFH		;  Tell DoTransfer something has happened
0593  32  006C		sta	DiskSectorDone
0596  E1		pop	h		;  restore the registers
0597  D1		pop	d
0598  F1		pop	psw
0599  C9		ret			;  from interrupt

		;  Subroutine:  UpdateCylinder.
		;  After a type 1 command has executed check the Track register in the 1797.
		;  Check if Track register matches DCylinder, and update Cylinder if so.
		;  On entry:
		;    DCylinder = Desired Cylinder 

		UpdateCylinder:
059A  DB  85		in	FDCTrack	;  Check track register
059C  21  005F		lxi	h,DCylinder	;  Pointer to desired cylinder
059F  BE		cmp	m
05A0  CA  0000		jz	DoUpdateCylinder	;  Check if correct
		CommandTrackError:
05A3  E1	        pop	h		; balance return on stack
05A4  21  0247		lxi	h,ErrorCommandTrackError	;  ERROR:  Track register is not correct
05A7  CD  0385	        call	MakeBadIOCBResult
05AA  C3  00FB		jmp	FinishCommand

		;  Track register is correct. Update Cylinder location.
		DoUpdateCylinder:
05AD  32  005E		sta	Cylinder
05B0  C9		ret


		;  Subroutine:  DoSeek.
		;  Issue the Seek command.
		;  Assumes FDC Track register is up-to-date.
		;  Check validity of track number (**debugging only).
		;  Load Data register, and issue command.
		;  On entry:
		;    D = Desired Track 
		;    E = Command

		DoSeek:
05B1  7A		mov	a,d		;  Check if desired Track OK
05B2  FE  4D		cpi	77
05B4  DA  0000		jc	SeekTrackOK	;  c =>  Cylinder number OK
		TrackToBig:
05B7  E1		pop	h		; make stack balance
05B8  21  0248		lxi	h,ErrorTrackToBig		;  ERROR:  Track number is too large.
05BB  CD  0385		call	MakeBadIOCBResult
			
05BE  C3  00FB		jmp	FinishCommand	;  return bad status

		;  Note there is no need to check to see if the track number is negative.  In
		;  two's complement negative numbers are greater than 77 so would be caught in the
		;  test above.
		;  Track number in A is OK.
		SeekTrackOK:
05C1  D3  87		out	FDCData	;  Set desired track in Data Register
		;  Check destination cylinder.
		;  If greater than PreCompStart then set EnPreComp, else clear EnPreComp.
05C3  3A  0052		lda	FDCStateVal	;  Check density in FDCStateVal
05C6  E6  08		ani	DDenMask	;  Mask out Double density mask
05C8  CA  0000		jz	SetNoPreComp	;  z => single density
		;  Double density.  Check if PreComp needed.
05CB  26  2B		mvi	H,PreCompStart	;  Check if WritePrecomp should be changed
05CD  7A		mov	a,d		;  Desired cylinder to A
05CE  BC		cmp	H		;  A has desired cylinder, H has start of PreComp
05CF  DA  0000		jc	SetNoPreComp	;  c => present cylinder < PreComp boundry so no precomp	
		;  Enable write Precomp.
		SetPreComp:
05D2  3E  40		mvi	a,FDCEnPreCompMask
05D4  21  0052		lxi	h,FDCStateVal		;  Point to FDCStateVal
05D7  B6		ora	m			;  Set the bit
		IssueSeek:
05D8  77		mov	m,a 			;  Store in FDCStateVal
05D9  D3  E8		out	FDCState		;  Store in FDCState
05DB  7B		mov	a,e		;  Get Command
05DC  D3  84		out	FDCCommand
05DE  C9		ret

		;  Set write precomp.
		SetNoPreComp:
05DF  3E  BF		mvi	a,nFDCEnPreCompMask	;  Clear write precomp. bit
05E1  21  0052		lxi	h,FDCStateVal		;  Point to FDCStateVal
05E4  A6		ana	m			;  Clear the bit
05E5  C3  05D8		jmp	IssueSeek



		;  Subroutine:  DoSectorCmd.
		;  Implements Read commands: Read Sector, Read Address.
		;  Implements Write commands: Write Sector, Write Deleted Sector.
		;  Assumes Head at track and Track Register up to date.
		;  On entry:
		;    Sector = Desired sector 
		;    SectorCmd = Command
		;  Dma Control Block is set up in DiskDmaCB.
		;  Call subroutine at DoReadAddressTrack, for Read Address or Read Track.

		DoSectorCmd:
05E8  3A  0060		lda	Sector		;  Set up sector for FDC dhip
05EB  D3  86		out	FDCSector	;  Set desired sector in FDCSector Register
		ContSectorCmd:
		;  Program channel 0 of Dma controller for memory writes.
05ED  3A  0025		LDA	DiskDmaFunction
05F0  21  0026		lxi	h,DiskDmaCB	;  Point to DmaCB
05F3  CD  0000		call	StartFloppyChannel	;  Enable the channel

		;  Dma channel is now programmed and enabled.
		GoodDmaChannel:
05F6  3A  005C		lda	SectorCmd	;  Get FDC command
05F9  D3  84		out	FDCCommand	;  Issue command
		;  Turn on the Floppy Interrupts so FloppyIntr can handle the end of the
		;  transfer.  Clear 7.5 FF.
		EnFloppyInterrupt:
05FB  3E  14		mvi	a,ResetRst75+Rst75DisableMsk	;  RST 7.5, clear 7.5 FF
05FD  CD  0000		call	EnableRST	;  Enable the Floppy interrupt but let the
						;  calling routine actually enable interrupts
0600  3E  FF		mvi	A,0FFH		;  turn on DiskActive for DoTransfer
0602  32  006B		sta	DiskActive
0605  C9		ret


		;  Subroutine:   Delay.
		;  Floppy Delay subroutine.
		;  Call at Delay (no A register setup) for delay of ~15.6 us.
		;  Call at Delay9 (no A register setup) for delay of ~9.5 us.
		;  Call at DelayV for variable wait:
		;    If (A) = k, then Delay = 32+ 14k cycles
		;			   =  10.67 + 4.67k usec at 3MHz CPU
		;		    k=9 => > 50 us.
		Delay:
0606  3E  01		mvi	a,1
		DelayV:
0608  3D		dcr	a
0609  C2  0608		jnz	DelayV
		Delay9:
060C  C9		ret

		SaveFloppyTaskResumeAddressAndYield:
060D  22  0001		SHLD	FloppyTaskResumeAddress
		FloppyTaskYield:
0610  			DS	0
		{Pass control to the next task specified in Domino.cfg}

			END	FloppyTask