;  DESCRIPTION:     Subroutine allowing FDExerciser to format a track.
;    This routine allows the user to format a track on the floppy.  It
;    was taken out of FloppyTask as that was getting too big for Bravo.

;  Last modification by Grundler:  3-Jun-83 10:05:20 comment the changes of 25-May-83 16:37:13
;  Last modification by Grundler: 25-May-83 16:37:13 added subroutine DelayPastIndexPulse
;  Last modification by Fasnacht/Grundler: 24-May-83 12:10:45
;  Last modification by Jim Frandeen: May 21, 1982  7:46 AM

;  File:  [Idun]<WDLion>DirectFormat.asm
;  Written by Dan Davies

;  Modification History:
;	- Tried new formatting code, 8085 feeds bytes directly to FDC chip
;	  as it needs them (January 5, 1981  11:13 AM)
;	- Added single density formatting (January 8, 1981  11:10 AM)
;	- convert for Domino8 (January 10, 1981  5:06 PM)
;	- prevent IOP from locking up if there is an error as soon as the
;	  command is issued (write protect, Not ready, etc)
;	- (February 2, 1981  1:18 PM)
;	Change format of Dma Control Block - (March 10, 1982  11:52 AM)

;  Notes:



;  DEFINITION FILES:

	get "CommonDefs"	; Common defs
	get "SysDefs"	; system defs
	get "FloppyDefs"	; floppy disk defs


;  IMPORTS/EXPORTS:

	IMP	Sector, SectorCnt, Gap3Len	;  from FloppyTask
	IMP	Cylinder, QuarterSectorLen	;  from FloppyTask
	IMP	DiskStatus, DiskStatusHi	;  from FloppyTask
	IMP	DiskHead, EncSectorLen	;  from FloppyTask

	IMP	StartFloppyChannel,ClearDmaChannel	;  From DmaSubs

	EXP	WriteDDTrack, WriteSDTrack	;  to FloppyTask


; DEFINITIONS:

;  FDC definitions.
;  FDC State Register definitions.
WriteTrackCmd	equ	0F4H	; WriteTrack command (E=1)

;  Single density.  IBM 3740: 128 bytes/sector, 26 sectors/track.
SDsectorLength	equ	00H	; Encoded sector length (128 bytes)
SDsectorSize		equ	128	;  Actual sector length
NoSDSectors		equ	26	;  Number of sectors
SDGapFourFFvals	equ	0FF28H	;  Number of FFs at end of Gap Four
SDGapFour00vals	equ	00006H	;  Number of 00s at end of Gap Four
SDIndexMark		equ	0FCH	;  Code used to produce index mark
SDGapOneFFvals	equ	0FF1AH	;  Number of FFs in Gap One (post index)
SDGapThree00vals	equ	00006H	;  Number of 00s at end of Gap Three
SDIDAddrMark		equ	0FEH	;  Code used to produce ID addr mark
SDWrCRCCode		equ	0F7H	;  Code used to produce 2 byte CRC
SDGapTwoFFvals	equ	0FF0BH	;  Number of FFs in Gap Two
SDGapTwo00vals	equ	00006H	;  Number of 00s at end of Gap Two
SDDataAddrMark	equ	0FBH	;  Code used to produce Data Rec addr mark
SDDataVals		equ	0E520H	;  Top byte=dummy Data, Bottom byte= (size
					;  of data record)/4=128/4=32.
SDGapThreeFFvals	equ	0FF1BH	;  Number of FFs at beginning of Gap Three


;  Double density.  IBM 2D: 512 bytes/sector, 15 sectors/track (except track 0).
DDsectorLength	equ	02H	; Encoded sector length (512 bytes)
QuarterDDsectorSize	equ	80H	; Actual sector length/4 (Note: 00 = 256, do twice, all data sectors written in 4 blocks)
NoDDSectors	equ	15		;  Number of sectors
DDGapFour4Evals	equ	04E50H	; Number of 4Es between physical index
					; mark and soft index in Gap Four
DDGapFour00vals	equ	0000CH	; number of 00 preceeding soft index mk
DDGapFourF6vals	equ	0F603H	; number of F6s preceeding index mark
DDIndexMark		equ	0FCH	; code used to produce index mark on disk
DDGapOne4Evals	equ	04E32H	; number of 4Es in Gap One (Post Index)
DDGapThree00vals	equ	0000CH	; number of 00s preceeding ID addr mk
DDGapThreeF5vals	equ	0F503H	; number of F5s preceeding ID addr mk
DDIDAddrMark		equ	0FEH	; code used to produce ID addr mk
WrCRCCode		equ	0F7H	; code used to produce 2 byte CRC
DDGapTwo4Evals	equ	04E16H	; number of 4Es in Gap Two
DDGapTwo00vals	equ	0000CH	; number of 00s in Gap Two
DDGapTwoF5vals	equ	0F503H	; number of F5s preceeding Data Record
DDDataAddrMark	equ	0FBH	; code used to produce Data addr mk
DDDataVals		equ	04080H	; dummy data (top byte), (size of data
					; record)/4 in bottom byte (512/4=128)
