mirror of
https://github.com/livingcomputermuseum/Darkstar.git
synced 2026-03-03 02:18:08 +00:00
39 lines
36 KiB
Plaintext
39 lines
36 KiB
Plaintext
head 1.1;
|
|
branch 1.1.1;
|
|
access ;
|
|
symbols start:1.1.1.1 Xerox:1.1.1;
|
|
locks ; strict;
|
|
comment @;; @;
|
|
|
|
|
|
1.1
|
|
date 2001.08.12.22.22.20; author freier; state Exp;
|
|
branches 1.1.1.1;
|
|
next ;
|
|
|
|
1.1.1.1
|
|
date 2001.08.12.22.22.20; author freier; state Exp;
|
|
branches ;
|
|
next ;
|
|
|
|
|
|
desc
|
|
@@
|
|
|
|
|
|
|
|
1.1
|
|
log
|
|
@Initial revision
|
|
@
|
|
text
|
|
@
|
|
{ File: [Iris]<WMicro>DLion>DirectFormat.asm
|
|
|
|
DEG : 1-Sep-84 16:44:03 Add copyright notice
|
|
DEG : 3-Jun-83 10:05:20 comment the changes of 25-May-83 16:37:13
|
|
DEG : 25-May-83 16:37:13 added subroutine DelayPastIndexPulse
|
|
AEF /DEG : 24-May-83 12:10:45
|
|
Jim JXF : May 21, 1982 7:46 AM
|
|
|
|
Written by Dan dD
|
|
|
|
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@
|
|
|
|
|
|
1.1.1.1
|
|
log
|
|
@first add
|
|
@
|
|
text
|
|
@@
|