Files
RomWBW/Source/HBIOS/fd.asm
Wayne Warthen 0bdecc9a4a Floppy Fix
Floppy I/O was failing for slower CPUs when the timer interrupts were active.  Now fixed.

Credit to Jorge Jorge Rodrigues for finding this problem and pointing me in the right direction for the fix.
2019-09-06 15:47:46 -07:00

1980 lines
48 KiB
NASM

;
;==================================================================================================
; FLOPPY DISK DRIVER
;==================================================================================================
;
; TODO:
;
;
; PORTS
;
#IF ((FDMODE == FDMODE_DIO) | (FDMODE == FDMODE_ZETA) | (FDMODE == FDMODE_DIO3))
FDC_MSR .EQU $36 ; 8272 MAIN STATUS REGISTER
FDC_DATA .EQU $37 ; 8272 DATA PORT
FDC_DIR .EQU $38 ; DATA INPUT REGISTER
FDC_DOR .EQU $3A ; DIGITAL OUTPUT REGISTER (LATCH)
FDC_DMA .EQU $3C ; PSEUDO DMA DATA PORT
#ENDIF
#IF (FDMODE = FDMODE_ZETA2)
FDC_MSR .EQU $30 ; 8272 MAIN STATUS REGISTER
FDC_DATA .EQU $31 ; 8272 DATA PORT
FDC_DOR .EQU $38 ; DIGITAL OUTPUT REGISTER
FDC_DCR .EQU $28 ; CONFIGURATION CONTROL REGISTER
FDC_TC .EQU $38 ; TERMINAL COUNT (W/ DACK)
#ENDIF
#IF (FDMODE == FDMODE_DIDE)
FDC_BID .EQU $20 ; IO RANGE 20H-3FH
FDC_MSR .EQU $2A ; 8272 MAIN STATUS REGISTER
FDC_DATA .EQU $2B ; 8272 DATA PORT
FDC_DOR .EQU $2C ; DOR
FDC_DCR .EQU $2D ; DCR
FDC_DACK .EQU $3C ; DACK
FDC_TC .EQU $3D ; TERMINAL COUNT (W/ DACK)
FDC_DMA .EQU $3C ; NOT USED BY DIDE
#ENDIF
#IF (FDMODE == FDMODE_N8)
FDC_MSR .EQU $8C ; 8272 MAIN STATUS REGISTER
FDC_DATA .EQU $8D ; 8272 DATA PORT
FDC_DOR .EQU $92 ; DOR
FDC_DCR .EQU $91 ; DCR
FDC_DACK .EQU $90 ; DACK
FDC_TC .EQU $93 ; TERMINAL COUNT (W/ DACK)
FDC_DMA .EQU $3C ; NOT USED BY N8
#ENDIF
#IF (FDMODE == FDMODE_RCSMC)
FDC_MSR .EQU $50 ; 8272 MAIN STATUS REGISTER
FDC_DATA .EQU $51 ; 8272 DATA PORT
FDC_DOR .EQU $58 ; DIGITAL OUTPUT REGISTER (LATCH)
#ENDIF
#IF (FDMODE = FDMODE_RCWDC)
FDC_MSR .EQU $50 ; 8272 MAIN STATUS REGISTER
FDC_DATA .EQU $51 ; 8272 DATA PORT
FDC_DOR .EQU $58 ; DIGITAL OUTPUT REGISTER
FDC_DCR .EQU $48 ; CONFIGURATION CONTROL REGISTER
FDC_TC .EQU $58 ; TERMINAL COUNT (W/ DACK)
#ENDIF
;
; DISK OPERATIONS
;
DOP_READ .EQU 0 ; READ OPERATION
DOP_WRITE .EQU 1 ; WRITE OPERATION
DOP_FORMAT .EQU 2 ; FORMAT OPERATION
DOP_READID .EQU 3 ; READ ID OPERATION
;
; FDC RESULT CODES
;
FRC_OK .EQU 0 ; 00
FRC_NOTIMPL .EQU -01H ; FF
FRC_CMDERR .EQU -02H ; FE
FRC_ERROR .EQU -03H ; FD
FRC_ABORT .EQU -04H ; FC
FRC_BUFMAX .EQU -05H ; FB
FRC_ABTERM .EQU -08H ; F8
FRC_INVCMD .EQU -09H ; F7
FRC_DSKCHG .EQU -0AH ; F6
FRC_ENDCYL .EQU -0BH ; F5
FRC_DATAERR .EQU -0CH ; F4
FRC_OVERRUN .EQU -0DH ; F3
FRC_NODATA .EQU -0EH ; F2
FRC_NOTWRIT .EQU -0FH ; F1
FRC_MISADR .EQU -10H ; F0
FRC_TOFDCRDY .EQU -11H ; EF
FRC_TOSNDCMD .EQU -12H ; EE
FRC_TOGETRES .EQU -13H ; ED
FRC_TOEXEC .EQU -14H ; EC
FRC_TOSEEKWT .EQU -15H ; EB
;
; FD DEVICE CONFIGURATION
;
FD_DEVCNT .EQU 2 ; 2 DEVICES SUPPORTED
FD_CFGSIZ .EQU 8 ; SIZE OF CFG TBL ENTRIES
;
; PER DEVICE DATA OFFSETS
; ; OFFSET OF...
FD_DEV .EQU 0 ; DEVICE NUMBER (BYTE)
FD_STAT .EQU 1 ; LAST STATUS (BYTE)
FD_MEDTYP .EQU 2 ; MEDIA TYPE FDM... (BYTE)
FD_CURTRK .EQU 3 ; CURRENT TRACK (BYTE)
FD_HST .EQU 4 ; HOSTS SEEK POSITION
FD_HSTTRK .EQU FD_HST + 0 ; HOST TRACK (WORD)
FD_HSTSEC .EQU FD_HST + 2 ; HOST SECTOR (BYTE)
FD_HSTHD .EQU FD_HST + 3 ; HOST HEAD (BYTE)
;
FD_CFGTBL:
; DEVICE 0, PRIMARY MASTER
.DB 0 ; DEVICE NUMBER
.DB 0 ; DEVICE STATUS
.DB FDMEDIA ; MEDIA TYPE
.DB $FF ; CURRENT TRACK
.DW 0 ; HOST TRACK
.DB 0 ; HOST SECTOR
.DB 0 ; HOST HEAD
#IF (FD_DEVCNT >= 2)
; DEVICE 1, PRIMARY SLAVE
.DB 1 ; DRIVER DEVICE NUMBER
.DB 0 ; DEVICE STATUS
.DB FDMEDIA ; MEDIA TYPE
.DB $FF ; CURRENT TRACK
.DW 0 ; HOST TRACK
.DB 0 ; HOST SECTOR
.DB 0 ; HOST HEAD
#ENDIF
;
#IF ($ - FD_CFGTBL) != (FD_DEVCNT * FD_CFGSIZ)
.ECHO "*** INVALID FD CONFIG TABLE ***\n"
#ENDIF
;
.DB $FF ; END MARKER
;
#IF (FDTRACE > 0)
;
; FDC STATUS CODE STRINGS
;
FSS_OK .TEXT "OK$"
FSS_NOTIMPL .TEXT "NOT IMPLEMENTED$"
FSS_CMDERR .TEXT "COMMAND ERROR$"
FSS_ERROR .TEXT "ERROR$"
FSS_ABORT .TEXT "ABORT$"
FSS_BUFMAX .TEXT "BUFFER EXCEEDED$"
FSS_ABTERM .TEXT "ABNORMAL TERMINATION$"
FSS_INVCMD .TEXT "INVALID COMMAND$"
FSS_DSKCHG .TEXT "DISK CHANGE$"
FSS_ENDCYL .TEXT "END OF CYLINDER$"
FSS_DATAERR .TEXT "DATA ERROR$"
FSS_OVERRUN .TEXT "OVERRUN$"
FSS_NODATA .TEXT "NO DATA$"
FSS_NOTWRIT .TEXT "NOT WRITABLE$"
FSS_MISADR .TEXT "MISSING ADDRESS MARK$"
FSS_TOFDCRDY .TEXT "FDC READY TIMEOUT$"
FSS_TOSNDCMD .TEXT "SENDCMD TIMEOUT$"
FSS_TOGETRES .TEXT "GET RESULTS TIMEOUT$"
FSS_TOEXEC .TEXT "EXEC TIMEOUT$"
FSS_TOSEEKWT .TEXT "SEEK WAIT TIMEOUT$"
;
; FDC STATUS STRING TABLE
;
FSST: .DB FRC_OK \ .DW FSS_OK
FSST_ENTSIZ .EQU $ - FSST
.DB FRC_NOTIMPL \ .DW FSS_NOTIMPL
.DB FRC_CMDERR \ .DW FSS_CMDERR
.DB FRC_ERROR \ .DW FSS_ERROR
.DB FRC_ABORT \ .DW FSS_ABORT
.DB FRC_BUFMAX \ .DW FSS_BUFMAX
.DB FRC_ABTERM \ .DW FSS_ABTERM
.DB FRC_INVCMD \ .DW FSS_INVCMD
.DB FRC_DSKCHG \ .DW FSS_DSKCHG
.DB FRC_ENDCYL \ .DW FSS_ENDCYL
.DB FRC_DATAERR \ .DW FSS_DATAERR
.DB FRC_OVERRUN \ .DW FSS_OVERRUN
.DB FRC_NODATA \ .DW FSS_NODATA
.DB FRC_NOTWRIT \ .DW FSS_NOTWRIT
.DB FRC_MISADR \ .DW FSS_MISADR
.DB FRC_TOFDCRDY \ .DW FSS_TOFDCRDY
.DB FRC_TOSNDCMD \ .DW FSS_TOSNDCMD
.DB FRC_TOGETRES \ .DW FSS_TOGETRES
.DB FRC_TOEXEC \ .DW FSS_TOEXEC
.DB FRC_TOSEEKWT \ .DW FSS_TOSEEKWT
FSST_COUNT .EQU (($ - FSST) / FSST_ENTSIZ) ; # ENTRIES IN TABLE
#ENDIF
;
; FDC COMMANDS
;
CFD_READ .EQU 00000110B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N
CFD_READDEL .EQU 00001100B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N
CFD_WRITE .EQU 00000101B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N
CFD_WRITEDEL .EQU 00001001B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N
CFD_READTRK .EQU 00000010B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N
CFD_READID .EQU 00001010B ; CMD,HDS/DS --> ST0,ST1,ST2,C,H,R,N
CFD_FMTTRK .EQU 00001101B ; CMD,HDS/DS,N,SC,GPL,D --> ST0,ST1,ST2,C,H,R,N
CFD_SCANEQ .EQU 00010001B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,STP --> ST0,ST1,ST2,C,H,R,N
CFD_SCANLOEQ .EQU 00011001B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,STP --> ST0,ST1,ST2,C,H,R,N
CFD_SCANHIEQ .EQU 00011101B ; CMD,HDS/DS,C,H,R,N,EOT,GPL,STP --> ST0,ST1,ST2,C,H,R,N
CFD_RECAL .EQU 00000111B ; CMD,DS --> <EMPTY>
CFD_SENSEINT .EQU 00001000B ; CMD --> ST0,PCN
CFD_SPECIFY .EQU 00000011B ; CMD,SRT/HUT,HLT/ND --> <EMPTY>
CFD_DRVSTAT .EQU 00000100B ; CMD,HDS/DS --> ST3
CFD_SEEK .EQU 00001111B ; CMD,HDS/DS --> <EMPTY>
CFD_VERSION .EQU 00010000B ; CMD --> ST0
;
;
; Specify Command:
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; |Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
; | 1 | ----- STEP RATE ----- | -- HEAD UNLOAD TIME - |
; | 2 | ------------ HEAD LOAD TIME ----------- | NDM |
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
;
;
; Step Rate (milliseconds): Head Unload Time (milliseconds): Head Load Time (milliseconds):
; +------+------+------+------+------+ +------+------+------+------+------+ +------+------+------+------+------+
; | | BITRATE | | | BITRATE | | | BITRATE |
; | VAL | 1.0M | 500K | 300K | 250K | | VAL | 1.0M | 500K | 300K | 250K | | VAL | 1.0M | 500K | 300K | 250K |
; +------+------+------+------+------+ +------+------+------+------+------+ +------+------+------+------+------+
; | 0 | 8.0 | 16.0 | 26.7 | 32.0 | | 0 | 128 | 256 | 426 | 512 | | 0 | 128 | 256 | 426 | 512 |
; | 1 | 7.5 | 15.0 | 25.0 | 30.0 | | 1 | 8 | 16 | 26.7 | 32 | | 1 | 1 | 2 | 3.3 | 4 |
; | 2 | 7.0 | 14.0 | 23.3 | 28.0 | | 2 | 16 | 32 | 53.3 | 64 | | 2 | 2 | 4 | 6.7 | 8 |
; | ... | ... | ... | ... | ... | | ... | ... | ... | ... | ... | | ... | ... | ... | ... | ... |
; | 14 | 1.0 | 2.0 | 3.3 | 4.0 | | 14 | 112 | 224 | 373 | 448 | | 126 | 126 | 252 | 420 | 504 |
; | 15 | 0.5 | 1.0 | 1.7 | 2.0 | | 15 | 120 | 240 | 400 | 480 | | 127 | 127 | 254 | 423 | 508 |
; +------+------+------+------+------+ +------+------+------+------+------+ +------+------+------+------+------+
;
; IBM PS/2 CALLS FOR:
; STEP RATE: 3ms (6ms FOR ALL 41mm OR 720K DRIVES)
; HEAD LOAD TIME: 15ms
;
; STATIC CONFIGURATION, NEVER CHANGES (PRIVATE)
;
FCD_MT .EQU 000H ; MULTI-TRACK, WE DON'T USE, SET TO 0
FCD_MFM .EQU 001H ; MFM, 0=FM, 1=MFM, WE USE MFM ALWAYS
FCD_SK .EQU 000H ; SKIP MODE, WE DON'T USE, SET TO 0
FCD_N .EQU 002H ; SECTOR SIZE, N=2 FOR 512 BYTES
FCD_DTL .EQU 0FFH ; DATA LENGTH (WHEN N=0, SET TO FF OTHERWISE)
FCD_STP .EQU 001H ; SECTOR SCAN TYPE, 1=CONTIG, 2=ALTERNATING
;
FCD_PC720 .DB 050H ; NUMBER OF CYLINDERS
.DB 002H ; NUMBER OF HEADS
.DB 009H ; NUMBER OF SECTORS
.DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1)
.DB 009H ; SECTOR COUNT
.DW 200H ; SECTOR SIZE IN BYTES
.DB 02AH ; GAP LENGTH (R/W)
.DB 050H ; GAP LENGTH (FORMAT)
.DB (13 << 4) | 0 ; SRT = 6ms, HUT = 512ms
.DB (4 << 1) | 1 ; HLT = 16ms, ND = YES
.DB DOR_BR250 ; DOR
.DB DCR_BR250 ; DCR
.IF (($ - FCD_PC720) != FCD_LEN)
.ECHO "*** FCD_PC720 SIZE ERROR!!! ***\n"
.ENDIF
;
FCD_PC144 .DB 050H ; NUMBER OF CYLINDERS
.DB 002H ; NUMBER OF HEADS
.DB 012H ; NUMBER OF SECTORS
.DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1)
.DB 012H ; SECTOR COUNT
.DW 200H ; SECTOR SIZE IN BYTES
.DB 01BH ; GAP LENGTH (R/W)
.DB 06CH ; GAP LENGTH (FORMAT)
.DB (13 << 4) | 0 ; SRT = 3ms, HUT = 256ms
.DB (8 << 1) | 1 ; HLT = 16ms, ND = YES
.DB DOR_BR500 ; DOR
.DB DCR_BR500 ; DCR
.IF (($ - FCD_PC144) != FCD_LEN)
.ECHO "*** FCD_PC144 SIZE ERROR!!! ***\n"
.ENDIF
;
FCD_PC360 .DB 028H ; NUMBER OF CYLINDERS
.DB 002H ; NUMBER OF HEADS
.DB 009H ; NUMBER OF SECTORS
.DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1)
.DB 009H ; SECTOR COUNT
.DW 200H ; SECTOR SIZE IN BYTES
.DB 02AH ; GAP LENGTH (R/W)
.DB 050H ; GAP LENGTH (FORMAT)
.DB (13 << 4) | 0 ; SRT = 6ms, HUT = 512ms
.DB (4 << 1) | 1 ; HLT = 16ms, ND = YES
.DB DOR_BR250 ; DOR
.DB DCR_BR250 ; DCR
.IF (($ - FCD_PC360) != FCD_LEN)
.ECHO "*** FCD_PC360 SIZE ERROR!!! ***\n"
.ENDIF
;
FCD_PC120 .DB 050H ; NUMBER OF CYLINDERS
.DB 002H ; NUMBER OF HEADS
.DB 00FH ; NUMBER OF SECTORS
.DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1)
.DB 00FH ; SECTOR COUNT
.DW 200H ; SECTOR SIZE IN BYTES
.DB 01BH ; GAP LENGTH (R/W)
.DB 054H ; GAP LENGTH (FORMAT)
.DB (10 << 4) | 0 ; SRT = 6ms, HUT = 256ms
.DB (8 << 1) | 1 ; HLT = 16ms, ND = YES
.DB DOR_BR500 ; DOR
.DB DCR_BR500 ; DCR
.IF (($ - FCD_PC120) != FCD_LEN)
.ECHO "*** FCD_PC120 SIZE ERROR!!! ***\n"
.ENDIF
;
FCD_PC111 .DB 04DH ; NUMBER OF CYLINDERS
.DB 002H ; NUMBER OF HEADS
.DB 00FH ; NUMBER OF SECTORS
.DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1)
.DB 00FH ; SECTOR COUNT
.DW 200H ; SECTOR SIZE IN BYTES
.DB 01BH ; GAP LENGTH (R/W)
.DB 054H ; GAP LENGTH (FORMAT)
.DB (13 << 4) | 0 ; SRT = 3ms, HUT = 256ms
.DB (25 << 1) | 1 ; HLT = 50ms, ND = YES
.DB DOR_BR500 ; DOR
.DB DCR_BR500 ; DCR
.IF (($ - FCD_PC111) != FCD_LEN)
.ECHO "*** FCD_PC111 SIZE ERROR!!! ***\n"
.ENDIF
;
; FCD LOOKUP TABLE (CALLED TO SET HL TO ADDRESS OF MEDIA DATA ABOVE)
; ENTRIES BELOW MUST MATCH COUNT AND VALUES OF FDMXXX IN STD.ASM
;
FCD_TBL:
LD HL,FCD_PC720 \ RET ; FDM720 = 0
LD HL,FCD_PC144 \ RET ; FDM144 = 1
LD HL,FCD_PC360 \ RET ; FDM360 = 2
LD HL,FCD_PC120 \ RET ; FDM120 = 3
LD HL,FCD_PC111 \ RET ; FDM111 = 4
;
; DOR BITS (3AH)
;
; DISKIO 250KBPS 500KBPS
; ------- ------- -------
;D7 /DC/RDY 1 (N/A) 1 (N/A)
;D6 /REDWC (DENSITY) 0 (DD) 1 (HD)
;D5 P0* (PRECOMP BIT 0) 1 \ 0 \
;D4 P1* (PRECOMP BIT 1) 0 (125NS) 1 (125NS)
;D3 P2* (PRECOMP BIT 2) 0 / 0 /
;D2 MINI (BITRATE) 1 (250KBPS) 0 (500KBPS)
;D1 /MOTOR (ACTIVE LO) 1 (OFF) 1 (OFF)
;D0 TC (TERMINAL COUNT) 0 (OFF) 0 (OFF)
;
; *NOTE: FOR 9229 DATA SEPARATOR USED IN DISKIO, VALUE OF PRECOMP BITS CHANGES WITH MINI
; IF MINI=1 (250KBPS), USE 001 FOR 125NS PRECOMP, IF MINI=0, USE 010 FOR 125NS PRECOMP
;
#IF (FDMODE == FDMODE_DIO)
DOR_BR250 .EQU 10100100B ; 250KBPS W/ MOTOR ON
DOR_BR500 .EQU 11010000B ; 500KBPS W/ MOTOR ON
DOR_INIT .EQU 11010010B ; INITIAL DEFAULT LATCH VALUE
#ENDIF
;
; ZETA/DISKIO3 250KBPS 500KBPS
; ------------ ------- -------
;D7 /FDC_RST 1 (RUN) 1 (RUN)
;D6 DENSEL 1 (DD) 0 (HD)
;D5 P0 (PRECOMP BIT 0) 1 \ 1 \
;D4 P1 (PRECOMP BIT 1) 0 (125NS) 0 (125NS)
;D3 P2 (PRECOMP BIT 2) 0 / 0 /
;D2 MINI (BITRATE) 1 (250KBPS) 0 (500KBPS)
;D1 MOTOR 0 (OFF) 0 (OFF)
;D0 TC 0 (OFF) 0 (OFF)
;
; MOTOR AND DENSITY SELECT ARE INVERTED ON ZETA/DISKIO3 VS. DIO
;
#IF ((FDMODE == FDMODE_ZETA) | (FDMODE == FDMODE_DIO3))
DOR_BR250 .EQU 11100110B ; 250KBPS W/ MOTOR ON
DOR_BR500 .EQU 10100010B ; 500KBPS W/ MOTOR ON
DOR_INIT .EQU 10100000B ; INITIAL DEFAULT LATCH VALUE
#ENDIF
;
; RCSMC 250KBPS 500KBPS
; ------------ ------- -------
;D7 /FDC_RST 1 (RUN) 1 (RUN)
;D6 DENSEL 0 (DD) 1 (HD)
;D5 P0 (PRECOMP BIT 0) 1 \ 1 \
;D4 P1 (PRECOMP BIT 1) 0 (125NS) 0 (125NS)
;D3 P2 (PRECOMP BIT 2) 0 / 0 /
;D2 MOTORB 0 (OFF) 0 (OFF)
;D1 MOTORA 0 (OFF) 0 (OFF)
;D0 TC 0 (OFF) 0 (OFF)
;
; MOTOR INVERTED VS. DIO, DENSITY SELECT LIKE DIO
;
#IF (FDMODE == FDMODE_RCSMC)
; RCSMC HAS NO MINI (BITRATE) LATCH AT D2. INSTEAD, D1 AND
; D2 PROVIDE INDEPENDENT MOTOR CONTROL FOR EACH DRIVE.
; MINI (BITRATE) IS A HARDWARE JUMPER (JP3)
; JP3: 1-2 IS DD (MINI HIGH) AND 2-3 IS HD (MINI LOW)
; JP3 *MUST* BE SET CORRECTLY FOR MEDIA USED
; THE CORRECT MOTOR BIT IS SET IN MOTOR ON, NEITHER SET HERE
DOR_BR250 .EQU 10100000B ; 250KBPS W/ MOTOR OFF!
DOR_BR500 .EQU 11100000B ; 500KBPS W/ MOTOR OFF!
DOR_INIT .EQU 11100000B ; INITIAL DEFAULT LATCH VALUE
#ENDIF
;
; *** DIDE/N8/ZETA V2 ***
;
#IF ((FDMODE == FDMODE_DIDE) | (FDMODE == FDMODE_N8) | (FDMODE == FDMODE_ZETA2) | (FDMODE == FDMODE_RCWDC))
DOR_INIT .EQU 00001100B ; SOFT RESET INACTIVE, DMA ENABLED
DOR_BR250 .EQU DOR_INIT
DOR_BR500 .EQU DOR_INIT
#ENDIF
;
; DCR (ONLY APPLIES TO DIDE, N8, AND ZETA V2)
;
DCR_BR250 .EQU 01H ; 250KBPS
DCR_BR500 .EQU 00H ; 500KBPS
;
#IF (FDTRACE > 0)
;
; FDC COMMAND STRINGS
;
FCS_NOP .DB "NOP$"
FCS_READ .DB "READ$"
FCS_READDEL .DB "READDEL$"
FCS_WRITE .DB "WRITE$"
FCS_WRITEDEL .DB "WRITEDEL$"
FCS_READTRK .DB "READTRK$"
FCS_READID .DB "READID$"
FCS_FMTTRK .DB "FMTTRK$"
FCS_SCANEQ .DB "SCANEQ$"
FCS_SCANLOEQ .DB "SCANLOEQ$"
FCS_SCANHIEQ .DB "SCANHIEQ$"
FCS_RECAL .DB "RECAL$"
FCS_SENSEINT .DB "SENSEINT$"
FCS_SPECIFY .DB "SPECIFY$"
FCS_DRVSTAT .DB "DRVSTAT$"
FCS_SEEK .DB "SEEK$"
FCS_VERSION .DB "VER$"
;
; FDC COMMAND TABLE
;
FCT .DB CFD_READ \ .DW FCS_READ
FCT_ENTSIZ .EQU $ - FCT
.DB CFD_READDEL \ .DW FCS_READDEL
.DB CFD_WRITE \ .DW FCS_WRITE
.DB CFD_WRITEDEL \ .DW FCS_WRITEDEL
.DB CFD_READTRK \ .DW FCS_READTRK
.DB CFD_READID \ .DW FCS_READID
.DB CFD_FMTTRK \ .DW FCS_FMTTRK
.DB CFD_SCANEQ \ .DW FCS_SCANEQ
.DB CFD_SCANLOEQ \ .DW FCS_SCANLOEQ
.DB CFD_SCANHIEQ \ .DW FCS_SCANHIEQ
.DB CFD_RECAL \ .DW FCS_RECAL
.DB CFD_SENSEINT \ .DW FCS_SENSEINT
.DB CFD_SPECIFY \ .DW FCS_SPECIFY
.DB CFD_DRVSTAT \ .DW FCS_DRVSTAT
.DB CFD_SEEK \ .DW FCS_SEEK
.DB CFD_VERSION \ .DW FCS_VERSION
FCT_COUNT .EQU (($ - FCT) / FCT_ENTSIZ) ; # ENTRIES IN TABLE
#ENDIF
;
; DRIVER FUNCTION TABLE
;
FD_FNTBL:
.DW FD_STATUS
.DW FD_RESET
.DW FD_SEEK
.DW FD_READ
.DW FD_WRITE
.DW FD_VERIFY
.DW FD_FORMAT
.DW FD_DEVICE
.DW FD_MEDIA
.DW FD_DEFMED
.DW FD_CAP
.DW FD_GEOM
#IF (($ - FD_FNTBL) != (DIO_FNCNT * 2))
.ECHO "*** INVALID FD FUNCTION TABLE ***\n"
#ENDIF
;
FD_VERIFY:
FD_FORMAT:
FD_DEFMED:
CALL PANIC ; INVALID SUB-FUNCTION
;
;
;
FD_DEVICE:
LD D,DIODEV_FD ; D := DEVICE TYPE
LD E,(IY+FD_DEV) ; E := PHYSICAL DEVICE NUMBER
#IF (FDMEDIA == FDM720)
LD C,%11010100 ; 3.5" DS/DD
#ENDIF
#IF (FDMEDIA == FDM144)
LD C,%11011000 ; 3.5" DS/HD
#ENDIF
#IF (FDMEDIA == FDM360)
LD C,%10110100 ; 5.25" DS/DD
#ENDIF
#IF (FDMEDIA == FDM120)
LD C,%10111000 ; 5.25" DS/HD
#ENDIF
#IF (FDMEDIA == FDM111)
LD C,%10010100 ; 8" DS/DD
#ENDIF
XOR A ; SIGNAL SUCCESS
RET
;
; FD_MEDIA
;
FD_MEDIA:
LD A,E ; GET FLAGS
OR A ; SET FLAGS
JR Z,FD_MEDIA4 ; JUST REPORT CURRENT STATUS AND MEDIA
#IF (FDMAUTO)
; SETUP TO READ TRK 0, HD 0, SEC 0
;LD A,C ; C STILL HAS REQUESTED DRIVE
LD A,(IY+FD_DEV) ; GET DRIVE UNIT
;AND 0FH
LD (FCD_DS),A
LD A,0
LD (FCD_C),A
LD (FCD_H),A
INC A
LD (FCD_R),A
LD A,DOP_READID
LD (FCD_DOP),A
#IF (FDTRACE < 3)
; SUPPRESS TRACING FOR MEDIA TESTS
LD A,0
LD (FCD_TRACE),A
#ENDIF
LD B,5
FD_MEDIARETRY:
; TRY PRIMARY MEDIA CHOICE FIRST
LD A,FDMEDIA
CALL FD_TESTMEDIA
JR Z,FD_MEDIA3 ; IF SUCCESS, WE ARE DONE
; TRY ALTERNATE MEDIA CHOICE
LD A,FDMEDIAALT
CALL FD_TESTMEDIA
JR Z,FD_MEDIA3 ; IF SUCCESS, WE ARE DONE
DJNZ FD_MEDIARETRY
; NO JOY, RETURN WITH E=0 (NO MEDIA)
;LD HL,(FDDS_MEDIAADR)
;LD (HL),0 ; SET TO NO MEDIA
LD (IY+FD_MEDTYP),0 ; SET DRIVE = NO MEDIA
LD E,0
OR $FF ; SIGNAL ERROR
RET
FD_TESTMEDIA:
;LD HL,(FDDS_MEDIAADR)
;LD (HL),A
LD (IY+FD_MEDTYP),A
PUSH BC
CALL FD_START
POP BC
RET
FD_MEDIA3:
#IF (FDTRACE < 3)
; RESTORE TRACING FOR MEDIA TESTS
LD A,FDTRACE
LD (FCD_TRACE),A
#ENDIF
#ENDIF
FD_MEDIA4:
#IF (FDTRACE >= 3)
LD DE,FDSTR_SELECT
CALL WRITESTR
;LD BC,(FDDS_MEDIAADR)
;CALL PRTHEXWORD
#ENDIF
; LOAD THE MEDIA BYTE
;LD HL,(FDDS_MEDIAADR)
;LD A,(HL)
LD A,(IY+FD_MEDTYP) ; GET CURRENT MEDIA TYPE
ADD A,MID_FD720 ; ASSUMES MID_ VALUES ARE IN SAME ORDER AS FDM VALUES
#IF (FDTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
LD E,A ; MOVE MEDIA VALUE TO E
XOR A ; SIGNAL SUCCESS
RET
;
;
;
FD_CAP:
CALL FD_GEOM ; GET GEOMETRY
; HL=TRACKS, D=HEADS, E=SECTORS
PUSH HL ; SAVE TRACK COUNT
LD H,D ; HEADS COUNT TO H
RES 7,H ; MAKE SURE LBA CAPABILITY BIT IS CLEARED
CALL MULT8 ; HL := H * E FOR SECTORS / CYL
POP DE ; RECOVER TRACKS, E == TRACK COUNT
LD H,L ; MOVE WORKING COUNT L --> H
CALL MULT8 ; HL := H * E FOR TOTAL SECTORS
LD DE,0 ; HI WORD ALWAYS ZERO
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
;
;
FD_GEOM:
LD A,(IY+FD_MEDTYP) ; GET CURRENT MEDIA TYPE
RLCA ; TABLE IS 4 BYTE ENTRIES
RLCA ; A = A * 4
LD HL,FCD_TBL ; HL = START OF TABLE
LD D,0 ; SET DE TO TABLE OFFSET
LD E,A
ADD HL,DE ; OFFSET BASED ON DESIRED MEDIA
CALL JPHL ; CALL THE TABLE ENTRY (SEE FCD_TBL)
; HL NOW POINTS TO START OF DESIRED MEDIA INFO
LD A,(HL) ; GET TRACKS
INC HL ; POINT TO HEADS
LD D,(HL) ; GET HEADS
SET 7,D ; SET LBA CAPABILITY BIT
INC HL ; POINT TO SECTORS
LD E,(HL) ; GET SECTORS
LD L,A ; L := TRACKS
LD H,0 ; HI WORD OF TRACKS IS ALWAYS ZERO
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
; FD_INIT
;
FD_INIT:
CALL NEWLINE ; FORMATTING
PRTS("FD: IO=0x$")
LD A,FDC_MSR
CALL PRTHEXBYTE
PRTS(" UNITS=2$")
;
; SETUP THE DISPATCH TABLE ENTRIES
;
LD B,FD_DEVCNT ; LOOP CONTROL
LD IY,FD_CFGTBL ; START OF CFG TABLE
FD_INIT0:
PUSH BC ; SAVE LOOP CONTROL
LD BC,FD_FNTBL ; BC := FUNC TABLE ADR
PUSH IY ; CFG ENTRY POINTER
POP DE ; COPY TO DE
CALL DIO_ADDENT ; ADD ENTRY, BC IS NOT DESTROYED
CALL FD_INITUNIT ; DO UNIT INITIALIZATION
LD BC,FD_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,BC ; BUMP IY TO NEXT ENTRY
POP BC ; RESTORE BC
DJNZ FD_INIT0 ; LOOP AS NEEDED
;
LD A,FDTRACE
LD (FCD_TRACE),A
;
LD BC,0
LD (FCD_IDLECNT),BC
;
LD A,DOR_INIT
LD (FST_DOR),A
;
CALL FC_RESETFDC
CALL FD_CLRDSKCHG
;
LD A,TRUE
LD (FCD_FDCRDY),A
;
RET
;
; UNIT INITIALIZATION
;
FD_INITUNIT:
LD (IY+FD_STAT),0 ; CLEAR STATUS
LD (IY+FD_MEDTYP),FDMEDIA ; SET DEFAULT MEDIA TYPE
LD (IY+FD_CURTRK),$FE ; SPECIAL VALUE FOR CURTRK
RET
;
; FD_IDLE QUIESCES THE FLOPPY SUBSYSTEM (MOTOR OFF)
; AFTER BEING CALLED ENOUGH TIMES...
; SHOULD IT INVALIDATE THE BUFFER???
;
FD_IDLE:
LD BC,(FCD_IDLECNT)
LD A,B
OR C
RET Z ; COUNTER ALREADY FIRED
DEC BC ; DECREMENT COUNTER
LD (FCD_IDLECNT),BC ; SAVE IT
LD A,B
OR C
RET NZ ; STILL COUNTING DOWN, RETURN
CALL FC_MOTOROFF ; COUNTER JUST EXPIRED, SHUTDOWN MOTOR!
RET
;
; FD_STATUS
;
FD_STATUS:
LD A,(IY+FD_CURTRK) ; A = CURRENT TRACK
CP 0FFH ; IS CURRENT TRACK = $FF?
JR Z,FD_STATUS1 ; IF SO, NOT READY
XOR A ; A = 0 = OK
RET ; RETURN
FD_STATUS1:
OR A ; A ALREADY = $FF, JUST SET FLAGS
RET
;
;
;
FD_RESET:
XOR A ; ALWAYS OK
RET
;
; FD_CLRDSKCHG
;
FD_CLRDSKCHG:
; PROCESS ANY PENDING DISK CHANGE NOTIFICATIONS
LD B,5
FD_CLRDSKCHG1:
PUSH BC
CALL FC_SENSEINT
POP BC
LD A,(FST_RC)
CP FRC_DSKCHG
RET NZ ; NO MORE DISK CHANGE NOTIFICATIONS
DJNZ FD_CLRDSKCHG1
;
; FD_WTSEEK
;
; WAIT FOR PENDING SEEK OPERATION TO COMPLETE BY POLLING SENSEINT
; AND WAITING FOR ABTERM OR OK.
;
FD_WTSEEK:
LD BC,1000H
FD_WTSEEKLOOP:
PUSH BC
CALL FC_SENSEINT
POP BC
LD A,(FST_RC) ; CHECK RC
CP FRC_ABTERM ; ABTERM = DONE/FAILED
JR Z,FD_RETRC
CP FRC_OK ; OK = DONE/SUCCESS
JR Z,FD_RETRC
DEC BC ; CHECK LOOP COUNTER IN BC
LD A,B ; "
OR C ; "
JR NZ,FD_WTSEEKLOOP ; LOOP UNTIL COUNTER EXHAUSTED
FD_RETRC:
LD A,(FST_RC)
OR A
RET ; TIMEOUT/FAILED
;
; FD_FDCRESET
;
FD_FDCRESET:
CALL FC_RESETFDC
CALL FD_CLRDSKCHG
LD A,TRUE
LD (FCD_FDCRDY),A
; MARK ALL DRIVES AS NEEDING RECALIBRATION
; NOTE THAT IF THE VALUE IS CURRENT $FF,
; WE NEED TO LEAVE IT ALONE, SO WE 'OR' IN THE
; $FE TO AVOID THIS SCENARIO.
PUSH IY ; SAVE CURRENT IY
LD B,FD_DEVCNT ; LOOP CONTROL
LD DE,FD_CFGSIZ ; SIZE OF CFG ENTRY
LD IY,FD_CFGTBL ; START OF CFG TABLE
FD_FDCRESET1:
LD A,(IY+FD_CURTRK) ; GET CURRENT TRACK
OR $FE ; APPLY NEW VALUE
LD (IY+FD_CURTRK),A ; UPDATE TRACK
ADD IY,DE ; BUMP IY TO NEXT ENTRY
DJNZ FD_FDCRESET1 ; LOOP AS NEEDED
POP IY ; RESTORE IY
RET
;
; FD_DRIVERESET
;
; ATTEMPT TO FULLY RESET FLOPPY DRIVE, PRIMARILY RECALIBRATE
;
FD_DRIVERESET:
CALL FC_SPECIFY
RET NZ ; ERROR, BAIL OUT
CALL FC_RECAL
RET NZ ; ERROR, BAIL OUT
; FIRST RECAL MAY FAIL TO REACH TRACK 0
; SO WE TRY ONCE MORE IN CASE OF A FAILURE
CALL FD_WTSEEK
RET Z
; SECOND TRY, ONLY IF NEEDED
CALL FC_RECAL
RET NZ ; ERROR, BAIL OUT
CALL FD_WTSEEK
RET
;
;
;
FD_SEEK:
; DE:HL CONTAINS EITHER LBA OR CHS
BIT 7,D ; TEST LBA BIT
JR Z,FD_SEEK9 ; IF NOT LBA, JUST SAVE INCOMING VALUE
; NEED TO CONVERT LBA IN DE:HL TO CHS
; NOTE: FLOPPY LBA WILL NEVER EXCEED 16 BITS, SO WE IGNORE DE ENTIRELY
PUSH HL ; SAVE HL
CALL FD_GEOM ; E := SPT, D := HDS
POP HL ; RESTORE HL
RET NZ ; BAIL OUT ON ERROR
RES 7,D ; MAKE SURE LBA BIT IS CLEARED
LD (FD_CURGEOM),DE ; SAVE AS FD_CURSPT & FD_CURHDS
LD A,(FD_CURSPT) ; A := SECTORS PER TRACK
LD D,0 ; DE := SPT
LD E,A
CALL DIV16 ; DIVIDE, REMAINDER (SECTOR #) IN HL
PUSH HL ; SAVE SECTOR #
PUSH BC ; CYLINDERS AND HEADS BACK TO HL
POP HL
LD A,(FD_CURHDS) ; A := HEADS PER CYLINDER
LD D,0 ; DE : = HEADS PER CYLINDER
LD E,A
CALL DIV16 ; DIVIDE, BC := TRACK, REMAINDER (HEAD #) IN HL
PUSH HL ; SAVE HEAD #
PUSH BC ; COPY TRACK # TO HL
POP HL
POP BC ; RECOVER HEAD #
LD D,C ; HEAD # TO D
POP BC ; RECOVER SECTOR #
LD E,C ; SECTOR # TO E
FD_SEEK9: ; NOT LBA, JUST SAVE THE CHS VALUE IN CFG ENTRY
PUSH HL ; SAVE INCOMING HL TO (SP)
LD A,FD_HST ; A := HST OFFSET IN CFG ENTRY
CALL LDHLIYA ; HL := HST VALUE ADR
EX (SP),HL ; RESTORE INCOMING HL, HST ADR TO (SP)
POP BC ; HST ADR TO BC
CALL ST32 ; SAVE HST IN CFG ENTRY
XOR A ; SIGNAL SUCCESS
RET
;
FD_READ:
CALL HB_DSKREAD ; HOOK HBIOS DISK READ SUPERVISOR
LD (FD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
LD A,DOP_READ
JR FD_RW
;
FD_WRITE:
CALL HB_DSKWRITE ; HOOK HBIOS DISK WRITE SUPERVISOR
LD (FD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
LD A,DOP_WRITE
JR FD_RW
;
FD_RW:
LD (FCD_DOP),A ; SAVE REQUESTED DISK OPERATION
LD A,E ; BLOCK COUNT TO A
OR A ; SET FLAGS
RET Z ; ZERO SECTOR I/O, RETURN W/ E=0 & A=0
LD B,A ; INIT SECTOR DOWNCOUNTER
LD C,0 ; INIT SECTOR READ/WRITE COUNT
PUSH BC ; SAVE COUNTERS
CALL FD_GEOM ; E := SPT, D := HDS
POP BC ; RESTORE COUNTERS
JR NZ,FD_RW4 ; BAIL OUT ON ERROR
RES 7,D ; MAKE SURE LBA BIT IS CLEARED
LD (FD_CURGEOM),DE ; SAVE AS FD_CURSPT & FD_CURHDS
FD_RW1:
PUSH BC ; SAVE COUNTERS
CALL FD_RUN ; PERFORM SECTOR READ/WRITE
JR NZ,FD_RW3 ; IF ERROR, SKIP INCREMENT
; INCREMENT SECTOR AND CHECK FOR TRACK OVERFLOW
LD A,FD_HSTSEC ; HST SECTOR OFFSET IN CFG
CALL LDHLIYA ; HL := ADR OF HST SECTOR
INC (HL) ; INCREMENT SECTOR
LD A,(FD_CURSPT) ; A := SECTORS PER TRACK
CP (HL) ; COMPARE SPT TO CURRENT SECTOR
JR NZ,FD_RW2 ; IF NO OVERFLOW, DONE
; RESET SECTOR, INCREMENT HEAD, AND CHECK FOR CYLINDER OVERFLOW
LD (HL),0 ; RESET SECTOR TO ZERO
INC HL ; POINT TO HST HEAD
INC (HL) ; INCREMENT HEAD
LD A,(FD_CURHDS) ; A : = HEADS
CP (HL) ; COMPARE HEADS TO HST HEAD
JR NZ,FD_RW2 ; IF NO OVERFLOW, DONE
; RESET HEAD AND INCREMENT TRACK
LD (HL),0 ; RESET HEAD TO ZERO
DEC HL ; POINT TO HST TRACK
DEC HL ; ...
DEC HL ; ...
INC (HL) ; INCREMENT HST TRACK LSB
FD_RW2:
LD HL,FD_DSKBUF+1 ; POINT TO MSB OF BUFFER ADR
INC (HL) ; BUMP DMA BY
INC (HL) ; ... 512 BYTES
XOR A ; SIGNAL SUCCESS
FD_RW3:
POP BC ; RECOVER COUNTERS
JR NZ,FD_RW4 ; IF ERROR, BAIL OUT
INC C ; BUMP COUNT OF SECTORS READ
DJNZ FD_RW1 ; LOOP AS NEEDED
FD_RW4:
LD E,C ; SECTOR READ COUNT TO E
LD HL,(FD_DSKBUF) ; CURRENT DMA TO HL
OR A ; SET FLAGS BASED ON RETURN CODE
RET ; AND RETURN, A HAS RETURN CODE
;
FD_RUN:
; UPDATE DRIVE SELECTION
LD A,(IY+FD_DEV) ; GET UNIT
LD (FCD_DS),A ; UPDATE FCD_DS TO NEW VALUE
; MAP HSTTRK TO FCD_H, FCD_C
LD A,(IY+FD_HSTTRK) ; GET TRACK VALUE (CYLINDER IS MORE ACCURATE)
LD (FCD_C),A ; ... AND MOVE IT TO CYL PARM
LD A,(IY+FD_HSTHD) ; GET HEAD VALUE
LD (FCD_H),A ; ... AND MOVE IT TO HEAD PARM
; MAP HSTSEC TO FCD_R
LD A,(IY+FD_HSTSEC)
INC A ; SWITCH FROM ZERO BASED TO ONE BASED
LD (FCD_R),A
; SET RETRY COUNTER
LD B,5
FD_RETRY:
PUSH BC
CALL FD_START
POP BC
LD A,(FST_RC) ; CHECK RESULT
OR A
RET Z ; SUCCESS
DJNZ FD_RETRY ; RETRY TILL COUNTER EXHAUSTED
#IF (FDTRACE == 1)
CALL FC_PRTRESULTS
#ENDIF
LD A,(FST_RC)
OR A ; OTHERWISE SET FLAGS BASED ON RC (IN A)
RET ; AND GIVE UP
;
;
;
FD_START:
LD A,(FCD_FDCRDY)
CP TRUE
CALL NZ,FD_FDCRESET
; COPY MEDIA CONFIG INTO FCD
; THIS IS HERE TO ACCOMMODATE DIFFERENT MEDIA
; IN DIFFERENT FLOPPY UNITS.
LD A,(IY+FD_MEDTYP) ; A = MEDIA BYTE
RLCA ; TABLE IS 4 BYTE ENTRIES
RLCA ; A = A * 4
LD HL,FCD_TBL ; HL = START OF TABLE
LD D,0 ; SET DE TO TABLE OFFSET
LD E,A
ADD HL,DE ; OFFSET BASED ON DESIRED MEDIA
CALL JPHL ; CALL THE TABLE ENTRY (SEE FCD_TBL)
LD DE,FCD ; DE = DESTINATION
LD BC,FCD_LEN ; BC = BYTES TO COPY
LDIR ; BYTES COPY FROM MDB TO FCD
CALL FC_MOTORON ; INCLUDES LATCH SETUP
LD A,(IY+FD_CURTRK)
CP 0FEH ; FF = DRIVE NEEDS TO BE RESET
JR C,FD_RUN0 ; NO RESET NEEDED, BYPASS
CALL FD_DRIVERESET
JR NZ,FD_RUNERR
LD (IY+FD_CURTRK),0 ; ZERO CUR TRACK POS
FD_RUN0:
; COMPARE CURRENT TRACK WITH REQUESTED TRACK TO SEE IF SEEK NEEDED
LD A,(FCD_C)
CP (IY+FD_CURTRK)
JR Z,FD_RUN1 ; FDDS_TRKADR == FCD_C, SKIP SEEK
; INITIATE SEEK TO NEW TRACK
CALL FC_SEEK
JR NZ,FD_RUNERR
; WAIT FOR SEEK TO COMPLETE
CALL FD_WTSEEK
JR NZ,FD_RUNERR
; RECORD NEW CURRENT TRACK
LD A,(FCD_C)
LD (IY+FD_CURTRK),A
FD_RUN1:
; GET THE REQUESTED OPERATION
LD A,(FCD_DOP)
; SETUP RETURN ADDRESS
LD HL,FD_RUNCHK
PUSH HL
; DISPATCH TO FUNCTION
CP DOP_READ
JR Z,FC_READ
CP DOP_WRITE
JR Z,FC_WRITE
CP DOP_READID
JR Z,FC_READID
CALL PANIC
FD_RUNCHK:
#IF (DSKYENABLE)
CALL FD_DSKY
#ENDIF
FD_RUNEXIT:
LD A,(FST_RC)
OR A
RET Z
FD_RUNERR:
; INDICATE THAT A CONTROLLER RESET IS DESIRED
LD A,FALSE
LD (FCD_FDCRDY),A
; FLAG DRIVE IN ERROR STATUS BY SETTING TRKADR == FF
LD (IY+FD_CURTRK),$FF
JP FD_RETRC
#IF (DSKYENABLE)
FD_DSKY:
LD HL,DSKY_HEXBUF
LD A,(FCD_C)
LD (HL),A
INC HL
LD A,(FCD_R)
LD (HL),A
INC HL
LD A,(FRB_ST0)
LD (HL),A
INC HL
LD A,(FRB_ST1)
LD (HL),A
CALL DSKY_HEXOUT
RET
#ENDIF
;
;===============================================================================
; FLOPPY DISK CONTROL SERVICES (PHYSICAL DEVICE CONTROL FOR FDC HARDWARE)
;===============================================================================
;
; ENTRY POINTS FOR FDC COMMANDS
;
FC_READ:
LD A,CFD_READ | 11100000B
CALL FC_SETUPIO
JP FOP
FC_WRITE:
LD A,CFD_WRITE | 11000000B
CALL FC_SETUPIO
JP FOP
FC_READID:
LD A,CFD_READID | 01000000B
CALL FC_SETUPCMD
JP FOP
FC_RECAL:
LD A,CFD_RECAL | 00000000B
CALL FC_SETUPCMD
JP FOP ; FIX: DO WE NEED TO REMOVE HDS BITS FROM SECOND BYTE?
FC_SENSEINT:
LD A,CFD_SENSEINT | 00000000B
CALL FC_SETUPCMD
LD A,1 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE
LD (FCP_LEN),A
JP FOP
FC_SPECIFY:
LD A,CFD_SPECIFY | 00000000B
CALL FC_SETUPSPECIFY
JP FOP
FC_DRVSTAT:
LD A,CFD_DRVSTAT | 00000000B
CALL FC_SETUPCMD
JP FOP
FC_SEEK:
LD A,CFD_SEEK | 00000000B
CALL FC_SETUPSEEK
JP FOP
;
; HELPER FUNCTIONS TO SETUP CMDBUF
;
FC_SETUPCMD:
; TRICKY... THE INCOMING BYTE IN A MUST CONTAIN THE COMMAND CODE ITSELF
; IN THE LOW 5 BITS PLUS IT MUST SET WHICH OF THE DESIRED BITS IT WANTS
; IN THE HIGH 3 BITS. WE 'AND' THIS WITH THE TEMPATE BITS TO PRODUCE
; THE CORRECT FINAL COMMAND BYTE
LD DE,FCP_BUF
AND 5FH ; MT=0, MFM=1, SK=0, CMD=11111
LD (DE),A ; SAVE THE BYTE
AND 00011111B ; ISOLATE JUST THE COMMAND BITS
LD (FCP_CMD),A ; SAVE IT FOR LATER
INC DE
LD A,(FCD_H) ; START WITH HDS
AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY
RLCA ; MAKE ROOM FOR DS BITS
RLCA ;
LD B,A ; SAVE WHAT WE HAVE SO FAR IN B
LD A,(FCD_DS) ; GET DS VALUE
AND 03H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY
OR B ; COMBINE WITH SAVED
LD (DE),A ; SAVE THE BYTE
INC DE
LD A,2 ; LENGTH IS 2 BYTES AT THIS POINT
LD (FCP_LEN),A
RET
FC_SETUPIO:
CALL FC_SETUPCMD
LD A,(FCD_C)
LD (DE),A
INC DE
LD A,(FCD_H)
LD (DE),A
INC DE
LD A,(FCD_R)
LD (DE),A
INC DE
LD A,FCD_N
LD (DE),A
INC DE
LD A,(FCD_EOT)
LD (DE),A
INC DE
LD A,(FCD_GPL)
LD (DE),A
INC DE
LD A,FCD_DTL
LD (DE),A
INC DE
LD A,9
LD (FCP_LEN),A
RET
FC_SETUPSEEK:
CALL FC_SETUPCMD ; START WITH GENERIC IO CMD
LD A,(FCD_C)
LD (DE),A
INC DE
LD A,3
LD (FCP_LEN),A
RET
FC_SETUPSPECIFY:
CALL FC_SETUPCMD
DEC DE ; BACKUP 1 BYTE, WE ONLY WANT FIRST BYTE
LD A,(FCD_SRTHUT)
LD (DE),A ; SAVE THE BYTE
INC DE
LD A,(FCD_HLTND)
LD (DE),A ; SAVE THE BYTE
INC DE
LD A,3
LD (FCP_LEN),A
RET
;
; SET FST_DOR
;
FC_SETDOR
LD (FST_DOR),A
OUT (FDC_DOR),A
#IF (FDTRACE >= 3)
CALL NEWLINE
LD DE,FDSTR_DOR
CALL WRITESTR
LD DE,FDSTR_ARROW
CALL WRITESTR
CALL PC_SPACE
LD A,(FST_DOR)
CALL PRTHEXBYTE
#ENDIF
RET
;
; SET FST_DCR
;
#IF ((FDMODE == FDMODE_DIDE) | (FDMODE == FDMODE_N8) | (FDMODE == FDMODE_ZETA2) | (FDMODE == FDMODE_RCWDC))
;
FC_SETDCR
LD (FST_DCR),A
OUT (FDC_DCR),A
#IF (FDTRACE >= 3)
CALL NEWLINE
LD DE,FDSTR_DCR
CALL WRITESTR
LD DE,FDSTR_ARROW
CALL WRITESTR
CALL PC_SPACE
LD A,(FST_DCR)
CALL PRTHEXBYTE
#ENDIF
RET
;
#ENDIF
;
; RESET FDC BY PULSING BIT 7 OF LATCH LOW
;
FC_RESETFDC:
#IF (FDTRACE >= 3)
LD DE,FDSTR_RESETFDC
CALL WRITESTR
#ENDIF
LD A,(FST_DOR)
PUSH AF
#IF ((FDMODE == FDMODE_ZETA) | (FDMODE == FDMODE_DIO3) | (FDMODE == FDMODE_RCSMC))
RES 7,A
#ENDIF
#IF ((FDMODE == FDMODE_DIDE) | (FDMODE == FDMODE_N8) | (FDMODE == FDMODE_ZETA2) | (FDMODE == FDMODE_RCWDC))
LD A,0
#ENDIF
CALL FC_SETDOR
CALL DELAY
POP AF
CALL FC_SETDOR
LD DE,150 ; DELAY: 16us * 150 = 2.4ms
CALL VDELAY
RET
;
; PULSE TERMCT TO TERMINATE ANY ACTIVE EXECUTION PHASE
;
FC_PULSETC:
#IF ((FDMODE == FDMODE_DIDE) | (FDMODE == FDMODE_N8) | (FDMODE == FDMODE_ZETA2) | (FDMODE == FDMODE_RCWDC))
IN A,(FDC_TC)
#ELSE
LD A,(FST_DOR)
SET 0,A
OUT (FDC_DOR),A
RES 0,A
OUT (FDC_DOR),A
#ENDIF
RET
;
; SET FST_DOR FOR MOTOR CONTROL ON
;
FC_MOTORON:
LD BC,300H
; LD BC,10H
LD (FCD_IDLECNT),BC
#IF (FDTRACE >= 3)
LD DE,FDSTR_MOTON
CALL WRITESTR
#ENDIF
#IF ((FDMODE == FDMODE_DIO) | (FDMODE == FDMODE_ZETA) | (FDMODE == FDMODE_DIO3))
LD A,(FST_DOR)
PUSH AF
LD A,(FCD_DOR) ; GET NEW LATCH VALUE (W/ MOTOR ON)
CALL FC_SETDOR ; AND IMPLEMENT IT
POP AF
#IF ((FDMODE == FDMODE_ZETA) | (FDMODE == FDMODE_DIO3))
XOR 00000010B ; MOTOR BIT INVERTED ON ZETA
#ENDIF
BIT 1,A ; SET FLAGS SET BASED ON CURRENT MOTOR BIT
RET Z ; MOTOR WAS PREVIOUSLY ON, WE ARE DONE
#ENDIF
#IF (FDMODE == FDMODE_RCSMC)
LD A,(FCD_DS) ; GET DRIVE SELECTED (0 OR 1)
LD C,%00000010 ; MASK FOR MOTORA ON (BIT 1 IS MOTORA)
OR A ; SET FLAGS BASED ON FCD_DS
JR Z,FC_MOTORON1 ; IF FCD_DS == 0, MOTORA IS CORRECT
LD C,%00000100 ; ELSE MASK FOR MOTORB ON (BIT 2 IS MOTORB)
FC_MOTORON1:
LD A,(FST_DOR) ; GET CURRENT DOR VALUE
PUSH AF ; SAVE IT
LD A,(FCD_DOR) ; GET NEW DOR VALUE (W/O MOTOR BITS SET)
OR C ; ADD THE MOTOR BITS
CALL FC_SETDOR ; AND IMPLEMENT NEW VALUE
POP AF ; RECOVER PREVIOUS DOR VALUE
AND %00000110 ; ISOLATE PREVIOUS MOTOR BITS
CP C ; COMPARE TO NEW MOTOR BITS
RET Z ; SKIP DELAY, MOTOR WAS ALREADY ON
#ENDIF
#IF ((FDMODE == FDMODE_DIDE) | (FDMODE == FDMODE_N8) | (FDMODE == FDMODE_ZETA2) | (FDMODE == FDMODE_RCWDC))
; SETUP DCR FOR DIDE HARDWARE
LD A,(FCD_DCR) ; GET NEW DCR VALUE
CALL FC_SETDCR ; AND IMPLEMENT IT
LD HL,FST_DOR ; POINT TO FDC_DOR
LD A,(HL) ; START WITH CURRENT DOR
PUSH AF
AND 11111100B ; GET RID OF ANY ACTIVE DS BITS
LD C,A ; SAVE IT FOR NOW
LD A,(FCD_DS) ; NOW GET CURRENT DS
LD B,A ; PUT IN B FOR LATER
OR C ; COMBINE WITH SAVED DOR
LD C,A ; RE-SAVE IT
INC B ; SET UP B AS LOOP COUNTER (DS + 1)
LD A,00001000B ; STARTING BIT PATTERN FOR MOTOR
FC_MOTORON1:
RLA ; SHIFT LEFT
DJNZ FC_MOTORON1 ; DS TIMES
OR C ; COMBINE WITH SAVED
LD (HL),A ; COMMIT THE NEW VALUE TO FST_DOR
CALL FC_SETDOR ; OUTPUT TO CONTROLLER
LD C,A
POP AF
CP C
RET Z ; MOTOR WAS PREVIOUSLY ON
#ENDIF
#IF (FDTRACE >= 3)
LD DE,FDSTR_MOTDELAY
CALL WRITESTR
#ENDIF
CALL LDELAY ; DELAY FOR MOTOR SPINUP IF NOT PREVIOUSLY ON
RET
;
; SET FST_DOR FOR MOTOR CONTROL OFF
;
FC_MOTOROFF:
LD A,(FCD_FDCRDY)
CP TRUE
CALL NZ,FD_FDCRESET
LD A,DOR_INIT
CALL FC_SETDOR ; OUTPUT TO CONTROLLER
#IF (FDTRACE >= 3)
LD DE,FDSTR_MOTOFF
CALL WRITESTR
#ENDIF
RET
;
;===============================================================================
; FDC OPERATIONS
;===============================================================================
;
FOP:
;
; INITIALIZATION
;
LD A,0
LD (FRB_LEN),A
LD A,FRC_OK
LD (FST_RC),A
;
; CLEAR FDC, DISCARD ANY PENDING BYTES (GARBAGE?)
;
LD B,0 ; B IS LOOP COUNTER
FOP_CLR1:
CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR
IN A,(FDC_MSR) ; GET STATUS
AND 0C0H ; ISOLATE HIGH NIBBLE, RQM/DIO/NDM/CB
CP 0C0H ; LOOKING FOR RQM=1, DIO=1, BYTES PENDING
JR NZ,FOP_CMD1 ; NO BYTES PENDING, GO TO NEXT PHASE
IN A,(FDC_DATA) ; GET THE PENDING BYTE AND DISCARD
DJNZ FOP_CLR1
JP FOP_TOFDCRDY ; OTHERWISE, TIMEOUT
;
; SEND COMMAND
;
FOP_CMD1:
LD HL,FCP_BUF
LD A,(FCP_LEN)
LD D,A ; D = CMD BYTES TO SEND
FOP_CMD2: ; START OF LOOP TO SEND NEXT BYTE
LD B,0 ; B IS LOOP COUNTER
FOP_CMD4: ; START OF STATUS LOOP, WAIT FOR FDC TO BE READY FOR BYTE
CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR
IN A,(FDC_MSR) ; READ MAIN STATUS REGISTER
AND 0C0H ; ISOLATE RQM/DIO
CP 080H ; LOOKING FOR RQM=1, DIO=0 (FDC READY FOR A BYTE)
JR Z,FOP_CMD6 ; GOOD, GO TO SEND BYTE
CP 0C0H ; HMMMM... RQM=1 & DIO=1, FDC WANTS TO SEND US DATA, UNEXPECTED
JP Z,FOP_RES ; GO IMMEDIATELY TO RESULTS???
DJNZ FOP_CMD4 ; LOOP TILL COUNTER EXHAUSTED
JP FOP_TOSNDCMD ; COUNTER EXHAUSTED, TIMEOUT / EXIT
FOP_CMD6: ; SEND NEXT BYTE
LD A,(HL) ; POINT TO NEXT BYTE TO SEND
OUT (FDC_DATA),A ; PUSH IT TO FDC
INC HL ; INCREMENT POINTER FOR NEXT TIME
DEC D ; DECREMENT NUM BYTES LEFT TO SEND
JR NZ,FOP_CMD2 ; DO NEXT BYTE
;
; EXECUTION PHASE
;
FOP_X1:
LD A,(FCP_CMD)
CP CFD_READ
JR Z,FXR_READ
CP CFD_WRITE
JR Z,FXR_WRITE
CP CFD_READID
JR Z,FXR_NULL
JP FOP_RES
;;
;; EXECUTION ROUTINES
;;
;
; NULL EXECUTION, NO DATA TO READ/WRITE (USED BY SPECIFY, READID, ETC.)
;
; DO NOTHING, BUT WAIT FOR EXEC B IT TO CLEAR FDC READY.
; LOOP NEEDS TO ALLOW FOR 2 FULL ROTATIONS OF THE DISK
; WHICH IS 400ms AT 300RPM
;
FXR_NULL:
LD BC,$7000 ; LOOP COUNTER, $7000 * 16us = ~485ms
FXR_NULL1:
CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR
IN A,(FDC_MSR) ; GET MSR
AND 0E0H ; ISOLATE RQM/DIO/NDM
CP 0C0H ; WE WANT RQM=1,DIO=1,NDM=0 (READY TO READ A BYTE W/ EXEC INACTIVE)
JP Z,FOP_RES ; EXEC DONE, EXIT CLEAN W/O PULSING TC
;JP Z,FXR_NULL2 ; *DEBUG*
DEC BC ; DECREMENT COUNTER (16 BIT)
LD A,B ; CHECK FOR ZERO
OR C ; "
JR NZ,FXR_NULL1 ; NOT ZERO YET, KEEP CHECKING
JP FOP_TOEXEC ; TIMEOUT EXIT
;FXR_NULL2: ; *DEBUG*
; CALL PRTHEXBYTE
; CALL PRTHEXWORD
; JP FOP_RES
;
; READ DATA
;
FXR_READ:
HB_DI ; TIME CRITICAL , INTERRUPTS WILL CAUSE I/O ERRS
LD HL,(FD_DSKBUF) ; POINT TO SECTOR BUFFER START
LD DE,(FCD_SECSZ)
; TIMEOUT COUNTER IS CPU MHZ / 4 (MAKING SURE IT IS AT LEAST 1)
; LD A,(CPUMHZ + 3) / 4
LD A,(CB_CPUMHZ) ; GET CPU SPEED IN MHZ
ADD A,3 ; ROUND UP
SRL A ; SHIFT RIGHT TWICE
;SRL A ; ... TO DIVIDE BY 4
;INC A ; MAKE SURE RESULT IS AT LEAST 1
LD (FCD_TO),A ; INIT TIMEOUT COUNTER
FXRR1 LD C,0 ; OUTER LOOP TIMEOUT COUNTER
FXRR2: LD B,0 ; SETUP FOR 256 ITERATIONS
FXRR3: IN A,(FDC_MSR) ; GET MSR
CP 0F0H ; WE WANT RQM=1,DIO=1,NDM=1,BUSY=1 (READY TO RECEIVE A BYTE W/ EXEC ACTIVE)
JR Z,FXRR4 ; GOT IT, DO BYTE READ
DJNZ FXRR3 ; NOT READY, LOOP IF COUNTER NOT ZERO
JR FXRR5 ; COUNTER ZERO, GO TO OUTER LOOP LOGIC
FXRR4: IN A,(FDC_DATA) ; GET PENDING BYTE
LD (HL),A ; STORE IT IN BUFFER
INC HL ; INCREMENT THE BUFFER POINTER
DEC DE ; DECREMENT BYTE COUNT
LD A,D
OR E
JR NZ,FXRR2 ; IF NOT ZERO, REPEAT LOOP
JR FXR_END ; CLEAN EXIT
FXRR5: ; OUTER LOOP, REALLY ONLY HAPPENS WHEN WAITING FOR FIRST BYTE OR ABORTED
AND 0E0H ; ISOLATE RQM/DIO/NDM
CP 0C0H ; IF RQM=1, DIO=1, NDM=0 (EXECUTION ABORTED)
JR Z,FXR_ABORT ; BAIL OUT TO ERR ROUTINE, FIX: GO TO SPECIFIC ROUTINE FOR THIS???
DEC C
JR NZ,FXRR2 ; IF NOT ZERO, LOOP SOME MORE
LD A,(FCD_TO)
DEC A
LD (FCD_TO),A
JR NZ,FXRR1
JR FXR_TO ; OTHERWISE, TIMEOUT ERROR
;
; WRITE DATA
;
FXR_WRITE:
HB_DI ; TIME CRITICAL , INTERRUPTS WILL CAUSE I/O ERRS
LD HL,(FD_DSKBUF) ; POINT TO SECTOR BUFFER START
LD DE,(FCD_SECSZ)
; TIMEOUT COUNTER IS CPU MHZ / 4 (MAKING SURE IT IS AT LEAST 1)
; LD A,(CPUMHZ + 3) / 4
LD A,(CB_CPUMHZ) ; GET CPU SPEED IN MHZ
ADD A,3 ; ROUND UP
SRL A ; SHIFT RIGHT TWICE
;SRL A ; ... TO DIVIDE BY 4
;INC A ; MAKE SURE RESULT IS AT LEAST 1
LD (FCD_TO),A
FXRW1 LD C,0 ; OUTER LOOP TIMEOUT COUNTER
FXRW2: LD B,0 ; SETUP FOR 256 ITERATIONS
FXRW3: IN A,(FDC_MSR) ; GET MSR
CP 0B0H ; WE WANT RQM=1,DIO=0,NDM=1,BUSY=1 (READY TO SEND A BYTE W/ EXEC ACTIVE)
JR Z,FXRW4 ; GOT IT, DO BYTE WRITE
DJNZ FXRW3 ; NOT READY, LOOP IF COUNTER NOT ZERO
JR FXRW5 ; COUNTER ZERO, GO TO OUTER LOOP LOGIC
FXRW4: LD A,(HL) ; GET NEXT BYTE TO WRITE
OUT (FDC_DATA),A ; WRITE IT
INC HL ; INCREMENT THE BUFFER POINTER
DEC DE ; DECREMENT LOOP COUNTER
LD A,D
OR E
JR NZ,FXRW2 ; IF NOT ZERO, REPEAT LOOP
JR FXR_END ; CLEAN EXIT
FXRW5: ; OUTER LOOP, REALLY ONLY HAPPENS WHEN WAITING FOR FIRST BYTE OR ABORTED
AND 0E0H ; ISOLATE RQM/DIO/NDM
CP 0C0H ; IF RQM=1, DIO=1, NDM=0 (EXECUTION ABORTED)
JR Z,FXR_ABORT ; BAIL OUT TO ERR ROUTINE
DEC C
JR NZ,FXRW2 ; IF NOT ZERO, LOOP SOME MORE
LD A,(FCD_TO)
DEC A
LD (FCD_TO),A
JR NZ,FXRW1
JR FXR_TO ; OTHERWISE, TIMEOUT ERROR
;
; COMMON COMPLETION CODE FOR ALL EXECUTION ROUTINES
;
FXR_TO: ; TIMEOUT
HB_EI ; INTERRUPTS OK AGAIN
JP FOP_TOEXEC ; EXEC TIMEOUT
;
FXR_ABORT: ; EXECUTION ABORTED
HB_EI ; INTERRUPTS OK AGAIN
JR FOP_RES ; GET RSEULTS, NO NEED TO PULSE TC
;
FXR_END: ; EXECUTION COMPLETED NORMALLY
CALL FC_PULSETC ; PULSE TC TO END EXECUTION
HB_EI ; INTERRUPTS OK AGAIN
JR FOP_RES ; GET RSEULTS
;
; RESULTS PHASE
;
FOP_RES:
LD HL,FRB ; POINT TO RECEIVE BUFFER
FOP_RES0:
LD BC,$7000 ; LOOP COUNTER, $7000 * 16us = ~458ms
FOP_RES1:
CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR
IN A,(FDC_MSR) ; READ MAIN STATUS REGISTER
AND 0F0H ; ISOLATE RQM/DIO/EXEC/BUSY
CP 0D0H ; LOOKING FOR RQM/DIO/BUSY
JR Z,FOP_RES2 ; GOOD, GO TO RECEIVE BYTE
CP 080H ; CHECK FOR RQM=1, DIO=0 (NOTHING LEFT)
JR Z,FOP_EVAL ; IF NOTHING LEFT, ALL DONE, GO TO EOD/EXIT
DEC BC ; DECREMENT COUNTER (16 BIT)
LD A,B ; CHECK FOR ZERO
OR C ; ""
JR NZ,FOP_RES1 ; LOOP TILL COUNTER EXHAUSTED
;IN A,(FDC_MSR) ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
JR FOP_TOGETRES ; OTHERWISE TIMEOUT ERROR
FOP_RES2: ; PROCESS NEXT PENDING BYTE
LD A,FRB_SIZ ; GET BUF SIZE
CP D ; REACHED MAX?
JR Z,FOP_BUFMAX ; HANDLE BUF MAX/EXIT
IN A,(FDC_DATA) ; GET THE BYTE
LD (HL),A ; SAVE VALUE
INC HL ; INCREMENT BUF POS
INC D ; INCREMENT BYTES RECEIVED
PUSH HL
LD HL,FRB_LEN ; POINT TO BUFFER LENGTH
LD (HL),D ; UPDATE NUMBER OF BYTES RECEIVED
POP HL
JR FOP_RES0 ; CONTINUE READ LOOP
;
; EXIT POINTS
;
FOP_NOTIMPL:
LD A,FRC_NOTIMPL
JR FOP_ERR
FOP_CMDERR:
LD A,FRC_CMDERR
JR FOP_ERR
FOP_ERROR:
LD A,FRC_ERROR
JR FOP_ERR
FOP_ABORT:
LD A,FRC_ABORT
JR FOP_ERR
FOP_BUFMAX:
LD A,FRC_BUFMAX
JR FOP_ERR
FOP_TOFDCRDY:
LD A,FRC_TOFDCRDY
JR FOP_ERR
FOP_TOSNDCMD:
LD A,FRC_TOSNDCMD
JR FOP_ERR
FOP_TOGETRES:
LD A,FRC_TOGETRES
JR FOP_ERR
FOP_TOEXEC:
LD A,FRC_TOEXEC
JR FOP_ERR
FOP_ERR:
LD (FST_RC),A
FOP_EVAL:
LD A,(FCP_CMD)
; DRVSTAT IS WEIRD, HAS ONLY ST3, NOTHING TO EVAL
CP CFD_DRVSTAT
JR Z,FOP_EXIT
; DO WE HAVE ST0?
LD A,(FRB_LEN)
CP 1
JP M,FOP_EXIT
FOP_EVALST0:
LD A,(FRB_ST0)
AND 11000000B
CP 01000000B ; ABTERM
JR Z,FOP_ABTERM
CP 10000000B ; INVCMD
JR Z,FOP_INVCMD
CP 11000000B ; DSKCHG
JR Z,FOP_DSKCHG
JR FOP_EXIT
FOP_ABTERM:
; SENSEINT DOES NOT USE ST1
LD A,(FCP_CMD)
CP CFD_SENSEINT
JR Z,FOP_ABTERM1
; DO WE HAVE ST1?
LD A,(FRB_LEN)
CP 2
JP M,FOP_ABTERM1
JR FOP_EVALST1
FOP_ABTERM1: ; NO FURTHER DATA, SET FST TO ABTERM
LD C,FRC_ABTERM
JR FOP_SETFST
FOP_INVCMD:
LD C,FRC_INVCMD
JR FOP_SETFST
FOP_DSKCHG:
LD C,FRC_DSKCHG
JR FOP_SETFST
FOP_EVALST1:
LD A,(FRB_ST1)
LD C,FRC_ENDCYL
BIT 7,A
JR NZ,FOP_SETFST
LD C,FRC_DATAERR
BIT 5,A
JR NZ,FOP_SETFST
LD C,FRC_OVERRUN
BIT 4,A
JR NZ,FOP_SETFST
LD C,FRC_NODATA
BIT 2,A
JR NZ,FOP_SETFST
LD C,FRC_NOTWRIT
BIT 1,A
JR NZ,FOP_SETFST
LD C,FRC_MISADR
BIT 0,A
JR NZ,FOP_SETFST
JR FOP_EXIT
FOP_SETFST:
LD A,C
LD (FST_RC),A
FOP_EXIT:
#IF (FDTRACE >= 2)
CALL FC_PRTRESULTS
#ENDIF
JP FD_RETRC
#IF (FDTRACE > 0)
;
;===============================================================================
; COMMAND PROCESSING STATUS DISPLAY
;===============================================================================
;
; PRINT STATUS
;
FC_PRTFST:
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD A,(FST_RC) ; A GETS FST_RC
LD B,FSST_COUNT ; B GETS TABLE ENTRY COUNT
LD HL,FSST
LD DE,FSST_ENTSIZ ; TABLE ENTRY LENGTH
FC_PRTFST0: ; START OF LOOP
LD C,(HL)
CP C
JR Z,FC_PRTFST1 ; FOUND CODE
ADD HL,DE ; POINT TO NEXT ENTRY
DJNZ FC_PRTFST0 ; CHECK NEXT ENTRY TILL COUNT IS ZERO
; NO MATCHING ENTRY, PRINT THE HEX VALUE
CALL PC_SPACE
CALL PC_LBKT
CALL PRTHEXBYTE
CALL PC_RBKT
JR FC_PRTFSTX
FC_PRTFST1: ; ENTRY FOUND, PRINT IT
CALL PC_SPACE
INC HL
LD E,(HL)
INC HL
LD D,(HL)
CALL PC_LBKT
CALL WRITESTR
CALL PC_RBKT
FC_PRTFSTX:
POP HL
POP DE
POP BC
POP AF
RET
;
; PRINT COMMAND
;
FC_PRTCMD:
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD A,(FCP_CMD) ; A GETS THE COMMAND CODE
LD B,FCT_COUNT ; B GETS TABLE ENTRY COUNT
LD HL,FCT
LD DE,FCT_ENTSIZ ; TABLE ENTRY LENGTH
FCPC_LOOP: ; START OF LOOP
LD C,(HL)
CP C
JR Z,FCPC_MATCH ; FOUND CODE
ADD HL,DE ; POINT TO NEXT ENTRY
DJNZ FCPC_LOOP ; CHECK NEXT ENTRY TILL COUNT IS ZERO
; NO MATCHING ENTRY, PRINT THE HEX VALUE
CALL PC_SPACE
CALL PC_LBKT
CALL PRTHEXBYTE
CALL PC_RBKT
JR FCPC_EXIT
FCPC_MATCH: ; ENTRY FOUND, PRINT IT
INC HL
LD E,(HL)
INC HL
LD D,(HL)
CALL WRITESTR
FCPC_EXIT:
POP HL
POP DE
POP BC
POP AF
RET
;
; PRINT RESULTS
;
FC_PRTRESULTS:
; IF TRACE IS SET, FORCE PRINT RESULTS
LD A,(FCD_TRACE)
OR A
RET Z ; IF TRACE = 0, BE SILENT!
CP 3 ; IS TRACE >= 3 ?
JR NC,FCPR2 ; YES, SO FORCE PRINT EVERYTHING!
; IF RC=OK, GET OUT, NOTHING TO PRINT
LD A,(FST_RC)
CP FRC_OK
RET Z
; SPECIAL CASE, DON'T PRINT IF SENSEINT & INVCMD/DSK CHG/ABTERM
LD A,(FCP_CMD)
CP CFD_SENSEINT
JR NZ,FCPR2
LD A,(FST_RC)
CP FRC_INVCMD
JR Z,FCPR_EXIT
CP FRC_DSKCHG
JR Z,FCPR_EXIT
CP FRC_ABTERM
JR Z,FCPR_EXIT
JR FCPR_EXIT
FCPR2:
CALL NEWLINE
LD DE,FDSTR_FD
CALL WRITESTR
CALL PC_COLON
CALL PC_SPACE
CALL FC_PRTCMD
LD A,(FCP_LEN)
LD DE,FCP_BUF
CALL PRTHEXBUF
LD DE,FDSTR_ARROW
CALL WRITESTR
LD A,(FRB_LEN)
LD DE,FRB
CALL PRTHEXBUF
LD A,(FST_RC)
CALL FC_PRTFST
FCPR_EXIT:
RET
;
; STRING CONSTANTS
;
FDSTR_ARROW .TEXT " -->$"
FDSTR_NORESP .TEXT "DRIVE NOT RESPONDING$"
FDSTR_FD .TEXT "FD$"
#IF (FDTRACE >= 3)
FDSTR_MOTON .TEXT "\r\nMOTOR ON$"
FDSTR_MOTOFF .TEXT "\r\nMOTOR OFF$"
FDSTR_MOTDELAY .TEXT "\r\nMOTOR DELAY$"
FDSTR_DOR .TEXT "DOR$"
FDSTR_DCR .TEXT "DCR$"
FDSTR_RESETFDC .TEXT "\r\nRESET FDC$"
FDSTR_SELECT .TEXT "\r\nSELECT: $"
#ENDIF ; (FDTRACE >= 3)
#ENDIF ; (FDTRACE > 0)
;
;
;==================================================================================================
; FLOPPY DISK DRIVER - DATA
;==================================================================================================
;
; FDC COMMAND PHASE
;
FCP_CMD .DB 000H
FCP_LEN .DB 00H
FCP_BUF:
FCP_CMDX .DB 0
FCP_HDSDS .DB 0
FCP_C .DB 0
FCP_H .DB 0
FCP_R .DB 0
FCP_N .DB 0
FCP_EOT .DB 0
FCP_GPL .DB 0
FCP_DTL .DB 0
FCP_BUFSIZ .EQU $-FCP_BUF
;
; FDC STATUS
;
FST_RC .DB 00H
FST_DOR .DB 00H
FST_DCR .DB 00H
;
; FDC RESULTS BUFFER
;
FRB_LEN .DB 00H
FRB
FRB_ST0
FRB_ST3 .DB 0
FRB_ST1
FRB_PCN .DB 0
FRB_ST2 .DB 0
FRB_C .DB 0
FRB_H .DB 0
FRB_R .DB 0
FRB_N .DB 0
FRB_SIZ .EQU $-FRB
;
; FDC COMMAND DATA
;
FCD: ; FLOPPY CONFIGURATION DATA (PUBLIC) MANAGED AS A "BLOCK"
FCD_NUMCYL .DB 000H ; NUMBER OF CYLINDERS
FCD_NUMHD .DB 000H ; NUMBER OF HEADS
FCD_NUMSEC .DB 000H ; NUMBER OF SECTORS
FCD_SOT .DB 000H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1)
FCD_EOT ; END OF TRACK SECTOR (SAME AS SC SINCE SOT ALWAYS 1)
FCD_SC .DB 000H ; SECTOR COUNT
FCD_SECSZ .DW 000H ; SECTOR SIZE IN BYTES
FCD_GPL .DB 000H ; GAP LENGTH (R/W)
FCD_GPLF .DB 000H ; GAP LENGTH (FORMAT)
FCD_SRTHUT .DB 000H ; STEP RATE, IBM PS/2 CALLS FOR 3ms, 0DH = 3ms SRT, HEAD UNLOAD TIME
FCD_HLTND .DB 000H ; HEAD LOAD TIME, IBM PS/2 CALLS FOR 15ms 08H = 16ms HUT
FCD_DOR .DB 000H ; DOR VALUE
FCD_DCR .DB 000H ; DCR VALUE
FCD_LEN .EQU $ - FCD
; DYNAMICALLY MANAGED (PUBLIC)
FCD_DS .DB 001H ; DRIVE SELECT (UNIT NUMBER 0-3)
FCD_C .DB 000H ; CYLINDER
FCD_H .DB 000H ; HEAD
FCD_R .DB 001H ; RECORD
FCD_D .DB 0E5H ; DATA FILL BYTE
; STATUS MANAGEMENT
FCD_DOP .DB 0FFH ; CURRENT OPERATION (SEE DOP_...)
FCD_IDLECNT .DW 0 ; IDLE COUNT
FCD_TRACE .DB 0 ; TRACE LEVEL
FCD_TO .DB 0 ; TIMEOUT COUNTDOWN TIMER
FCD_FDCRDY .DB 0 ; FALSE MEANS FDC RESET NEEDED
;
; GENERAL WORKING STORAGE
;
FD_DSKBUF .DW 0
FD_CURGEOM .EQU $ ; TWO BYTES BELOW
FD_CURSPT .DB 0 ; CURRENT SECTORS PER TRACK
FD_CURHDS .DB 0 ; CURRENT HEADS