{ 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