{ File: [Iris]<WMicro>DLion>DirectFormat.asm
Grundler: 1-Sep-84 16:44:03 Add copyright notice
Grundler: 3-Jun-83 10:05:20 comment the changes of 25-May-83 16:37:13
Grundler: 25-May-83 16:37:13 added subroutine DelayPastIndexPulse
Fasnacht/Grundler: 24-May-83 12:10:45
Jim Frandeen: May 21, 1982 7:46 AM
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)
}
{ Copyright (C) 1982, 1983 by Xerox Corporation. All rights reserved.}
; 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.
; 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