DDGapThree4Evals	equ	04E36H	; number of 4Es in Gap Three

ForceInterruptCmd	equ	0D0H;
IndexMask	equ	2;


;  VARIABLES

;  Because the data request from the floppy chip is latched and that latch
;  can only be cleared by a DMA cycle, we end each format with a DMA cycle.
FormatDmaCB:
	dw	1			;  only 1 byte
	dw	0			;  from address 0

;  Subroutine:  WriteSDTrack.
;  Start the FDC chip and feed it data bytes at the right speed.  It formats
;  the track at single density.
;  The format of the track is set by loading the Sector size/4 and the
;  size of Gap Three from the IOCB.  The code is modified to set these
;  parameters.
;  Cylinder has current cylinder number.
;  SectorCnt holds the number of sectors to be written on this track.
;  Sector holds the number of the next sector to be written.
;  EncSectorLen holds the encoded value for the sector length.
;  DiskHead has the current side.
;  QuarterSectorLen current sector len/4 (fits in a byte).
;  Gap3Len holds the number of fill chars in gap three.

WriteSDTrack:
	lda	QuarterSectorLen	;  get the sector len/4
	sta	SDSetQuarterData1val+1	;  set as parameter to lxi d
						;  instruction
	dcr	a			;  The other three quarters need the count
					;  minus 1 since they write a byte while
					;  loading the parameter
	sta	SDSetQuarterData2val+1	; set second quarter parm
	sta	SDSetQuarterData3val+1	; set third quarter parm
	sta	SDSetQuarterData4val+1	; set fourth quarter parm
	lda	Gap3Len		; get num of fill chars in Gap 3
	dcr	a			;  This should also be decremented before
					;  storing so we may end the block one
					;  byte early to check the sector count
	sta	SDSetThreeFFval+1	;  set this in lxi d instruction
	di				;  make sure we are not disturbed
	PUSH	B			;  make sure we do not upset RS232C
	lxi	b,8000H+FDCStatus	;  load address of FDC status port, we will
					;  always address it using memory-mapped IO
SDSetFourFFval:
	lxi	d,SDGapFourFFvals-1	;  d ← FF (value of beginning of gap 1) and
					;  e ← number of FFs in Gap Four-1.
	call	DelayPastIndexPulse	;  make sure that we do not issue the 
					;  write track command while we are over 
					;  the index mark.
					
					;  Note that the HL register pair contains
					;  the address of the FDC Command register
	mvi	M,WriteTrackCmd		;  start formatting the track
	lxi	h,0			;  wait for FDC chip to get ready after
					;  receiving the command.  This takes 6 us
					;  or 18 cycles.
	lxi	h,8000H+FDCData	;  load address of FDC data port, use "m"
					;  type operations to address it.

;  wait until it asks for the first byte. Note we also go if any error happens
;  so we don't get stuck waiting for the data request if it won't show up
SDChar1Lp:
	ldax	b		;  ready for first data byte yet?
	ani	FDCDRQMask+FDCNotReady+FDCWrProt	;  data request or error flag
				;  on yet?)
	jz	SDChar1Lp	;  no, keep waiting

;  give 1st byte of gap to the chip, wait until the physical index hole comes
;  around and the chip really starts writing.
	mov	m,d		;  send first data byte of gap one
SDChar2Lp:
	ldax	b		;  ready for 2nd char yet (index hole seen)?
	mov	m,d		;  send a byte just in case the chip sent its
				;  request just after the last sample.  If we
				;  waited until exiting the loop, we might be too
				;  late.  Note a couple of extra leader bytes are
				;  sent here and above, but so what.
	ani	FDCDRQMask+FDCNotReady+FDCWrProt
				;  did it want the second byte?
	jz	SDChar2Lp	;  no, keep waiting
;  The time is now between 22 and 47 cycles after the request that allowed us
;  to escape was set.  That request was satisfied by the data sent above.  The
;  next request will rise between (96-47=49) and (96-22=74) cycles from now.
;  That request should be answered between 1 and 69 cycles after it rises (max
;  service time=23.5 us).  Hence, the next data byte should be sent no sooner
;  than 74+1 cycles from now and no later than 49+69=118 cycles from now.  We
;  will send the next byte in 78 cycles.
	nop			;  delay 4 cycles, leave the Zero flag cleared
				;  so the synchronization loop tries to speed up.
				;  This seems marginally safer than slowing down.
	call	SDDelay	;  delay another 48 cycles, total of 52
	call	SDWrtBlkSkip	;  delay another 18 cycles for a total of 70
				;  the instruction at SDWrtBlkSkip writes the data
				;  in 8 cycles, a total of 78 as planned.  The
				;  SDWrtBlock loop then synchronizes with the
				;  FDC chip.
