mirror of
https://github.com/livingcomputermuseum/Darkstar.git
synced 2026-03-08 04:00:45 +00:00
39 lines
60 KiB
Plaintext
39 lines
60 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.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
|
||
@@
|