{ File: NewFloppyTask.asm
Last edited by:
JMM 7-Oct-85 13:14:16 Added Mitch's patches and restored FloppyDefs name.
Last change by MPL 24-Sep-85 13:48:05 timeouts in WaitFDC
Changes located around lines beginning with ## Mitch ##
and ending with lines ## EndMitch ##
Last change by Mitch Lichtenberg 8-Jan-84 23:58:02 no more 58x MP errors!
Last change by Dennis Grundler 25-May-83 16:50:35: remove dependency on BC register pair
Last change by Jim Frandeen July 27, 1982 10:24 AM: new IO Page format
Last change by Jim Frandeen May 24, 1982 10:29 AM: Simplify resource control and task management, change format of Dma Control Block
- Have Disk pointers wrap around if ends of buffer will extend over end of ring buffer area. (May 29, 1981 2:14 PM)
- Use DisableRST and EnableRST subs (February 24, 1981 12:31 PM)
- Renamed to FloppyTask (February 4, 1981 3:50 PM)
- use interrupts in an attempt to transfer runs of sectors at full speed (January 23, 1981 11:01 AM)
- Consolidate Read and Write transfers into one main routine - Added Format track (January 8, 1981 3:27 PM) (December 30, 1980 4:24 PM)
- Interleave sectors during sector command (December 28, 1980 6:27 PM)
- 10 Step-Ins moved from Initialize command to Recalibrate command (December 26, 1980 5:17 PM)
- WaitFDCCompletion changed back to test Interrupt bit in IntReq register.
- reset the virtual IOCB address in the CSB as a completion signal instead of clearing the valid flag. The valid flag is being discarded because of atomicity problems. (December 20, 1980 2:50 PM)
- test busy flag in 1797 directly in "WaitFDCCompletion", code seems to stall waiting for the interrupt even though busy has gone false (December 19, 1980 7:35 PM)
- attempt to get rid of Data Lost on Writes by starting CP DMA transfers before disk DMA transfers (December 19, 1980 6:14 PM)
- All Yields except Block made null (***debug) (October 12, 1980 3:26 PM)
Written by Roy OgusAugust 26, 1980 1:22 P.M.
}
; DEFINITION FILES:
get "SysDefs" ; System defs
get "CommonDefs" ; Common defs
get "FloppyDefs" ; Floppy defs
; IMPORTS/EXPORTS:
IMP ByteToWord ; From CPSubs
IMP CheckCPDMAComplete ; From CPSubs
IMP ClearDmaChannel ; From DmaSubs
IMP Copy ; From CPSubs
IMP DoNakedNotify ; From CPSubs
IMP DisableRST ; From Common
IMP EnableRST ; From Common
IMP EndFDBuffer ; From Buffer
IMP ErrorReport ; From MPTask
IMP FloppyActiveSwitch1 ; From DmaSubs
IMP FloppyActiveSwitch2 ; From DmaSubs
IMP FloppyActiveSwitch3 ; From DmaSubs
IMP FloppyActiveSwitch4 ; From RS232CGet
IMP FloppyIocbAddr ; From Common
IMP FloppyIocbAddrHi ; From Common
IMP PortBusyFlag ; From CPSubs
IMP ReadCPbuffer ; From CPSubs
IMP ReadDmaCompletion ; From DmaSubs
IMP StartCPReadDMA ; From CPSubs
IMP StartCPWriteDMA ; From CPSubs
IMP StartFloppyChannel ; From DmaSubs
IMP StartFDBuffer ; From Buffer
IMP WordToByte ; From CPSubs
IMP WriteCPbuffer ; From CPSubs
IMP WriteDDTrack ; From DirectFormat
IMP WriteSDTrack ; From DirectFormat
IMP ZeroCommand ; From Common
EXP CPXferCnt ; ** Debugging
EXP Cylinder ; For DirectFormat
EXP DiskHead ; For DirectFormat
EXP DiskStatus ; For DirectFormat
EXP DiskStatusHi ; For DirectFormat
EXP EncSectorLen ; For DirectFormat
EXP FloppyIOCB
EXP FloppyIntr ; for Common
EXP Gap3Len ; For DirectFormat
EXP QuarterSectorLen; For DirectFormat
EXP Sector ; For DirectFormat
EXP SectorCnt ; For DirectFormat
;
DB opJMP
FloppyTaskResumeAddress:
DW StartFloppyTask
; JMP WaitForPortToReadIOCB
; JMP WaitForPortForBufService
; JMP WaitCPDmaCompletion
; JMP TestDiskSectorDone
; Floppy task internal storage.
FloppyCSB: ;14021
DS 1*2
; IOCB:
; Note: words 11 and 12 are only used by the Format Track command.
FloppyIOCB:
ds 2 ; Word 0: Buffer Pointer (low 16 bits)
ds 2 ; Word 1: Buffer Pointer (high)
ds 2 ; Word 2: Buffer size (words)
ds 2 ; Word 3: Sector Length (words)
ds 2 ; Word 4: Context (bit 11-Troy/IBM', bit 12-Double/Single'
ds 2 ; Word 5: Cylinder
ds 2 ; Word 6: Head (bits 0:7), Sector (bits 8:15)
ds 2 ; Word 7: Sector count
ds 2 ; Word 8: IOCB result
ds 2 ; Word 9: IOCB command
ds 2 ; Word 10: Escape Command
ds 2 ; Word 11: SectorLen/4 (bits 0:7),
; Encoded Sector len (bits 8:15)
ds 2 ; Word 12: SectorsPerTrack (bits 0:7)
; NumFillCharsInGap3 (bits 8:15)
ds 6 ; Word 13-15: (Reserved)
; DATA BUFFER (it should be at least 2k bytes = 4 standard sectors):
; (In Buffer.asm)
; CONTROL Blocks:
DiskDmaFunction:
ds 1 ; Function field: DmaRead or DmaWrite
; Dma control block for Disk operation:
DiskDmaCB:
ds 2 ; Size of data buffer (bytes)
DiskBufPtr:
ds 2 ; Start of data buffer
; CP Port Control Blocks:
; Items that are not fixed are marked with an '*'
; PCB for IOCB Fetch.
FDIocbPCB:
ds 2 ; *CP buffer pointer (low): IOCB pointer from CSB
DW CPIOCBHi ; CP buffer pointer (high)
dw 32 ; CP buffer count (bytes): IOCB size
dw FloppyIOCB ; Pointer to IOP buffer: Local IOCB
; PCB for data transfer.
FDDataPCB:
ds 2 ; *CP buffer pointer (low): Data buffer address
DW 1 ; *CP buffer pointer (high): Data buffer address
; govern the transfer. Swapped + Virtual.
ds 2 ; *CP buffer count (bytes): Data buffer size
CPBufPtr:
ds 2 ; *Pointer to IOP buffer: Local Data buffer
; PCB for IOCB result update.
FDResultPCB:
ds 2 ; *CP buffer pointer (low): IOCB.sectorCount
DW CPIOCBHi ; CP buffer pointer (high)
dw 4 ; CP buffer count (bytes): Sector count, result
dw FloppyIOCB+IocbSectorCnt ; Pointer to IOP buffer: Local IOCB.sectorCount
; PCB to reset FloppyIocbAddr.
ResetFloppyIocbAddr:
dw FloppyIocbLoc ; CP buffer pointer (low): IOCB pointer
dw CPIOPageHi ; CP buffer pointer (high)
dw 2 ; CP buffer count (bytes): IOCB size
DW ZeroCommand ; Pointer to IOP buffer:
; PCB to read Floppy CSB.
ReadFloppyCSB:
dw FloppyCSBLoc ; CP buffer pointer (low): IOCB pointer
dw CPIOPageHi ; CP buffer pointer (high)
dw FloppyCSBSize ; CP buffer count (bytes): IOCB size
DW FloppyCSB ; Pointer to IOP buffer:
; Miscellaneous storage:
FDCStateVal:
ds 1 ; constant for FDC state register
DefaultFDCState:
db DefFDCStateVal ; constant for FDC state register
DiskStatus:
ds 2 ; Disk status after operation (Low: internal, Hi: external)
DiskStatusHi equ DiskStatus+1
RemainSize:
ds 2 ; Remaining size of IOCB buffer
RemainSizeHi equ RemainSize+1
RestoreCmd:
ds 1 ; Restore Command
SeekCmd:
ds 1 ; Seek Command
ReadSectorCmd:
ds 1 ; Read Sector Command
WriteSectorCmd:
ds 1 ; Write Sector Command
SectorCmd:
ds 1 ; Sector Command storage for the sector command
; Disk address data.
DiskHead:
ds 1 ; Current side
Cylinder:
ds 1 ; Current cylinder
DCylinder:
ds 1 ; Desired cylinder in Seek
; Note: Sector, NewIntCycle, ErrorSectorCnt MUST be adjacent bytes!!.
Sector:
ds 1 ; Current sector
F1797:
dw 8H ; F1797 commands: 8H => 1797, 0 => 1793
S800:
dw 0H ; SA800 drive: 2H => SA800, 0 => SA850
; Variables used to format a track
SectorCnt:
ds 1 ; number of sectors left in track being
; formatted
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)
; Disk and CP transfer counts
CPXferCnt:
ds 1 ; # or CP transfers to do in disk operation
; Error mask used in transfer
DkErrorMask:
ds 1 ; one mask for reads, one for writes
; Boolean used to decide if disk is running
DiskActive:
ds 1 ; 0FF=> disk is doing a data transfer,
; 00 => no data transfer
; Boolean used to decide if CP<->IOP transfer should occur
DiskSectorDone:
ds 1 ; 00 => no transfer needed
; 0FF => need a transfer
; **Debugging variables:
NoYield:
db 0 ; 1 = Yield only at end of Command
;
; -------------------------------------------------------------------
{ Check CSB-IOCB pointer. Get high byte of virtual address. If it is non-zero, start the floppy. No IOCB will ever be put in virtual page zero.}
StartFloppyTask:
lda FloppyIocbAddrHi
ora a
JZ FloppyTaskYield
LDA PortBusyFlag
ORA A
JNZ FloppyTaskYield
LXI H,ReadFloppyCSB
CALL ReadCPBuffer ;Read Floppy CSB
{Transfer the IOCB into local memory.}
call Disable75 ; turn off all FDC interrupts so commands
; that don't use them won't be bothered
LHLD FloppyIocbAddr ; Fetch IOCB pointer in main memory (low 16 bits)
shld FDIocbPCB+CPAddr3 ; Store in PCB CP address slot
; Setup the FDResultPCB with the pointer to the Sector count in the IOCB.
lxi D,CPIocbSectorCnt ; DE ← SectorCnt index
dad D ; H,L ← Pointer to SectorCnt location in IOCB
shld FDResultPCB+CPAddr3 ; Store back (Note: assume no 64K crossing)
lxi h,FDIocbPCB ; Pointer to FDIocbPCB CPport control block
call ReadCPbuffer ; Read CP main memory
; IOCB is in local memory. Clear the Result slot.
lxi h,0 ; Result cleared
shld FloppyIOCB+IocbResult
; Dispatch on command.
FDCommandDisp:
lda FloppyIOCB+IocbCommand ; Get command (low 8 bits)
lxi h,FDCommandTable ; Point to command table
jmp CommandDispatch
; Command table is a table of starting address of the command code.
FDCommandTable:
C0: dw DoNOPCmd ; NOP (command=0)
C1: dw DoReadSectorCmd ; Read Sector (command=1)
C2: dw DoWriteSectorCmd ; Write Sector (command=2)
C3: dw DoWriteDelSectorCmd ; Write Deleted Sector (command=3)
C4: dw DoReadIDCmd ; ReadID (command=4)
C5: dw DoFormatTrackCmd ; FormatTrack (command=5)
C6: dw DoRecalibrateCmd ; Recalibrate (command=6)
C7: dw DoInitializeCmd ; Initialize (command=7)
C8: dw DoEscapeCmd ; Escape (command=8)
EndCommandTable:
nop
MaxFloppyCommand equ (EndCommandTable-FDCommandTable)/2 ; Max. number +1
; Check for valid command, and dispatch on the command.
; Jump to Contents [CommandTable + 2*(Command)].
CommandDispatch:
cpi MaxFloppyCommand ; Check for largest command
jnc NoValidCommand ; nc => command (in A) >= MaxCommand
add a ; A ← 2*(Command), assume less than 128 commands
mov e,a ; DE ← 2*Command
mvi d,0
dad d ; HL ← CommandTable+2*(Command)
; Command Found. H,L points to appropriate slot
mov e,m ; Address to D,E
inx h
mov d,m
push d ; stack ← starting addr of command's routine
{When the floppy and RS232C are both running, we must disable interrupts whenever DmaActive is being updated.}
MVI A,opJZ
STA FloppyActiveSwitch1
STA FloppyActiveSwitch2
STA FloppyActiveSwitch3
MVI A,opDI
STA FloppyActiveSwitch4
ret ; Jump to command
; The command was not valid. Mark IOCB with error result.
NoValidCommand:
lxi h,ErrorNoValidCommand ; ERROR: Invalid command
call MakeBadIOCBResult
jmp FinishCommand ; return to normal mode
;
; COMMAND processing.
; All commands assume that the current context is up to date, and that the controller
; is enabled and ready for a command.
; Command 0: NOP command.
{ Issue a Force Interrupt command, to obtain a type 1 status.}
DoNOPCmd:
mvi a,ForceInt0Cmd ; Issue command with immediate interrupt
out FDCCommand ; Issue command
; Wait for completion (should be immediate).
call WaitFDCBusy ; Call end of FDCReset subroutine
;--new 7-Oct-85 12:03:23
jnc FDCNoopOkay
lxi h,597 ; FDCTimeoutInNOP
call MakeBadIOCBResult
jmp FinishCommand
;--endnew 7-Oct-85 12:03:23
FDCNoopOkay:
call ReadFDCStatus ; Call end of WaitFDCCompletion for status
; Command complete. Form IOCB result from status in B,C.
call MakeIOCBResultType1 ; Convert B,C into an IOCB.result, store in IOCB
; Finish up the command. This code used by all commands.
FinishCommand:
; Update the Sector Count and Result fields of the IOCB in main memory.
; The fields are already updated in local IOCB.
; Then clear the IOCB pointer in the CSB, and do a naked notify.
; Call the subroutine at NotifyCPResultNoAcq if the CP port is already acquired.
WaitForPortToNotifyResult:
LDA PortBusyFlag
ORA A
JZ NotifyCPResult
LXI H,WaitForPortToNotifyResult
JMP SaveFloppyTaskResumeAddressAndYield
NotifyCPResult:
lxi h,FDResultPCB ; Pointer to FDResultPCB CPport control block
call WriteCPbuffer ; Write IOCB in main memory
; Clear FloppyIocbAddr.
lxi h,ResetFloppyIocbAddr
call WriteCPbuffer ; Write IOCB in main memory
; Do the naked notify.
lhld FloppyCSB ; Mask is in first word
call DoNakedNotify
FinishCommand1:
{When the floppy and RS232C are both running, we must disable interrupts whenever DmaActive is being updated. Put things back so that we don't disable interrupts. This is important for fast RS232C.}
MVI A,opJMP
STA FloppyActiveSwitch1
STA FloppyActiveSwitch2
STA FloppyActiveSwitch3
XRA A ;A ← NOP
STA FloppyActiveSwitch4
LXI H,StartFloppyTask
JMP SaveFloppyTaskResumeAddressAndYield
; Command 1: Read Sector.
{ The Read Sector command can operate on a run of sectors on the same track.
To overlap disk operations with transfers to main memory, double buffering is used.
}
DoReadSectorCmd:
lda ReadSectorCmd ; Set sector command
sta SectorCmd
DoReadAddrCmd:
mvi a,ReadErrorMask ; Set the read error mask for
sta DkErrorMask ; Do Transfer
xra a ; Disk has done nothing yet
sta DiskSectorDone ; so start with a disk op.
mvi a,InDmaFunc ; Set up Dma CB for Reads, memory writes
jmp DoTransfer ; Do the rest of the command
; Command 2: Write Sector.
{ The Write Sector command can operate on a run of sectors on the same track.
To overlap disk operations with transfers to main memory, double buffering is used.
}
DoWriteSectorCmd:
lda WriteSectorCmd ; Fix write sector command
ani nDeletedDataMask ; Clear the Deleted data bit
sta SectorCmd ; Store in command location
DoWriteSectorStart:
mvi a,WriteErrorMask ; Set proper error mask used to
sta DkErrorMask ; detect FDC errors during writes
mvi a,0FFH ; say disk has "written" all
sta DiskSectorDone ; sectors so we will start by
; filling all from the CP
mvi a,OutDmaFunc ; Set up Dma CB for Writes, memory reads
jmp DoTransfer ; go finish the Write command
; Command 3: Write Deleted Sector.
DoWriteDelSectorCmd:
lda WriteSectorCmd ; Fix write sector command
ori DeletedDataMask ; Set the Deleted data bit
sta SectorCmd
jmp DoWriteSectorStart
; Command 4: Read ID.
{ Read the first Disk ID field. Set up Sector command, change "sector length",
and jump into Read Sector command.}
DoReadIDCmd:
mvi a,RAddrCmd ; Set ReadID command
sta SectorCmd
lxi h,RAddrDataLen ; Fake sector length
shld FloppyIOCB+IocbSectorLen
jmp DoReadAddrCmd
; Command 5: Format Track.
{ The Format Track can format a run of track on one side of the disk. The recording density is specified in bit 12 of the IocbMiscContext word of the IOCB. The number of sectors per track is in IocbSectorCnt. The desired Cylinder is in IocbCylinder and the side in IocbHead. The encoded sector length is in IocbEncSectorLen, the Sector Length/4 (always fits in a byte) is in IocbQuarterSectorLen, the number of fill characters in Gap Three is in IocbGap3Len and the number of track to be formatted is in IocbTrackCnt. All parameters needed by DirectFormat must be put in individual variables. DirectFormat cannot access them in FloppyIOCB since it cannot have an imported variable (FloppyIOCB) in expressions. This command loops seeking to a track, then formatting it.
}
DoFormatTrackCmd:
; First, was there a disk change? If so, don't format the new disk
call CheckDiskChange
ora a ; 0=> no disk change, nz=> disk changed
jnz FinishCommand ; If disk changed, quit now
; Set the context for operation and update FDCState register. (Density, head, format)
call SetIOCBContext ; Set up the current context
; Check whether we need to do a seek. Compare desired cylinder with current value.
lxi h,Cylinder ; Point to current value
lda FloppyIOCB+IocbCylinder ; Assumes high part of cylinder is 0
cmp m
jz FormatTrackStart ; z => same, don't need a seek
FormatTrackSeek:
sta DCylinder
mov d,a ; Desired cylinder to D
mvi a,SkCmdNoV ; do a seek with no verify since
; there is probably trash there
mov e,a ; Command to E
call DoSeek
; Wait for disk completion. Yield if not complete.
call WaitFDCCompletion
; Command is complete. D has internal status, E has external status.
; Check for error completion in D (Not Ready, Seek error, CRC error, Busy)
FormatTrkSeekComplete:
mov a,D
ani Type1ErrorMask ; Check for error status
jnz FormatTrackStepError ; nz => there was an error, abort
; Check and update the Cylinder location.
call UpdateCylinder
; Successful seek. Start the Format.
; Decide whether to do single or double density and go do the appropriate
; routine
FormatTrackStart:
mvi a,0 ; load number of first sector-1 (code does
; increment before storing
sta Sector
lda FloppyIOCB+IocbEncSectorLen ; set the encoded sector
; length
sta EncSectorLen
lda FloppyIOCB+IocbSectorsPerTrack ; get number of sectors
; per track
sta SectorCnt
lda FloppyIOCB+IocbQuarterSectorLen ; set the data record len/4
sta QuarterSectorLen
lda FloppyIOCB+IocbGap3Len ; Set the number of fill chars
; in Gap 3
sta Gap3Len
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:
{Direct format used to return the disk status in the BC register pair as well as in DiskStatus and DiskStatusHi. Now we don't take any chances messing with BC while interrupts can occur.}
LDA DiskStatusHi
MOV E,A
LDA DiskStatus
MOV D,A
ani WriteErrorMask ; Check for error status
jnz FormatTrackError ; nz => there was an error, abort the IOCB
; Successful Format. Store completion in IOCB, in case it's the last
; written.
call MakeIOCBResultType2 ; Convert DE into an IOCB.result,
; store in IOCB
; See if there are any more tracks to format
lda FloppyIOCB+IocbSectorCnt ; This holds the Track count
dcr a ; have done one more track
sta FloppyIOCB+IocbSectorCnt
jz EndFormatTrk ; quit if no more Tracks to do
lda Cylinder ; Else, calculate next cylinder num
inr a
jmp FormatTrackSeek ; and go format that track on this
; side
; Exits:
FormatTrackStepError:
call SetErrorResult ; Set the Error bit
call MakeIOCBResultType1 ; Update Result location (from B,C)
jmp EndFormatTrk
; Completion error in the Format Track command.
; Set the error bits in IOCB.Result
FormatTrackError:
call SetErrorResult ; Set the Error bit
call MakeIOCBResultType2 ; Convert DE into an IOCB.result
EndFormatTrk:
jmp FinishCommand
; Command 6: Recalibrate.
{ Do a Restore command, and check for Track 0. Set Cylinder to 0. Before the Restore
is done, do 10 step-ins to ensure that the subsequent restore will work}
DoRecalibrateCmd:
mvi d,10 ; Issue 10 Stepin commands
mvi e,StepInCmdNoV ; Send StepIn commands (no verify)
InitStepLoop:
mov a,e
out FDCCommand ; Issue command
; Wait for completion.
XCHG
SHLD SaveDEOverYield
call WaitFDCCompletion ; A = completion
DB opLXID ;DE ← SaveDEOverYield
SaveDEOverYield:
DW 0
; Don't care what the status is. Any errors will be caught by the Recalibrate.
dcr d ; More Step-ins?
jnz InitStepLoop
; We should now be beyond (in) from Track 00. Do the Restore.
DoRestore:
lda RestoreCmd
out FDCCommand ; Start the command
; Wait for disk completion. Yield if not complete.
call WaitFDCCompletion
; Command is complete. D has internal status, E has external status.
; Check for error completion in D (Not Ready, Seek error, CRC error, Busy)
mov a,D
ani Type1ErrorMask ; Check for error status
jnz RestoreError ; nz => there was an error, abort
; Check for Track 0 bit.
mov a,D
ani FDCTk0Mask
jz RecalibrateError ; z => NO track 00 bit, error
; Set completion.
call MakeIOCBResultType1 ; Convert B,C into an IOCB.result, store in IOCB
; Check and update the Cylinder location.
xra a ; Desired cyclinder is 0
sta DCylinder
call UpdateCylinder
; Complete command.
EndRecalibrate:
jmp FinishCommand ; Finish up the command
; Error exits.
; Error on the Restore command.
RestoreError:
call SetErrorResult ; Set the Error bit
call MakeIOCBResultType1 ; Update Result location (from B,C)
jmp EndRecalibrate
; No track 0 bit. Set recalibrate error.
RecalibrateError:
mvi a,ErrorMask ; Set the Error bit in Result
ori RecalibrateErrorMask ; Set the RecalibrateError bit
call SetErrorResult1 ; Store the Error bits
call MakeIOCBResultType1 ; Update Result location (from B,C)
jmp EndRecalibrate
; Command 7: Initialize.
{ This command is the first command that should be issued to the FloppyTask.
It will initialize the floppy controller, set default values of single density, head=0,
no Precomp, and IBM format in the FDC State register. Setup the values of the various
disk commands. The DiskChange bit in the drive is cleared.}
DoInitializeCmd:
call FDCReset ; Reset the controller
;new 7-Oct-85 12:03:23
jnc FDCResetOkay2
lxi h,598 ; FDCTimeoutInInit
call MakeBadIOCBResult
jmp FinishCommand
;endnew 7-Oct-85 12:03:23
; Initialize various command values.
FDCResetOkay2:
lda F1797 ; Form ReadSector command
adi RSectorCmd
sta ReadSectorCmd
lda F1797 ; Form WriteSector command
adi WSectorCmd
sta WriteSectorCmd
lda S800 ; Form Restore command
adi ResCmd
sta RestoreCmd
lda S800 ; Form Seek command
adi SkCmd
sta SeekCmd
; Disable interrupts from the FDC chip
call Disable75
; Do a NOP command to get the disk status
jmp DoNOPCmd
; Command 8: Escape.
{ Look at the next word in the IOCB to determine the Escape command.
Currently, 1 => Clear Disk Change, #1 => invalid.
}
DoEscapeCmd:
lda FloppyIOCB+IocbEscCommand ; Get escape command
cpi ClrDiskChgCmd ; Is it a Disk Change Clear?
jz DoDiskChgClr ; z => Disk change clear
InvalidEscapeCmd:
lxi h,ErrorInvalidEscapeCmd ; ERROR: Invalid Escape command
call MakeBadIOCBResult
jmp FinishCommand
; Clear disk change by disabling drive and then enabling the drive with drive select.
DoDiskChgClr:
lda FDCStateVal ; Get current value for FDCState
ani nDriveSelectMask ; Clear Drive Select
out FDCState
ori DriveSelectMask ; Set Drive Select again
out FDCState
jmp DoNOPCmd ; Do a NOP command to get the status
;
; FLOPPY DISK SUBROUTINES.
; Subroutine: Disable75
; Disable the RST 7.5 interrupt, clear the RST 7.5 FF.
Disable75:
mvi a,ResetRst75+Rst75DisableMsk ; RST 7.5, clear 7.5 FF
call DisableRST ; Do the disable
ret
;new - 7-Oct-85 12:03:23
; Subroutine: FDCReset.
; Reset the FDC, and abort the automatic Restore with a ForceInterupt.
; Set the Context in FDCState and FDCStateVal to the default values.
; The diskChange bit in the drive is also cleared.
; Returns status in A, after command completes.
FDCReset:
mvi l,3 ; count = we will try three times to reset
FDCResetLoop:
mvi a,DisableFDC ; Clear state register, reset FDC, enable Waits
out FDCState
mvi a,9 ; Wait for more than 50 usec (MR pulse)
call DelayV
lda DefaultFDCState ; Get default values
sta FDCStateVal ; Get control byte for FDC
out FDCState
call Disable75 ; Disable the Floppy interrupt
mvi a,ForceInt0Cmd ; Issue Force Interrupt command
out FDCCommand
call Delay ; Busy status not available for 12 usec
call WaitFDCBusy ; wait for FDC busy
jnc FDCResetOkay
dcr l ; couldn't reset FDC
jnz FDCResetLoop
stc
ret
FDCResetOkay:
stc
cmc
ret
; Wait for Busy to reset. Status returned in A and D.
; The loop here is where the DLion hangs occasionally when doing rapid floppy
; operations.
WaitFDCBusy:
push h
lxi h,5000 ; or other count-down value. ***
WaitFDCBusyLoop:
in FDCStatus ; Check for restore command completion
mov D,a
ani FDCBusyMask
jz WaitFDCBusyExit
; count down for timeout
dcr l
jnz WaitFDCBusyLoop
dcr h
jnz WaitFDCBusyLoop
; timeout occured.
pop h
stc
ret
; normal exit.
WaitFDCBusyExit:
mov a,D ; Return status in A too
pop h
stc
cmc
ret
;endnew - 7-Oct-85 12:03:23
; Subroutine: WaitFDCCompletion.
; Wait for the completion of the FDC command.
; If the command is not complete, the task will Yield control.
; Note the use of this routine and interrupt processing are
; mutually exclusive.
; On exit:
; D = internal status
; E = external status
; DiskStatus = full status
WaitFDCCompletion:
; We save the return address because the stack is not saved over a Yield
POP H ;HL ← return address
SHLD WaitFDCCompletionRet
WaitFDC:
lda NoYield
ora a
JNZ CheckIntReqCompletion ; nz => Don't do Yield
LXI H,CheckIntReqCompletion
JMP SaveFloppyTaskResumeAddressAndYield
CheckIntReqCompletion:
in IntReq ; Check for completion in IntReq register
xri IntReqMask ; Complement active low signals
ani FDCIntMask
jz WaitFDC ; z => controller still busy
; Command has completed. Get status.
in FDCStatus ; Read internal status
mov D,a ; Save in reg D
sta DiskStatus ; Store status from chip
in FDCStatusReg ; Get external status register.
sta DiskStatusHi ; Store
mov E,a ; Move to E
DB opJMP ;JMP WaitFDCCompletionRet
WaitFDCCompletionRet:
DW 0 ; Return
ReadFDCStatus:
{This subroutine is just like the end of WaitFDCCompletion except that it uses the stack to return.}
in FDCStatus ; Read internal status
mov D,a ; Save in reg D
sta DiskStatus ; Store status from chip
ReadFDCStatusHi:
in FDCStatusReg ; Get external status register.
sta DiskStatusHi ; Store
mov E,a ; Move to E
RET
; Subroutine: UpdateDataPCB.
; Update the CP address, and IOP address in the DataPCB.
; Update IOP address in DataPCB;
; Note that the address update adds the count to the 3-byte address.
; On Exit:
; H,L = Address of Next IOP buffer
UpdateDataPCB:
lda FloppyIOCB+IocbCommandHi ; Check Command [0]
ora a
jm UpdateIOPPtr ; m => High bit is 1, no adjustment of Size
lhld FDDataPCB+CPCnt ; H,L ← count in the DataPCB
CALL ByteToWord ; H,L ← count in words
xchg ; D,E ← count in words
lhld FDDataPCB+CPAddr3 ; H,L ← low address word in the DataPCB
dad d ; H,L ← Low CPAddress + TransferCount
shld FDDataPCB+CPAddr3 ; Store back low address word in the DataPCB
lda FDDataPCB+CPAddr1 ; A ← High address byte
aci 0 ; Add in any possible carry from above addition
sta FDDataPCB+CPAddr1 ; Store back in PCB
; Update IOP buffer pointer.
UpdateIOPPtr:
lhld CPBufPtr ; get CP's pointer to IOP buffer
call IncrIOPPtr ; increment it in ring buff fashion
shld CPBufPtr ; and store it away
ret
; Subroutine: IncrIOPPtr.
; Set up an IOP data buffer pointer based on the value in HL.
; On entry:
; H,L = original contents of buffer pointer.
; On exit:
; H,L = New buffer pointer contents
; Only A and HL are changed.
; Note that the ring buffer wraps around if the END of the NEXT buffer lies
; after the end of the ring buffer, not only if the beginning of the next buffer
; does. This protects us from sectors that are too large and from not being on
; a proper buffer boundry. The second case will happen when we switch from
; transferring small sectors to transferring large ones.
IncrIOPPtr:
push d ; save regs
push h
xchg ; DE ← orig buf ptr
lhld DiskDmaCB+DmaBufCnt ; HL ← length of buffer
dad h ; HL ← length of two buffers
dcx h ; HL ← distance to end of next buffer
dad d ; HL ← pointer to end of next buffer area
lxi d,EndFDBuffer ; get end of total buffer area
call HLcmpDE ; Compare end of next buffer-End of Buffer area
jc FinIncrIOP ; if end of next buffer<= End of buff area, incr this
; buffer pointer
lxi h,StartFDBuffer ; else, start at beginning of buf area
pop d ; toss old pointer
pop d ; restore DE
ret
; point to the next disk buffer area in the ring buffer
FinIncrIOP:
pop d ; get original buffer pointer
lhld DiskDmaCB+DmaBufCnt ; HL ← length of disk buffer
dad d ; HL ← ptr to next disk buffer
pop d ; restore DE
ret
; Subroutine HLcmpDE.
; Set the conditions flags according to the double register compare HL-DE.
; Only the A reg and the flags are affected.
HLcmpDE:
mov a,h ; compare the hi bytes first
cmp d
rnz ; only compare the lo bytes if the hi
mov a,l ; bytes are equal
cmp e
ret
; Subroutines to insert the result bits from the disk operation into the IOCB.
; The subroutines assume that the Result word was cleared at the start of the operation.
; (In parenthesis is indicated what is done after a Type1, and Type2 command)
; Bit 0: diskChange (copy, copy)
; Bit 1: 0 (0, 0)
; Bit 2: twoSided (copy, copy)
; Bit 3: DiskID (copy, copy)
; Bit 4: error (error, error) - inserted externally to subroutine
; Bit 5: 0 (0, 0)
; Bit 6: recalibrateError (RestoreError/0, 0)
; Bit 7: dataLost (0, dataLost from bit 13)
; Bit 8: notReady (copy, copy)
; Bit 9: writeProtect (copy, copy)
; Bit 10: deletedData (0, copy)
; Bit 11: recordNotFound (copy, copy)
; Bit 12: crcError (copy, copy)
; Bit 13: track00 (copy, from Seek)
; Bit 14: index (copy, from Seek)
; Bit 15: busy (copy, copy)
; Subroutine: SetErrorResult.
; Set the Error bit in the IOCBResult.
; Call at SetErrorResult1 with different error mask in A.
SetErrorResult:
mvi a,ErrorMask ; Set the Error bit in Result
SetErrorResult1:
lxi h,FloppyIOCB+IocbResultHi
ora m
mov m,a
ret
; Subroutine: MakeIOCBResultType1.
; Insert the Result bits in D and E into the IOCB result.
; D has low bits (internal status), E has high bits (external status).
MakeIOCBResultType1:
; Process low part of Result.
lxi h,FloppyIOCB+IocbResult ; Point to Low Result in IOCB
mvi a,nType1ZeroMaskLo ; Clear values that should be zero
ana D
ora m
mov m,a ; Store back in IOCB
; Process high part of Result.
inx h ; Point to High Result in IOCB
mvi a,nType1ZeroMaskHi ; Clear values that should be zero
ana E
ora m
mov m,a ; Store back in IOCB
ret
; Subroutine: MakeBadIOCBResult - make the IOCB bad result flags
; HL -> would-be MP code.
MakeBadIOCBResult:
shld FloppyIOCB+IocbResult ; store MP code in result slot
lda FloppyIOCB+IocbResult+1 ; load Hi byte
ori nIocbErrorMaskOr ; set error bit
sta FloppyIOCB+IocbResult+1
ret
; Subroutine: MakeIOCBResultType2.
; Insert the Result bits in D and E into the IOCB result.
; D has low bits (internal status), E has high bits (external status).
MakeIOCBResultType2:
; Process low part of Result.
lxi h,FloppyIOCB+IocbResult ; Point to Low Result in IOCB
mvi a,nType2ZeroMaskLo ; Clear values that should be zero
ana D
ora m
mov m,a ; Store back in IOCB
; Process high part of Result.
inx h ; Point to High Result in IOCB
mvi a,nType2ZeroMaskHi ; Clear values that should be zero
ana E
ora m
mov m,a ; Store back in IOCB
; Fix up DataLost. Move bit 5 in DiskStatus to bit 7 in Result.
lda DiskStatus ; Get original low status
ani FDCLostData ; Isolate the DataLost bit
rrc ; Align appropriately
rrc
ora m
mov m,a ; Store back in IOCB
ret
; Subroutine: CheckDiskChange.
; Check if the DiskChange bit is True.
; If so, set the Error and DiskChange bits in IOCB.result.
; On exit:
; A = 0 : No disk change
; A # 0 : Disk change, IOCB result updated
CheckDiskChange:
; Check the DiskChange bit. If true, abort this IOCB, with Error, DiskChange in Result.
call ReadFDCStatusHi ; Read external status (returned in C)
mov a,E
ani DiskChangeMask ; Mask DiskChange bit
rz ; z => no DiskChange, proceed
; There was a DiskChange.
mvi D,0 ; Fake a DE status
call SetErrorResult ; Set Error bit
call MakeIOCBResultType1 ; Update Result location (from DE)
mvi a,-1 ; Set A nonzero
ret
; Subroutine: SetIOCBContext.
; Set the controller context from the values in the IOCB.
; The parameters changed are Double/Single' density, Troy/IBM', Side select.
; Both FDCState and FDCStateVal are changed.
SetIOCBContext:
lxi h,FDCStateVal ; Point to FDCStateVal
mvi a,nMiscContextMask ; Clear out old context bits in FDCStateVal
ana m
mov m,a ; Store back in FDCStateVal
lda FloppyIOCB+IocbMiscContext
ani MiscContextMask ; Mask out other bits
ora m ; OR in Double/Single', Troy/IBM' bits
mov m,a ; Store back in FDCStateVal
lda FloppyIOCB+IocbHead
ani HeadMask
sta DiskHead
push h ; Save H,L temporarilly
jz ContextSide0 ; Side 0 specified
; Side 1 specified.
mvi a,Side1Mask
ora m ; OR in Side 1
mov D,a ; Save FDCState value in D
; Fix up Read/Write commands.
lxi h,SectorCmd ; Set S bit in current SectorCmd
mvi a,Type2SMask
ora m
mov m,a
jmp SetContext
; Side 0 specified.
ContextSide0:
mvi a,nSide1Mask
ana m ; Clear Side 1 bit
mov D,a ; Save FDCState value in D
; Fix up Read/Write commands.
lxi h,SectorCmd ; Clear S bit in current SectorCmd
mvi a,nType2SMask
ana m
mov m,a
SetContext:
pop h ; Restore H,L to point to FDCStateVal
mov a,D ; Restore saved value from D
mov m,a ; Store back saved value in FDCStateVal
out FDCState ; Output to controller
ret
; Subroutine: DoTransfer
{ This routine is used in all read and write sector commands. The common code reduces the total size of Domino while providing a relatively clean procedure.
}
; On Entry:
; SectorCmd: holds command word
; DiskSectorDone: holds Boolean telling whether to start disk or CP
; transfer first
; A: holds DMA function mask
; Algorithm:
; Set up the Dma control blocks
; IF DiskChange, set ERROR and quit
; seek to proper cylinder if necessary
; WHILE DiskStatus=0 AND FloppyIOCB+IocbSectorCnt>0 DO
; WHILE DiskActive DO
; IF DiskSectorDone THEN
; IF CPXferCnt>0 THEN
; call DoCPBufService to transfer sectors between CP and IOP until
; no IOP Vacancies exist or CPXferCnt=0;
; call Yield;
; ENDLOOP;
; {Disk is now stopped}
; IF DiskStatus=0 AND FloppyIOCB+IocbSectorCnt>0 THEN
; DoSectorCmd to start or restart the disk
; ENDLOOP;
; Post disk status and quit
DoTransfer:
sta DiskDmaFunction ; Store function in Dma CB
lxi h,StartFDBuffer ; Setup the IOP buffer pointer
shld CPBufPtr ; in DataPCB
shld DiskBufPtr ; and in DiskDmaCB
xra a ;
sta DiskActive ; the disk is not transferring data now,
; Initialize the DataPCB with CP buffer address, and count, and IOP buffer.
; CP address from IOCB buffer address, transfer count from sector length in IOCB.
lxi h,FloppyIOCB ; Source
lxi d,FDDataPCB ; Destination
mvi A,3 ; No. of bytes (long address). Note the flags in the MS byte are not
; changed.
call Copy ; This copies the 3 bytes of address leaving flags untouched.
lhld FloppyIOCB+IocbSectorLen ; Transfer count=sector length (words)
CALL WordToByte
shld FDDataPCB+CPCnt ; Store in PCB count field
shld DiskDmaCB+DmaBufCnt ; Store in Dma CB count field
; Set the context for operation and update FDCState register. (Density, head, format)
call SetIOCBContext
; Check the DiskChange bit.
call CheckDiskChange ; is there a disk change?
ora a
jnz FinishCommand ; nz => had a DiskChange, quit
; Check whether we need to do a seek. Compare desired cylinder with current value.
lxi h,Cylinder ; Point to current value
lda FloppyIOCB+IocbCylinder ; Assumes high part of cylinder is 0
cmp m
jz FinXferSetup ; z => same, don't need a seek
sta DCylinder
mov d,a ; Desired cylinder to D
lda SeekCmd
mov e,a ; Command to E
call DoSeek
; Nothing else to do, so wait for disk completion. Yield if not complete.
call WaitFDCCompletion
; Command is complete. D has internal status, E has external status.
; Check for error completion in D (Not Ready, Seek error, CRC error, Busy)
mov a,D
ani Type1ErrorMask ; Check for error status
jnz HadXferSeekError ; nz => there was an error, abort
; Check and update the Cylinder location.
call UpdateCylinder
; Successful seek. Start the Transfer.
FinXferSetup:
xra a ; clear the Disk status
sta DiskStatus ; there have been no errors so far.
lda FloppyIOCB+IocbSectorCnt ; Set the length of the
sta CPXferCnt ; CP transfer
lda FloppyIOCB+IocbSector ; Sector ← IOCB.FirstSector
sta Sector ; set initial sector number
jmp WaitForDisk ; Choose whether to start the
; CP or disk transfer first
; We either got behind the disk or it hasn't started yet. Start it in either
; case
StartDisk:
call DoSectorCmd ; restart the disk
WaitForDisk:
ei ; make sure Disk can finish
lda NoYield
ora a
JNZ TestDiskSectorDone ; nz => Don't do Yield
LXI H,TestDiskSectorDone
JMP SaveFloppyTaskResumeAddressAndYield
TestDiskSectorDone:
lda DiskSectorDone ; should the CP service the IOP
ora a ; buffer? (this is set when a write op
; begins, reset when a read op begins)
jz CheckDiskActive ; no, see if the transfer is still
; going
lda CPXferCnt ; yes, are there any more sectors
ora a ; to transfer?
cnz DoCPBufService ; yes, fill the IOP buffer
; The IOP buffer needs no service from the CP. Is the disk still running?
CheckDiskActive:
di ; make sure the disk status does
; not change while it is being
; investigated
lda DiskActive
ora a ; nz => yes
jnz WaitForDisk ; so wait for it
lda DiskStatus ; no, did it stop because of an
; error?
ora a ; nz => there was an error
jnz PostDkStatus ; so stop now
lda FloppyIOCB+IocbSectorCnt ; no, did it finish the
ei ; (it's ok for things to happen
; again)
ora a ; transfer?
jnz StartDisk ; transfer faltered, so start or
; restart the disk.
; Post the final Disk Status and release the CP port
PostDkStatus:
ei ; just in case we got here via an error
; posted in DiskStatus
lda CPXferCnt ; Are there any sectors to finish up?
ora a ; nz => disk just stopped, either ok or
cnz DoCPBufService ; because of error. Transfer all remaining
; sectors.
lda DiskStatusHi ; get final external status
mov E,a ; pass status word in DE
lda DiskStatus ; get final internal status
mov D,a
; See if an error result should be posted
ora a
cnz SetErrorResult ; no, set the Error bit
call MakeIOCBResultType2 ; Update Result location (from B,C)
jmp FinishCommand ; Finish up the command
; Error branches:
; Error in the Seek command.
HadXferSeekError:
call SetErrorResult ; Set the Error bit
call MakeIOCBResultType1 ; Update Result location (from DE)
jmp FinishCommand ; quit now
; Subroutine: DoCPBufService
{This routine is used to transfer data between the CP and IOP memories. There is one large buffer area for the Floppy disk. It is divided into multiple buffers each holding one sector. The CP pointer to this buffer is in CPBufPtr. The disk pointer is in DiskBufPtr. This routine keeps transferring data, advancing CPBufPtr and decrmenting CPXferCnt until a) CPXfercnt reaches 0 or b) CPBufPtr=DiskBufPtr. If it leaves the buffer full, it resets DiskSectorDone. }
DoCPBufService:
POP H ;HL ← return address
SHLD DoCPBufServiceReturn
WaitForPortForBufService:
LDA PortBusyFlag
ORA A
JZ StartBufTransfer
LXI H,WaitForPortForBufService
JMP SaveFloppyTaskResumeAddressAndYield
; Transfer the data for the first sector
; Is this a read or write operation? The commands are 001=ReadSector,
; 010=WriteSector, 011=WriteDelectedSector and 100=ReadID. Hence if bit
; 0000 0010 is off, it is a Read op so we should write to the CP.
StartBufTransfer:
ei ; let the interrupt routine in during
; CP transfers
lxi h,FDDataPCB ; Pointer to FDDataPCB CPport control block
lda FloppyIOCB+IocbCommand ; get the command
ani 2 ; Is this a read from the disk?
jnz ReadFromCP ; no, so read from CP, write to disk
call StartCPWriteDMA ; yes, start the Dma write to CP main memory
jmp WaitCPDmaCompletion
ReadFromCP:
call StartCPReadDMA ; data going to Disk from CP
; Wait for the transfer to complete
WaitCPDmaCompletion:
call CheckCPDMAComplete
JNZ CPComplete
LXI H,WaitCPDmaCompletion
JMP SaveFloppyTaskResumeAddressAndYield
CPComplete:
call UpdateDataPCB ; Update CP address, IOP ptr in PCB
; Was that the last CP transfer?
di ; don't let the interrupt routine change
; things now
lda CPXferCnt
dcr a
sta CPXferCnt
jz FinCPBufSrvc ; yes, just finish up the buffer service
; More sectors to transfer, is the IOP buffer area full?
xchg ; DE ← addr of next buffer to be transferred
; by CP
lhld DiskBufPtr ; HL ← addr of buff being used by disk now
call HLcmpDE ; is the buffer area full (z=>yes)
jnz StartBufTransfer ; yes, finish the buffer service
; Buffer is now full, reset DiskSectorDone, release CP port and finish
FinCPBufSrvc:
xra a ; turn off DiskSectorDone
sta DiskSectorDone ; note interrupts are disabled now
ei ; let the disk finish now
DB opJMP ;JMP DoCPBufServiceReturn
DoCPBufServiceReturn:
DW 0 ;RET
; Interrupt Routine: FloppyIntr
; This routine is called whenever the disk finishes a transfer of one
; sector. It checks the status, the count of remaining sectors and the
; buffer pointers. Another sectors is started if and only if there was no
; error on the last one, there are more sectors to do and there is either
; data available to write or space available for data to be read into.
FloppyIntr:
push psw ; Save the Regs
push d
push h
ei ; ok for other processes (RS232) to interrupt
; this one.
in FDCStatusReg ; Get external status register.
sta DiskStatusHi ; save it
mov E,a ; Save for DMA test below
in FDCStatus ; Read internal status
mov D,a ; prepare to test disk status
lda DkErrorMask ; get proper error mask for this operation
ana D ; look for errors in that operation
sta DiskStatus ; Store status from chip
jnz StopDiskService ; if error, quit now
; No disk error, check DMA completion, and clear the disk DMA channel.
; If both DMA indicators agree the chip completed, no errors. If they
; disagree, we jump to ErrorReport showing which one failed. If both
; say DMA didn't complete, we post LostData and return.
lxi h,0 ; initialize offset into DMA error table
; Check that DMA terminated.
mov a,E ; Get external disk status
ani FDCEndCountMask
jnz GoodDmaEndCount1
mvi l,4
GoodDmaEndCount1:
mvi E,FloppyChannelMask
call ReadDMACompletion ; Get completion
jnz EndDmaCheck ; nz => channel completed, look at both
; indicators
mvi a,8 ; else set the error flag
ora l
mov l,a
EndDmaCheck:
lxi d,DmaCheckTable ; load base of instruction table used to
; check the DMA completion
dad d ; calculate the address of the code used to
; deal with the DMA completion or lack of it
pchl ; go deal with the DMA
DmaCheckTable:
jmp SectorCmdDone ; Both DMAs ok
nop ; each table entry is 4 bytes
jmp NoDmaEndCount1 ; only the External DMA End Count failed
nop
jmp NoDmaEndCount2 ; only internal DMA End Count failed
nop
; both DMA endcounts agree the DMA did not finish. Probably this means the
; sector length was wrong, so say LostData and quit
mvi a,FDCLostData ; set the Lost Data bit
sta DiskStatus
jmp StopDiskService ; stop the Transfer now
; Only the External DMA end count indicator failed
NoDmaEndCount1:
lxi h,ErrorNoDmaEndCount1 ; ERROR: External Dma End Count not set
jmp ErrorReport ; Loop forever
; Only the Internal DMA end count indicator failed
NoDmaEndCount2:
lxi h,ErrorNoDmaEndCount2 ; ERROR: Internal Dma End Count
; not set
jmp ErrorReport ; Loop forever
; Decrement the Disk count for the operation that just
; completed successfully
SectorCmdDone:
lda Sector ; increment the Sector num so the CP
inr a ; routine will know where to retry
sta Sector
lda FloppyIOCB+IocbSectorCnt ; get the num of sectors left
dcr a ; update it
sta FloppyIOCB+IocbSectorCnt
; Update the IOP buffer address. Note this must be done even if that
; was the last sector to allow DoCPBufService to transfer all sectors.
lhld DiskBufPtr
call IncrIOPPtr ; Point to the next buffer location
shld DiskBufPtr ; Update mem pointer
xchg ; has the disk caught up to the CP transfer?
lhld CPBufPtr ; Compare the two ring buf ptrs
call HLcmpDE
jz StopDiskService ; yes, can't do any more transfers
lda FloppyIOCB+IocbSectorCnt ; get the num of sectors left
ora a ; is it zero?
jz StopDiskService ; quit if that was the last sector to
; be transferred
; There were no errors, there are more sectors to do and there is buffer
; space to use, do start another Sector
call DoSectorCmd
jmp FinDiskService ; and finish up
; There is some reason to stop now. Disable the interrupts and stop
StopDiskService:
xra a ; turn off DiskActive
sta DiskActive
call Disable75 ; turn off the Floppy interrupts
mvi a,FloppyChannelMask ; turn off the DMA, just to make sure
call ClearDmaChannel
FinDiskService:
mvi a,0FFH ; Tell DoTransfer something has happened
sta DiskSectorDone
pop h ; restore the registers
pop d
pop psw
ret ; from interrupt
; Subroutine: UpdateCylinder.
; After a type 1 command has executed check the Track register in the 1797.
; Check if Track register matches DCylinder, and update Cylinder if so.
; On entry:
; DCylinder = Desired Cylinder
UpdateCylinder:
in FDCTrack ; Check track register
lxi h,DCylinder ; Pointer to desired cylinder
cmp m
jz DoUpdateCylinder ; Check if correct
CommandTrackError:
pop h ; balance return on stack
lxi h,ErrorCommandTrackError ; ERROR: Track register is not correct
call MakeBadIOCBResult
jmp FinishCommand
; Track register is correct. Update Cylinder location.
DoUpdateCylinder:
sta Cylinder
ret
; Subroutine: DoSeek.
; Issue the Seek command.
; Assumes FDC Track register is up-to-date.
; Check validity of track number (**debugging only).
; Load Data register, and issue command.
; On entry:
; D = Desired Track
; E = Command
DoSeek:
mov a,d ; Check if desired Track OK
cpi 77
jc SeekTrackOK ; c => Cylinder number OK
TrackToBig:
pop h ; make stack balance
lxi h,ErrorTrackToBig ; ERROR: Track number is too large.
call MakeBadIOCBResult
jmp FinishCommand ; return bad status
; Note there is no need to check to see if the track number is negative. In
; two's complement negative numbers are greater than 77 so would be caught in the
; test above.
; Track number in A is OK.
SeekTrackOK:
out FDCData ; Set desired track in Data Register
; Check destination cylinder.
; If greater than PreCompStart then set EnPreComp, else clear EnPreComp.
lda FDCStateVal ; Check density in FDCStateVal
ani DDenMask ; Mask out Double density mask
jz SetNoPreComp ; z => single density
; Double density. Check if PreComp needed.
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 so no precomp
; Enable write Precomp.
SetPreComp:
mvi a,FDCEnPreCompMask
lxi h,FDCStateVal ; Point to FDCStateVal
ora m ; Set the bit
IssueSeek:
mov m,a ; Store in FDCStateVal
out FDCState ; Store in FDCState
mov a,e ; Get Command
out FDCCommand
ret
; Set write precomp.
SetNoPreComp:
mvi a,nFDCEnPreCompMask ; Clear write precomp. bit
lxi h,FDCStateVal ; Point to FDCStateVal
ana m ; Clear the bit
jmp IssueSeek
; Subroutine: DoSectorCmd.
; Implements Read commands: Read Sector, Read Address.
; Implements Write commands: Write Sector, Write Deleted Sector.
; Assumes Head at track and Track Register up to date.
; On entry:
; Sector = Desired sector
; SectorCmd = Command
; Dma Control Block is set up in DiskDmaCB.
; Call subroutine at DoReadAddressTrack, for Read Address or Read Track.
DoSectorCmd:
lda Sector ; Set up sector for FDC dhip
out FDCSector ; Set desired sector in FDCSector Register
ContSectorCmd:
; Program channel 0 of Dma controller for memory writes.
LDA DiskDmaFunction
lxi h,DiskDmaCB ; Point to DmaCB
call StartFloppyChannel ; Enable the channel
; Dma channel is now programmed and enabled.
GoodDmaChannel:
lda SectorCmd ; Get FDC command
out FDCCommand ; Issue command
; Turn on the Floppy Interrupts so FloppyIntr can handle the end of the
; transfer. Clear 7.5 FF.
EnFloppyInterrupt:
mvi a,ResetRst75+Rst75DisableMsk ; RST 7.5, clear 7.5 FF
call EnableRST ; Enable the Floppy interrupt but let the
; calling routine actually enable interrupts
mvi A,0FFH ; turn on DiskActive for DoTransfer
sta DiskActive
ret
; Subroutine: Delay.
; Floppy Delay subroutine.
; Call at Delay (no A register setup) for delay of ~15.6 us.
; Call at Delay9 (no A register setup) for delay of ~9.5 us.
; Call at DelayV for variable wait:
; If (A) = k, then Delay = 32+ 14k cycles
; = 10.67 + 4.67k usec at 3MHz CPU
; k=9 => > 50 us.
Delay:
mvi a,1
DelayV:
dcr a
jnz DelayV
Delay9:
ret
SaveFloppyTaskResumeAddressAndYield:
SHLD FloppyTaskResumeAddress
FloppyTaskYield:
DS 0
{Pass control to the next task specified in Domino.cfg}
END FloppyTask