{ 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, 19
81 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. T
he 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 (Decem
ber 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.asm"
END SysDefs
get "CommonDefs.asm"
END ExecDefs
get "FloppyDefs.asm"
END FloppyDefs
; 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
;
0000 C3 DB opJMP
FloppyTaskResumeAddress:
0001 0000 DW StartFloppyTask
; JMP WaitForPortToReadIOCB
; JMP WaitForPortForBufService
; JMP WaitCPDmaCompletion
; JMP TestDiskSectorDone
; Floppy task internal storage.
FloppyCSB: ;14021
0003 DS 1*2
; IOCB:
; Note: words 11 and 12 are only used by the Format Track command.
FloppyIOCB:
0005 ds 2 ; Word 0: Buffer Pointer (low 16 bits)
0007 ds 2 ; Word 1: Buffer Pointer (high)
0009 ds 2 ; Word 2: Buffer size (words)
000B ds 2 ; Word 3: Sector Length (words)
000D ds 2 ; Word 4: Context (bit 11-Troy/IBM', bit 12-Double/Single'
000F ds 2 ; Word 5: Cylinder
0011 ds 2 ; Word 6: Head (bits 0:7), Sector (bits 8:15)
0013 ds 2 ; Word 7: Sector count
0015 ds 2 ; Word 8: IOCB result
0017 ds 2 ; Word 9: IOCB command
0019 ds 2 ; Word 10: Escape Command
001B ds 2 ; Word 11: SectorLen/4 (bits 0:7),
; Encoded Sector len (bits 8:15)
001D ds 2 ; Word 12: SectorsPerTrack (bits 0:7)
; NumFillCharsInGap3 (bits 8:15)
001F ds 6 ; Word 13-15: (Reserved)
; DATA BUFFER (it should be at least 2k bytes = 4 standard sectors):
; (In Buffer.asm)
; CONTROL Blocks:
DiskDmaFunction:
0025 ds 1 ; Function field: DmaRead or DmaWrite
; Dma control block for Disk operation:
DiskDmaCB:
0026 ds 2 ; Size of data buffer (bytes)
DiskBufPtr:
0028 ds 2 ; Start of data buffer
; CP Port Control Blocks:
; Items that are not fixed are marked with an '*'
; PCB for IOCB Fetch.
FDIocbPCB:
002A ds 2 ; *CP buffer pointer (low): IOCB pointer from CSB
002C 0000 DW CPIOCBHi ; CP buffer pointer (high)
002E 0020 dw 32 ; CP buffer count (bytes): IOCB size
0030 0005 dw FloppyIOCB ; Pointer to IOP buffer: Local IOCB
; PCB for data transfer.
FDDataPCB:
0032 ds 2 ; *CP buffer pointer (low): Data buffer address
0034 0001 DW 1 ; *CP buffer pointer (high): Data buffer address
; govern the transfer. Swapped + Virtual.
0036 ds 2 ; *CP buffer count (bytes): Data buffer size
CPBufPtr:
0038 ds 2 ; *Pointer to IOP buffer: Local Data buffer
; PCB for IOCB result update.
FDResultPCB:
003A ds 2 ; *CP buffer pointer (low): IOCB.sectorCount
003C 0000 DW CPIOCBHi ; CP buffer pointer (high)
003E 0004 dw 4 ; CP buffer count (bytes): Sector count, result
0040 0013 dw FloppyIOCB+IocbSectorCnt ; Pointer to IOP buffer: Local IOCB.sectorCount
; PCB to reset FloppyIocbAddr.
ResetFloppyIocbAddr:
0042 FF13 dw FloppyIocbLoc ; CP buffer pointer (low): IOCB pointer
0044 0000 dw CPIOPageHi ; CP buffer pointer (high)
0046 0002 dw 2 ; CP buffer count (bytes): IOCB size
0048 0000 DW ZeroCommand ; Pointer to IOP buffer:
; PCB to read Floppy CSB.
ReadFloppyCSB:
004A FF21 dw FloppyCSBLoc ; CP buffer pointer (low): IOCB pointer
004C 0000 dw CPIOPageHi ; CP buffer pointer (high)
004E 0002 dw FloppyCSBSize ; CP buffer count (bytes): IOCB size
0050 0003 DW FloppyCSB ; Pointer to IOP buffer:
; Miscellaneous storage:
FDCStateVal:
0052 ds 1 ; constant for FDC state register
DefaultFDCState:
0053 85 db DefFDCStateVal ; constant for FDC state register
DiskStatus:
0054 ds 2 ; Disk status after operation (Low: internal, Hi: external)
DiskStatusHi equ DiskStatus+1
RemainSize:
0056 ds 2 ; Remaining size of IOCB buffer
RemainSizeHi equ RemainSize+1
RestoreCmd:
0058 ds 1 ; Restore Command
SeekCmd:
0059 ds 1 ; Seek Command
ReadSectorCmd:
005A ds 1 ; Read Sector Command
WriteSectorCmd:
005B ds 1 ; Write Sector Command
SectorCmd:
005C ds 1 ; Sector Command storage for the sector command
; Disk address data.
DiskHead:
005D ds 1 ; Current side
Cylinder:
005E ds 1 ; Current cylinder
DCylinder:
005F ds 1 ; Desired cylinder in Seek
; Note: Sector, NewIntCycle, ErrorSectorCnt MUST be adjacent bytes!!.
Sector:
0060 ds 1 ; Current sector
F1797:
0061 0008 dw 8H ; F1797 commands: 8H => 1797, 0 => 1793
S800:
0063 0000 dw 0H ; SA800 drive: 2H => SA800, 0 => SA850
; Variables used to format a track
SectorCnt:
0065 ds 1 ; number of sectors left in track being
; formatted
Gap3Len:
0066 ds 1 ; number of fill chars in Gap 3 of Diskette
QuarterSectorLen:
0067 ds 1 ; size of data record/4.
EncSectorLen:
0068 ds 1 ; encoded length of sector (00=>128 bytes,
; 01=>256, 10=>512, 11=> 1024 bytes)
; Disk and CP transfer counts
CPXferCnt:
0069 ds 1 ; # or CP transfers to do in disk operation
; Error mask used in transfer
DkErrorMask:
006A ds 1 ; one mask for reads, one for writes
; Boolean used to decide if disk is running
DiskActive:
006B ds 1 ; 0FF=> disk is doing a data transfer,
; 00 => no data transfer
; Boolean used to decide if CP<->IOP transfer should occur
DiskSectorDone:
006C ds 1 ; 00 => no transfer needed
; 0FF => need a transfer
; **Debugging variables:
NoYield:
006D 00 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:
006E 3A 0000 lda FloppyIocbAddrHi
0071 B7 ora a
0072 CA 0000 JZ FloppyTaskYield
0075 3A 0000 LDA PortBusyFlag
0078 B7 ORA A
0079 C2 0000 JNZ FloppyTaskYield
007C 21 004A LXI H,ReadFloppyCSB
007F CD 0000 CALL ReadCPBuffer ;Read Floppy CSB
{Transfer the IOCB into local memory.}
0082 CD 0000 call Disable75 ; turn off all FDC interrupts so commands
; that don't use them won't be bothered
0085 2A 0000 LHLD FloppyIocbAddr ; Fetch IOCB pointer in main memory (low 16 bits)
0088 22 002A shld FDIocbPCB+CPAddr3 ; Store in PCB CP address slot
; Setup the FDResultPCB with the pointer to the Sector count in the IOCB.
008B 11 0007 lxi D,CPIocbSectorCnt ; DE ← SectorCnt index
008E 19 dad D ; H,L ← Pointer to SectorCnt location in IOCB
008F 22 003A shld FDResultPCB+CPAddr3 ; Store back (Note: assume no 64K crossing)
0092 21 002A lxi h,FDIocbPCB ; Pointer to FDIocbPCB CPport control block
0095 CD 0000 call ReadCPbuffer ; Read CP main memory
; IOCB is in local memory. Clear the Result slot.
0098 21 0000 lxi h,0 ; Result cleared
009B 22 0015 shld FloppyIOCB+IocbResult
; Dispatch on command.
FDCommandDisp:
009E 3A 0017 lda FloppyIOCB+IocbCommand ; Get command (low 8 bits)
00A1 21 0000 lxi h,FDCommandTable ; Point to command table
00A4 C3 0000 jmp CommandDispatch
; Command table is a table of starting address of the command code.
FDCommandTable:
00A7 0000 C0: dw DoNOPCmd ; NOP (command=0)
00A9 0000 C1: dw DoReadSectorCmd ; Read Sector (command=1)
00AB 0000 C2: dw DoWriteSectorCmd ; Write Sector (command=2)
00AD 0000 C3: dw DoWriteDelSectorCmd ; Write Deleted Sector (command=3)
00AF 0000 C4: dw DoReadIDCmd ; ReadID (command=4)
00B1 0000 C5: dw DoFormatTrackCmd ; FormatTrack (command=5)
00B3 0000 C6: dw DoRecalibrateCmd ; Recalibrate (command=6)
00B5 0000 C7: dw DoInitializeCmd ; Initialize (command=7)
00B7 0000 C8: dw DoEscapeCmd ; Escape (command=8)
EndCommandTable:
00B9 00 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:
00BA FE 09 cpi MaxFloppyCommand ; Check for largest command
00BC D2 0000 jnc NoValidCommand ; nc => command (in A) >= MaxCommand
00BF 87 add a ; A ← 2*(Command), assume less than 128 commands
00C0 5F mov e,a ; DE ← 2*Command
00C1 16 00 mvi d,0
00C3 19 dad d ; HL ← CommandTable+2*(Command)
; Command Found. H,L points to appropriate slot
00C4 5E mov e,m ; Address to D,E
00C5 23 inx h
00C6 56 mov d,m
00C7 D5 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 upda
ted.}
00C8 3E CA MVI A,opJZ
00CA 32 0000 STA FloppyActiveSwitch1
00CD 32 0000 STA FloppyActiveSwitch2
00D0 32 0000 STA FloppyActiveSwitch3
00D3 3E F3 MVI A,opDI
00D5 32 0000 STA FloppyActiveSwitch4
00D8 C9 ret ; Jump to command
; The command was not valid. Mark IOCB with error result.
NoValidCommand:
00D9 21 0244 lxi h,ErrorNoValidCommand ; ERROR: Invalid command
00DC CD 0000 call MakeBadIOCBResult
00DF C3 0000 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:
00E2 3E D0 mvi a,ForceInt0Cmd ; Issue command with immediate interrupt
00E4 D3 84 out FDCCommand ; Issue command
; Wait for completion (should be immediate).
00E6 CD 0000 call WaitFDCBusy ; Call end of FDCReset subroutine
;--new 7-Oct-85 12:03:23
00E9 D2 0000 jnc FDCNoopOkay
00EC 21 0255 lxi h,597 ; FDCTimeoutInNOP
00EF CD 0000 call MakeBadIOCBResult
00F2 C3 0000 jmp FinishCommand
;--endnew 7-Oct-85 12:03:23
FDCNoopOkay:
00F5 CD 0000 call ReadFDCStatus ; Call end of WaitFDCCompletion for status
; Command complete. Form IOCB result from status in B,C.
00F8 CD 0000 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:
00FB 3A 0000 LDA PortBusyFlag
00FE B7 ORA A
00FF CA 0000 JZ NotifyCPResult
0102 21 00FB LXI H,WaitForPortToNotifyResult
0105 C3 0000 JMP SaveFloppyTaskResumeAddressAndYield
NotifyCPResult:
0108 21 003A lxi h,FDResultPCB ; Pointer to FDResultPCB CPport control block
010B CD 0000 call WriteCPbuffer ; Write IOCB in main memory
; Clear FloppyIocbAddr.
010E 21 0042 lxi h,ResetFloppyIocbAddr
0111 CD 0000 call WriteCPbuffer ; Write IOCB in main memory
; Do the naked notify.
0114 2A 0003 lhld FloppyCSB ; Mask is in first word
0117 CD 0000 call DoNakedNotify
FinishCommand1:
{When the floppy and RS232C are both running, we must disable interrupts whenever DmaActive is being upda
ted. Put things back so that we don't disable interrupts. This is important for fast RS232C.}
011A 3E C3 MVI A,opJMP
011C 32 0000 STA FloppyActiveSwitch1
011F 32 0000 STA FloppyActiveSwitch2
0122 32 0000 STA FloppyActiveSwitch3
0125 AF XRA A ;A ← NOP
0126 32 0000 STA FloppyActiveSwitch4
0129 21 006E LXI H,StartFloppyTask
012C C3 0000 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:
012F 3A 005A lda ReadSectorCmd ; Set sector command
0132 32 005C sta SectorCmd
DoReadAddrCmd:
0135 3E 9D mvi a,ReadErrorMask ; Set the read error mask for
0137 32 006A sta DkErrorMask ; Do Transfer
013A AF xra a ; Disk has done nothing yet
013B 32 006C sta DiskSectorDone ; so start with a disk op.
013E 3E 40 mvi a,InDmaFunc ; Set up Dma CB for Reads, memory writes
0140 C3 0000 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:
0143 3A 005B lda WriteSectorCmd ; Fix write sector command
0146 E6 FE ani nDeletedDataMask ; Clear the Deleted data bit
0148 32 005C sta SectorCmd ; Store in command location
DoWriteSectorStart:
014B 3E DD mvi a,WriteErrorMask ; Set proper error mask used to
014D 32 006A sta DkErrorMask ; detect FDC errors during writes
0150 3E FF mvi a,0FFH ; say disk has "written" all
0152 32 006C sta DiskSectorDone ; sectors so we will start by
; filling all from the CP
0155 3E 80 mvi a,OutDmaFunc ; Set up Dma CB for Writes, memory reads
0157 C3 0000 jmp DoTransfer ; go finish the Write command
; Command 3: Write Deleted Sector.
DoWriteDelSectorCmd:
015A 3A 005B lda WriteSectorCmd ; Fix write sector command
015D F6 01 ori DeletedDataMask ; Set the Deleted data bit
015F 32 005C sta SectorCmd
0162 C3 014B 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:
0165 3E C0 mvi a,RAddrCmd ; Set ReadID command
0167 32 005C sta SectorCmd
016A 21 0003 lxi h,RAddrDataLen ; Fake sector length
016D 22 000B shld FloppyIOCB+IocbSectorLen
0170 C3 0135 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 specifie
d 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 Ioc
bEncSectorLen, 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. A
ll parameters needed by DirectFormat must be put in individual variables. DirectFormat cannot access the
m in FloppyIOCB since it cannot have an imported variable (FloppyIOCB) in expressions. This command loop
s seeking to a track, then formatting it.
}
DoFormatTrackCmd:
; First, was there a disk change? If so, don't format the new disk
0173 CD 0000 call CheckDiskChange
0176 B7 ora a ; 0=> no disk change, nz=> disk changed
0177 C2 00FB jnz FinishCommand ; If disk changed, quit now
; Set the context for operation and update FDCState register. (Density, head, format)
017A CD 0000 call SetIOCBContext ; Set up the current context
; Check whether we need to do a seek. Compare desired cylinder with current value.
017D 21 005E lxi h,Cylinder ; Point to current value
0180 3A 000F lda FloppyIOCB+IocbCylinder ; Assumes high part of cylinder is 0
0183 BE cmp m
0184 CA 0000 jz FormatTrackStart ; z => same, don't need a seek
FormatTrackSeek:
0187 32 005F sta DCylinder
018A 57 mov d,a ; Desired cylinder to D
018B 3E 1A mvi a,SkCmdNoV ; do a seek with no verify since
; there is probably trash there
018D 5F mov e,a ; Command to E
018E CD 0000 call DoSeek
; Wait for disk completion. Yield if not complete.
0191 CD 0000 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:
0194 7A mov a,D
0195 E6 99 ani Type1ErrorMask ; Check for error status
0197 C2 0000 jnz FormatTrackStepError ; nz => there was an error, abort
; Check and update the Cylinder location.
019A CD 0000 call UpdateCylinder
; Successful seek. Start the Format.
; Decide whether to do single or double density and go do the appropriate
; routine
FormatTrackStart:
019D 3E 00 mvi a,0 ; load number of first sector-1 (code does
; increment before storing
019F 32 0060 sta Sector
01A2 3A 001B lda FloppyIOCB+IocbEncSectorLen ; set the encoded sector
; length
01A5 32 0068 sta EncSectorLen
01A8 3A 001E lda FloppyIOCB+IocbSectorsPerTrack ; get number of sectors
; per track
01AB 32 0065 sta SectorCnt
01AE 3A 001C lda FloppyIOCB+IocbQuarterSectorLen ; set the data record len/4
01B1 32 0067 sta QuarterSectorLen
01B4 3A 001D lda FloppyIOCB+IocbGap3Len ; Set the number of fill chars
; in Gap 3
01B7 32 0066 sta Gap3Len
01BA 3A 0052 lda FDCStateVal
01BD E6 08 ani DDenMask ; is the double density bit on?
01BF C2 0000 jnz DoDoubleDensityFormat ; yes, format for double density
01C2 CD 0000 call WriteSDTrack ; no, format a single density track
01C5 C3 0000 jmp CheckFormatResult ; and see if done with run of tracks
DoDoubleDensityFormat:
01C8 CD 0000 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 DiskSt
atusHi. Now we don't take any chances messing with BC while interrupts can occur.}
01CB 3A 0055 LDA DiskStatusHi
01CE 5F MOV E,A
01CF 3A 0054 LDA DiskStatus
01D2 57 MOV D,A
01D3 E6 DD ani WriteErrorMask ; Check for error status
01D5 C2 0000 jnz FormatTrackError ; nz => there was an error, abort the IOCB
; Successful Format. Store completion in IOCB, in case it's the last
; written.
01D8 CD 0000 call MakeIOCBResultType2 ; Convert DE into an IOCB.result,
; store in IOCB
; See if there are any more tracks to format
01DB 3A 0013 lda FloppyIOCB+IocbSectorCnt ; This holds the Track count
01DE 3D dcr a ; have done one more track
01DF 32 0013 sta FloppyIOCB+IocbSectorCnt
01E2 CA 0000 jz EndFormatTrk ; quit if no more Tracks to do
01E5 3A 005E lda Cylinder ; Else, calculate next cylinder num
01E8 3C inr a
01E9 C3 0187 jmp FormatTrackSeek ; and go format that track on this
; side
; Exits:
FormatTrackStepError:
01EC CD 0000 call SetErrorResult ; Set the Error bit
01EF CD 0000 call MakeIOCBResultType1 ; Update Result location (from B,C)
01F2 C3 0000 jmp EndFormatTrk
; Completion error in the Format Track command.
; Set the error bits in IOCB.Result
FormatTrackError:
01F5 CD 0000 call SetErrorResult ; Set the Error bit
01F8 CD 0000 call MakeIOCBResultType2 ; Convert DE into an IOCB.result
EndFormatTrk:
01FB C3 00FB 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:
01FE 16 0A mvi d,10 ; Issue 10 Stepin commands
0200 1E 5A mvi e,StepInCmdNoV ; Send StepIn commands (no verify)
InitStepLoop:
0202 7B mov a,e
0203 D3 84 out FDCCommand ; Issue command
; Wait for completion.
0205 EB XCHG
0206 22 0000 SHLD SaveDEOverYield
0209 CD 0000 call WaitFDCCompletion ; A = completion
020C 11 DB opLXID ;DE ← SaveDEOverYield
SaveDEOverYield:
020D 0000 DW 0
; Don't care what the status is. Any errors will be caught by the Recalibrate.
020F 15 dcr d ; More Step-ins?
0210 C2 0202 jnz InitStepLoop
; We should now be beyond (in) from Track 00. Do the Restore.
DoRestore:
0213 3A 0058 lda RestoreCmd
0216 D3 84 out FDCCommand ; Start the command
; Wait for disk completion. Yield if not complete.
0218 CD 0000 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)
021B 7A mov a,D
021C E6 99 ani Type1ErrorMask ; Check for error status
021E C2 0000 jnz RestoreError ; nz => there was an error, abort
; Check for Track 0 bit.
0221 7A mov a,D
0222 E6 04 ani FDCTk0Mask
0224 CA 0000 jz RecalibrateError ; z => NO track 00 bit, error
; Set completion.
0227 CD 0000 call MakeIOCBResultType1 ; Convert B,C into an IOCB.result, store in IOCB
; Check and update the Cylinder location.
022A AF xra a ; Desired cyclinder is 0
022B 32 005F sta DCylinder
022E CD 0000 call UpdateCylinder
; Complete command.
EndRecalibrate:
0231 C3 00FB jmp FinishCommand ; Finish up the command
; Error exits.
; Error on the Restore command.
RestoreError:
0234 CD 0000 call SetErrorResult ; Set the Error bit
0237 CD 0000 call MakeIOCBResultType1 ; Update Result location (from B,C)
023A C3 0231 jmp EndRecalibrate
; No track 0 bit. Set recalibrate error.
RecalibrateError:
023D 3E 08 mvi a,ErrorMask ; Set the Error bit in Result
023F F6 02 ori RecalibrateErrorMask ; Set the RecalibrateError bit
0241 CD 0000 call SetErrorResult1 ; Store the Error bits
0244 CD 0000 call MakeIOCBResultType1 ; Update Result location (from B,C)
0247 C3 0231 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:
024A CD 0000 call FDCReset ; Reset the controller
;new 7-Oct-85 12:03:23
024D D2 0000 jnc FDCResetOkay2
0250 21 0256 lxi h,598 ; FDCTimeoutInInit
0253 CD 0000 call MakeBadIOCBResult
0256 C3 00FB jmp FinishCommand
;endnew 7-Oct-85 12:03:23
; Initialize various command values.
FDCResetOkay2:
0259 3A 0061 lda F1797 ; Form ReadSector command
025C C6 80 adi RSectorCmd
025E 32 005A sta ReadSectorCmd
0261 3A 0061 lda F1797 ; Form WriteSector command
0264 C6 A0 adi WSectorCmd
0266 32 005B sta WriteSectorCmd
0269 3A 0063 lda S800 ; Form Restore command
026C C6 08 adi ResCmd
026E 32 0058 sta RestoreCmd
0271 3A 0063 lda S800 ; Form Seek command
0274 C6 1C adi SkCmd
0276 32 0059 sta SeekCmd
; Disable interrupts from the FDC chip
0279 CD 0000 call Disable75
; Do a NOP command to get the disk status
027C C3 00E2 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:
027F 3A 0019 lda FloppyIOCB+IocbEscCommand ; Get escape command
0282 FE 01 cpi ClrDiskChgCmd ; Is it a Disk Change Clear?
0284 CA 0000 jz DoDiskChgClr ; z => Disk change clear
InvalidEscapeCmd:
0287 21 0246 lxi h,ErrorInvalidEscapeCmd ; ERROR: Invalid Escape command
028A CD 0000 call MakeBadIOCBResult
028D C3 00FB jmp FinishCommand
; Clear disk change by disabling drive and then enabling the drive with drive select.
DoDiskChgClr:
0290 3A 0052 lda FDCStateVal ; Get current value for FDCState
0293 E6 FE ani nDriveSelectMask ; Clear Drive Select
0295 D3 E8 out FDCState
0297 F6 01 ori DriveSelectMask ; Set Drive Select again
0299 D3 E8 out FDCState
029B C3 00E2 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:
029E 3E 14 mvi a,ResetRst75+Rst75DisableMsk ; RST 7.5, clear 7.5 FF
02A0 CD 0000 call DisableRST ; Do the disable
02A3 C9 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:
02A4 2E 03 mvi l,3 ; count = we will try three times to reset
FDCResetLoop:
02A6 3E 80 mvi a,DisableFDC ; Clear state register, reset FDC, enable Waits
02A8 D3 E8 out FDCState
02AA 3E 09 mvi a,9 ; Wait for more than 50 usec (MR pulse)
02AC CD 0000 call DelayV
02AF 3A 0053 lda DefaultFDCState ; Get default values
02B2 32 0052 sta FDCStateVal ; Get control byte for FDC
02B5 D3 E8 out FDCState
02B7 CD 029E call Disable75 ; Disable the Floppy interrupt
02BA 3E D0 mvi a,ForceInt0Cmd ; Issue Force Interrupt command
02BC D3 84 out FDCCommand
02BE CD 0000 call Delay ; Busy status not available for 12 usec
02C1 CD 0000 call WaitFDCBusy ; wait for FDC busy
02C4 D2 0000 jnc FDCResetOkay
02C7 2D dcr l ; couldn't reset FDC
02C8 C2 02A6 jnz FDCResetLoop
02CB 37 stc
02CC C9 ret
FDCResetOkay:
02CD 37 stc
02CE 3F cmc
02CF C9 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:
02D0 E5 push h
02D1 21 1388 lxi h,5000 ; or other count-down value. ***
WaitFDCBusyLoop:
02D4 DB 84 in FDCStatus ; Check for restore command completion
02D6 57 mov D,a
02D7 E6 01 ani FDCBusyMask
02D9 CA 0000 jz WaitFDCBusyExit
; count down for timeout
02DC 2D dcr l
02DD C2 02D4 jnz WaitFDCBusyLoop
02E0 25 dcr h
02E1 C2 02D4 jnz WaitFDCBusyLoop
; timeout occured.
02E4 E1 pop h
02E5 37 stc
02E6 C9 ret
; normal exit.
WaitFDCBusyExit:
02E7 7A mov a,D ; Return status in A too
02E8 E1 pop h
02E9 37 stc
02EA 3F cmc
02EB C9 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
02EC E1 POP H ;HL ← return address
02ED 22 0000 SHLD WaitFDCCompletionRet
WaitFDC:
02F0 3A 006D lda NoYield
02F3 B7 ora a
02F4 C2 0000 JNZ CheckIntReqCompletion ; nz => Don't do Yield
02F7 21 0000 LXI H,CheckIntReqCompletion
02FA C3 0000 JMP SaveFloppyTaskResumeAddressAndYield
CheckIntReqCompletion:
02FD DB E9 in IntReq ; Check for completion in IntReq register
02FF EE FF xri IntReqMask ; Complement active low signals
0301 E6 80 ani FDCIntMask
0303 CA 02F0 jz WaitFDC ; z => controller still busy
; Command has completed. Get status.
0306 DB 84 in FDCStatus ; Read internal status
0308 57 mov D,a ; Save in reg D
0309 32 0054 sta DiskStatus ; Store status from chip
030C DB E8 in FDCStatusReg ; Get external status register.
030E 32 0055 sta DiskStatusHi ; Store
0311 5F mov E,a ; Move to E
0312 C3 DB opJMP ;JMP WaitFDCCompletionRet
WaitFDCCompletionRet:
0313 0000 DW 0 ; Return
ReadFDCStatus:
{This subroutine is just like the end of WaitFDCCompletion except that it uses the stack to return.}
0315 DB 84 in FDCStatus ; Read internal status
0317 57 mov D,a ; Save in reg D
0318 32 0054 sta DiskStatus ; Store status from chip
ReadFDCStatusHi:
031B DB E8 in FDCStatusReg ; Get external status register.
031D 32 0055 sta DiskStatusHi ; Store
0320 5F mov E,a ; Move to E
0321 C9 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:
0322 3A 0018 lda FloppyIOCB+IocbCommandHi ; Check Command [0]
0325 B7 ora a
0326 FA 0000 jm UpdateIOPPtr ; m => High bit is 1, no adjustment of Size
0329 2A 0036 lhld FDDataPCB+CPCnt ; H,L ← count in the DataPCB
032C CD 0000 CALL ByteToWord ; H,L ← count in words
032F EB xchg ; D,E ← count in words
0330 2A 0032 lhld FDDataPCB+CPAddr3 ; H,L ← low address word in the DataPCB
0333 19 dad d ; H,L ← Low CPAddress + TransferCount
0334 22 0032 shld FDDataPCB+CPAddr3 ; Store back low address word in the DataPCB
0337 3A 0034 lda FDDataPCB+CPAddr1 ; A ← High address byte
033A CE 00 aci 0 ; Add in any possible carry from above addition
033C 32 0034 sta FDDataPCB+CPAddr1 ; Store back in PCB
; Update IOP buffer pointer.
UpdateIOPPtr:
033F 2A 0038 lhld CPBufPtr ; get CP's pointer to IOP buffer
0342 CD 0000 call IncrIOPPtr ; increment it in ring buff fashion
0345 22 0038 shld CPBufPtr ; and store it away
0348 C9 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:
0349 D5 push d ; save regs
034A E5 push h
034B EB xchg ; DE ← orig buf ptr
034C 2A 0026 lhld DiskDmaCB+DmaBufCnt ; HL ← length of buffer
034F 29 dad h ; HL ← length of two buffers
0350 2B dcx h ; HL ← distance to end of next buffer
0351 19 dad d ; HL ← pointer to end of next buffer area
0352 11 0000 lxi d,EndFDBuffer ; get end of total buffer area
0355 CD 0000 call HLcmpDE ; Compare end of next buffer-End of Buffer area
0358 DA 0000 jc FinIncrIOP ; if end of next buffer<= End of buff area, incr this
; buffer pointer
035B 21 0000 lxi h,StartFDBuffer ; else, start at beginning of buf area
035E D1 pop d ; toss old pointer
035F D1 pop d ; restore DE
0360 C9 ret
; point to the next disk buffer area in the ring buffer
FinIncrIOP:
0361 D1 pop d ; get original buffer pointer
0362 2A 0026 lhld DiskDmaCB+DmaBufCnt ; HL ← length of disk buffer
0365 19 dad d ; HL ← ptr to next disk buffer
0366 D1 pop d ; restore DE
0367 C9 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:
0368 7C mov a,h ; compare the hi bytes first
0369 BA cmp d
036A C0 rnz ; only compare the lo bytes if the hi
036B 7D mov a,l ; bytes are equal
036C BB cmp e
036D C9 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:
036E 3E 08 mvi a,ErrorMask ; Set the Error bit in Result
SetErrorResult1:
0370 21 0016 lxi h,FloppyIOCB+IocbResultHi
0373 B6 ora m
0374 77 mov m,a
0375 C9 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.
0376 21 0015 lxi h,FloppyIOCB+IocbResult ; Point to Low Result in IOCB
0379 3E DF mvi a,nType1ZeroMaskLo ; Clear values that should be zero
037B A2 ana D
037C B6 ora m
037D 77 mov m,a ; Store back in IOCB
; Process high part of Result.
037E 23 inx h ; Point to High Result in IOCB
037F 3E B0 mvi a,nType1ZeroMaskHi ; Clear values that should be zero
0381 A3 ana E
0382 B6 ora m
0383 77 mov m,a ; Store back in IOCB
0384 C9 ret
; Subroutine: MakeBadIOCBResult - make the IOCB bad result flags
; HL -> would-be MP code.
MakeBadIOCBResult:
0385 22 0015 shld FloppyIOCB+IocbResult ; store MP code in result slot
0388 3A 0016 lda FloppyIOCB+IocbResult+1 ; load Hi byte
038B F6 40 ori nIocbErrorMaskOr ; set error bit
038D 32 0016 sta FloppyIOCB+IocbResult+1
0390 C9 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.
0391 21 0015 lxi h,FloppyIOCB+IocbResult ; Point to Low Result in IOCB
0394 3E F9 mvi a,nType2ZeroMaskLo ; Clear values that should be zero
0396 A2 ana D
0397 B6 ora m
0398 77 mov m,a ; Store back in IOCB
; Process high part of Result.
0399 23 inx h ; Point to High Result in IOCB
039A 3E B0 mvi a,nType2ZeroMaskHi ; Clear values that should be zero
039C A3 ana E
039D B6 ora m
039E 77 mov m,a ; Store back in IOCB
; Fix up DataLost. Move bit 5 in DiskStatus to bit 7 in Result.
039F 3A 0054 lda DiskStatus ; Get original low status
03A2 E6 04 ani FDCLostData ; Isolate the DataLost bit
03A4 0F rrc ; Align appropriately
03A5 0F rrc
03A6 B6 ora m
03A7 77 mov m,a ; Store back in IOCB
03A8 C9 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.
03A9 CD 031B call ReadFDCStatusHi ; Read external status (returned in C)
03AC 7B mov a,E
03AD E6 80 ani DiskChangeMask ; Mask DiskChange bit
03AF C8 rz ; z => no DiskChange, proceed
; There was a DiskChange.
03B0 16 00 mvi D,0 ; Fake a DE status
03B2 CD 036E call SetErrorResult ; Set Error bit
03B5 CD 0376 call MakeIOCBResultType1 ; Update Result location (from DE)
03B8 3E FF mvi a,-1 ; Set A nonzero
03BA C9 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:
03BB 21 0052 lxi h,FDCStateVal ; Point to FDCStateVal
03BE 3E E7 mvi a,nMiscContextMask ; Clear out old context bits in FDCStateVal
03C0 A6 ana m
03C1 77 mov m,a ; Store back in FDCStateVal
03C2 3A 000D lda FloppyIOCB+IocbMiscContext
03C5 E6 18 ani MiscContextMask ; Mask out other bits
03C7 B6 ora m ; OR in Double/Single', Troy/IBM' bits
03C8 77 mov m,a ; Store back in FDCStateVal
03C9 3A 0012 lda FloppyIOCB+IocbHead
03CC E6 01 ani HeadMask
03CE 32 005D sta DiskHead
03D1 E5 push h ; Save H,L temporarilly
03D2 CA 0000 jz ContextSide0 ; Side 0 specified
; Side 1 specified.
03D5 3E 20 mvi a,Side1Mask
03D7 B6 ora m ; OR in Side 1
03D8 57 mov D,a ; Save FDCState value in D
; Fix up Read/Write commands.
03D9 21 005C lxi h,SectorCmd ; Set S bit in current SectorCmd
03DC 3E 02 mvi a,Type2SMask
03DE B6 ora m
03DF 77 mov m,a
03E0 C3 0000 jmp SetContext
; Side 0 specified.
ContextSide0:
03E3 3E DF mvi a,nSide1Mask
03E5 A6 ana m ; Clear Side 1 bit
03E6 57 mov D,a ; Save FDCState value in D
; Fix up Read/Write commands.
03E7 21 005C lxi h,SectorCmd ; Clear S bit in current SectorCmd
03EA 3E FD mvi a,nType2SMask
03EC A6 ana m
03ED 77 mov m,a
SetContext:
03EE E1 pop h ; Restore H,L to point to FDCStateVal
03EF 7A mov a,D ; Restore saved value from D
03F0 77 mov m,a ; Store back saved value in FDCStateVal
03F1 D3 E8 out FDCState ; Output to controller
03F3 C9 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:
03F4 32 0025 sta DiskDmaFunction ; Store function in Dma CB
03F7 21 0000 lxi h,StartFDBuffer ; Setup the IOP buffer pointer
03FA 22 0038 shld CPBufPtr ; in DataPCB
03FD 22 0028 shld DiskBufPtr ; and in DiskDmaCB
0400 AF xra a ;
0401 32 006B 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.
0404 21 0005 lxi h,FloppyIOCB ; Source
0407 11 0032 lxi d,FDDataPCB ; Destination
040A 3E 03 mvi A,3 ; No. of bytes (long address). Note the flags in the MS byte are not
; changed.
040C CD 0000 call Copy ; This copies the 3 bytes of address leaving flags untouched.
040F 2A 000B lhld FloppyIOCB+IocbSectorLen ; Transfer count=sector length (words)
0412 CD 0000 CALL WordToByte
0415 22 0036 shld FDDataPCB+CPCnt ; Store in PCB count field
0418 22 0026 shld DiskDmaCB+DmaBufCnt ; Store in Dma CB count field
; Set the context for operation and update FDCState register. (Density, head, format)
041B CD 03BB call SetIOCBContext
; Check the DiskChange bit.
041E CD 03A9 call CheckDiskChange ; is there a disk change?
0421 B7 ora a
0422 C2 00FB jnz FinishCommand ; nz => had a DiskChange, quit
; Check whether we need to do a seek. Compare desired cylinder with current value.
0425 21 005E lxi h,Cylinder ; Point to current value
0428 3A 000F lda FloppyIOCB+IocbCylinder ; Assumes high part of cylinder is 0
042B BE cmp m
042C CA 0000 jz FinXferSetup ; z => same, don't need a seek
042F 32 005F sta DCylinder
0432 57 mov d,a ; Desired cylinder to D
0433 3A 0059 lda SeekCmd
0436 5F mov e,a ; Command to E
0437 CD 0000 call DoSeek
; Nothing else to do, so wait for disk completion. Yield if not complete.
043A CD 02EC 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)
043D 7A mov a,D
043E E6 99 ani Type1ErrorMask ; Check for error status
0440 C2 0000 jnz HadXferSeekError ; nz => there was an error, abort
; Check and update the Cylinder location.
0443 CD 0000 call UpdateCylinder
; Successful seek. Start the Transfer.
FinXferSetup:
0446 AF xra a ; clear the Disk status
0447 32 0054 sta DiskStatus ; there have been no errors so far.
044A 3A 0013 lda FloppyIOCB+IocbSectorCnt ; Set the length of the
044D 32 0069 sta CPXferCnt ; CP transfer
0450 3A 0011 lda FloppyIOCB+IocbSector ; Sector ← IOCB.FirstSector
0453 32 0060 sta Sector ; set initial sector number
0456 C3 0000 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:
0459 CD 0000 call DoSectorCmd ; restart the disk
WaitForDisk:
045C FB ei ; make sure Disk can finish
045D 3A 006D lda NoYield
0460 B7 ora a
0461 C2 0000 JNZ TestDiskSectorDone ; nz => Don't do Yield
0464 21 0000 LXI H,TestDiskSectorDone
0467 C3 0000 JMP SaveFloppyTaskResumeAddressAndYield
TestDiskSectorDone:
046A 3A 006C lda DiskSectorDone ; should the CP service the IOP
046D B7 ora a ; buffer? (this is set when a write op
; begins, reset when a read op begins)
046E CA 0000 jz CheckDiskActive ; no, see if the transfer is still
; going
0471 3A 0069 lda CPXferCnt ; yes, are there any more sectors
0474 B7 ora a ; to transfer?
0475 C4 0000 cnz DoCPBufService ; yes, fill the IOP buffer
; The IOP buffer needs no service from the CP. Is the disk still running?
CheckDiskActive:
0478 F3 di ; make sure the disk status does
; not change while it is being
; investigated
0479 3A 006B lda DiskActive
047C B7 ora a ; nz => yes
047D C2 045C jnz WaitForDisk ; so wait for it
0480 3A 0054 lda DiskStatus ; no, did it stop because of an
; error?
0483 B7 ora a ; nz => there was an error
0484 C2 0000 jnz PostDkStatus ; so stop now
0487 3A 0013 lda FloppyIOCB+IocbSectorCnt ; no, did it finish the
048A FB ei ; (it's ok for things to happen
; again)
048B B7 ora a ; transfer?
048C C2 0459 jnz StartDisk ; transfer faltered, so start or
; restart the disk.
; Post the final Disk Status and release the CP port
PostDkStatus:
048F FB ei ; just in case we got here via an error
; posted in DiskStatus
0490 3A 0069 lda CPXferCnt ; Are there any sectors to finish up?
0493 B7 ora a ; nz => disk just stopped, either ok or
0494 C4 0000 cnz DoCPBufService ; because of error. Transfer all remaining
; sectors.
0497 3A 0055 lda DiskStatusHi ; get final external status
049A 5F mov E,a ; pass status word in DE
049B 3A 0054 lda DiskStatus ; get final internal status
049E 57 mov D,a
; See if an error result should be posted
049F B7 ora a
04A0 C4 036E cnz SetErrorResult ; no, set the Error bit
04A3 CD 0391 call MakeIOCBResultType2 ; Update Result location (from B,C)
04A6 C3 00FB jmp FinishCommand ; Finish up the command
; Error branches:
; Error in the Seek command.
HadXferSeekError:
04A9 CD 036E call SetErrorResult ; Set the Error bit
04AC CD 0376 call MakeIOCBResultType1 ; Update Result location (from DE)
04AF C3 00FB 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 f
or 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, advanc
ing CPBufPtr and decrmenting CPXferCnt until a) CPXfercnt reaches 0 or b) CPBufPtr=DiskBufPtr. If it lea
ves the buffer full, it resets DiskSectorDone. }
DoCPBufService:
04B2 E1 POP H ;HL ← return address
04B3 22 0000 SHLD DoCPBufServiceReturn
WaitForPortForBufService:
04B6 3A 0000 LDA PortBusyFlag
04B9 B7 ORA A
04BA CA 0000 JZ StartBufTransfer
04BD 21 04B6 LXI H,WaitForPortForBufService
04C0 C3 0000 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:
04C3 FB ei ; let the interrupt routine in during
; CP transfers
04C4 21 0032 lxi h,FDDataPCB ; Pointer to FDDataPCB CPport control block
04C7 3A 0017 lda FloppyIOCB+IocbCommand ; get the command
04CA E6 02 ani 2 ; Is this a read from the disk?
04CC C2 0000 jnz ReadFromCP ; no, so read from CP, write to disk
04CF CD 0000 call StartCPWriteDMA ; yes, start the Dma write to CP main memory
04D2 C3 0000 jmp WaitCPDmaCompletion
ReadFromCP:
04D5 CD 0000 call StartCPReadDMA ; data going to Disk from CP
; Wait for the transfer to complete
WaitCPDmaCompletion:
04D8 CD 0000 call CheckCPDMAComplete
04DB C2 0000 JNZ CPComplete
04DE 21 04D8 LXI H,WaitCPDmaCompletion
04E1 C3 0000 JMP SaveFloppyTaskResumeAddressAndYield
CPComplete:
04E4 CD 0322 call UpdateDataPCB ; Update CP address, IOP ptr in PCB
; Was that the last CP transfer?
04E7 F3 di ; don't let the interrupt routine change
; things now
04E8 3A 0069 lda CPXferCnt
04EB 3D dcr a
04EC 32 0069 sta CPXferCnt
04EF CA 0000 jz FinCPBufSrvc ; yes, just finish up the buffer service
; More sectors to transfer, is the IOP buffer area full?
04F2 EB xchg ; DE ← addr of next buffer to be transferred
; by CP
04F3 2A 0028 lhld DiskBufPtr ; HL ← addr of buff being used by disk now
04F6 CD 0368 call HLcmpDE ; is the buffer area full (z=>yes)
04F9 C2 04C3 jnz StartBufTransfer ; yes, finish the buffer service
; Buffer is now full, reset DiskSectorDone, release CP port and finish
FinCPBufSrvc:
04FC AF xra a ; turn off DiskSectorDone
04FD 32 006C sta DiskSectorDone ; note interrupts are disabled now
0500 FB ei ; let the disk finish now
0501 C3 DB opJMP ;JMP DoCPBufServiceReturn
DoCPBufServiceReturn:
0502 0000 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:
0504 F5 push psw ; Save the Regs
0505 D5 push d
0506 E5 push h
0507 FB ei ; ok for other processes (RS232) to interrupt
; this one.
0508 DB E8 in FDCStatusReg ; Get external status register.
050A 32 0055 sta DiskStatusHi ; save it
050D 5F mov E,a ; Save for DMA test below
050E DB 84 in FDCStatus ; Read internal status
0510 57 mov D,a ; prepare to test disk status
0511 3A 006A lda DkErrorMask ; get proper error mask for this operation
0514 A2 ana D ; look for errors in that operation
0515 32 0054 sta DiskStatus ; Store status from chip
0518 C2 0000 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.
051B 21 0000 lxi h,0 ; initialize offset into DMA error table
; Check that DMA terminated.
051E 7B mov a,E ; Get external disk status
051F E6 40 ani FDCEndCountMask
0521 C2 0000 jnz GoodDmaEndCount1
0524 2E 04 mvi l,4
GoodDmaEndCount1:
0526 1E 01 mvi E,FloppyChannelMask
0528 CD 0000 call ReadDMACompletion ; Get completion
052B C2 0000 jnz EndDmaCheck ; nz => channel completed, look at both
; indicators
052E 3E 08 mvi a,8 ; else set the error flag
0530 B5 ora l
0531 6F mov l,a
EndDmaCheck:
0532 11 0000 lxi d,DmaCheckTable ; load base of instruction table used to
; check the DMA completion
0535 19 dad d ; calculate the address of the code used to
; deal with the DMA completion or lack of it
0536 E9 pchl ; go deal with the DMA
DmaCheckTable:
0537 C3 0000 jmp SectorCmdDone ; Both DMAs ok
053A 00 nop ; each table entry is 4 bytes
053B C3 0000 jmp NoDmaEndCount1 ; only the External DMA End Count failed
053E 00 nop
053F C3 0000 jmp NoDmaEndCount2 ; only internal DMA End Count failed
0542 00 nop
; both DMA endcounts agree the DMA did not finish. Probably this means the
; sector length was wrong, so say LostData and quit
0543 3E 04 mvi a,FDCLostData ; set the Lost Data bit
0545 32 0054 sta DiskStatus
0548 C3 0000 jmp StopDiskService ; stop the Transfer now
; Only the External DMA end count indicator failed
NoDmaEndCount1:
054B 21 024A lxi h,ErrorNoDmaEndCount1 ; ERROR: External Dma End Count not set
054E C3 0000 jmp ErrorReport ; Loop forever
; Only the Internal DMA end count indicator failed
NoDmaEndCount2:
0551 21 024B lxi h,ErrorNoDmaEndCount2 ; ERROR: Internal Dma End Count
; not set
0554 C3 0000 jmp ErrorReport ; Loop forever
; Decrement the Disk count for the operation that just
; completed successfully
SectorCmdDone:
0557 3A 0060 lda Sector ; increment the Sector num so the CP
055A 3C inr a ; routine will know where to retry
055B 32 0060 sta Sector
055E 3A 0013 lda FloppyIOCB+IocbSectorCnt ; get the num of sectors left
0561 3D dcr a ; update it
0562 32 0013 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.
0565 2A 0028 lhld DiskBufPtr
0568 CD 0349 call IncrIOPPtr ; Point to the next buffer location
056B 22 0028 shld DiskBufPtr ; Update mem pointer
056E EB xchg ; has the disk caught up to the CP transfer?
056F 2A 0038 lhld CPBufPtr ; Compare the two ring buf ptrs
0572 CD 0368 call HLcmpDE
0575 CA 0000 jz StopDiskService ; yes, can't do any more transfers
0578 3A 0013 lda FloppyIOCB+IocbSectorCnt ; get the num of sectors left
057B B7 ora a ; is it zero?
057C CA 0000 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
057F CD 0000 call DoSectorCmd
0582 C3 0000 jmp FinDiskService ; and finish up
; There is some reason to stop now. Disable the interrupts and stop
StopDiskService:
0585 AF xra a ; turn off DiskActive
0586 32 006B sta DiskActive
0589 CD 029E call Disable75 ; turn off the Floppy interrupts
058C 3E 01 mvi a,FloppyChannelMask ; turn off the DMA, just to make sure
058E CD 0000 call ClearDmaChannel
FinDiskService:
0591 3E FF mvi a,0FFH ; Tell DoTransfer something has happened
0593 32 006C sta DiskSectorDone
0596 E1 pop h ; restore the registers
0597 D1 pop d
0598 F1 pop psw
0599 C9 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:
059A DB 85 in FDCTrack ; Check track register
059C 21 005F lxi h,DCylinder ; Pointer to desired cylinder
059F BE cmp m
05A0 CA 0000 jz DoUpdateCylinder ; Check if correct
CommandTrackError:
05A3 E1 pop h ; balance return on stack
05A4 21 0247 lxi h,ErrorCommandTrackError ; ERROR: Track register is not correct
05A7 CD 0385 call MakeBadIOCBResult
05AA C3 00FB jmp FinishCommand
; Track register is correct. Update Cylinder location.
DoUpdateCylinder:
05AD 32 005E sta Cylinder
05B0 C9 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:
05B1 7A mov a,d ; Check if desired Track OK
05B2 FE 4D cpi 77
05B4 DA 0000 jc SeekTrackOK ; c => Cylinder number OK
TrackToBig:
05B7 E1 pop h ; make stack balance
05B8 21 0248 lxi h,ErrorTrackToBig ; ERROR: Track number is too large.
05BB CD 0385 call MakeBadIOCBResult
05BE C3 00FB 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:
05C1 D3 87 out FDCData ; Set desired track in Data Register
; Check destination cylinder.
; If greater than PreCompStart then set EnPreComp, else clear EnPreComp.
05C3 3A 0052 lda FDCStateVal ; Check density in FDCStateVal
05C6 E6 08 ani DDenMask ; Mask out Double density mask
05C8 CA 0000 jz SetNoPreComp ; z => single density
; Double density. Check if PreComp needed.
05CB 26 2B mvi H,PreCompStart ; Check if WritePrecomp should be changed
05CD 7A mov a,d ; Desired cylinder to A
05CE BC cmp H ; A has desired cylinder, H has start of PreComp
05CF DA 0000 jc SetNoPreComp ; c => present cylinder < PreComp boundry so no precomp
; Enable write Precomp.
SetPreComp:
05D2 3E 40 mvi a,FDCEnPreCompMask
05D4 21 0052 lxi h,FDCStateVal ; Point to FDCStateVal
05D7 B6 ora m ; Set the bit
IssueSeek:
05D8 77 mov m,a ; Store in FDCStateVal
05D9 D3 E8 out FDCState ; Store in FDCState
05DB 7B mov a,e ; Get Command
05DC D3 84 out FDCCommand
05DE C9 ret
; Set write precomp.
SetNoPreComp:
05DF 3E BF mvi a,nFDCEnPreCompMask ; Clear write precomp. bit
05E1 21 0052 lxi h,FDCStateVal ; Point to FDCStateVal
05E4 A6 ana m ; Clear the bit
05E5 C3 05D8 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:
05E8 3A 0060 lda Sector ; Set up sector for FDC dhip
05EB D3 86 out FDCSector ; Set desired sector in FDCSector Register
ContSectorCmd:
; Program channel 0 of Dma controller for memory writes.
05ED 3A 0025 LDA DiskDmaFunction
05F0 21 0026 lxi h,DiskDmaCB ; Point to DmaCB
05F3 CD 0000 call StartFloppyChannel ; Enable the channel
; Dma channel is now programmed and enabled.
GoodDmaChannel:
05F6 3A 005C lda SectorCmd ; Get FDC command
05F9 D3 84 out FDCCommand ; Issue command
; Turn on the Floppy Interrupts so FloppyIntr can handle the end of the
; transfer. Clear 7.5 FF.
EnFloppyInterrupt:
05FB 3E 14 mvi a,ResetRst75+Rst75DisableMsk ; RST 7.5, clear 7.5 FF
05FD CD 0000 call EnableRST ; Enable the Floppy interrupt but let the
; calling routine actually enable interrupts
0600 3E FF mvi A,0FFH ; turn on DiskActive for DoTransfer
0602 32 006B sta DiskActive
0605 C9 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:
0606 3E 01 mvi a,1
DelayV:
0608 3D dcr a
0609 C2 0608 jnz DelayV
Delay9:
060C C9 ret
SaveFloppyTaskResumeAddressAndYield:
060D 22 0001 SHLD FloppyTaskResumeAddress
FloppyTaskYield:
0610 DS 0
{Pass control to the next task specified in Domino.cfg}
END FloppyTask