{ 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