;  At this point, we are synchronized with the FDC chip and have written the
;  FFs at the end of Gap Four.  If the
;  cycle on which the ldax b is executed is called cycle number 0 in the
;  loop, we are now at cycle 7.  This is because the JNZ instruction in
;  SDWrtBlock takes 7 cycles, not 10, to exit and the return takes 10
;  cycles.  The normal course of the loop is to take the JNZ branch in 10
;  cycles.  Hence we are now at time (7-10)+10 or time 7.  Our next task is
;  to write the zeros in Gap Four.  The parameters are loaded below and the
;  first one is written.  The normal SDWrtBlock loop is then called to write
;  the rest.  Hereafter the numbers in parentheses
;  give the time AFTER the referenced instruction has been executed.  These
;  Times are shown mod 96.
	lxi	d,0		;  (17) wait
SDSetFour00val:
	lxi	d,SDGapFour00vals-1	;  (27) d←00, e ← # or 0s in Gap Four-1
	mov	m,d		;  (35) time to store the first zero,  This is
				;  3 cycles early, but still acceptable.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized
;  Write the index mark and start the block of FFs that begin Gap One.
	mvi	a,SDIndexMark	;  (14) get the char that causes the index mark to
				;  be written.
SDSetOneFFval:
	lxi	d,SDGapOneFFvals	; (24) d ← FF, e ← # or FFs in post index
	out	FDCData	;  (35) write index mark 3 cycles early, but still
				;  acceptably on time.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  Start the loop used to write the sectors.  The first thing to do is write
;  The 00s that end Gap Three.
SDSectorLp:
	lxi	d,0		;  (17) wait
SDSetThree00val:
	lxi	d,SDGapThree00vals-1	;  (27) d←00, e ← # of 00s in Gap Three-1
	mov	m,d		;  (35) write the first 00 a little early, but
				;  still ok.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  Store the ID address mark and start on the Header.  Note there is no
;  synchronization while writing the Header, it is assumed that we cannot
;  drift very far in these 5 bytes.
	mvi	a,SDIDAddrMark	;  (14) get the char that causes the
				;  ID address mark to be written.
	lxi	d,0		;  (24) wait
	out	FDCData	;  (35) write ID mark 3 cycles early, but still
				;  acceptably on time.
	ldax	b		;  (43) wait
	call	SDDelay	;  (91) wait
	ani	0FFH		;  (2)  wait
	lda	Cylinder	;  (15) get current cylinder number
	lda	Cylinder	;  (28) wait
	mov	m,a		;  (36) write it in header, 2 cycles early
	ldax	b		;  (44) wait
	call	SDDelay	;  (92) wait
	ani	0FFH		;  (3)  wait
	lda	DiskHead	;  (16)  wait
	lda	DiskHead	;  (29)  get side number for Header
	mov	m,a		;  (37) store one cycle early, this is safe
	in	FDCStatus	;  (48) wait
	call	SDDelay	;  (0)  wait
	lda	Sector		;  (13) get the number of this sector
	inr	a		;  (17) compute this sector number
	sta	Sector		;  (30) update the sector number
	mov	m,a		;  (38) store it on the disk, right on time
	lxi	d,0		;  (48) wait
	call	SDDelay	;  (0)  wait
	lda	EncSectorLen	;  (13) wait
	nop			;  (17) wait
	lda	EncSectorLen	;  (30) get the encoded value
				;  for the sector length (00=>128, 01=>256,
				;  10=>512, 11=>1024)
	mov	m,a		;  (38) store the sector length in the Header
	lxi	d,0		;  (48) wait
	call	SDDelay	;  (0)  wait
	push	d		;  (10) wait
	pop	d		;  (22)  wait
	mvi	a,SDWrCRCCode	;  (29) A ← code that causes the chip to write a
				;  two byte CRC at the end of the Header.  After
				;  this is sent, we must wait two byte time before
				;  continuing.
	mov	m,a		;  (37) cause the Header field CRC to be written
;  Delay over the next byte and prepare to send the first part of Gap Two
	in	FDCStatus	;  (48) wait
	call	SDDelay	;  (0)  wait
	lxi	d,0		;  (10) wait
SetTwoFFSDval:
	lxi	d,SDGapTwoFFvals	; (20) d ← FF, e ← # of FFs in Gap Two
	ani	0FFH		;  (27) wait
	ldax	b		;  (35) wait
	call	SDWrtBlkEntry	;  (53) start writing the FFs right on time.
;  Finish up Gap Two with its 00s
	lxi	d,0		;  (17) wait
SDSetTwo00val:
	lxi	d,SDGapTwo00vals-1	;  (27) d←00, e ← # of 00s in Gap Two-1
	mov	m,d		;  (35) write the first 00 a little late, but
				;  still in plenty of time.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  Store the Data Record address mark and start on the first quarter of the
;  data record.  The data record is done in quarters because it may be as
;  long as 1024 bytes and the byte count may not be larger than 256.
	mvi	a,SDDataAddrMark	;  (14) get the char that causes the
				;  Data address mark to be written.
SDSetQuarterData1val:
	lxi	d,SDDataVals	;  (24) d ← dummy data, e ← length of data rec/4
	out	FDCData	;  (35) write Data mark 3 cycles early, but still
				;  acceptably on time.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  store second quarter of data record
	lxi	d,0		;  (17) wait
