{ File: [Iris]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