1
0
mirror of https://github.com/livingcomputermuseum/Darkstar.git synced 2026-03-08 04:00:45 +00:00
Files
livingcomputermuseum.Darkstar/D/IOP/Source/MoonFloppy.asm,v
2023-09-27 16:17:41 -07:00

39 lines
60 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.09; author freier; state Exp;
branches 1.1.1.1;
next ;
1.1.1.1
date 2001.08.12.22.22.09; author freier; state Exp;
branches ;
next ;
desc
@@
1.1
log
@Initial revision
@
text
@; Copyright (C) 1980 by Xerox Corporation. All rights reserved.
get "SysDefs.asm" ; system defs (tests defs below)
get "MOONBootDefs.asm" ; system defs (tests defs below)
get "MOONBootLinkDefs.asm" ; system defs (tests defs below)
get "MOONLinkDefs.asm" ; system links
get "MOONSysDefs.asm" ; system defs
; EXPORTS:
EXP FSetUp
; 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
; VARIABLES
;
; FLOPPY DISK subroutines.
; Constant data variables:
ECylinder:
db 0 ; Ending Cyclinder read in a run
ESector:
db 0 ; Ending sector read in a run
SectorSize:
dw SectorLen ; Sector size (bytes)
SectorSize0:
dw SectorLen/4 ; Sector size (bytes)
SectorSize1:
dw SectorLen/2 ; Sector size (bytes)
FloppyBuffer:
dw FloppyBuf ; Pointer to start of Floppy buffer
CurrentSize:
dw SectorLen ; Sector size (bytes)
Test:
db 0
StopOnErr:
db 0H
FError:
db 0
CheckSum:
db 0
DoCheckSum
db 0
Gap3Len:
ds 1 ; number of fill chars in Gap 3 of Diskette
QuarterSectorLen:
ds 1 ; size of data record/4.
EncSectorLen:
ds 1 ; encoded length of sector (00=>128 bytes,
; 01=>256, 10=>512, 11=> 1024 bytes)
DiskStatusHi:
ds 1 ; encoded length of sector (00=>128 bytes,
; 01=>256, 10=>512, 11=> 1024 bytes)
Initial:
db 0
Observed:
db 0
DmaActive:
db 0
WriteSectorCom:
db 0
FSetUp:
LXI SP, UserStkStart
mvi a,0
sta FError
call ExtTEIIndexAdd ; Returns with TestN * 16 + EIBuffer in h and l
mov a,m ;
sta Test
inx h
mov a,m ;
sta StopOnErr
inx h
mov a,m ;
sta DSide
inx h
mov a,m ;
sta DCylinder
inx h
mov a,m ;
sta Sector
inx h
mov a,m ;
sta ECylinder
inx h
mov a,m ;
sta ESector
inx h
mov a,m ;
sta DoCheckSum
inx h
mov a,m ;
sta Initial
inx h
mov a,m ;
sta Observed
mvi a,0FFH
sta Side
in FDCStatus
sta DiskStatus ; Store away
in FDCStatusReg ; Read SA800 bit (now no-floppy bit)
sta DiskStatusHi
ani FDCSA800Mask
jnz NoFloppyFound ; nz => no Floppy
call FDCReset
call GenData
lda Test
cpi 0
jz FloppyInit
cpi 1
jz ReadSectorRun
cpi 3
jz FormatRun
cpi 4
jz WriteSectorRun
lda CheckSum
sta FError
jmp Logit
Logit:
call ExtTEIIndexAdd ; Returns with TestN * 16 + EIBuffer in h and l
mvi e,9
mvi d,0
dad d
lda Observed
mov m,a ;
inx h
lda DiskStatus
mov m,a ;
inx h
lda DiskStatusHi
mov m,a ;
inx h
lda Side
mov m,a ;
inx h
lda Cylinder
mov m,a ;
inx h
lda Sector
mov m,a ;
inx h
lda FError
mov m,a ;
call ExtMonitorKeyIn
lda StopOnErr
cpi 0
jnz FSetUp
jmp ExtMonitorTestDone
; EXTERNAL Subroutine: FloppyInit.
; Reset the floppy controller, and restore the the heads.
FloppyInit:
mvi a,0
sta Side
sta CheckSum
mvi a,ReadSectorCmd ; Form ReadSector command
sta ReadSectorCom
mvi a,WriteSectorCmd ; Form ReadSector command
sta WriteSectorCom
mvi a,RestoreCmd ; Initialize Restore command
sta RestoreCom
; This entry point is the value of the Restore command is to be changed.
; First check if there is a floppy (based on switch setting).
FloppyInitE:
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
InitializeAgain:
mvi a,DisableFDC ; clear state register, reset FDC, enable Waits
out FDCState
lxi h,4 ; Wait for more than 50 usec (MR pulse)
call Delay
call FDCReset
mvi c,5 ; Issue 5 Stepin commands
mvi e,StepInCmdNoV ; Send StepIn commands (no verify)
InitializeStepLoop:
mov a,e
call SendCommand ; Issue command
; Don't care what the status is. Errors will be caught by the Restore.
ani Type1HardMask ; (Not Ready, Busy)
jnz Type1HardError ; nz => hard error
dcr c
jnz InitializeStepLoop
; We should now be beyond (in) from Track 00. Issue Restore.
lda RestoreCom
mov e,a
mvi d,0 ; D ¬ 0
RestoreAgain:
call SendCommand ; Issue Restore
ani Type1HardMask ; (Not Ready, Busy)
jnz Type1HardError ; nz => hard error
jmp CheckTrk0
; We had a soft error. Try again. (Later: restore and try again)
DecrRestoreRetry:
lxi h,RetryCount
dcr m
jnz InitializeAgain ; z => Too many retries
; Too many retries. Report error.
RestoreFail:
mvi c,ErrorRestoreFail
jmp ErrorReport ; ERROR: Too many Restore failures.
; Check the Track 0 bit in the Status.
CheckTrk0:
lda DiskStatus
ani FDCTk0Mask ; Track 0 bit set?
jz DecrRestoreRetry ; z => no track 0 bit
; Restore was successful.
; We are now sitting at track 0. Cylinder is initialized.
; Initialize Side and Sector, ReadSectorCom and SeekCom.
RestoreGood:
mvi a,0
sta Cylinder
mvi a,SeekCmd
sta SeekCom
jmp LogIt
; Switch setting indicated that no floppy was installed.
NoFloppyFound:
mvi c,ErrorNoFloppy
jmp ErrorReport ; ERROR: No floppy drive installed.
; Subroutine: WriteSectorRun.
; The floppy buffer was empty. Write the next run of sectors from the disk into the floppy buffer.
; If the end of a track is encountered before the end of the run, then stop the run.
;
; On entry:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
;
; On exit:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
WriteSectorRun:
lhld FloppyBuffer
shld FloppyBufPtr
Call SetContex
; Check if a seek is needed.
lxi h,Cylinder ; Point to current cylinder
lda DCylinder ; Assumes high part of Cylinder=0
cmp m
cnz DoSeekCmd ; nz => different, do a seek, update Cylinder
; Now do the read sector.
; Buffer pointer in FloppyBufPtr, sector size in SectorSize, sector number in Sector.
WriteSectorLoop:
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
WriteAgain:
call ExtMonitorKeyIn
lda Sector
mov d,a ; Desired sector in D
lda WriteSectorCom ; WriteSector command to E
mov e,a
call DoSector ; Do the read, soft error status returned in A
; (A) = 0 for no errors, (A) # 0 for soft errors (with errors = 1).
ora a
jz WriteSectorGood ; z => no errors
; We had a soft error. Try again. (Later: restore and try again)
DecrWriteRetry:
lxi h,RetryCount
dcr m
jnz WriteAgain ; z => Too many retries
; Too many retries. Report error.
WriteSectorFail:
mvi c,ErrorWriteSectorFail
jmp ErrorReport ; ERROR: Too many read sector failures.
; The sector was read without error.
; Increment the Buffer count, by the sector size.
; Check if more sectors are too be read.
WriteSectorGood:
lda Cylinder
mov h,a
lda Sector
mov l,a
lda ESector
xra l
jnz MoreWSectors
lda ECylinder
xra h
jz LogIt ; z => Sector run completed
; Still more sectors to be read. Fix the next disk address.
; Increment the Buffer pointer, by the sector size.
MoreWSectors:
lxi h,Sector ; Increment sector number
inr m
lda Cylinder
cpi 0
jnz MoreWSectors1
mvi a,27
jmp MoreWSectors2
MoreWSectors1:
mvi a,16
MoreWSectors2:
cmp m
jz FormNextWDiskAddress
jmp WriteSectorLoop ; Do Next sector same cyc
FormNextWDiskAddress:
call FormNextDiskAddress
jmp WriteSectorRun
; Subroutine: ReadSectorRun.
; The floppy buffer was empty. Read the next run of sectors from the disk into the floppy buffer.
; If the end of a track is encountered before the end of the run, then stop the run.
;
; On entry:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
;
; On exit:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
ReadSectorRun:
lhld FloppyBuffer
shld FloppyBufPtr
Call SetContex
; Check if a seek is needed.
lxi h,Cylinder ; Point to current cylinder
lda DCylinder ; Assumes high part of Cylinder=0
cmp m
cnz DoSeekCmd ; nz => different, do a seek, update Cylinder
; Now do the read sector.
; Buffer pointer in FloppyBufPtr, sector size in SectorSize, sector number in Sector.
ReadSectorLoop:
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
ReadAgain:
call ExtMonitorKeyIn
lda Sector
mov d,a ; Desired sector in D
lda ReadSectorCom ; ReadSector command to E
mov e,a
call DoSector ; Do the read, soft error status returned in A
; (A) = 0 for no errors, (A) # 0 for soft errors (with errors = 1).
ora a
jz ReadSectorGood ; z => no errors
; We had a soft error. Try again. (Later: restore and try again)
DecrReadRetry:
lxi h,RetryCount
dcr m
jnz ReadAgain ; z => Too many retries
; Too many retries. Report error.
ReadSectorFail:
mvi c,ErrorReadSectorFail
jmp ErrorReport ; ERROR: Too many read sector failures.
; The sector was read without error.
; Increment the Buffer count, by the sector size.
; Check if more sectors are too be read.
ReadSectorGood:
call CheckSumIt
lda Cylinder
mov h,a
lda Sector
mov l,a
lda ESector
xra l
jnz MoreRSectors
lda ECylinder
xra h
jz LogIt ; z => Sector run completed
; Still more sectors to be read. Fix the next disk address.
; Increment the Buffer pointer, by the sector size.
MoreRSectors:
lxi h,Sector ; Increment sector number
inr m
lda Cylinder
cpi 0
jnz MoreRSectors1
mvi a,27
jmp MoreRSectors2
MoreRSectors1:
mvi a,16
MoreRSectors2:
cmp m
jz FormNextRDiskAddress
jmp ReadSectorLoop ; Do it again
FormNextRDiskAddress:
call FormNextDiskAddress
jmp ReadSectorRun
CheckSumIt:
lda DoCheckSum
cpi 0
rz
lhld CurrentSize
xchg
lhld FloppyBuffer
cpi 2
jz CheckData
CkAgain:
lda CheckSum
add m
aci 0
sta CheckSum
inx h
dcx d
mov a,e
cpi 0
jnz CkAgain
mov a,d
cpi 0
rz
jmp CkAgain
CheckData:
lda Initial
xra m
jnz DataError
inx h
dcx d
mov a,e
cpi 0
jnz CheckData
mov a,d
cpi 0
jnz CheckData
call GenData
ret
DataError:
mov a,m
sta Observed
mvi c,ErrorDataFail
jmp ErrorReport ; ERROR: Too many Restore failures.
GenData:
lxi h,SectorLen
xchg
lhld FloppyBuffer
GenAgain:
lda Test
cpi 4
jnz GenAgain1
lda Initial
GenAgain1:
mov m,a
inx h
dcx d
mov a,e
cpi 0
jnz GenAgain
mov a,d
cpi 0
rz
jmp GenAgain
; Subroutine: FormNextDiskAddress.
; Form the disk address of the beginning of the next track.
; Single-sided disk:
; Sector ¬ 1;
; DCylinder ¬ Cylinder + 1;
; Double-sided disk:
; Sector ¬ 1;
FormNextDiskAddress:
mvi a,1 ; Sector ¬ 1;
sta Sector
lda Cylinder
inr a
sta DCylinder
Ret
; Subroutine: DoSeekCmd.
; A seek is needed. Do the command, and retry if errors occur.
; On entry:
; Cylinder has current cylinder.
; DCylinder has desired cylinder.
; The sector number is greater than the maximum on a track. Move to the next track.
DoSeekCmd:
call ExtMonitorKeyIn
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
SeekAgain:
Call SetContex
lda SeekCom ; Seek command to E
mov e,a
lda Test
cpi 3
jnz DoSeekCmd1
mvi a,SkCmdNoV ; Seek command to E
mov e,a
DoSeekCmd1:
call DoSeek ; Do the seek, soft error status returned in A
; (A) = 0 for no errors, (A) # 0 for soft errors (with errors = 1).
; Track location was updated if command was successful.
ora a
rz ; z => Seek was successful, return
; We had a soft error. Try again. (Later: restore and try again)
DecrSeekRetry:
lxi h,RetryCount
dcr m
jnz SeekAgain ; z => Too many retries
; Too many retries. Report error.
SeekFail:
mvi c,ErrorSeekFail
jmp ErrorReport ; ERROR: Too many Seek failures.
; Subroutine: DoSector.
; Implement Sector command.
; Assumes Head at track and Track Register up to date.
; On entry:
; D = Desired sector
; E = Command
; On exit:
; A = Internal FDC completion status
DoSector:
mov a,d ; Set up sector for ReadSector command
out FDCSector ; Set desired sector in FDCSector Register
; Program channel 0 of Dma controller for memory writes.
; All interrupts are disabled:
; di ; Disable interrupts while programming Dma
lhld FloppyBufPtr
mov a,l ; Program low buffer address
out DmaCh0Addr
mov a,h ; Program high buffer address
out DmaCh0Addr
lhld SectorSize
lda Cylinder
cpi 0
jnz Not0
lhld SectorSize1
lda Side
cpi 0
jnz Not0
lhld SectorSize0
Not0:
shld CurrentSize
dcx h ; Decrement for Dma
mov a,l ; Program low Count
out DmaCh0Count
lda Test
cpi 4
jnz DmaR
mov a,h ; Program high Count
ori DmaReadBit ; OR in function (memory read)
jmp DmaS
DmaR:
mov a,h ; Program high Count
ori DmaWriteBit ; OR in function (memory writes)
DmaS:
out DmaCh0Count
mvi a,EnFloppyChannel ; Enable channel 0: TCS, EW, EN0
out DmaMode
; ; Re-enable interrupts
; Dma channel is now programmed and enabled.
DoSectorContinue:
mov a,e ; Get FDC command
call SendCommand ; Issue command
sta DiskStatus ; Store away
lda Test
cpi 4
jz CkWriteError
CkReadError:
lda DiskStatus
; Command has ended. Check completion status.
; Check for error completion (internal status in A, external in B).
; (Not Ready, RNF, CRC error, Lost Data, Busy)
ani ReadErrorMask
sta DiskStatus ; Store away
ani ReadHardMask ; (Not Ready, Busy)
jz DoSectorGood
ReadHardError:
mvi c,ErrorReadHardError
jmp ErrorReport ; ERROR: Read Sector hard error
CkWriteError:
lda DiskStatus
; Command has ended. Check completion status.
; Check for error completion (internal status in A, external in B).
; (Not Ready,WritrProtect RNF, CRC error, Lost Data, Busy)
ani WriteErrorMask
sta DiskStatus ; Store away
ani WriteHardMask ; (Not Ready, Busy,WritrProtect)
jz DoSectorGood
WriteHardError:
mvi c,ErrorWriteHardError
jmp ErrorReport ; ERROR: Read Sector hard error
; Correct FDC termination.
; If we had an RNF or LostData error then we shouldn't check for DMA completion.
DoSectorGood:
lda DiskStatus ; Restore status
ani FDCRNFMask
jnz DoSectorDone ; Don't check Dma
lda DiskStatus ; Restore status
ani FDCLostData
jnz DoSectorDone ; Don't check Dma
; Check that DMA terminated.
mov a,b ; B has external Status Reg value
ani FDCEndCountMask
jnz GoodDmaEndCount1
NoDmaEndCount1:
mvi c,ErrorNoDmaEndCount1
jmp ErrorReport ; ERROR: External Dma End Count register not set
; Check internal Dma status to ensure completion.
GoodDmaEndCount1:
in DmaStatus ; Read internal Dma Status
ani DmaCh0Mask
jnz DoSectorDone ; nz => channel completed
NoDmaEndCount2:
mvi c,ErrorNoDmaEndCount2
jmp ErrorReport ; ERROR: Internal Dma End Count not set.
; Read sector done without any hard errors.
DoSectorDone:
xra a
out DmaMode ; Disable Dma
lda DiskStatus ; Return status
ret
; Subroutine: DoSeek.
; Issue seek command. Assumes FDC Track register is up-to-date.
; Checks validity of track number on completion.
; Update DDen bit in FDCState for dsestination cylinder.
; Note that PreComp bit does not have to be changed since we do no writing.
; On entry:
; D = Desired Cylinder
; E = Command
; On exit:
; A = Internal FDC completion status
DoSeek:
mov a,d ; Check if desired Track OK
cpi 77
jc SeekCylinderOK
TrackToBig:
mvi c,ErrorTrackToBig
jmp ErrorReport ; ERROR: Track number is too large.
; Track number is less than 77.
; Output desired cylinder for Seek.
SeekCylinderOK:
out FDCData ; Set desired track in Data Register for Seek
mov a,e ; Get command
call SendCommand ; Issue command
; Check for error completion (in A).
; (Not Ready, Seek error, CRC error, Busy)
sta DiskStatus ; Store away
ani Type1ErrorMask
mov b,a ; save away
ani Type1HardMask ; (Not Ready, Busy)
jz CheckTrack ; z => no hard error
Type1HardError:
mvi c,ErrorType1HardError
jmp ErrorReport ; ERROR: Type 1 hard error
CheckTrack:
in FDCTrack ; Check track register
xra d
jz UpdateCylinder ; z => Track register is correct
CommandTrackError:
mvi c,ErrorCommandTrackError
jmp ErrorReport ; ERROR: Track register is not correct
; Track register is correct. Update Cylinder location.
UpdateCylinder:
mov l,d
mvi h,0
shld Cylinder
ExitType1Cmd:
mov a,b ; Return status
ret
FDCReset:
mvi a,DefaultFDCStateVal ; Get control byte for FDC
sta FDCStateVal
out FDCState
mvi a,ForceInt0Cmd ; Issue Force Interrupt command
out FDCCommand
mvi e,200
WaitFDCBusy:
dcr e
jz WaitError
lxi h,1 ; Busy status not available for 12 usec
call Delay
in FDCStatusReg
sta DiskStatusHi
in FDCStatus ; Check for restore command completion
mov b,a
ani FDCBusyMask
jnz WaitFDCBusy
mov a,b ; Return status in A too
lda Cylinder
out FDCTrack ; Check track register
ret
WaitError:
mvi c,12
jmp ErrorReport ; ERROR: Floppy stays Busy.
; Subroutine: SendCommand.
; Issue the command to the FDC.
; Command is issued, and the Int Req register is polled for command completion.
; Call subroutine at GetFDCStatus to get status only, after Busy resets.
; On entry:
; A = Command
; On exit:
; A = Internal status
; B = External status
SendCommand:
out FDCCommand
push d
mvi e,0
GetFDCStatus:
dcr e
jz SendComError
lxi h,0
DelayLoop1:
in IntReq ; Check for completion in IntReq Reg.
xri IntReqMask ; complement signals which are active low
ani FDCIntMask
jnz GetFDCStatus1
dcx h ; [6]
xra a ; [4]
cmp l ; [4]
jnz DelayLoop1 ; [10]
cmp h ; [4]
jnz DelayLoop1 ; [10]
jmp GetFDCStatus
GetFDCStatus1:
pop d
; Command has completed. Get status.
in FDCStatusReg ; Get external status register.
sta DiskStatusHi
mov b,a ; Save in B
in FDCStatus ; Check for restore command completion
sta DiskStatus ; Store away
ret ; return status in A, external status in B
SendComError:
mvi c,13
jmp ErrorReport ; ERROR: Comman did not compleat.
; Subroutine: ErrorReport.
; Blink the maintenance panel with the error number.
; No return from this subroutine.
; On entry: C >= 0 => Blink (MPOffset + C) in MP
; C < 0 => Blink current number in MP
ErrorReport:
mov a,c
sta FError
jmp LogIt
; Subroutine: Delay.
; Wait the period of time specified in H,L.
; H,L restored, A restored.
; Delay is 74 + 24*const cycles (const in H,L)
; = 25 + 8* const usec
; Maximum delay: H,L = 0FFFFH = ~.5 sec
Delay:
push psw ; [12] Save A
push h ; [12] Save H,L
; Inner loop approximately 24 cycles (=8 usec)
DelayLoop:
dcx h ; [6]
xra a ; [4]
cmp l ; [4]
jnz DelayLoop ; [10]
cmp h ; [4]
jnz DelayLoop ; [10]
pop h ; [10] Restore H,L
pop a ; [10] Restore A
ret ; [12]
; Subroutine: FormatRun.
; On entry:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
;
; On exit:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
FormatRun:
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
FormatRun1:
mvi a,0D4H ; Issue Force Interrupt command
out FDCCommand
lxi h,0
DelayLoop4:
in IntReq ; Check for completion in IntReq Reg.
xri IntReqMask ; complement signals which are active low
ani FDCIntMask
jnz FormatRun2
dcx h ; [6]
xra a ; [4]
cmp l ; [4]
jnz DelayLoop4 ; [10]
cmp h ; [4]
jnz DelayLoop4 ; [10]
mvi c,18
jmp ErrorReport ; ERROR: Force index error.
FormatRun2:
in FDCStatus ; Check for restore command completion
sta DiskStatus ; Store away
call ExtMonitorKeyIn
Call SetContex
lxi h,Cylinder ; Point to current cylinder
lda DCylinder ; Assumes high part of Cylinder=0
cmp m
cnz DoSeekCmd ; nz => different, do a seek, update Cylinder
; Now do the Format.
FormatLoop:
mvi a,0 ; load number of first sector-1 (code does
; increment before storing
sta Sector
mvi b,2 ; set the encoded sector
mvi c,15 ; get number of sectors
mvi d,512/4 ; set the data record len/4
mvi e,84 ; Set the number of fill chars
lda Cylinder
cpi 0
jnz NotC0
mvi b,0 ; set the encoded sector
mvi c,26 ; get number of sectors
mvi d,128/4 ; set the data record len/4
mvi e,27 ; Set the number of fill chars
lda Side
cpi 0
jz NotC0
mvi b,1 ; set the encoded sector
mvi d,256/4 ; set the data record len/4
mvi e,54 ; Set the number of fill chars
NotC0:
mov a,b ; set the encoded sector
sta EncSectorLen
mov a,c ; get number of sectors
sta SectorCnt
mov a,d ; set the data record len/4
sta QuarterSectorLen
mov a,e ; Set the number of fill chars
sta Gap3Len
lxi h,400 ; Get past physical index
call Delay
lda FDCStateVal
ani DDenMask ; is the double density bit on?
jnz DoDoubleDensityFormat ; yes, format for double density
call WriteSDTrack ; no, format a single density track
jmp CheckFormatResult ; and see if done with run of tracks
DoDoubleDensityFormat:
call WriteDDTrack ; format a double density track
CheckFormatResult:
mov a,b
ani WriteErrorMask ; Check for error status
jz FormatGood ; nz => there was an error, abort the IOCB
lxi h,RetryCount
dcr m
jnz FormatRun1 ; z => Too many retries
; Too many retries. Report error.
mvi c,ErrorFormatFail
jmp ErrorReport ; ERROR: Too many read sector failures.
; The sector was read without error.
; Increment the Buffer count, by the sector size.
; Check if more tracks to format.
FormatGood:
lda Cylinder
mov h,a
lda ECylinder
xra h
jz LogIt ; z => Format run completed
; Still more tracks to format. Fix the next disk address.
lda Cylinder
inr a
sta DCylinder
jmp FormatRun2 ; Do it again
; 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
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.
mvi a,WriteTrackCmd ; prepare to write the track
di ; make sure we are not disturbed
out FDCCommand ; 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 Side ; (16) wait
lda Side ; (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.
; Side 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
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.
mvi a,WriteTrackCmd ; prepare to start the format cycle
di ; make sure we are not disturbed
out FDCCommand ; 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 Side ; (36) wait
lda Side ; (1) wait
lda Side ; (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
mov b,a ; Move to nB
sta DiskStatus ; Store status from chip
ReadFDCStatusHi:
in FDCStatusReg ; Get external status register.
sta DiskStatusHi ; Store
mov c,a ; Move to C
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 DoSectorDone ; return from DirectFormat
FormatDmaCB:
dw 1 ; only 1 byte
dw 0 ; from address 0
;
{ Subroutine: StartFloppyChannel [H,L: POINTER to DmaControlBlock].
Set up the address, count, Dma function of the CP channel.
Enable the Dma Channel. Note: TCS and EW are set in Mode automatically.
On entry:
A = function: DmaRead or DmaWrite
HL = pointer to Dma Control Block (format described above).
byte 0: Byte count low,
byte 1: Byte count high,
byte 2: Memory Address Low,
byte 3: Memory Address high,
}
StartDmaChannel:
StartFloppyChannel:
MOV E,M ;E ¬ low byte count
INX H ;Point to High count
MOV D,M ;D ¬ High byte count
DCX D ;Decrement for Dma controller
ORA D ;OR in Dma function
MOV D,A
MOV A,E
OUT DmaCh0Count ;Send low count
MOV A,D
OUT DmaCh0Count ;Send high count
INX H ;Point to Low address
MOV A,M ;E ¬ Memory Address Low
OUT DmaCh0Addr ;Send low address
INX H
MOV A,M ;D ¬ Memory Address High
OUT DmaCh0Addr ;Send high address
{Now we are ready to start the DMA for the Floppy channel. When we send the command to the DMA controller, the low 4 bits of the command specify which channels to start. We must start the Floppy channel as well as any other channels that are currently busy.
The problem is to know what channels are currently busy so we can send the proper command to the controller. DmaActive is the command we last sent to the DMA to start one or more channels. The low 4 bits of this command indicate which channels were last started. Some of these channels may have completed since then.
In order to find out what channels have completed, we must read the Status Register. The low 4 bits of this register indicate what channels have completed. However, we cannot read this register and use this information to restart channels that have not completed, because a channel could complete in this interval. To leave a channel enabled is like pointing a loaded gun at our memory. So what me must do is to stop all channels before we read the Status Register.}
LDA DmaActive
MOV D,A ;Save DmaActive in D
XRA A
OUT DmaMode ;Disable channels
IN DmaStatus ;Read TerminalCount flags
CMA ;A ¬ inverted TerminalCount Flags
{Now A contains zero in bit positions of channels that have completed and ones in all other bit positions. We AND this with DmaActive. This gives us a command to restart all channels that need to be restarted. We then OR in the bit for the CP channel, send the command to the DMA, and save DmaActive.}
ANA D
ORI DmaCh0Mask
OUT DmaMode ;Start Dma
STA DmaActive ;Store back in DmaActive
RET
; 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.
SetContex:
lda DCylinder ; Desired cylinder to D
mov d,a
; Check if Cylinder 0 and force to single density.
ora a
jnz NSetSD ; z => At cylinder 0, force single density
lda DSide
cpi 0
jz SetSD
; The head is not at cylinder 0. Check the Density.
NSetSD:
mvi a,FDCDDenMask ; Set the DDen bit
call SetFDCStateBit
mvi H,PreCompStart ; Check if WritePrecomp should be changed
mov a,d ; Desired cylinder to A
cmp H ; A has desired cylinder, H has start of PreComp
jc SetNoPreComp ; c => present cylinder < PreComp boundry
; Enable write Precomp.
SetPreComp:
mvi a,FDCEnPreCompMask
call SetFDCStateBit
jmp CkSide
SetNoPreComp:
mvi a,nFDCEnPreCompMask ; Clear double density bit
call ClearFDCStateBit
jmp CkSide
; Set single density (clear the DDen bit).
SetSD:
mvi a,nFDCDDenMask ; Clear double density bit
call ClearFDCStateBit
jmp SetNoPreComp
CkSide:
lxi h,Side ; Point to current Side
lda DSide ; Get desired Side
cmp m
jnz DoSwitchSide ; nz => different, switch the side select
lxi h,FDCStateVal ; Point to FDCStateVal
mov a,m
out FDCState ; Store in FDCState
EndContex:
ret
; Subroutine: SetFDCStateBit.
; Set a bit in FDCState and FDCStateVal.
; On entry: A = Mask of bit to be set.
SetFDCStateBit:
lxi h,FDCStateVal ; Point to FDCStateVal
ora m ; Set the bit
StoreNewBit:
mov m,a ; Store in FDCStateVal
ret
; Subroutine: ClearFDCStateBit.
; Clear a bit in FDCState and FDCStateVal.
; On entry: A = Complement of mask of bit to be cleared.
ClearFDCStateBit:
lxi h,FDCStateVal ; Point to FDCStateVal
ana m ; Clear the bit
jmp StoreNewBit
; Subroutine: DoSwitchSide.
; Switch the side select values in Side and FDCState.
; On entry: H,L points to Side
; A = DSide
DoSwitchSide:
mov m,a ; Side ¬ DSide
ora a ; Check if desired side is 0 or 1
jnz SetSide1 ; nz => DSide is 1
SetSide0:
mvi a,nFDCSide1Mask ; Set Side 0 (clear Side 1 bit)
call ClearFDCStateBit
out FDCState ; Store in FDCState
lxi h,ReadSectorCom ; Fix up ReadSector Cmd
mvi a,nType2SMask ; Clear S bit
ana m
mov m,a
lxi h,WriteSectorCom ; Fix up ReadSector Cmd
mvi a,nType2SMask ; Clear S bit
ana m
mov m,a
EndSwitchSide:; Delay to wait for head settling time.
lxi h,1
call Delay
jmp EndContex
SetSide1:
mvi a,FDCSide1Mask ; Set Side 1
call SetFDCStateBit
out FDCState ; Store in FDCState
lxi h,ReadSectorCom ; Fix up ReadSector Cmd
mvi a,Type2SMask ; Set S bit
ora m
mov m,a
lxi h,WriteSectorCom ; Fix up ReadSector Cmd
mvi a,Type2SMask ; Set S bit
ora m
mov m,a
jmp EndSwitchSide
END MoonFloppy
@
1.1.1.1
log
@first add
@
text
@@