SDSetQuarterData2val:
	lxi	d,SDDataVals-1	;  (27) d←dummy data, e ← (length of data
				;  record/4)-1 (to account for byte just written)
	mov	m,d		;  (35) write the next data val a little early,
				;  but still in plenty of time.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  store third quarter of data record
	lxi	d,0		;  (17) wait
SDSetQuarterData3val:
	lxi	d,SDDataVals-1	;  (27) d←dummy data, e ← (length of data
				;  record/4)-1 (to account for byte just written)
	mov	m,d		;  (35) write the next data val a little early,
				;  but still in plenty of time.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  store fourth quarter of data record
	lxi	d,0		;  (17) wait
SDSetQuarterData4val:
	lxi	d,SDDataVals-1	;  (27) d←dummy data, e ← (length of data
				;  record/4)-1 (to account for byte just written)
	mov	m,d		;  (35) write the next data val a little early,
				;  but still in plenty of time.
	call	SDWrtBlkEntry	;  (53) jump to place in loop where we will be
				;  properly synchronized.
;  Write the CRC at the end of the data record.  Allow two byte times for it
;  to be written, then start Gap Three.
	ldax	b		;  (15)  wait
	ldax	b		;  (23)  wait
	mvi	a,SDWrCRCCode	;  (30)  get char that causes chip to write two-
				;  byte CRC
	mov	m,a		;  (38) tell chip about CRC right on time
;  Delay over the next byte and prepare to send the first part of Gap Three
SDSetThreeFFval:
	lxi	d,SDGapThreeFFvals-1	; (48) d ← FF, e ← # of FFs in Gap Three
				;  minus 1.  This allows us to finish early and
				;  decide whether to do another sector or not.
	call	SDDelay	;  (0)  wait
	xthl			;  (18) wait
	xthl			;  (36) restore (HL, stack) and wait
	call	SDWrtBlock	;  (54) start writing the FFs 1 cycle late.
;  We get here 1 byte before the end of the FF portion of Gap Three.  We see
;  if there are more sectors to do.  If so, go back to SDSectorLp to
;  finsh this gap and start another.  If not, keep sending FFs until the
;  FDC chip turns off the Busy flag (finishes with the track)
	lda	SectorCnt	;  (20) get the number of sectors left
	dcr	a		;  (24) decrement sector count and
	sta	SectorCnt	;  (37) update it.
	mov	m,d		;  (45) write last FF in Gap Three.  This is a
				;  little late but we should be able to write
				;  as late as cycle 61.
	call	SDDelay	;  (93) wait
	jnz	SDSectorLp	;  (finished=>4, more Sectors=>7) If not done
				;  yet, continue with the rest of gap Three.
				;  Note that SectorLp is entered at time 7
				;  from the writing of the Post-Index Gap One
				;  also (Amazing!!).
;  All sectors have been written.  We must maintain synchronization while
;  supplying bytes until the Busy flag drops.  We do this in a two part loop.
;  The first synchronizes, the second detects a drop in Busy.
	jmp	SDLastLpEntry	;  (14) start the loop at roughly the right time
SDLastLp:
	ldax	b		;  (8) get the data request flag.
	ani	FDCDRQMask	;  (15) are we too early or too late?
SDLastLpEntry:
	jnz	SDFastLast	;  (Too Early=>22, Too Late=>25)
	rnz			;  (Too Early=>28) delay extra if we were a little
				;  ahead of the request.
SDFastLast:
	nop			;  (Early=>32, Late=>29) wait
	mov	m,d		;  (Early=>40, Late=>37) store the data roughly
				;  10 us after the request should have risen
	call	SDDelay	;  (Early=>88, Late=>85) wait
	lxi	d,SDGapThreeFFvals	;  (Early=>2, Late=>95) wait
