;::::::::::::::::::::::::::::::::::::::::::::****************************** ; Floppy Disk Routines ***** Hardware Dependent ***** ; - D-X Designs Pty Ltd P112 - ****************************** ; ; 1.3 - 26 Aug 01 - Cleaned up for GPL Release. HFB ; 1.2c- 12 May 97 - Cleaned up source, modified STSIZE Code (again). HFB ; 1.2b- 22 Apr 97 - Changed 5.25" Hi/Lo Speed controls. HFB ; 1.0a- 23 Mar 97 - (test) fixes. HFB ; 1.0 - 13 Aug 96 - Initial Release for P112 from YASMIO. HFB ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Floppy Units are accessed on the P112 using the on-board SMC FDC37C665. ; An optional assembly flag controls whether Polled or DMA-controlled IO is ; used in operation. The DMA mode does not use any Interrupts in this ; preliminary version, but instead polls the INT1* line as Bit 6 on Port C ; of the Z182. ; This software uses the following registers: ; 0DE - Port C, Bit 6=INT1*, Bit 7=INT2* (DRC) ; 090 - Configuration Control Port (CFCNTL) ; 091 - Configuration Data Port (CFDATA) ; The Chip configuration must be altered only to change the polarity on the ; Density signal applied to Pin 2 on the Floppy Connectors. Optional code ; which may be used forces this lead Hi/Lo as required for different formats ; using two bits of Configuration Register #5. CR5 EQU 5 ; FDC/IDE setups ;Bit 7 6 5 4 3 2 1 0 ; | | | | | +-+-+- unused ; | | | +-+------- 00 = Density Normal (tracks Data Rate) ; | | | 01 = (reserved), 10 = Force "1", 11 = Force "0" ; +-+-+----------- unused ; 092 - Drive Control Register (Write Only) ; 7 6 5 4 3 2 1 0 ; | | | | | | +-+-- Drive (00=0, 01=1, 10=2, 11=3) ; | | | | | +------ 1 = Normal Opn, 0 = Reset Controller ; | | | | +-------- 1 = Enable DMA Pins, 0 = Disable DRQ,DAK,INT pins ; | | | +---------- 1 = Enable Drive 0 Motor ; | | +------------ 1 = Enable Drive 1 Motor ; | +-------------- 1 = Enable Drive 2 Motor ; +---------------- 1 = Enable Drive 3 Motor ; 093 - (Not Used) ; 094 - Data-Rate Select (Write) / Main Status Register (Read) ; 7 6 5 4 3 2 1 0 (Write) ; 7 6 5 4 3 2 1 0 (Read) ; | | | | +-+-+-+-- Drives Seeking (0=B0 Set, 1=B1 Set,.. 3=B3 Set) ; | | | +---------- 1 = Command In Progress, 0 = Command Ended ; | | +------------ 1 = Non-DMA Execution, 0 = DMA Execution ; | +-------------- 1 = Read, 0 = Write ; +---------------- 1 = Request for Master, 0 = Internal Execution ; ; 095 - Data/Command Register (Read/Write) ; (Byte Writes/Reads) ; 096 - (Not Used) ; 097 - Data Rate Register (Write) / Disk Changed Bit (Read) ; 7 6 5 4 3 2 1 0 (Write) ; | | | | | | +-+-- 00=500 kb/s, RPM/LC Hi, 01=250/300 kb/s (RPM/LC Lo) ; | | | | | | 10=250 kb/s, RPM/LC Lo, 11=1000 kb/s (RPM/LC Hi/Lo) ; +-+-+-+-+-+------ (Not Used) ; ; 7 6 5 4 3 2 1 0 (Read) ; | +-+-+-+-+-+-+-- (Tri-State, used for HD Controller) ; +---------------- 1 = Disk Changed (latched complement of DSKCHG inp) ; ; 0A0 - DMA I/O Select Port (DMA configuration Only) IF BANKED COMMON /BANK2/ ELSE CSEG ENDIF ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; STMODE (Function 0) - Set the FDC mode for Read/Write operations. ; ; Enter : A = Single-Density Flag (0 = Double Dens, 0FFH = Single Dens) ; Return: Nothing ; Uses : AF All other Registers Preserved/Not Affected ; ; Assumes STSIZE and STSECT called first ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: STMODE: PUSH BC ; Save Regs PUSH HL CPL ; Flip Bits so 1=MFM, 0=FM AND 01000000B ; keep only bit of interest LD (MODE),A ; Set MT, MF, SK for Commands LD B,A ; (for later) LD A,(DRVSIZ) DEC A ; becomes: 8"=0, 5"=1, 3"=2 LD L,2 ; (prepare for 8") JR Z,STMOD2 ; ..jump if 8" (same as HD) DEC L ; Else set to 3/5" Low Speed (1) LD A,(DRVSPD) OR A ; Low Speed? JR Z,STMOD2 ; ..jump if So INC L ; Else bump to 2 for Hi-Speed LD A,(DRVSIZ) SUB 2 ; 5.25" Hi-Density? JR Z,STMOD2 ; ..jump if So pointing to 8"/5.25"HD LD L,8+GAP3HD-FM5G3 ; Else Offset to 3.5" HD Gaps JR STMOD4 ; ..and set SecSize STMOD2: BIT 6,B ; MFM Set? JR Z,STMOD3 ; ..jump if Not SCF ; Else Set Carry if MFM STMOD3: RL L ; Shift into LSB, Size * 2 ADD HL,HL ADD HL,HL ; Size * 8, MFM * 4 (only 1 matters) STMOD4: LD A,(RSZ) LD (NBYTS),A ; save in Comnd Blk OR A PUSH AF ADD A,L ; Add Sector Size to computation LD L,A ; and Store POP AF LD A,129 ; (Prepare default) JR Z,STMOD1 ; ..jump if 128-byte Sectors (DTL=128) XOR A ; Else DTL is 0FFH (0-->FF) STMOD1: DEC A ; Correct DTL Value LD (DTL),A ; set LD A,L ; Xfer index to A LD HL,FM5G3-8 ; No low speed on 8" CALL ADDAHL ; Index into gap 3 table LD A,(HL) LD (GPL),A ; store Gap Length POP HL POP BC RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; STSIZE (Function 1) - Set Drive Size (3.5", 5.25", 8"), Drive Speed ; (High/Low) Capability, and a Boolean flag for whether Motor Control is ; needed by the Drive. ; ; Enter : A = Hi Speed Flag ( 0 = Normal, 0FFH = High Speed Capable) ; D = Motor Flag (0 = No Motor Control, 0FFH = Motor needed) ; E = Drive Size (0 = Hard, 001 = 8", 010 = 5.25", 011 = 3.5") ; Return: Nothing ; Uses : AF All other Registers Preserved/Not Affected ; ; Assumes STHDRV Called Previously. Call before calling STMODE. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: STSIZE: LD (DRVSPD),A ; Save Drive Speed Flag OR A ; (set flag, 0=Normal, FF=Hi-Speed/Density) LD A,E LD (DRVSIZ),A ; Save Drive Size Byte LD A,D LD (FDMOT),A ; Set Drive Motor Needed flag PUSH DE ; (Save Regs) LD A,00000000B ; (Preset Hi 500 kbps, 3.5 & 5.25" Rate) JR NZ,STSIZ2 ; ..jump if Hi-Density/Speed to Set DEC E DEC E ; 5.25" (010B -> 00)? LD A,00000010B ; (Prepare for 250 kbps) JR NZ,STSIZ2 ; ..jump if Not 5.25" w/Rate Set PUSH HL LD A,(HDR) ; Else use a routine from FLOPPY.Z80 to CALL PHYSCL ; point to Physical Drive Byte (ICFG-xx) BIT 6,(HL) ; Hi-Density capable? POP HL LD A,00000010B ; (Prepare for 250 kbps) JR Z,STSIZ2 ; ..jump if No LD A,00000001B ; Else set to 300 kbps (@360 rpm = 250kbps) STSIZ2: OUT (DRR),A ; Set Rate in FDC Reg LD D,A ; preserve Rate bits IN0 A,(1FH) ; Read Cntrl Reg (B7=1 if Hi Speed) RLA ; Speed to Bit Carry..Turbo? LD A,(SPEED) ; (Get Processor Rate in MHz) JR C,STSIZ8 ; ..jump if Turbo for longer delay SRL A ; Else divide rate by 2 STSIZ8: INC D DEC D ; 500 kb/s (Hi-Speed) Rate (D=0)? JR NZ,STSIZ9 ; ..jump if Not LD A,1 ; Else minimum delay for "High-Speed" STSIZ9: LD (DLYCNT),A ; save delay count POP DE RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; STHDRV (Function 2) - Set Head and Drive for Disk Operations. ; ; Enter : A = Unit # in D0-D1, Head in D2 ; Return: Nothing ; Uses : AF All other Registers Preserved/Not Affected ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: STHDRV: LD (HDR),A ; Save the Combined Head/Drive Byte PUSH BC ; Save Regs LD B,A ; and the HDR byte SRL A ; Move SRL A ; Head to B0 LD (HD),A ; and Save LD A,(ACTIVE) ; Get current Activation Byte IF FDDMA AND 11111000B ; keep only motors and DMA Bit ELSE AND 11110000B ; If Polled, keep only motors ENDIF OR B ; add drive bit POP BC ; Restore Regs JP ACTIV8 ; ..exit saving new byte and activating FDC ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; STSECT (Function 3) - Set Sector Number, Sector Size and Last Sector # ; ; Enter : A = Physical Sector Number ; D = Sector Size (0=128, 1=256, 2=512, 3=1024) ; E = Last Physical Sector # on Side ; Return: Nothing ; Uses : AF All other Registers Preserved/Not Affected ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: STSECT: LD (SECT),A ; Set Sector Number LD A,D ; Get sector size LD (RSZ),A ; save for commands LD A,E ; Get last sector number LD (EOT),A ; save in Command Block RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; SPEC (Function 4) - Do a Specify Command, setting Step Rate and Head ; Load/Unload Time. Values are rounded up if not even increments. ; ; Enter : A = Step Rate (in mS; Bit 7 = 1 for 8" drive ; D = Head Unload Time (in mS) ; E = Head Load Time (in mS) ; Return: Nothing ; Uses : AF All other Registers Preserved/Not Affected ; ; Assumes STSIZE called previously to set DRVSPD variable. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: SPEC: PUSH BC ; Save Regs PUSH DE AND 7FH ; Strip 8" Bit from Step Rate ; At 250 kb/s MFM (125 kb/s FM), the times are doubled, so we must ; Divide the delays by two, else leave alone for 500 kb/s LD B,A ; Save the Step Rate LD A,(DRVSPD) ; Get Speed (Data Rate) OR A ; High Speed? LD A,B ; (restore Step Rate) JR NZ,SPEC0 ; ..jump if 500 kb/s (High Density) SRL A ; Divide Step Rate by 2 ADC A,0 ; round Up for partial SRL D ; Divide Head Unload Time by 2 JR NC,SPEC11 ; ..jump if No Rounding Corr. INC D SPEC11: SRL E ; Divide Head Load Time by 2 JR NC,SPEC0 ; ..jump if No Rounding Corr. INC E ; Else Round SPEC0: NEG ; Get 2's Complement of Step Rate AND 0FH ; mask LD B,A ; (save) LD A,D ; Get Head Unload Time in mS ADD A,0FH ; force Rounding up JR C,SPEC00 ; ..jump if Overflow to Max AND 0F0H ; Keep Time MOD 16 JR NZ,SPEC3 ; ..jump if Not Zero SPEC00: LD A,0F0H ; Else go to Maximum allowed SPEC3: OR B ; Add in Step Rate RLCA RLCA RLCA RLCA ; Swap Nibbles LD D,A ; put combined byte back in D CALL WRDY ; Wait for RQM (hope DIO is Low!), retain Ints LD A,03H ; Do an FDC Specify Command OUT (DR),A CALL WRDY LD A,D ; first Rate Byte (Step Rate, HUT) OUT (DR),A CALL WRDY LD A,E ; Get Head Load Time ADD A,A ; Shift rate to B7..1 IF NOT FDDMA ; (Bit 0 = 0 for DMA) OR 1 ; Insure Non-DMA Operation ENDIF OUT (DR),A POP DE ; Restore Regs POP BC RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; RECAL (Function 5) - Recalibrate Drive (moves heads to track 0). ; ; Enter : Nothing ; Return: A = 0 if Ok, NZ if Error. Flags reflect A ; Uses : AF All other Registers Preserved/Not Affected ; ; NOTE: BC Must be preserved by this routine. ; Assumes STHDRV, SPEC, STSIZE and STMODE called first. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: RECAL: LD A,3 ; Give this 3 chances to Home RECAL1: LD (RETRYS),A PUSH BC ; Save needed regs LD BC,2*256+7 ; Two bytes, Recalibrate = 7 PUSH HL CALL FDCMD ; execute Recalibrate POP HL IF NOT FDDMA CALL FDCDN ; Clear Pending Ints, Wait for Seek Complete AND 00010000B ; Homed? (B4=1 if No Trk0 found) ENDIF POP BC ; (restore regs) JR Z,RECOK ; ..jump to Store if Ok LD A,(RETRYS) DEC A ; Any trys left? JR NZ,RECAL1 ; ..loop if So DEC A ; Else set Error Flag (0-->FF) RET RECOK: PUSH HL ; Save Regs CALL IDXTRK ; Point to Current Drive in Track Array XOR A ; get a Zero LD (HL),A ; set Drive's Trk to 0 POP HL ; Restore regs RET ; and return ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; SEEK (Function 6) - Set the Track for disk operations and seek to it. ; ; Enter : A = Desired Track Number ; D = Verify flag (0=No, FF=Yes) ; E = Double-step Flag (E <> 0 for Double-step) ; Return: A = 0, Zero Flag Set (Z) if Ok, A <> 0 Zero Clear (NZ) if Error ; Uses : AF All other Registers Preserved/Not Affected ; ; Assumes STHDRV, SPEC, STSIZE and STMODE are called first. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: SEEK: LD (TTRK),A ; Save Track to Seek PUSH HL ; Save Regs used here PUSH DE PUSH BC CALL IDXTRK ; Point to Current Trk for Selected Drive CP (HL) ; Is desired Track same as last logged? JR NZ,SEEKNV ; ..jump if Not Same INC D ; Else Set to No Verify (FF->0) SEEKNV: LD A,E ; Get Double-step flag LD (STEP2),A ; ..and save locally LD A,(MXRTRY) ; Get the maximum Count SEEK1: LD (RETRYS),A ; save remaining Retry Count LD A,E OR A ; Double Step? LD A,(TTRK) ; Restore Track # JR Z,SEEK2 ; ..jump if No Double Step ADD A,A ; Else double Track # SEEK2: LD (TRK),A ; Save the Track # in Comnd Block LD BC,3*256+0FH ; (3-byte Seek Command = 0FH) PUSH HL CALL FDCMD ; Execute the Seek POP HL IF FDDMA OR A ; Insure flags set ELSE CALL FDCDN ; Clear Pending Int, wait for Seek Complete AND 01000000B ; Set NZ if Abnormal Termination ENDIF JR NZ,SEEKER ; ..jump if Error Seeking INC D ; Are we Verifying (FF -> 0)? DEC D ; (Correct for Test, 0 -> FF) CALL NZ,READID ; Read next ID Mark if So JR Z,SEEKX ; ..exit if Ok SEEKER: LD A,(RETRYS) ; Else get trys remaining DEC A ; Any left (80-track could need two)? JR NZ,SEEK1 ; ..loop to try again if More DEC A ; Else set Error Flag (0->FF) SEEKX: LD E,A ; (Save status byte) LD A,(TTRK) LD (TRK),A ; Restore "Real" Track Number JR NZ,SEEKXX ; ..jump if Error LD (HL),A ; Else update Track in Index SEEKXX: LD A,E ; Restore Status OR A ; set flags POP BC ; Restore Regs POP DE POP HL RET ;......................................................................... ; Actual Read or Write ACTRW: LD HL,(ACTDMA) ; Get actual DMA Addr LD A,(RDOP) OR A ; Read operation? JR Z,SWRITE ; No, must be Write ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; SREAD (Function 7) - Read a Sector from the Floppy Disk. The Mode, ; Head/Drive, Track, and Sector must have already been set. ; ; Enter : HL --> Read Buffer ; Return: A = 0, Zero Set (Z) if Ok, A <> 0, Zero Clear (NZ) if Error. ; Uses : AF,HL. All other Registers Preserved/Not Affected ; ; Assumes STMODE, STHDRV, STSECT, SPEC and SEEK Called First. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: SREAD: LD A,0110B ; Load 765 Read Command (06H) IF FDDMA PUSH AF ; Save Command LD A,11001101B ; Set DMA Direction ; |||||||+- ? ; ||||||+-- MOD = Cycle Steal (ignored in I/O) ; ||||++--- Source = I/O (fixed) ; ||++----- Dest = Memory (Auto-Inc) ; ++------- ? ENDIF JR RW ; ..and continue below ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; SWRITE (Function 8) - Write a Sector to the Floppy Disk. The Mode, ; Head/Drive, Track, and Sector must have already been set. ; ; Enter : HL --> Write Buffer ; Return: A = 0, Zero Flag Set (Z) if Ok, A <> 0 Zero Clear (NZ) if Errors ; Uses : AF,HL. All other registers Preserved/Not Affected. ; Assumes STMODE, STHDRV, STSECT, SPEC and SEEK Called First. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: SWRITE: LD A,0101B ; Load 765 Write Command (05H) IF FDDMA PUSH AF ; Save Command LD A,11110001B ; Set DMA Direction ; |||||||+- ? ; ||||||+-- MOD = Cycle Steal (ignored in I/O) ; ||||++--- Source = Memory (Auto-Inc) ; ||++----- Dest = I/O (fixed) ; ++------- ? RW: OUT0 (DMODE),A ; Set DMA Channel #0 direction POP AF ; restore Rd/Wr Comnd ELSE RW: ENDIF ;fddma PUSH BC ; Save Regs PUSH DE IF FDDMA CALL SFDMA ; Set DMA Transfer Regs LD HL,MODE ; Pt to Mode Flag OR (HL) ; add to Command ELSE LD C,A ; (Save Command) LD A,(MODE) ; Get Mode Flag OR C ; add Read/Write Command to Mode Flag ENDIF ;fddma LD C,A ; to Reg LD B,9 ; Read/Write Comnds are 9 bytes LD A,(TSBSCF) ; Get Special Format Flag OR A ; Special? JR NZ,RW0 ; ..jump if Not LD (HD),A ; Else Side 1 coded with Hd # 0 RW0: LD A,(RSZ) ; Get Sector Size Code LD (NBYTS),A ; and Set in Comnd Blk LD A,(EOT) ; Get Last Sctr # PUSH AF ; (save for Exit) LD A,(SECT) ; Get Desired Sector # LD (EOT),A ; make last to Read only one Sector PUSH HL CALL FDCMD ; Execute Read/Write POP HL POP AF ; Restore Last Sctr # LD (EOT),A ; to Comnd Blk LD A,(ST1) ; Get Status Reg 1 AND 34H ; Return Any Error Bits POP DE ; Restore Regs POP BC RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; READID (Function 9) - Read the first Valid Address Mark on a track. ; ; Enter : Nothing ; Return: A = 0 if Ok, NZ if Error. Flags reflect A ; Uses : AF All other Registers Preserved/Not Affected ; ; Assumes STHDRV, SPEC, STSIZE and STMODE called first. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: READID: LD A,(MODE) ; Get Command byte from block OR 0AH ; or in Command PUSH BC ; Save regs LD B,2 ; two bytes in ReadID Command LD C,A ; move Command to C PUSH HL CALL FDCMD ; Activate Controller POP HL LD A,(ST1) ; Get Status Reg 1 AND 25H ; Keep only Error Bits POP BC ; Restore regs RET ; ..and quit ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; RETDST (Function 10) - Return the status of a drive. ; This routine reports a "765" Controller type instead of actual number. ; ; Enter : Nothing ; Return: A = Status byte ; BC = 765 (FDC Controller Type) ; HL = Address of Status Byte ; Uses : AF,BC,HL All other Registers Preserved/Not Affected ; ; Assumes STHDRV called first ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: RETDST: IF FDDMA CALL WRDY ; Wait for RQM to be Set LD A,0100B ; Load Return Drive Status Command (04H) OUT0 (DR),A ; send to FDC CALL WRDY LD A,(HDR) ; Get Drive and Head OUT (DR),A ; send it too CALL FDCIN0 ; Read Stat at Command End ELSE LD BC,2*256+04H ; 2-byte Return Drive Status Command CALL FDCMD ENDIF ;fddma LD HL,ST0 ; Point to Status Byte (Reg 3 contents) LD A,(HL) ; fetch it LD BC,765 ; load Controller ID RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; FMTTRK (Function 11) - Format a complete track on one side of a Floppy ; Disk. The Mode, Head/Drive, Track, and Sector must have been set. ; ; NOTE: The contents of the Format Data Block varies between controllers, ; so RETDST should be called to determine the controller type before ; setting up data structures. ; ; Enter : D = Formatting Sctrs/Track value ; E = Formatting Gap 3 Byte Count ; HL = Pointer to Controller-dependent Format Data block ; Return: A = 0, Zero Flag Set (Z) if Ok, A <> 0 Zero Clear (NZ) if Errors ; Uses : All Primary Registers ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: FMTTRK: PUSH DE ; Save for Later IF FDDMA LD A,11110001B ; Set DMA Direction ; |||||||+- ? ; ||||||+-- MOD = Cycle Steal (ignored in I/O) ; ||||++--- Source = Memory (Auto-Inc) ; ||++----- Dest = I/O (fixed) ; ++------- ? OUT0 (DMODE),A ; Set DMA Channel #0 direction LD A,(TPABNK) ; Format Data comes from TPA Bank IF BANKED RL H ; Eliminate the MSB of DMA Addr ADC A,0 ; offset bank # by 32k banks RRA ; shift Bank LSB to Carry RR H ; Move Bank # LSB (Carry) to Address MSB ENDIF ;banked LD (FDMAB+2),A ; Set the Bank Value LD (FDMAB),HL ; and DMA Address for Bank in DMA Comnd Block LD HL,_DMA ; Get DMA IO Port addr LD (FDMAB+3),HL ; place in DMA Comnd Block XOR A LD (FDMAB+5),A ; and Null out rest of Dest CALL STFDMA ; Set DMA for Format ELSE ;~fddma IF BANKED LD BC,(TPABNK) ; Get Source and Dest Banks CALL XMOVE ; set them up ENDIF LD DE,HSTBUF ; Move the data to Host Buffer LD BC,256 ; Two Sector's worth should be enough CALL MOVE ; and move it POP DE ; Restore SPT (D) and Gap3 (E) ENDIF ;~fddma LD A,(MODE) OR 1101B ; set command to Format LD C,A ; and place in Reg LD HL,TRK LD A,(RSZ) LD (HL),A ; Place values in Command Block INC HL LD (HL),D INC HL LD (HL),E INC HL LD (HL),0E5H ; Set byte to write as Data LD B,6 ; Six bytes in Format Command IF NOT FDDMA LD HL,HSTBUF ENDIF CALL FDCMD ; Execute ! LD A,(ST1) ; Get Status AND 92H ; Return Error bits in A RET ;============================================================================= ; FDCMD - Send Command to FDC ; Enter: B = # of Bytes in Command, C = Command Byte ; HL -> Buffer for Read/Write Data (If Needed) ; Exit : AF = Status byte ; Uses : AF,BC FDCMD: PUSH HL ; save regs CALL MOTOR ; Insure motors are On LD HL,COMND ; Point to Command Block LD (HL),C ; command passed in C LD C,DR ; Set Data Port Addr OTLOOP: CALL WRDY ; Wait for RQM (hoping DIO is Low) (No Ints) OUTI ; Output Command bytes to FDC JR NZ,OTLOOP ; ..loop til all bytes sent POP HL ; Restore Transfer Addr DI ; No Ints during IO to avoid data loss FDCI1: IF FDDMA CALL FDCINT ; "Call" the Interrupt handler instead of Int RET C ; ..quit if Timeout Error (C, A=FF) LD A,(ST0) ; Else get first byte of Status AND 0C0H ; check for Normal termination RET ; ..return w/Error Flags set ELSE CALL WRDY BIT 5,A ; In Execution Phase? JR Z,FDCRES ; ..jump if Not to check result BIT 6,A ; Write? JR NZ,FDCI2 ; ..jump if Not to Read OUTI ; Else Write a Byte from (HL) to (C) JR FDCI1 ; and check for next FDCI2: INI ; Read a byte from (C) to (HL) JR FDCI1 ; and check for next ; Enter the Result Phase of the Command. Gather returned bytes FDCRES: EI ; Interrupts Ok now if in Result Phase LD HL,ST0 ; Point to Status Result area ISGO: CALL WRDY BIT 4,A ; End of Status/Result? RET Z ; ..return if So BIT 6,A ; Another byte Ready? RET Z ; ..return if Not INI ; Else Read Result/Status Byte JR ISGO ; ..loop for next ;..... ; Check for Proper Termination of Seek/Recalibrate Actions by ; executing a Check Interrupt Command returning ST0 in A. FDCDN: PUSH HL ; Don't alter regs EI ; (Ints Ok Now) FDCDN0: CALL WRDY ; Ready? (leave Ints alone) LD A,08H ; Else Issue Sense Interrupt Status Comnd OUT0 (DR),A CALL WRDY IN0 A,(DR) ; Get first Result Byte (ST0) LD L,A CP 80H ; Invalid Command? JR Z,FDCDN0 ; ..jump to exit if So CALL WRDY IN0 A,(DR) ; Read Second Result Byte (Trk #) LD A,L BIT 5,A ; Command Complete? JR Z,FDCDN0 ; ..loop if Not POP HL RET ENDIF ;~fddma ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; MOTOR CONTROL. This routine performs final selection of the drive control ; latch and determines if the Motors are already spinning. If they are off ; and Motor control is needed, then the Motors are activated and the spinup ; delay time in tenths-of-seconds is performed before returning. ; ; Enter : Command byte in A ; Return: Head Delay bit set in Command in A if needed ; Uses : None. All Registers Preserved/Not Affected MOTOR: PUSH AF ; Save Reg LD A,(FDMOT) OR A ; Need Motor? JR Z,MOTORX ; ..jump to exit if Not LD A,(MOTIM) ; Get remaining seconds OR A ; Already On? LD A,(MONTIM) ; (get Default On Time) LD (MOTIM),A ; always reset EI ; Insure Ints are Active JR NZ,MOTORX ; ..exit if Motors On..they will stay On LD A,(HDR) ; Get current Drive IF FDDMA OR 11111100B ; Set All Motors and DMA On, Cntrlr Active ELSE OR 11110100B ; Set All Motors On, DMA Off, Cntrlr Active ENDIF CALL ACTIV8 ; Do It! LD A,(SPINUP) ; Get Spinup Time LD (MTM),A ; to GP Counter MOTOLP: LD A,(MTM) OR A ; Up to Speed? JR NZ,MOTOLP ; ..loop if Not MOTORX: POP AF ; Restore Reg RET ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Hardware-Dependent Host Read/Write Routine linked to from FLOPPY module. ; This routine Reads/Writes data from HSTBUF trying up to MXRTRY times ; before giving up. If an error occurs after the next-to-last try, the ; heads are homed to force a re-seek. ; ; Enter: (RDOP Set for desired operation) ; Exit : A = 0, Zero Set if Ok, A <> 0, Zero Reset if Errors ; Uses : AF,HL ; ; RDOP is set to 1 for Read, 0 for Write, TTRK set with desired Track ; number, STHDRV, STSECT, STMODE, SPEC all called previously. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: FHDRW: LD HL,HSTBUF ; Point to the host buffer LD (ACTDMA),HL ; and set Memory Pointer LD (FRWSTK),SP LD SP,FRWSTK ; Use Local Stack LD A,(MXRTRY) ; Get the maximum retry count RWF2: LD (RWRTRY),A PUSH DE ; (Save Regs) LD A,(STEP2) ; Get double-Step flag LD E,A LD D,0FFH ; (Verify needed) LD A,(TTRK) ; and track CALL SEEK ; Try to seek to the desired track POP DE ; (Restore Regs) CALL Z,ACTRW ; Call R/W if Seek succeeded LD (ERFLAG),A ; Save error code in any case JR Z,FHDRX ; ..jump to return if No Errors LD A,(RWRTRY) ; Get retry count CP 2 ; Are we on Next to last try? CALL Z,RECAL ; Return to Track 0 if so LD A,(RWRTRY) ; and re-fetch try count DEC A ; Do we have more retries left? JR NZ,RWF2 ; ..jump to try again if more tries remain CALL ERROR ; Else print Error Bios Message FHDRX: LD SP,(FRWSTK) ; Restore Entry Stack RET ; and Exit ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Gap 3 Length Table ; Index is: ((Drive_Factor * 8) + (MFM * 4) + RSZ) - 8 ; Where Drive_Factor is based on Size and Speed as: ; 5.25"/3.5" Low Speed = 1 ; 8" (Speed Ignored) = 2 ; 3.5" High-Density Disks use special GAP3 Table, while 5.25" High- ; Density disks use 8" DD GAP3 values. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; 5.25"/3.5" Single-Density GAP3. 300 rpm, 125 kbps, FM FM5G3: DEFB 7 ; 128 DEFB 5 ; 256 DEFB 20 ; 512 DEFB 27 ; 1024 ; 5.25"/3.5" Double-Density GAP3. 300 rpm, 250 kbps, MFM DEFB 0 ; 128 DEFB 9 ; 256 DEFB 13 ; 512 DEFB 27 ; 1024 ; 8" Single-Density / 5.25" High-Speed GAP3. 360 rpm, 250 kbps, FM DEFB 7 ; 128 DEFB 5 ; 256 DEFB 27 ; 512 DEFB 27 ; 1024 ; 8" Double-Density / 5.25" High-Speed GAP3. 360 rpm, 500 kbps, MFM GAP5HD: DEFB 0 ; 128 DEFB 15 ; 256 DEFB 11 ; 512 DEFB 17 ; 1024 ; 3.5" Hi-Density GAP3. 300 rpm, 500 kbps, MFM GAP3HD: DEFB 0 ; 128 DEFB 27 ; 256 DEFB 27 ; 512 DEFB 17 ; 1024 (27 if 10 spt) ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Error Printing Routine. This routine prints Error messages from the ; High-level Sector Read/Write routine when detected based on various ; parameters in the BIOS. ; Entering a Control-C after the message will cause a warm boot, anything ; else will ignore the error and return status to the caller. ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ERROR: IF BIOERM PUSH BC PUSH DE CALL PRINT DEFB BELL,CR,LF DEFC 'FDC Error, U' LD A,(HDR) ; Get unit number AND 3 CALL PDEC ; ..in decimal CALL PRINT DEFC ' ' LD A,(COMND) ; Get the command byte AND 3FH ; Else Mask Command CP 0FH ; 765 type Seek? JR NZ,ERR0 ; ..jump if Not CALL PRINT DEFC 'Seek' JR ERR4 ERR0: CP 05H ; 765 type Write? JR NZ,ERR1 ; ..jump if Not CALL PRINT DEFC 'Wr' JR ERR4 ERR1: CP 06H ; 765 type Read? JR NZ,ERR2 ; ..jump if Not CALL PRINT DEFC 'Rd' JR ERR4 ERR2: CALL PRINT DEFC 'Comnd = ' LD A,(COMND) CALL PHEX ERR4: CALL PRINT DEFC ', ' LD HL,ST0 ; Point to Status Bytes BIT 7,(HL) ; Abnormal Termination? JR Z,ERR5 CALL PRINT DEFC 'AT' JR ERR13 ERR5: BIT 4,(HL) ; Equipment Check? JR Z,ERR6 CALL PRINT DEFC 'EC' JR ERR13 ERR6: BIT 3,(HL) ; Not Ready? JR Z,ERR7 CALL PRINT DEFC 'NR' JR ERR13 ERR7: INC HL ; Point to ST1 BIT 5,(HL) ; Data Error? JR Z,ERR8 CALL PRINT DEFC 'DE' ERR8: BIT 2,(HL) ; No Data? JR Z,ERR9 CALL PRINT DEFC ' ND' ERR9: BIT 1,(HL) ; Write Protect? JR Z,ERR10 CALL PRINT DEFC ' NW' ERR10: BIT 0,(HL) ; Missing Address Mark? JR Z,ERR11 CALL PRINT DEFC ' MA' ERR11: INC HL ; Point to ST2 BIT 4,(HL) ; Wrong Cylinder? JR Z,ERR12 CALL PRINT DEFC ' WC' ERR12: CALL PRINT DEFC ' (T=' LD A,(TTRK) ; Get Track (Cyl) CALL PDEC ; Print cylinder (track) CALL PRINT DEFC ' H=' LD A,(HDR) RRCA RRCA AND 0001B CALL PDEC ; Print head CALL PRINT DEFC ' S=' LD A,(SECT) CALL PDEC ; Print sector CALL PRINT DEFC ')' ERR13: POP DE ; Restore Regs POP BC CALL CONIN ; Wait for any key SUB 3 ; ^C (reboot)? PUSH AF CALL PRINT DEFB CR,LF+80H ; Always echo crlf POP AF LD A,0FFH RET NZ ; ..Return Bad Status if Not user abort IF BANKED JP ABORT ELSE RST 0 ; Vector thru loc 0 so wboot may be ENDIF ; Intercepted by NZCOM, BGii, etc. ;..... ; Print value in A as a Decimal number (0-99) PDEC: LD DE,10 ; We work in decimal PD0: INC D SUB E JR NC,PD0 LD E,A LD A,D DEC A ; Is the first digit a Zero? JR Z,PD1 ; ..jump if so and Don't Print ADD A,'0' CALL OUTCHR PD1: LD A,E ADD A,'0'+10 JR OUTCHR ;..... ; Print value in A as two Hex digits PHEX: PUSH AF ; Print value in A as 2 Hex digits RRCA RRCA RRCA RRCA CALL PHEX0 POP AF PHEX0: AND 0FH ADD A,90H DAA ADC A,40H DAA ;..if Bioerm, fall thru to Outchr.. ELSE OR 0FFH ; Insure NZ RET ENDIF ;Bioerm OUTCHR: PUSH HL ; Print char in A to Console PUSH DE PUSH BC LD C,A CALL CONOUT POP BC POP DE POP HL RET ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Point to Last Track Storage Location for the Current Drive ; Enter: None ; Exit : HL -> Last track storage for this drive ; Uses : HL IDXTRK: PUSH AF ; Save regs LD A,(HDR) ; Get current drive/head AND 0011B ; mask off head LD HL,TRKARY ; Point to track storage block CALL ADDAHL ; Point to the byte POP AF ; restore regs RET ; and return to caller ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Wait for FDC RQM to become Ready, and return DIO status in Zero Flag ; Pause before reading status port based on CPU Speed and Data Rate. WRDY: LD A,(DLYCNT) ; Get computed delay count WRDY0: DEC A ; Done? JR NZ,WRDY0 ; ..loop if Not WRDYL: IN A,(MSR) ; Read Main Status Register BIT 7,A ; Interrupt Present? RET NZ ; Return if So JR WRDYL ; Else Loop ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; D M A S U P P O R T R O U T I N E S ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: IF FDDMA ; Set DMA Channel #0 for Format STFDMA: PUSH AF ; Save regs XOR A OUT0 (DSTAT),A ; (No DMA or Interrupts) LD L,D ; Get SPT value LD H,0 ; extend to Word value LD B,2 ; Multiply by 4 for Number of bytes STDMA2: ADD HL,HL ; * 2 STDMA3: DJNZ STDMA2 ; ..looping til done LD (FDMAB+6),HL ; Save Number of bytes to transfer LD HL,FDMAB ; Point to DMA Control block LD BC,8*256+SAR0L ; 8 bytes from (HL).. OTIMR ; to DMA Ch #0 Registers LD A,01100011B ; Enable DMA on Ch #0 ; |||||||+- DME ; ||||||+-- ? ; ||||++--- DMA Ints (Ch1, Ch0) ; ||++----- Bit Write En * (Ch1, Ch0) ; ++------- DMA Enable (Ch1, Ch0) OUT0 (DSTAT),A ; Command! POP AF ; Restore regs RET ;..... ; SFDMA - Set up DMA Channel #0 for Read/Write Operation. ; Number of sectors in Reg E is multiplied by Base Sector size to obtain ; the full number of bytes to transfer. ; Enter: HL -> Read/Write Buffer ; A = Read/Write Command SFDMA: PUSH AF ; Save Rd/Wr Command XOR A OUT0 (DSTAT),A ; Disable DMA/DMA Ints IF BANKED LD A,(SYSBNK) ; Set System Bank # ELSE LD A,(TPABNK) ; If Not Banked, Load TPA Bank # ENDIF RL H ; Place Address MSB in Carry ADC A,0 ; offset Bank # by 32k banks RRA ; shift Bank LSB to Carry RR H ; Move Bank # LSB (Carry) to Addr MSB LD C,A ; Save Bank # LD DE,_DMA ; Get Floppy DMA IO Port Address LD B,0 ; and Dummy Bank # POP AF PUSH AF ; Restore Rd/Wr Command AND 00001111B ; keeping only low nibble CP 0101B ; Write? JR Z,SFDMA0 ; ..jump if So; CHL = Source, BDE = Dest EX DE,HL ; Else LD A,B ; Swap Source LD B,C ; w/Dest LD C,A SFDMA0: LD (FDMAB),HL ; Save Source LD A,C ; in Block LD (FDMAB+2),A LD (FDMAB+3),DE ; and Dest LD A,B LD (FDMAB+5),A LD HL,0080H ; Set 1 Logical Sector Size for Calcs LD A,(RSZ) ; Get Physical Sector Size (in 128-byte recs) LD B,A INC B ; (compensate for first DJNZ) JR STDMA3 ; ..compute, set and activate ENDIF ;fddma ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; FDCINT - Used in the P112 as a polled routine in default configuration ; with Interrupts disabled. This code is assembled if the FDDMA equate ; is set TRUE. If your system uses Interrupts, add any additional code ; to save Stack pointer and AF, and insure that it is in the CODE segment. ; NOTE: that the service routine MUST be in the Common Memory if Interrupts ; (instead of polling ) are used to prevent problems when banked. ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ;;-- CSEG IF FDDMA FDCINT: ; Set local stack if Needed, Disable Interrupts for local calls PUSH BC PUSH DE PUSH HL LD BC,0000 ; Inner loop timeout LD D,30 ; Outer loop timeout LD HL,ST0 ; Point to Status Area Int00: IN0 A,(DRC) ; Read Port C BIT 6,A ; Int1* active? JR Z,Int01 ; ..jump if So DEC BC ; Else count down LD A,B OR C JR NZ,Int00 ; ..loop if Not timed out DEC D ; Outer loop done? JR NZ,Int00 ; ..loop if Not SUB 1 ; Else set Carry, A=FF LD (ST1),A ; Save Bad Status CALL FDRst ; Reset the Controller JR Int0X ; ..and Quit Int01: LD BC,DR ; Else Point BC to Data Port CALL WRDYw0 JR C,Int03 ; ..jump if Timeout Int02: CALL WRDY BIT 6,A ; Result phase over? JR Z,Int03 ; ..jump if So to Exit INI ; Read a byte from (C) to (HL) INC B ; (correct B for above dec) JR Int02 ; and check for next Int03: CALL WRDY LD A,08H ; Sense Interrupt Status Comnd OUT (DR),A CALL WRDY IN A,(DR) ; Get first Result Byte (ST0) CP 80H ; Invalid Command? JR Z,Int0X ; ..jump to exit if So LD (ST0),A ; Else save Status Byte 0 CALL WRDY IN A,(DR) ; Read Second Result Byte (Trk #) JR Int03 ; ..and loop Int0X: POP HL ; Restore Regs POP DE POP BC ; Restore Stack if Needed EI ; Insure Interrupts are Enabled RET ;..... ; Alternate entry for code that generates no "Interrupt" FDCIN0: PUSH BC ; Save regs PUSH DE PUSH HL LD HL,ST0 ; Put Status bytes here JR Int01 ; ..and continue mainline code ENDIF ;fddma ; Wait for Interrupt maintaining watchdog timer WRDYw0: PUSH BC ; Save Regs LD BC,0 ; Set Maximum count WRDYw1: IN A,(MSR) ; Get Status RLA ; Int bit to Carry JR C,WRDYw2 ; ..quit if Int DEC BC ; Else count down LD A,B OR C ; Timed Out? JR NZ,WRDYw1 ; ..loop if Not CALL FDRst ; Else Reset Controller WRDYw2: CCF ; Set Carry appropriately POP BC ; restore regs RET ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CSEG ;*** Remainder of Code MUST be in Main Memory *** ;======================================================================== ; Reset the Floppy Disk Controller. Called from CBOOT in case of Hard ; Disk Boot which leaves the controller in a "hung" state, and locally ; if the Controller times out (often due to changing diskettes). FDRst: PUSH AF ; Save any status XOR A OUT (DCR),A ; Place Controller in Reset state LD A,(ACTIVE) ; get current settings AND 0FCH ; keep only motors, DMA and Ready OUT (DCR),A ; and Restore POP AF ; Restore Status RET ;======================================================================== ; Motor Off routine. Called from TIM-DX and SELFLP2 which forces ; Motors/Timer to be Off state so spinup delay is forced on next selection. ;;ChgSpd: ; <<-- Required label for SELFLP2. Activate ; label here if switching motor speed MOTOFF: XOR A LD (MOTIM),A ; Insure Motors Timed Out and show OFF LD A,(ACTIVE) ; Get current settings IF FDDMA ; AND 00001111B ; strip of Motor bits ELSE AND 00000111B ; strip off Motor bits and DMA ENDIF ACTIV8: OR 00000100B ; (insure FDC out of Reset) LD (ACTIVE),A ; save OUT (DCR),A ; and Command! ChgSpd: RET ; <<-- Activate label here if using 300 kb ; for constant 360 rpm on 5.25" Drives ;======================== RAM Storage Area ============================== IF BANKED COMMON /B2RAM/ ; If banked, Local stack in Bank ELSE DSEG ; ..otherwise in Data Segment ENDIF ; DEFS 30 ; Bios R/W Entry 15-level Local Stack FRWSTK: DEFS 2 ; Storage for Entry Stack Pointer DSEG ; Place in Common memory ; Add storage for DMA Control Block if using DMA Transfers IF FDDMA FDMAB: DEFS 2 ; Source Addr (16-bits) DEFS 1 ; Source Bank DEFS 2 ; Dest Addr (16-bits) DEFS 1 ; Dest Bank DEFS 2 ; Byte Count of transfer ENDIF ;fddma ; NOTE: Variables listed as (** Global **) are accessed by other modules and ; MUST exist as defined. COMND: DEFS 1 ; Storage for Command in execution HDR: DEFS 1 ; Head (B2), Drive (B0,1) (** Global **) TRK: DEFS 1 ; Track (t) HD: DEFS 1 ; Head # (h) SECT: DEFS 1 ; Physical Sector Number NBYTS: DEFS 1 ; Bytes/Sector (n) EOT: DEFS 1 ; End-of-Track Sect # GPL: DEFS 1 ; Gap Length DTL: DEFS 1 ; Data Length RSZ: DEFS 1 ; Bytes/Sector. Must be placed outside of the ; Comnd Blk for FMTTRK to work. ; FDC Operation Result Storage Area ST0: DEFS 1 ; Status Byte 0 ST1: DEFS 1 ; Status Byte 1 (can also be PCN) ST2: DEFS 1 ; Status Byte 2 RC: DEFS 1 ; Track # (** Global **) RH: DEFS 1 ; Head # (0/1) RR: DEFS 1 ; Sector # (** Global **) RN: DEFS 1 ; Sector Size (** Global **) ACTDMA: DEFS 2 ; 16-bit DMA Address ;-->>> Do NOT re-order the following two bytes !! <<<-- MTM: DEFS 1 ; Floppy Time down-counter MOTIM: DEFS 1 ; Motor On Time Counter ; DISK Subsystem Variable Storage FDMOT: DEFS 1 ; Motor on required flag RDOP: DEFS 1 ; Read/write flag RETRYS: DEFS 1 ; Number of times to try Opns RWRTRY: DEFS 1 ; Number of read/write tries DRVSPD: DEFS 1 ; Drive Speed DRVSIZ: DEFS 1 ; Drive Size STEP2: DEFS 1 ; <> 0 for Double Step (** Global **) MODE: DEFS 1 ; Bit 6 = 1 if MFM, 0 = FM ACTIVE: DEFS 1 ; Current bits written to Dev Contr Reg (DCR) DLYCNT: DEFS 1 ; Delay value reading Main Status Reg FSPT: DEFS 1 ; Format Sectors/Track value TSBSCF: DEFS 1 ; 0=Hd always 0 (TSBSC) (** Global **) TTRK: DEFS 1 ; Storage for Track (** Global **) TRKARY: DEFS 4 ; Track storage locations for four drives ;=========================== End of FDC-DX ==============================