;  At this point we have reached the end of the first part of the loop.  For
;  The sake of clarity, we will assume the next instruction is perfectly
;  synchronized.  Carrying the Early and Late notation further is not
;  helpful.
	ldax	b		;  (8) sample the Busy Flag
	ani	FDCBusyMask	;  (15) has it been reset yet?
	jz	FinFormatTrack	;  (Not done=>22, Done => don't care)
	nop			;  (26) wait
	mov	m,d		;  (34) store another FF 1 cycle early
	nop			;  (38) wait
	Call	SDDelay	;  (86) wait
	jmp	SDLastLp	;  (0) start the synchronizing section right on
				;  time.

;  Subroutine:  WriteDDTrack.
;  Start the FDC chip and feed it data bytes at the right speed.  It formats
;  the track at single density.
;  The format of the track is set by loading the Sector size/4 and the
;  size of Gap Three from the IOCB.  The code is modified to set these
;  parameters.
;  Cylinder has current cylinder number.
;  SectorCnt holds the number of sectors to be written on this track.
;  Sector holds the number of the next sector to be written.
;  EncSectorLen holds the encoded value for the sector length.
;  DiskHead has the current side.
;  QuarterSectorLen current sector len/4 (fits in a byte).
;  Gap3Len holds the number of fill chars in gap three.

WriteDDTrack:
	lda	QuarterSectorLen	;  get the sector len/4
	sta	SetQuarterData1val+1	;  set as parameter to lxi d
					;  instruction
	dcr	a			;  The other three quarters need the count
					;  minus 1 since they write a byte while
					;  loading the parameter
	sta	SetQuarterData2val+1	; set second quarter parm
	sta	SetQuarterData3val+1	; set third quarter parm
	sta	SetQuarterData4val+1	; set fourth quarter parm
	lda	Gap3Len		; get num of fill chars in Gap 3
	dcr	a			;  This should also be decremented before
					;  storing so we may end the block one
					;  byte early to check the sector count
	sta	SetThree4Eval+1	;  set this in lxi d instruction
	di				;  make sure we are not disturbed
	PUSH	B			;  make sure we do not upset RS232C
	lxi	b,8000H+FDCStatus	;  load address of FDC status port, we will
					;  always address it using memory-mapped IO
SetFour4Eval:
	lxi	d,DDGapFour4Evals-1	;  d ← 4E (value of beginning of gap 1) and
					;  e ← number of 4Es in Gap Four-1.
					;  Note this number must be a multiple of 4
					;  or the synchronization loop won't work.

	call	DelayPastIndexPulse	;  make sure that we do not issue the 
					;  write track command while we are over 
					;  the index mark.
					
					;  Note that the HL register pair contains
					;  the address of the FDC Command register
	mvi	M,WriteTrackCmd		;  start formatting the track
	lxi	h,0			;  wait for FDC chip to get ready after
					;  receiving the command.  This takes 6 us
					;  or 18 cycles.
	lxi	h,8000H+FDCData	;  load address of FDC data port, use "m"
					;  type operations to address it.

;  wait until it asks for the first byte. Note we also go if any error happens
;  so we don't get stuck waiting for the data request if it won't show up
Char1Lp:
	ldax	b		;  ready for first data byte yet?
	ani	FDCDRQMask+FDCNotReady+FDCWrProt
				;  (data request or error flag on yet?)
	jz	Char1Lp	;  no, keep waiting

;  give 1st byte of gap to the chip, wait until the physical index hole comes
;  around and the chip really starts writing.
	mov	m,d		;  send first data byte of gap one
Char2Lp:
	ldax	b		;  ready for 2nd char yet (index hole seen)?
	mov	m,d		;  send a byte just in case the chip sent its
				;  request just after the last sample.  If we
				;  waited until exiting the loop, we might be too
				;  late.  Note a couple of extra leader bytes are
				;  sent here and above, but so what.
	ani	FDCDRQMask+FDCNotReady+FDCWrProt
				;  did it want the second byte (or should
				;  we just finish?)?
	jz	Char2Lp	;  no, keep waiting
;  The time is now between 22 and 47 cycles after the request that allowed us
;  to escape was set.  That request was satisfied by the data sent above.  The
;  next request will rise between (48-47) and (48-22) or 26 cycles from now.
;  That request should be answered between 1 and 33 cycles after it rises (max
;  service time=11.5 us).  Hence, the next data byte should be sent no sooner
;  than 26+1 cycles from now and no later than 1+33 cycles from now.  We
;  will send the next byte in 30 cycles.
	nop			;  delay 4 cycles, leave the Zero flag cleared
				;  so the synchronization loop tries to speed up.
				;  This seems marginally safer than slowing down.
	call	WrtBlkSkip	;  delay another 18 cycles for a total of 22.
				;  the instruction at WrtBlkSkip writes the data
				;  in 8 cycles, a total of 30 as planned.  The
				;  WrtBlock loop then synchronizes with the
				;  FDC chip.
;  At this point, we are synchronized with the FDC chip and have written the
;  4Es at the end of Gap Four.  If the
;  cycle immediately following the ldax b is called cycle number 0 in the
;  loop, we are now at cycle 7.  This is because the JNZ instruction in
;  WrtBlock takes 7 cycles, not 10, to exit and the return takes 10 cycles.
;  The normal course of the loop is to take the JNZ branch in 10 cycles.
;  Hence we are now at time (7-10)+10 or time 7.  Our next task is
;  to write the zeros in Gap Four.  The parameters are loaded below and the
;  first one is written.  The normal WrtBlock loop is then called to write
;  the rest.  Hereafter the numbers in parentheses
;  give the time AFTER the referenced instruction has been executed.  These
;  Times are shown mod 48.  The "ldax b" instruction in WrtBlock is given to
;  be executed at time 0.
	lxi	d,DDGapFour00vals	;  (17) d ← 0
	mov	m,d		;  (25) time to store the first zero,  This is
				;  2 cycles late, but still acceptable.
SetFour00val:
	lxi	d,DDGapFour00vals-1	;  (35) d←00, e ← # or 0s in Gap Four-1
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized
;  We have just written the 00s in Gap Four, write the three index-type
;  address marks before the index mark
	lxi	d,DDGapFourF6vals	;  (17) load d with first data val
	mov	m,d		;  (25) write the first FC a little late, but
				;  still in plenty of time.
SetFourF6val:
	lxi	d,DDGapFourF6vals-1	;  (35) d←F6, e ← 2
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  Write the index mark and start the block of 4Es that begin Gap One.
	mvi	a,DDIndexMark	;  (14) get the char that causes the index mark to
				;  be written.
	out	FDCData	;  (25) write index mark 2 cycles late, but still
				;  acceptably on time.
SetOne4Eval:
	lxi	d,DDGapOne4Evals	; (35) d ← 4E, e ← # or 4Es in post index
	call	WrtBlkEntry	;  (5)	jump to place in loop where we will be
				;  properly synchronized.
;  Start the loop used to write the sectors.  The first thing to do is write
;  The 00s that end Gap Three.
SectorLp:
	lxi	d,DDGapThree00vals	;  (17) load d with first data val
	mov	m,d		;  (25) write the first 00 a little late, but
				;  still in plenty of time.
SetThree00val:
	lxi	d,DDGapThree00vals-1	;  (35) d←00, e ← # of 00s in Gap Three-1
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  Store the 3 F5s that preceed the ID Record-type address mark.
	lxi	d,DDGapThreeF5vals	;  (17) load d with first data val
	mov	m,d		;  (25) write the first F5 a little late, but
				;  still in plenty of time.
SetThreeF5val:
	lxi	d,DDGapThreeF5vals-1	;  (35) d←F5, e ← 2
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  Store the ID address mark and start on the Header.  Note there is no
;  synchronization while writing the Header, it is assumed that we cannot
;  drift very far in these 5 bytes.
	mvi	a,DDIDAddrMark	;  (14) get the char that causes the
				;  ID address mark to be written.
	out	FDCData	;  (25) write ID mark 2 cycles late, but still
				;  acceptably on time.
	lxi	d,0		;  (35) wait
	ldax	b		;  (43) wait
	ani	0FFH		;  (2)  wait
	lda	Cylinder	;  (15) get current cylinder number
	mov	m,a		;  (23) write it in header, right on time
	lda	DiskHead	;  (36) wait
	lda	DiskHead	;  (1)  wait
	lda	DiskHead	;  (14) get side number for Header
	mov	m,a		;  (22) store one cycle early, this is safe
	lda	Sector		;  (35) get the number of this sector
	adi	1		;  (42) increment it a slow way
	ani	0FFH		;  (1)  wait
	sta	Sector		;  (14) update the sector number
	mov	m,a		;  (22) store it on the disk
	lda	Sector		;  (35) wait
	ani	0FFH		;  (42) wait
	ani	0FFH		;  (1)  wait
	lda	EncSectorLen	;  (14) get the encoded value
				;  for the sector length (00=>128, 01=>256,
				;  10=>512, 11=>1024)
	mov	m,a		;  (22) store the sector length in the Header
	lda	Sector		;  (35) wait
	lda	Sector		;  (0)  wait
	mvi	a,0		;  (7)  wait
	mvi	a,WrCRCCode	;  (14) A ← code that causes the chip to write a
				;  two byte CRC at the end of the Header.  After
				;  this is sent, we must wait two byte time before
				;  continuing.
	mov	m,a		;  (22) cause the Header field CRC to be written
;  Delay over the next byte and prepare to send the first part of Gap Two
	xthl			;  (40) wait
	xthl			;  (10) restore (HL, stack) and wait
	lxi	d,0		;  (20) wait
SetTwo4Eval:
	lxi	d,DDGapTwo4Evals	; (30) d ← 4E, e ← # of 4Es in Gap Two
	call	WrtBlock	;  (0) start writing the 4Es right on time.
;  Finish up Gap Two with its 00s
	lxi	d,DDGapTwo00vals	;  (17) load d with first data val
	mov	m,d		;  (25) write the first 00 a little late, but
				;  still in plenty of time.
SetTwo00val:
	lxi	d,DDGapTwo00vals-1	;  (35) d←00, e ← # of 00s in Gap Two-1
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  Store the 3 F5s that preceed the Data Record-type address mark.
	lxi	d,DDGapTwoF5vals	;  (17) load d with first data val
	mov	m,d		;  (25) write the first F5 a little late, but
				;  still in plenty of time.
SetTwoF5val:
	lxi	d,DDGapTwoF5vals-1	;  (35) d←F5, e ← 2
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  Store the Data Record address mark and start on the first quarter of the
;  data record.  The data record is done in quarters because it may be as
;  long as 1024 bytes and the byte count may not be larger than 256.
	mvi	a,DDDataAddrMark	;  (14) get the char that causes the
				;  Data address mark to be written.
	out	FDCData	;  (25) write Data mark 2 cycles late, but still
				;  acceptably on time.
SetQuarterData1val:
	lxi	d,DDDataVals	;  (35) d ← dummy data, e ← length of data rec/4
	call	WrtBlkEntry	;  (5)	jump to place in loop where we will be
				;  properly synchronized.
;  store second quarter of data record
	lxi	d,DDDataVals	;  (17) load d with next dummy data val
	mov	m,d		;  (25) write the next data val a little late, but
				;  still in plenty of time.
SetQuarterData2val:
	lxi	d,DDDataVals-1	;  (35) d←dummy data, e ← (length of data
				;  record/4)-1 (to account for byte just written)
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  store third quarter of data record
	lxi	d,DDDataVals	;  (17) load d with next dummy data val
	mov	m,d		;  (25) write the next data val a little late, but
				;  still in plenty of time.
SetQuarterData3val:
	lxi	d,DDDataVals-1	;  (35) d←dummy data, e ← (length of data
				;  record/4)-1 (to account for byte just written)
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  store fourth quarter of data record
	lxi	d,DDDataVals	;  (17) load d with next dummy data val
	mov	m,d		;  (25) write the next data val a little late, but
				;  still in plenty of time.
SetQuarterData4val:
	lxi	d,DDDataVals-1	;  (35) d←dummy data, e ← (length of data
				;  record/4)-1 (to account for byte just written)
	call	WrtBlkEntry	;  (5) jump to place in loop where we will be
				;  properly synchronized.
;  Write the CRC at the end of the data record.  Allow two byte times for it
;  to be written, then start Gap Three.
	mvi	a,WrCRCCode	;  (14)  get char that causes chip to write two-
				;  byte CRC
	mov	m,a		;  (22) tell chip about CRC one cycle early, but
				;  still acceptably on time
;  Delay over the next byte and prepare to send the first part of Gap Three
	xthl			;  (40) wait
	xthl			;  (10) restore (HL, stack) and wait
	lxi	d,0		;  (20) wait
SetThree4Eval:
	lxi	d,DDGapThree4Evals-1	; (30) d ← 4E, e ← # of 4Es in Gap Three
				;  minus 1.  This allows us to finish early and
				;  decide whether to do another sector or not.
	call	WrtBlock	;  (0) start writing the 4Es right on time.
;  We get here 1 byte before the end of the 4E portion of Gap Three.  We see
;  if there are more sectors to do.  If so, go back to SectorLp to finsh this
;  gap an start another.  If not, keep sending 4Es until the FDC chip turns
;  off the Busy flag (finishes with the track)
	lda	SectorCnt	;  (20) get the number of sectors left
	mov	m,d		;  (28) write last 4E in Gap Three.  This is a
				;  little late but we should be able to write
				;  as late as cycle 41.
	dcr	a		;  (32) decrement sector count and
	sta	SectorCnt	;  (45) update it.
	jnz	SectorLp	;  (finished=>4, more Sectors=>7) If not done
				;  yet, continue with the rest of gap Three.
				;  Note that SectorLp is entered at time 7
				;  from the writing of the Post-Index Gap One
				;  also (Amazing!!)
;  All sectors have been written.  We must maintain synchronization while
;  supplying bytes until the Busy flag drops.  We do this in a two part loop.
;  The first synchronizes, the second detects a drop in Busy.
	jmp	LastLpEntry	;  (14) start the loop at roughly the right time
LastLp:
	ldax	b		;  (8) get the data request flag.
	ani	FDCDRQMask	;  (15) are we too early or too late?
LastLpEntry:
	mov	m,d		;  (23) store the data roughly 5 us after the
				;  request should have risen
	jnz	FastLast	;  (Too Early=>30, Too Late=>33)
	rnz			;  (Too Early=>36) delay extra if we were a little
				;  ahead of the request.
FastLast:
	mvi	a,0		;  (Early=>43, Late=>40) wait
	mvi	a,0		;  (Early=> 2, Late=>47) wait
;  At this point we have reached the end of the first part of the loop.  For
;  The sake of clarity, we will assume the next instruction is perfectly
;  synchronized.  Carrying the Early and Late notation further is not
;  helpful.
	ldax	b		;  (8) sample the Busy Flag
	ani	FDCBusyMask	;  (15) has it been reset yet?
	mov	m,d		;  (23) store another 4E at the right time
	jz	FinFormatTrack	;  (Not done=>30, Done => don't care)
	ldax	b		;  (38) not done with Gap Four yet so wait
	jmp	LastLp		;  (0) start the synchronizing section right on
				;  time.

;  Command has completed.  Get status.
FinFormatTrack:
; Clear out the chip, just to make sure
	call	SDDelay	;  wait until chip has calmed down
	in	FDCStatus	;  Read internal status, this will clear the last
				;  interrupt even if the last read didn't
	sta	DiskStatus	;  Store status from chip
ReadFDCStatusHi:
	in	FDCStatusReg	;  Get external status register.
	sta	DiskStatusHi	;  Store
	POP	B		;  Restore BC for RS232C
	ei			;  turn interrupts back on
	lxi	h,FormatDmaCB	;  point to control block for dummy DMA cycle
	MVI	A,OutDmaFunc
	call	StartFloppyChannel	; cycle the DMA once to clear the last
				;  data request
;  The DMA must have done its cycle by now so turn it off.  Calling
;  ReadDmaCompletion will not be sufficient if there was no DMA cycle
;  because an early fatal error stopped all memory requests.
WaitForDma:
	mvi	a,FloppyChannelMask
	JMP	ClearDmaChannel	;  return from DirectFormat


;  SUBROUTINES

;  Subroutine:   WrtBlock.
;  Write a block of double density data to the floppy disk chip.
;  This loop must maintain synchronization while writing the data at the
;  correct time and decrementing the byte count.
;  On entry:
;    BC = Memory address of the FDCStatus port
;    D  = Data byte to be written in this block
;    E  = byte count
;    HL = Memory address of the FDCData port
;    Program execution is synchronized so that DRQ from the chip rises
;      between 4 us before and 7 us after entry to this routine.
WrtBlkEntry:
	jmp	WrtBlkSkip	;  (14) start the loop at the right time
WrtBlock:
	ldax	b		;  (8) get the data request flag.
	ani	FDCDRQMask+FDCLostData	;  (15) are we too early or
				;  too late? FDCLostData is here for debugging
WrtBlkSkip:
	mov	m,d		;  (23) store the data roughly 5 us after the
				;  request should have risen
	jnz	FastWrtBlock	;  (Too Early=>30, Too Late=>33)
	rnz			;  (Too Early=>36) delay extra if we were a little
				;  ahead of the request.
FastWrtBlock:
	dcr	e		;  (Early=>40, Late=>37) done writing the block?
	jnz	WrtBlock	;  (Early=>2, Late=>47 so Early=> slow down and
				;  late=> catch up), not done=> write another
				;  byte.
	ret			;  (Early=>9, Late=>6, say 7) all done so return


;  Subroutine:  SDWrtBlock.
;  Write a block of single density data to the floppy disk chip.
;  This loop must maintain synchronization while writing the data at the
;  correct time and decrementing the byte count.
;  On entry:
;    BC = Memory address of the FDCStatus port
;    D  = Data byte to be written in this block
;    E  = byte count
;    HL = Memory address of the FDCData port
;    Program execution is synchronized so that DRQ from the chip rises
;      between 11 1/3 us before and 12 1/3 us after entry to this routine.
SDWrtBlkEntry:
	call	SDDelay	;  (5)  call to SDWrtBlkEntry ends at time 53,
				;  SDDelay takes 48 cycles.  48+53=101=5 MOD 96.
	jmp	SDWrtBlkSkip	;  (15) start the loop at the right time

SDWrtBlock:
	ldax	b		;  (8) get the data request flag.
	ani	FDCDRQMask+FDCLostData	;  (15) are we too early or
				;  too late? FDCLostData is here for debugging
SDWrtBlkSkip:
	jnz	SDFastWrtBlock	;  (Too Early=>22, Too Late=>25)
	rnz			;  (Too Early=>28) delay extra if we were a little
				;  ahead of the request.
SDFastWrtBlock:
	dcr	e		;  (Early=>32, Late=>29) done writing the block?
	mov	m,d		;  (Early=>40, Late=>37) store the data roughly
				;  10 us after the request should have risen
	call	SDDelay	;  (Early=>88, Late=>85) wait
	jnz	SDWrtBlock	;  (Early=>2, Late=>95 so if early then slow down,
				;  if late then catch up), not done=> write
				;  another byte.
	ret			;  (Early=>9, Late=>6, call it 7) all done so
				;  return


;  Subroutine:  SDDelay
;  This routine takes exactly 48 cycles (16 us) from the time the call
;  instruction is begun until the return instruction is completed.  No
;  registers or flags are effected.

SDDelay:
	lxi	b,8000H+FDCStatus	;  (28) load bc with their present contents
	lxi	b,8000H+FDCStatus	;  (38) load bc with their present contents
	ret				;  (48) return having executed exactly
					;  48 cycles.

;  Subroutine:  DelayPastIndexPulse
;  This subroutine is not re-entrant.
;  	BC contains the address of the memory mapped Floppy Disk Controller's 
;          Status port.
;	HL will be used  to contain the address of the memory mapped Floppy Disk
;	   Controller's Command register.
;	A  will be used for general purpose.
;	DE remain unused.
;
;  It was discovered that issuing a "write track" command during the index pulse
;  caused the track to be formatted incorrectly (only the last sector was 
;  successfully written to the floppy).  To fix this problem we have added this 
;  subroutine which waits until we see the index pulse and then delay until we
;  have just past the index hole.  This insures that we issue the "write track"
;  command when we are NOT over the index hole.  Calls to this procedure were
;  added to both WriteSDTrack and WriteDDTrack subroutines.

DelayPastIndexPulse:
	LXI	H,8000H+FDCCommand

;  First wait until we are over the index hole.
WaitForIndexPulse:
	mvi	M, ForceInterruptCmd
	LDAX	B
	ani	IndexMask
	jz	WaitForIndexPulse
	
;  Then wait until we are past the index hole.	
WaitEndIndexPulse:
	mvi	M, ForceInterruptCmd
	LDAX	B
	ani	IndexMask
	jnz	WaitEndIndexPulse
	
	mvi	M, ForceInterruptCmd	;Make sure we are past index pulse.
	LDAX	B
	ani	IndexMask
	jnz	WaitEndIndexPulse
	
	ret
	


	END	FormatTrack