;=============================================================================== ; FDU - FLOPPY DISK UTILITY PROGRAM FOR RETROBREW COMPUTERS ;=============================================================================== ; ; AUTHOR: WAYNE WARTHEN (DERIVED FROM FDCMON BY ANDREW LYNCH & DAN WERNER) ;_______________________________________________________________________________ ; ; CHANGELOG: ; 2011-08-05: v1.0 INITIAL RELEASE ; 2011-08-25: v1.1 SUPPORT ZETA ; SUPPORT INT/WAIT ; 2011-08-28: v1.2 FIX ZETA RESET LOGIC (from Sergey) ; FIX ZETA MOTOR DISPLAY (from Sergey) ; NEW ZETA DISK CHANGE DISPLAY ; 2011-09-01: V1.3 ADDED CONFIGURABLE CPU FREQUENCY FOR DELAYS ; 2011-09-05: V1.4 USE CPU FREQUENCY FOR POLLING TIMEOUT ; 2011-09-19: V1.5 IMPROVED USE OF FDC RESET ON ZETA ; ADDED FDC RESET TO FDC CONTROL MENU ; 2011-09-24: V1.5a MINOR CORRECTION TO POLLING ROUTINE TO AVOID OVERRUN ; 2011-10-07: V2.0 PRELIMINARY SUPPORT FOR DUAL IDE ; 2011-10-13: V2.1 PRELIMINARY SUPPORT FOR N8 ; 2011-10-20: V2.2 INCLUDE BUSY & NDMA BITS IN RESULTS BYTE READY CHECKING ; 2011-10-22: V2.3 ADDED VERIFY FUNCTION ; 2011-11-25: V2.4 ADDED SUPPORT FOR DISKIO V3 ; 2012-01-09: V2.5 WARM START CP/M AT TERMINATION TO LOGOUT ALL DRIVES ; 2012-01-09: V2.6 EXPERIMENTAL SUPPORT FOR 5 1/4" DRIVES ; 2012-04-05: V2.7 SUPPORT FOR 8" DRIVES ; 2012-04-06: V2.7a FIXED MEDIA SELECTION MENU (DUPLICATE ENTRIES) ; 2012-04-08: V2.7b HANDLE DENSITY SELECT PROPERLY ; 2012-05-22: V2.8 ADDED NEW MEDIA DEFINITIONS (5.25", 320K) ; 2012-06-01: V2.9 ADDED INTERLEAVE CAPABILITY IN FORMAT COMMAND ; 2012-06-05: V3.0 DOCUMENTATION CLEANUP ; 2012-07-01: V3.1 MODIFY HLT FOR 8" MEDIA (50ms PER YD-180 SPEC) ; 2013-06-17: V3.2 CLEANED UP THE SRT, HLT, AND HUT VALUES ; 2015-02-10: V3.3 ADDED ZETA SBC V2 SUPPORT (SERGEY KISELEV) ; 2015-03-25: V4.0 RENAMED APP FDTST --> FD ; 2017-09-02: V5.0 RENAMED APP TO FDU (FLOPPY DISK UTILITY) ; DYNAMIC FDC SELECTION AT STARTUP ; DYNAMIC CPU SPEED ADJUSTMENT ; ;_______________________________________________________________________________ ; ; BUILDING: ; CAN BE BUILT WITH TASM LIKE THIS: ; TASM -t80 -b -fFF FD.ASM FD.COM FD.LST ; ; TODO: ; 1) CURRENT TRACK IS UPDATED EVEN IF SEEK FAILS! (DEFER, RECOVERS AUTOMATICALLY) ; 2) CLEAN UP UTILITY ROUTINES (DEFER, NOT IMPORTANT) ; 3) TIGHTEN UP THE TIMING LOOPS (DEFER, LOW PRIORITY, WORKING FINE AS IS) ; 4) MOVE STX EVALUATION TO DIO LEVEL? (NOT SURE..., LOW PRIORITY) ; 5) ALLOW TO ABORT AT PROMPTS ; 6) ALLOW TO ABORT OPERATION IN PROGRESS ; 7) MOVE CONTINUE PROMPT TO A SEPARATE ROUTINE ; ;_______________________________________________________________________________ ; ; CPUFREQ .EQU 20 ; IN MHZ, USED TO COMPUTE DELAY FACTORS ; FDC_DIO .EQU 0 FDC_DIO3 .EQU 1 FDC_ZETA .EQU 2 FDC_ZETA2 .EQU 3 FDC_DIDE .EQU 4 FDC_N8 .EQU 5 ; _DIO .EQU 1 << FDC_DIO _DIO3 .EQU 1 << FDC_DIO3 _ZETA .EQU 1 << FDC_ZETA _ZETA2 .EQU 1 << FDC_ZETA2 _DIDE .EQU 1 << FDC_DIDE _N8 .EQU 1 << FDC_N8 ; FALSE .EQU 0 TRUE .EQU ~FALSE ; ;=============================================================================== ; MAIN PROGRAM PROCEDURE ;=============================================================================== ; .ORG 00100H ; SAVE PREVIOUS STACK POINTER, AND SWITCH TO OUR STACK LD (STACKSAV),SP LD SP,STACK ; GENERAL INITIALIZATION (BIOS & SPEED DETECTION) CALL INIT JR NZ,EXIT ; SELECT FD CONTROLLER CALL NEWLINE CALL FDCSEL JR NZ,EXIT ; SETUP FOR DEFAULT MEDIA AND RUN THE MAIN MENU CALL DIO_SETMEDIA LD HL,MM_INFO CALL RUNMENU EXIT: LD C,00H ; CP/M SYSTEM RESET (WARM START) CALL 0005H ; RETURN TO CP/M ; CLEAN UP AND RETURN TO OS CALL NEWLINE LD SP,(STACKSAV) RET HALT ; SHOULD NEVER GET HERE ; ;=============================================================================== ; FD CONTROLLER SELECTION ;=============================================================================== ; INIT: ; DISPLAY PROGRAM STARTUP BANNER CALL NEWLINE LD DE,STR_BANNER CALL WRITESTR ; UNA UBIOS DETECTION... LD A,($FFFD) ; FIXED LOCATION OF UNA API VECTOR CP $C3 ; JP INSTRUCTION? JR NZ,INIT1 ; IF NOT, NOT UNA LD HL,($FFFE) ; GET JP ADDRESS LD A,(HL) ; GET BYTE AT TARGET ADDRESS CP $FD ; FIRST BYTE OF UNA PUSH IX INSTRUCTION JR NZ,INIT1 ; IF NOT, NOT UNA INC HL ; POINT TO NEXT BYTE LD A,(HL) ; GET NEXT BYTE CP $E5 ; SECOND BYTE OF UNA PUSH IX INSTRUCTION JR NZ,INIT1 ; IF NOT, NOT UNA LD DE,STR_UBIOS ; POINT TO UBIOS TAG CALL WRITESTR ; AND DISPLAY IT ; GET CPU SPEED FROM UBIOS LD C,0F8H ; UNA BIOS GET PHI FUNCTION RST 08 ; RETURNS SPEED IN HZ IN DE:HL LD B,4 ; DIVIDE MHZ IN DE:HL BY 100000H INIT0: SRL D ; ... TO GET APPROX CPU SPEED IN RR E ; ...MHZ. THROW AWAY HL, AND DJNZ INIT0 ; ...RIGHT SHIFT DE BY 4. INC E ; FIX UP FOR VALUE TRUNCATION LD A,E ; PUT IN A LD (CPUSPD),A ; SAVE IT JR INIT3 ; AND DONE INIT1: ; ROMWBW HBIOS DETECTION LD HL,(0FFFEH) ; HL := ADR OR ROMWBW HBIOS IDENT LD A,(HL) ; GET FIRST BYTE OF ROMWBW MARKER CP 'W' ; MATCH? JP NZ,INIT2 ; ABORT WITH INVALID CONFIG BLOCK INC HL ; NEXT BYTE (MARKER BYTE 2) LD A,(HL) ; LOAD IT CP ~'W' ; MATCH? JP NZ,INIT2 ; ABORT WITH INVALID CONFIG BLOCK LD DE,STR_HBIOS ; POINT TO HBIOS TAG CALL WRITESTR ; AND DISPLAY IT ; GET CPU SPEED FROM HBIOS LD B,0F8H ; HBIOS SYSGET FUNCTION 0XF8 LD C,0F0H ; CPUINFO SUBFUNCTION 0XF0 RST 08 ; DO IT, L := CPU SPEED IN MHZ LD A,L ; MOVE IT TO A LD (CPUSPD),A ; SAVE IT JR INIT3 ; AND DONE INIT2: ; NO KNOWN BIOS DETECTED, BAIL OUT W/ ERROR LD DE,STR_BIOERR CALL WRITESTR OR 0FFH RET INIT3: ; COMPUTE CPU SCALER FOR DELAY LOOPS LD A,(CPUSPD) CP 3 ; TEST FOR <= 2 (SPECIAL HANDLING) JR C,INIT4 ; IF <= 2, SPECIAL PROCESSING SUB 2 ; ADJUST AS REQUIRED BY DELAY FUNCTIONS JR INIT5 ; AND CONTINUE INIT4: LD A,1 ; USE THE MIN VALUE OF 1 INIT5: LD (CPUSCL),A ; UPDATE CPU SCALER VALUE ; REMAINDER OF BANNER CALL NEWLINE LD DE,STR_BANNER2 CALL WRITESTR ; INITIALIZATION DONE XOR A RET STR_BANNER .DB "Floppy Disk Utility (FDU) v5.0, 02-Sep-2017$" STR_BANNER2 .DB "Copyright (C) 2017, Wayne Warthen, GNU GPL v3","$" STR_HBIOS .DB " [HBIOS]$" STR_UBIOS .DB " [UBIOS]$" ; STR_BIOERR .DB "\r\n\r\n*** Unknown BIOS ***\r\n$" ; CPUSPD .DB 20 ; DEFAULT TO SAFE (HIGH) VALUE CPUSCL .DB 0 ; CPU SPEED DELAY SCALER (COMPUTED) ; ;=============================================================================== ; FD CONTROLLER SELECTION ;=============================================================================== ; FDCSEL: ; PROMPT LD DE,FSS_MENU CALL WRITESTR ; FDCSEL1: CALL GETKEY SUB '0' ; ASCII -> BINARY CP FDCCNT + 1 ; TOO HIGH? JR NC,FDCSEL1 ; IF SO, TRY AGAIN ; OR A ; SET FLAGS JR NZ,FDCSEL2 ; NOT ZERO, KEEP GOING OR 0FFH ; SET NZ FOR EXIT REQUEST RET ; AND RETURN ; FDCSEL2: ; SAVE SELECTED FDC IDENTIFIER DEC A ; CONVERT TO ZERO-BASED FDC ID LD (FDCID),A ; RECORD THE FDC ID PUSH AF ; SAVE IT ; ; CREATE AND SAVE A BIT MAPPED VALUE INC A ; PREPARE LOOP COUNT LD B,A ; AND PUT IN B XOR A ; START WITH ALL BITS OFF SCF ; ... AND CF SET FDCSEL3: RLA ; ROTATE BIT TO NEXT POSITION DJNZ FDCSEL3 ; AND CONTINUE TILL DONE LD (FDCBM),A ; SAVE BITMAP VALUE ; POP AF ; RESTORE FDC ID RLCA ; TIMES 4 RLCA ; ... FOR 4 BYTE ENTRIES LD HL,FDCTBL ; POINT TO FDC INSTANCE TABLE CALL ADDHLA ; OFFSET TO DESRIED ENTRY, A TRASHED LD E,(HL) ; LOAD LABEL PTR INTO DE INC HL ; ... LD D,(HL) ; ... LD (FDCLBL),DE ; SAVE LABEL PTR INC HL ; BUMP TO CFG POINTER LD E,(HL) ; LOAD CFG PTR INTO DE INC HL ; ... LD D,(HL) ; ... LD (FDCCFG),DE ; SAVE CFG PTR LD IY,(FDCCFG) ; AND INIT A WORKING COPY ; LD DE,(FDCLBL) ; GET LABEL POINTER CALL WRITESTR ; AND DISPLAY IT ; XOR A ; SIGNAL SUCCESS RET ; DONE ; ; TABLE OF FDC CONTROLLERS ; FDCTBL: ; LABEL CONFIG DATA ; ----- ----------- .DW STR_DIO, CFG_DIO .DW STR_DIO3, CFG_DIO3 .DW STR_ZETA, CFG_ZETA .DW STR_ZETA2, CFG_ZETA2 .DW STR_DIDE, CFG_DIDE .DW STR_N8, CFG_N8 FDCCNT .EQU ($-FDCTBL)/4 ; FD CONTROLLER COUNT ; ; FDC LABEL STRINGS ; STR_DIO .TEXT "DISKIO$" STR_DIO3 .TEXT "DISKIO3$" STR_ZETA .TEXT "ZETA$" STR_ZETA2 .TEXT "ZETA2$" STR_DIDE .TEXT "DUAL-IDE$" STR_N8 .TEXT "N8$" ; ; FDC CONFIGURATION BLOCKS ; CFG_MSR .EQU 0 CFG_DATA .EQU 1 CFG_DIR .EQU 2 CFG_DOR .EQU 3 CFG_DCR .EQU 4 CFG_DACK .EQU 5 CFG_TC .EQU 6 CFG_DMA .EQU 7 ; CFG_DIO: CFG_DIO3: CFG_ZETA: .DB 036H ; FDC MAIN STATUS REGISTER .DB 037H ; FDC DATA PORT .DB 038H ; DATA INPUT REGISTER .DB 03AH ; DIGITAL OUTPUT REGISTER (LATCH) .DB 0FFH ; DCR .DB 0FFH ; DACK .DB 0FFH ; TERMINAL COUNT (W/ DACK) .DB 03CH ; PSEUDO DMA DATA PORT ; CFG_ZETA2: .DB 030H ; FDC MAIN STATUS REGISTER .DB 031H ; FDC DATA PORT .DB 0FFH ; DATA INPUT REGISTER .DB 038H ; DIGITAL OUTPUT REGISTER .DB 028H ; CONFIGURATION CONTROL REGISTER .DB 0FFH ; DACK .DB 038H ; TERMINAL COUNT (W/ DACK) .DB 0FFH ; NOT USED BY ZETA SBC V2 ; CFG_DIDE: .DB 02AH ; FDC MAIN STATUS REGISTER .DB 02BH ; FDC DATA PORT .DB 0FFH ; DATA INPUT REGISTER .DB 02CH ; DOR .DB 02DH ; DCR .DB 03CH ; DACK .DB 03DH ; TERMINAL COUNT (W/ DACK) .DB 0FFH ; NOT USED BY DIDE ; CFG_N8: .DB 08CH ; FDC MAIN STATUS REGISTER .DB 08DH ; FDC DATA PORT .DB 0FFH ; DATA INPUT REGISTER .DB 092H ; DOR .DB 091H ; DCR .DB 090H ; DACK .DB 093H ; TERMINAL COUNT (W/ DACK) .DB 0FFH ; NOT USED BY N8 ; FDCID .DB 0 ; FDC IDENTIFIER (0 INDEXED) FDCBM .DB 0 ; FDC ID BITMAP FDCLBL .DW 0 ; POINTER TO ACTIVE FDC LABEL STRING FDCCFG .DW 0 ; POINTER TO ACTIVE CFG DATA ; FSS_MENU: .TEXT "\r\n" .TEXT "SELECT FLOPPY DISK CONTROLLER:\r\n" .TEXT " (0) Exit\r\n" .TEXT " (1) Disk IO ECB Board\r\n" .TEXT " (2) Disk IO 3 ECB Board\r\n" .TEXT " (3) Zeta SBC Onboard FDC\r\n" .TEXT " (4) Zeta 2 SBC Onboard FDC\r\n" .TEXT " (5) Dual IDE ECB Board\r\n" .TEXT " (6) N8 Onboard FDC\r\n" .TEXT "=== OPTION ===> $\r\n" ; ;=============================================================================== ; MAIN MENU ;=============================================================================== ; ; MAIN MENU DATA ; MM_TABLE .DB 'S' \ .DW MMS_SETUP, MM_SETUP MM_ENTSIZ .EQU $ - MM_TABLE .DB 'R' \ .DW MMS_READ, MM_READ .DB 'W' \ .DW MMS_WRITE, MM_WRITE .DB 'F' \ .DW MMS_FORMAT, MM_FORMAT .DB 'V' \ .DW MMS_VERIFY, MM_VERIFY .DB 'I' \ .DW MMS_INITBUF, MM_INITBUF .DB 'D' \ .DW MMS_DUMPBUF, MM_DUMPBUF .DB 'C' \ .DW MMS_FDCMENU, MM_FDCMENU .DB 'X' \ .DW MMS_EXIT, 0000H MM_COUNT .EQU (($ - MM_TABLE) / MM_ENTSIZ) ; # ENTRIES IN TABLE MM_INFO: .DW MM_DRAW .DW MM_TABLE .DB MM_ENTSIZ .DB MM_COUNT ; ; MAIN MENU DISPLAY STRINGS ; STR_MAINMENU: .TEXT "=======================<< FDU MAIN MENU >>======================\r\n" ; .TEXT "(S)ETUP: UNIT=XX MEDIA=XXXXXX MODE=XXXXXXXXXX TRACE=XX\r\n" .TEXT "(S)ETUP: UNIT=" MV_UNIT .TEXT "XX" .TEXT " MEDIA=" MV_MED .TEXT "XXXXXX" .TEXT " MODE=" MV_MODE .TEXT "XXXXXXXXXX" .TEXT " TRACE=" MV_TRC .TEXT "XX" .TEXT "\r\n" .TEXT "----------------------------------------------------------------\r\n" .TEXT "(R)EAD (W)RITE (F)ORMAT (V)ERIFY\r\n" .TEXT "(I)NIT BUFFER (D)UMP BUFFER FDC (C)MDS E(X)IT\r\n" .TEXT "=== OPTION ===> $\r\n" ; MMS_SETUP: .TEXT "SETUP$" MMS_READ: .TEXT "READ$" MMS_WRITE: .TEXT "WRITE$" MMS_FORMAT: .TEXT "FORMAT$" MMS_VERIFY: .TEXT "VERIFY$" MMS_INITBUF: .TEXT "INITIALIZE BUFFER$" MMS_DUMPBUF: .TEXT "DUMP BUFFER$" MMS_FDCMENU: .TEXT "FDC MENU$" MMS_EXIT: .TEXT "EXIT$" ; ; MAIN MENU DRAW PROCEDURE ; MM_DRAW: CALL NEWLINE CALL NEWLINE ; UPDATE FDC LABEL LD DE,STR_MAINMENU + 5 LD A,' ' LD (DE),A INC DE LD HL,(FDCLBL) CALL COPYSTR LD A,' ' LD (DE),A ; UPDATE UNIT LD DE,MV_UNIT LD A,(DCD_UNIT) CALL HEXSTRBYTE ; UPDATE MEDIA LD DE,(MDB_LABEL) LD HL,MV_MED CALL STRCPY ; UPDATE MODE LD A,(DCD_MD) RLA LD E,A LD D,0 LD HL,MDT ADD HL,DE LD E,(HL) INC HL LD D,(HL) EX DE,HL LD E,(HL) INC HL LD D,(HL) LD HL,MV_MODE CALL STRCPY ; UPDATE TRACE LD DE,MV_TRC LD A,(DCD_TRACE) CALL HEXSTRBYTE ; DISPLAY THE MENU LD DE,STR_MAINMENU CALL WRITESTR RET ; ; MAIN MENU FUNCTIONS ; MM_SETUP: CALL MM_GETSETUP CALL DIO_SETMEDIA RET MM_READ: LD A,DOP_READ LD (DCD_DOP),A CALL MM_GETTGT CALL MM_GETTGTPARMS CALL MMOP_PROC RET MM_WRITE: LD A,DOP_WRITE LD (DCD_DOP),A CALL MM_GETTGT CALL MM_GETTGTPARMS CALL MMOP_PROC RET MM_FORMAT: LD A,DOP_FORMAT LD (DCD_DOP),A CALL MM_GETTGT CALL MM_GETTGTPARMS CALL MM_GETINTRLV CALL MMOP_PROC RET MM_VERIFY: LD A,DOP_VERIFY LD (DCD_DOP),A CALL MM_GETTGT CALL MM_GETTGTPARMS CALL MM_SETVFYBUF CALL MMOP_PROC RET MM_INITBUF: CALL PC_SPACE LD DE,MMPS_IBOPT CALL WRITESTR MM_IBKEY: CALL GETKEYUC CP 'P' ; PATTERN JP Z,MM_IBPAT CP 'F' ; FILL JP Z,MM_IBFILL JP MM_IBKEY MM_IBPAT: LD DE,MMBS_PATTERN CALL PC_SPACE CALL WRITESTR CALL MM_GETPATTERN LD HL,BUFFER LD DE,BUFSIZ LD A,(DCD_PATTERN) LD B,A CALL PAT_BUFFER RET MM_IBFILL: LD DE,MMBS_FILL CALL WRITESTR CALL PC_SPACE CALL MM_GETFILL LD HL,BUFFER LD DE,BUFSIZ LD A,(DCD_FILL) LD B,A CALL FILL_BUFFER RET MM_DUMPBUF: LD HL,BUFFER ; SET ADDRESS TO DUMP LD DE,BUFFER ; SET END ADDRESS INC D ; INC D ; CALL DUMP_BUFFER ; DUMP BUFFER TO CONSOLE RET MM_FDCMENU: CALL DIO_INIT CALL FDCMENU CALL DIO_TERM RET ; ; MAIN MENU SUPPORT FUNCTIONS ; MM_GETSETUP: LD DE,MMP_UNIT LD HL,DCD_UNIT LD BC,0003H CALL GETHEXBYTERNG CALL MM_MEDIALIST LD DE,MMP_MEDIA LD HL,DCD_MT LD B,0 LD A,MIT_ENTCNT DEC A LD C,A CALL GETHEXBYTERNG MM_GETMODE: CALL MM_MODELIST LD DE,MMP_MODE LD HL,DCD_MD LD B,0 LD A,MDT_ENTCNT DEC A LD C,A CALL GETHEXBYTERNG ; VALIDATE MODE AGAINST CURRENT FDC INC A ; PREP VALUE FOR LOOP LD B,A ; PUT IN LOOP COUNTER XOR A ; CLEAR A SCF ; SET CF MM_GETMODE1: RLA ; ROTATE BIT ONE POSITION DJNZ MM_GETMODE1 ; UNTIL BIT SET FOR MODE SPECIFIED PUSH AF ; SAVE IT LD A,(FDCID) ; A := FDC ID LD HL,MD_MAP ; HL := START OF MODE MAP CALL ADDHLA ; OFFSET TO CORRECT ENTRY FOR FDC POP AF ; RECOVER MODE BIT VALUE AND (HL) ; MASK WITH MODE MAP OF FDC JR NZ,MM_TRACE ; NON-ZERO IS A MODE MATCH FOR FDC, CONTINUE LD DE,MM_MODEERR ; INVALID MODE FOR FDC ERROR MESSAGE CALL WRITESTR JR MM_GETMODE MM_TRACE: LD DE,MMP_TRACE LD HL,DCD_TRACE LD BC,0001H CALL GETHEXBYTERNG RET MM_GETFILL: LD DE,MMP_FILL LD HL,DCD_FILL CALL GETHEXBYTE RET MM_GETPATTERN: LD DE,MMP_PATTERN LD HL,DCD_PATTERN CALL GETHEXBYTE RET MM_GETTGT: CALL PC_SPACE LD A,(DCD_DOP) LD DE,MMPS_TGTF CP DOP_FORMAT JP Z,MM_GETTGT0 LD DE,MMPS_TGTRW MM_GETTGT0: CALL WRITESTR MM_GETTGT1: CALL GETKEYUC LD B,MMT_TRACK LD DE,MMTS_TRACK CP 'T' JP Z,MM_GETTGT3 LD B,MMT_DISK LD DE,MMTS_DISK CP 'D' JP Z,MM_GETTGT3 ; FORMAT CANNOT DO THE NEXT ONES (GETTGT2 CHECKS FOR THIS) LD B,MMT_SECTOR LD DE,MMTS_SECTOR CP 'S' JP Z,MM_GETTGT2 LD B,MMT_RANDOM LD DE,MMTS_RANDOM CP 'R' JP Z,MM_GETTGT2 JP MM_GETTGT1 MM_GETTGT2: ; PREVENT FORMAT FROM USING SECTOR OR RANDOM FUNCTIONS LD A,(DCD_DOP) CP DOP_FORMAT JP Z,MM_GETTGT1 MM_GETTGT3: LD A,B LD (MMD_TGT),A CALL WRITESTR RET MM_GETTGTPARMS: LD A,(MMD_TGT) CP MMT_DISK JP Z,MM_GETTGTPARMS1 CP MMT_RANDOM JP Z,MM_GETTGTPARMS1 CALL MM_GETTRACK CALL MM_GETHEAD LD A,(MMD_TGT) CP MMT_TRACK JP Z,MM_GETTGTPARMS1 CALL MM_GETSECTOR JP MM_GETTGTPARMSX MM_GETTGTPARMS1: LD A,1 ; FIX UP THE SECTOR VALUE TO BE 1 IF NOT SINGLE SECTOR MODE LD (DCD_SECTOR),A MM_GETTGTPARMSX: RET MM_GETTRACK: LD DE,MMP_TRACK LD HL,DCD_TRACK LD B,0 LD A,(MDB_NUMCYL) DEC A LD C,A CALL GETHEXBYTERNG RET MM_GETHEAD: LD DE,MMP_HEAD LD HL,DCD_HEAD LD B,0 LD A,(MDB_NUMHD) DEC A LD C,A CALL GETHEXBYTERNG RET MM_GETSECTOR: LD DE,MMP_SECTOR LD HL,DCD_SECTOR LD A,(MDB_SOT) LD B,A LD A,(MDB_EOT) LD C,A CALL GETHEXBYTERNG RET MM_GETINTRLV: LD DE,MMP_INTRLV LD HL,DCD_INTRLV LD B,1 LD A,(MDB_NUMSEC) LD C,A CALL GETHEXBYTERNG RET MM_MEDIALIST: LD HL,MIT LD B,MIT_ENTCNT LD C,0 MM_MEDIALISTLOOP: CALL NEWLINE LD A,C CALL PRTHEXBYTE CALL PC_COLON CALL PC_SPACE PUSH HL LD A,(HL) ; HL = ENTRY VALUE INC HL ; " LD H,(HL) ; " LD L,A ; " INC HL INC HL LD A,(HL) ; HL = ENTRY VALUE INC HL ; " LD D,(HL) ; " LD E,A ; " CALL WRITESTR POP HL INC HL INC HL INC C DJNZ MM_MEDIALISTLOOP RET MM_MODELIST: LD HL,MDT LD B,MDT_ENTCNT LD C,0 MM_MODELISTLOOP: CALL NEWLINE LD A,C CALL PRTHEXBYTE CALL PC_COLON CALL PC_SPACE PUSH HL LD A,(HL) ; HL = ENTRY VALUE INC HL ; " LD H,(HL) ; " LD L,A ; " INC HL INC HL LD A,(HL) ; HL = ENTRY VALUE INC HL ; " LD D,(HL) ; " LD E,A ; " CALL WRITESTR POP HL INC HL INC HL INC C DJNZ MM_MODELISTLOOP RET MM_SETVFYBUF: LD HL,BUFFER LD DE,VFYBUF LD BC,BUFSIZ LDIR RET ; ; MAIN MENU PROMPTS ; MMP_FILL .TEXT "FILL VALUE$" MMP_PATTERN .TEXT "PATTERN START VALUE$" MMP_UNIT .TEXT "UNIT$" MMP_TRACE .TEXT "TRACE LEVEL$" MMP_TRACK .TEXT "TRACK$" MMP_HEAD .TEXT "HEAD$" MMP_SECTOR .TEXT "SECTOR$" MMP_MEDIA .TEXT "MEDIA$" MMP_MODE .TEXT "MODE$" MMP_INTRLV .TEXT "INTERLEAVE$" ; ; MAIN MENU TARGET ; MMT_SECTOR .EQU 'S' ; PERFORM OPERATION ON ONE SECTOR MMT_TRACK .EQU 'T' ; PERFORM OPERATION ON ONE TRACK MMT_DISK .EQU 'D' ; PERFORM OPERATION ON ENTIRE DISK MMT_RANDOM .EQU 'R' ; PERFORM OPERATION ON RANDOM SECTORS ; ; MAIN MENU TARGET STRINGS ; MMTS_SECTOR .TEXT "SECTOR$" MMTS_TRACK .TEXT "TRACK$" MMTS_DISK .TEXT "DISK$" MMTS_RANDOM .TEXT "RANDOM$" ; ; MAIN MENU DATA ; MMD_TGT .DB MMT_SECTOR ; ; MAIN MENU PROMPT STRINGS ; MMPS_TGTRW .TEXT "(S)ECTOR, (T)RACK, (D)ISK, (R)ANDOM ===> $" MMPS_TGTF .TEXT "(T)RACK, (D)ISK ===> $" MMPS_IBOPT .TEXT "(P)ATTERN, (F)ILL ===> $" ; ; MAIN MENU BUFFER OPTIONS ; MMBS_PATTERN .TEXT "PATTERN$" MMBS_FILL .TEXT "FILL$" ; ; ; MM_MODEERR .TEXT "\r\n\r\n*** SELECTED MODE NOT SUPPORTED ON HARDWARE ***\r\n$" ; ;________________________________________________________________________________________________________________________________ ; ; MAIN MENU OPERATIONS ;________________________________________________________________________________________________________________________________ ; MMOP_PROC: CALL NEWLINE CALL NEWLINE LD A,FALSE LD (DCD_ABORT),A CALL DIO_INIT LD A,(MMD_TGT) CP MMT_DISK JP Z,MMOP_PROCDISK CP MMT_TRACK JP Z,MMOP_PROCTRK CP MMT_SECTOR JP Z,MMOP_PROCSEC CP MMT_RANDOM JP Z,MMOP_PROCRND JP MMOP_PROCX MMOP_PROCDISK: CALL MMOP_DISK JP MMOP_PROCX MMOP_PROCTRK: CALL MMOP_TRACK JP MMOP_PROCX MMOP_PROCSEC: CALL MMOP_SECTOR JP MMOP_PROCX MMOP_PROCRND: CALL MMOP_RANDOM JP MMOP_PROCX MMOP_PROCX: CALL DIO_TERM RET MMOP_DISK: LD A,0 LD (DCD_TRACK),A LD (DCD_HEAD),A MMOP_DISK1: LD A,(DCD_ABORT) CP TRUE JP Z,MMOP_DISKX CALL MMOP_TRACK LD A,(MDB_NUMHD) LD C,A LD A,(DCD_HEAD) ; INC HEAD INC A LD (DCD_HEAD),A CP C ; # OF HEADS JP NZ,MMOP_DISK1 ; LOOP IF LESS THAN # OF HEADS LD A,0 ; RESET HEAD LD (DCD_HEAD),A ; AND FALL THROUGH TO INC TRACK LD A,(MDB_NUMCYL) LD C,A LD A,(DCD_TRACK) ; INC TRACK INC A LD (DCD_TRACK),A CP C ; # OF TRACKS JP NZ,MMOP_DISK1 MMOP_DISKX: LD A,0 ; RESET TRACK TO A VALID VALUE LD (DCD_TRACK),A RET MMOP_TRACK: LD A,(DCD_DOP) ; SPECIAL CASE, FORMAT IS TRACK-AT-A-TIME CP DOP_FORMAT JP Z,MMOP_TRACKFMT LD A,(MDB_SOT) LD (DCD_SECTOR),A MMOP_TRACK1: LD A,(DCD_ABORT) CP TRUE JP Z,MMOP_TRACKX CALL DIO_RUN LD A,(MDB_EOT) LD C,A INC C ; ONE MORE THAN EOT LD A,(DCD_SECTOR) ; INC SECTOR INC A LD (DCD_SECTOR),A CP C ; > MAX SECTOR? JP NZ,MMOP_TRACK1 JP MMOP_TRACKX MMOP_TRACKFMT: CALL DIO_RUN JP MMOP_TRACKX MMOP_TRACKX: LD A,(MDB_SOT) ; RESET SECTOR TO A VALID VALUE LD (DCD_SECTOR),A RET MMOP_SECTOR: CALL DIO_RUN RET MMOP_RANDOM: LD B,20H ; READ 20H SECTORS RANDOMLY MMOP_RANDOM0: LD A,(DCD_ABORT) CP TRUE JP Z,MMOP_RANDOMX PUSH BC MMOP_RANDOM1: ; GENERATE RANDOM TRACK LD A,(MDB_NUMCYL) LD C,A CALL RNDBYTE AND 7FH ; USE 7 BITS FOR UP TO 128 TRACKS CP C ; MAX TRACK IS 4F + 1 = 50H JP P,MMOP_RANDOM1 LD (DCD_TRACK),A ; GENERATE RANDOM HEAD CALL RNDBYTE AND 01H ; JUST USE LOW ORDER BIT LD (DCD_HEAD),A MMOP_RANDOM2: ; GENERATE RANDOM SECTOR LD A,(MDB_EOT) LD C,A INC C ; ONE MORE THEN MAX SECTOR CALL RNDBYTE AND 1FH ; USE 5 BITS FOR UP TO 32 SECTORS CP C ; MAX SECTOR NUM IS 9 + 1 = 0AH JP P,MMOP_RANDOM2 CP 00H ; SECTOR NUM STARTS AT 1, DON'T ALLOW ZERO JP Z,MMOP_RANDOM2 LD (DCD_SECTOR),A CALL DIO_RUN POP BC DJNZ MMOP_RANDOM0 MMOP_RANDOMX: RET ; ;=============================================================================== ; DISK INPUT/OUTPUT SERVICES, DEVICE INDEPENDENT (LOGICAL) ACCESS TO STORAGE ; TRANSLATES BETWEEN LOGICAL AND PHYSICAL DEVICE OPERATIONS ;=============================================================================== ; ; STR_CONTINUE: .TEXT "CONTINUE? (A)BORT, (R)ETRY, (I)GNORE ===> $" ; DIO_SETMEDIA: ; INITIZLIZE THE MEDIA DESCRIPTION BLOCK ; FILL IN MDB BASED ON USER SELECTION LD A,(DCD_MT) ; A = MEDIA ID (OFFSET IN MEDIA INDEX TABLE) ADD A,A ; * 2 (2 BYTES PER ENTRY) LD H,0 ; MOVE IT TO HL LD L,A ; " LD DE,MIT ; DE = START OF MEDIA INDEX TABLE ADD HL,DE ; ADD HL TO DE, HL NOW POINTS TO DESIRED ENTRY LD A,(HL) ; HL = ENTRY VALUE INC HL ; " LD H,(HL) ; " LD L,A ; " LD DE,MDB ; HL = SOURCE, DE = DESTINATION LD BC,MDB_LEN ; BC = BYTES TO COPY LDIR ; COPY ENTRY TO FCD LD A,FALSE ; SET DRIVE READY TO FALSE! LD (DCD_DSKRDY),A ; " RET ; DIO_INIT: ; UPDATE DRIVE SELECTTION LD A,(FCD_DS) ; GET THE CURRENT DRIVE SELECTION LD B,A ; SAVE IN B LD A,(DCD_UNIT) ; GET THE NEW DRIVE SELECTION CP B ; CHANGED? ; WE NEED TO SET DRIVE STATUS TO NOT READY IFF IT CHANGED JP Z,DIO_INIT1 ; DO NOT RESET DRIVE STATUS! LD (FCD_DS),A ; UPDATE FCD_DS TO NEW VALUE LD A,FALSE ; SET DRIVE READY TO FALSE! LD (DCD_DSKRDY),A ; " DIO_INIT1: ; INITIALIZE TRACE SETTING LD A,(DCD_TRACE) LD (FCD_TRACE),A LD HL,MDB_FCD ; HL = SOURCE LD DE,FCD ; DE = DESTINATION LD BC,FCD_LEN ; BC = BYTES TO COPY LDIR ; BYTES COPY FROM MDB TO FCD LD A,0 ; ASSUME DMA (NON-DMA = 0) LD (FCD_ND),A LD A,(DCD_MD) CP MD_DRQWAIT JP Z,DIO_INIT2 ; YES, DMA NEEDED, DO IT LD A,1 ; SET TO NON-DMA (NDMA = 1) LD (FCD_ND),A DIO_INIT2: CALL FC_INIT CALL FC_MOTORON RET DIO_TERM: CALL FC_MOTOROFF RET ; ; DIO_CLRDSKCHG ; DIO_CLRDSKCHG: ; PROCESS ANY PENDING DISK CHANGE NOTIFICATIONS LD B,5 DIO_CLRDSKCHG1: PUSH BC CALL FC_SENSEINT POP BC CALL DIO_CHKFC CP FRC_DSKCHG RET NZ DJNZ DIO_CLRDSKCHG1 RET ; ; DIO_WTSEEK ; ; WAIT FOR PENDING SEEK OPERATION TO COMPLETE BY POLLING SENSEINT ; AND WAITING FOR ABTERM OR OK. ; DIO_WTSEEK: LD BC,1000H ; LD BC,20H ; *DEBUG* ; LD BC,1H ; *DEBUG* DIO_WTSEEKLOOP: PUSH BC CALL FC_SENSEINT POP BC LD A,(FST_RC) ; CHECK RC CP FRC_ABTERM ; ABTERM = DONE RET Z CP FRC_OK ; OK = DONE RET Z DEC BC ; CHECK LOOP COUNTER IN BC LD A,B ; " OR C ; " JP NZ,DIO_WTSEEKLOOP ; LOOP UNTIL COUNTER EXHAUSTED DIO_DRIVERESET: CALL NEWLINE CALL NEWLINE LD DE,STR_DRIVERESET CALL WRITESTR CALL FC_RESETFDC CALL DIO_CLRDSKCHG CALL DIO_CHKFC CP FRC_INVCMD ; INVALID COMMAND IS CORRECT RESPONSE HERE JP NZ,DIO_NORESP ; CONTINUE RESET SEQUENCE WITH 'SPECIFY' COMMAND CALL FC_SPECIFY CALL DIO_CHKFC JP NZ,DIO_NORESP CALL FC_RECAL CALL DIO_CHKFC JP NZ,DIO_NORESP ; CAREFUL... FIRST RECAL MAY FAIL TO REACH TRACK 0 ; SO WE ALLOW FOR A SECOND TRY IN CASE OF A FAILURE CALL DIO_WTSEEK CALL DIO_CHKFC JP Z,DIO_DRIVERESET1 ; SECOND TRY, ONLY IF NEEDED CALL FC_RECAL CALL DIO_CHKFC JP NZ,DIO_NORESP CALL DIO_WTSEEK CALL DIO_CHKFC JP NZ,DIO_NORESP JP DIO_DRIVERESET1 DIO_NORESP: CALL NEWLINE LD DE,STR_NORESP CALL WRITESTR RET DIO_DRIVERESET1: LD A,TRUE LD (DCD_DSKRDY),A LD A,0 LD (DCD_CURTRK),A RET DIO_CHKFC: LD A,(FST_RC) OR A RET DIO_RUN: LD A,(DCD_DSKRDY) CP TRUE JP Z,DIO_RUN0 CALL DIO_DRIVERESET LD A,(DCD_DSKRDY) CP TRUE JP NZ,DIO_RUNERR ; DRIVERESET FAILED! DIO_RUN0: CALL DIO_PROGRESS ; COPY PARMS OVER LD A,(DCD_UNIT) LD (FCD_DS),A LD A,(DCD_TRACE) LD (FCD_TRACE),A LD A,(DCD_TRACK) LD (FCD_C),A LD A,(DCD_HEAD) LD (FCD_H),A LD A,(DCD_SECTOR) LD (FCD_R),A LD A,(DCD_INTRLV) LD (FCD_X),A ; FIX: COMBINE WITH DCD_TRACK SETUP ABOVE? LD A,(DCD_CURTRK) LD B,A LD A,(DCD_TRACK) CP B JP Z,DIO_RUN1 ; SKIP SEEK IF POSSIBLE ; SEEK AND WAIT FOR COMPLETE CALL FC_SEEK CALL DIO_CHKFC JP NZ,DIO_RUNERR CALL DIO_WTSEEK CALL DIO_CHKFC JP NZ,DIO_RUNERR ; RECORD CURRENT TRACK ; FIX: SHOULD NOT ASSUME SEEK REQUEST SUCCEEDED (FC_READID?) LD A,(DCD_TRACK) LD (DCD_CURTRK),A DIO_RUN1: LD A,(DCD_DOP) CP DOP_READ JP Z,DIO_RUNREAD CP DOP_WRITE JP Z,DIO_RUNWRITE CP DOP_FORMAT JP Z,DIO_RUNFORMAT CP DOP_VERIFY JP Z,DIO_RUNVERIFY JP DIO_RUNX DIO_RUNREAD: CALL FC_READ JP DIO_RUNCHK DIO_RUNWRITE: CALL FC_WRITE JP DIO_RUNCHK DIO_RUNFORMAT: CALL FC_FMTTRK JP DIO_RUNCHK DIO_RUNVERIFY: CALL FC_READ JP DIO_RUNCHK DIO_RUNCHK: LD A,(FST_RC) OR A CP FRC_OK JP NZ,DIO_RUNERR ; HANDLE I/O ERROR LD A,(DCD_DOP) CP DOP_VERIFY JP NZ,DIO_RUNX ; NOT VERIFY, ALL DONE CALL DIO_VERIFY LD A,(FST_RC) OR A CP FRC_OK JP NZ,DIO_RUNERR ; HANDLE VERIFY ERROR JP DIO_RUNX DIO_RUNERR: LD A,FALSE LD (DCD_DSKRDY),A CALL FC_RESETFDC ; FIX... CALL NEWLINE LD DE,STR_CONTINUE ; ABORT/RETRY/IGNORE PROMPT CALL WRITESTR DIO_RUNERR1: CALL GETKEYUC CP 'A' JP Z,DIO_ABORT CP 'R' JP Z,DIO_RETRY CP 'I' JP Z,DIO_IGNORE JP DIO_RUNERR1 DIO_ABORT: LD DE,DIOCS_ABORT CALL WRITESTR LD A,TRUE LD (DCD_ABORT),A JP DIO_RUNX DIO_RETRY: LD DE,DIOCS_RETRY CALL WRITESTR JP DIO_RUN DIO_IGNORE: LD DE,DIOCS_IGNORE CALL WRITESTR JP DIO_RUNX DIO_RUNX: RET DIO_PROGRESS: LD A,(NEWLINE_USED) OR A JP Z,DIO_PROGRESS1 CALL NEWLINE LD A,0 LD (NEWLINE_USED),A DIO_PROGRESS1: CALL PC_CR LD DE,STR_PROGRESS CALL WRITESTR CALL PC_COLON CALL PC_SPACE LD DE,DPL_TRACK CALL WRITESTR CALL PC_EQUAL LD A,(DCD_TRACK) CALL PRTHEXBYTE CALL PC_SPACE LD DE,DPL_HEAD CALL WRITESTR CALL PC_EQUAL LD A,(DCD_HEAD) CALL PRTHEXBYTE CALL PC_SPACE LD DE,DPL_SECTOR CALL WRITESTR CALL PC_EQUAL LD A,(DCD_SECTOR) CALL PRTHEXBYTE RET DIO_VERIFY: LD HL,BUFFER LD DE,VFYBUF LD BC,BUFSIZ DIO_VERIFY1: LD A,(DE) CP (HL) JP NZ,DIO_VERIFY2 INC DE INC HL DEC BC LD A,B OR C JP NZ,DIO_VERIFY1 RET DIO_VERIFY2: PUSH DE CALL NEWLINE LD DE,STR_MISMATCH CALL WRITESTR POP DE LD A,D SUB VFYBUF >> 8 CALL PRTHEXBYTE LD A,E CALL PRTHEXBYTE LD A,FRC_MISMATCH LD (FST_RC),A RET ; ; CONTINUE PROMPT OPTION STRINGS ; DIOCS_ABORT .TEXT "ABORT$" DIOCS_RETRY .TEXT "RETRY$" DIOCS_IGNORE .TEXT "IGNORE$" ; ; DISK PROGRESS LABELS ; DPL_TRACK .TEXT "TRACK$" DPL_HEAD .TEXT "HEAD$" DPL_SECTOR .TEXT "SECTOR$" ; ; DISK OPERATIONS ; DOP_READ .EQU 0 ; READ OPERATION DOP_WRITE .EQU 1 ; WRITE OPERATION DOP_FORMAT .EQU 2 ; FORMAT OPERATION DOP_VERIFY .EQU 3 ; VERIFY OPERATION ; ; DEVICE CONTROL DATA ; DCD_FILL .DB 0E5H ; DEFAULT TO E5 (EMPTY DIRECTORY BYTE) DCD_PATTERN .DB 000H ; DEFAULT TO 00 DCD_TRACE .DB 000H ; TRACE LEVEL DCD_UNIT .DB 000H ; DEFAULT UNIT = 0 DCD_SECTOR .DB 001H ; DEFAULT SECTOR = 1 DCD_HEAD .DB 000H ; DEFAULT HEAD = 0 DCD_TRACK .DB 000H ; DEFAULT TRACK = 0 DCD_INTRLV .DB 002H ; DEFAULT INTERLEAVE = 2 DCD_DOP .DB DOP_READ ; DEFAULT OP = READ DCD_MT .DB MT_PC720 ; DEFAULT FLOPPY DEVICE = PC720 DCD_MD .DB MD_POLL ; DEFAULT MODE = POLL ; ; DEVICE CONTROL DATA (PRIVATE) ; DCD_DSKRDY .DB FALSE ; 0 = NOT RDY, 1 = RDY DCD_ABORT .DB FALSE ; 0 = CONT, 1 = ABORT DCD_CURTRK .DB 0FFH ; CURRENT TRACK, FF = UNKNOWN ; ; MODES ; MD_POLL .EQU 0 MD_INT .EQU 1 MD_INTFAST .EQU 2 MD_INTWAIT .EQU 3 MD_DRQWAIT .EQU 4 ; ; MODE MAPPING ; BIT IS SET FOR ALLOWED MODES PER FDC ; MD_MAP: .DB %00011111 ; DIO: POLL,INT,INTFAST,INTWAIT,DRQWAIT .DB %00000111 ; DIO3: POLL,INT,INTFAST .DB %00000111 ; ZETA: POLL,INT,INTFAST .DB %00000001 ; ZETA2:POLL .DB %00000001 ; DIDE: POLL .DB %00000001 ; N8: POLL ; ; MEDIA DESCRIPTION BLOCK ; MDB: MDB_LABEL .DW 000H ; ADDRESS OF MEDIA LABEL MDB_DESC .DW 000H ; ADDRESS OF MEDIA DESCRIPTION MDB_NUMCYL .DB 000H ; NUMBER OF CYLINDERS MDB_NUMHD .DB 000H ; NUMBER OF HEADS MDB_NUMSEC .DB 000H ; NUMBER OF SECTORS MDB_SOT .DB 000H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) MDB_FCD: ; FLOPPY CONFIGURATION DATA (PUBLIC) MANAGED AS A "BLOCK", MUST MATCH FCB BELOW MDB_EOT ; END OF TRACK SECTOR (SAME AS SC SINCE SOT ALWAYS 1) MDB_SC .DB 000H ; SECTOR COUNT MDB_SECSZ .DW 000H ; SECTOR SIZE IN BYTES MDB_GPL .DB 000H ; GAP LENGTH (R/W) MDB_GPLF .DB 000H ; GAP LENGTH (FORMAT) MDB_SRTHUT .DB 000H ; STEP RATE, IBM PS/2 CALLS FOR 3ms, 0DH = 3ms SRT, HEAD UNLOAD TIME MDB_HLT .DB 000H ; HEAD LOAD TIME, IBM PS/2 CALLS FOR 15ms 08H = 16ms HUT MDB_DORA .DB 000H ; OPERATIONS REGISTER VALUE FOR MEDIA MDB_DORB .DB 000H ; OPERATIONS REGISTER VALUE FOR MEDIA MDB_DORC .DB 000H ; OPERATIONS REGISTER VALUE FOR MEDIA MDB_DCR .DB 000H ; CONTROL REGISTER VALUE FOR MEDIA MDB_LEN .EQU $ - MDB ; MDT: ; MODE TABLE .DW MTB_POLL .DW MTB_INT .DW MTB_INTFAST .DW MTB_INTWAIT .DW MTB_DRQWAIT MDT_ENTCNT .EQU (($ - MDT) / 2) ; MTB_POLL .DW MTL_POLL ; ADDRESS OF MODE LABEL .DW MTS_POLL ; ADDRESS OF MODE DESCRIPTION MTL_POLL .TEXT "POLL $" MTS_POLL .TEXT "POLLING (RECOMMENDED)$" ; MTB_INT .DW MTL_INT ; ADDRESS OF MODE LABEL .DW MTS_INT ; ADDRESS OF MODE DESCRIPTION MTL_INT .TEXT "INT $" MTS_INT .TEXT "INTERRUPT (!!! READ MANUAL !!!)$" ; MTB_INTFAST .DW MTL_INTFAST ; ADDRESS OF MODE LABEL .DW MTS_INTFAST ; ADDRESS OF MODE DESCRIPTION MTL_INTFAST .TEXT "INT-FAST $" MTS_INTFAST .TEXT "FAST INTERRUPT (!!! READ MANUAL !!!)$" ; MTB_INTWAIT .DW MTL_INTWAIT ; ADDRESS OF MODE LABEL .DW MTS_INTWAIT ; ADDRESS OF MODE DESCRIPTION MTL_INTWAIT .TEXT "INT/WAIT $" MTS_INTWAIT .TEXT "INT/WAIT (!!! READ MANUAL !!!)$" ; MTB_DRQWAIT .DW MTL_DRQWAIT ; ADDRESS OF MODE LABEL .DW MTS_DRQWAIT ; ADDRESS OF MODE DESCRIPTION MTL_DRQWAIT .TEXT "DRQ/WAIT $" MTS_DRQWAIT .TEXT "DRQ/WAIT (!!! NOT YET IMPLEMENTED!!!)$" ; ; MEDIA TYPE INFORMATION ; MT_PC720 .EQU 0 MT_PC144 .EQU 1 MT_PC320 .EQU 2 MT_PC360 .EQU 3 MT_PC120 .EQU 4 MT_PC111 .EQU 5 ; MIT: ; MEDIA INDEX TABLE .DW MDB_PC720 .DW MDB_PC144 .DW MDB_PC320 .DW MDB_PC360 .DW MDB_PC120 .DW MDB_PC111 MIT_ENTCNT .EQU (($ - MIT) / 2) ; ; 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) ; MDB_PC720 .DW DTL_PC720 ; ADDRESS OF MEDIA LABEL .DW DTS_PC720 ; ADDRESS OF MEDIA DESCRIPTION .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) FCB_PC720 .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 ; HLT = 16ms .DB DORA_BR250 ; OPERATIONS REGISTER VALUE .DB DORB_BR250 ; OPERATIONS REGISTER VALUE .DB DORC_BR250 ; OPERATIONS REGISTER VALUE .DB DCR_BR250 ; CONTROL REGISTER VALUE .IF (($ - MDB_PC720) != MDB_LEN) .ECHO "*** FCB SIZE ERROR!!! ***\n" .ENDIF DTL_PC720 .TEXT "720KB $" DTS_PC720 .TEXT "3.5\" 720KB - 9 SECTORS, 2 SIDES, 80 TRACKS, DOUBLE DENSITY$" ; MDB_PC144 .DW DTL_PC144 ; ADDRESS OF MEDIA LABEL .DW DTS_PC144 ; ADDRESS OF MEDIA DESCRIPTION .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) FCB_PC144 .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 ; HLT = 16ms .DB DORA_BR500 ; OPERATIONS REGISTER VALUE .DB DORB_BR500 ; OPERATIONS REGISTER VALUE .DB DORC_BR500 ; OPERATIONS REGISTER VALUE .DB DCR_BR500 ; CONTROL REGISTER VALUE .IF (($ - MDB_PC144) != MDB_LEN) .ECHO "*** FCB SIZE ERROR!!! ***\n" .ENDIF DTL_PC144 .TEXT "1.44MB$" DTS_PC144 .TEXT "3.5\" 1.44MB - 18 SECTORS, 2 SIDES, 80 TRACKS, HIGH DENSITY$" ; MDB_PC320 .DW DTL_PC320 ; ADDRESS OF MEDIA LABEL .DW DTS_PC320 ; ADDRESS OF MEDIA DESCRIPTION .DB 028H ; NUMBER OF CYLINDERS .DB 002H ; NUMBER OF HEADS .DB 008H ; NUMBER OF SECTORS .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) FCB_PC320 .DB 008H ; 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 ; HLT = 16ms .DB DORA_BR250 ; OPERATIONS REGISTER VALUE .DB DORB_BR250 ; OPERATIONS REGISTER VALUE .DB DORC_BR250 ; OPERATIONS REGISTER VALUE .DB DCR_BR250 ; CONTROL REGISTER VALUE .IF (($ - MDB_PC320) != MDB_LEN) .ECHO "*** FCB SIZE ERROR!!! ***\n" .ENDIF DTL_PC320 .TEXT "320KB $" DTS_PC320 .TEXT "5.25\" 320KB - 8 SECTORS, 2 SIDES, 40 TRACKS, DOUBLE DENSITY$" ; MDB_PC360 .DW DTL_PC360 ; ADDRESS OF MEDIA LABEL .DW DTS_PC360 ; ADDRESS OF MEDIA DESCRIPTION .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) FCB_PC360 .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 ; HLT = 16ms .DB DORA_BR250 ; OPERATIONS REGISTER VALUE .DB DORB_BR250 ; OPERATIONS REGISTER VALUE .DB DORC_BR250 ; OPERATIONS REGISTER VALUE .DB DCR_BR250 ; CONTROL REGISTER VALUE .IF (($ - MDB_PC360) != MDB_LEN) .ECHO "*** FCB SIZE ERROR!!! ***\n" .ENDIF DTL_PC360 .TEXT "360KB $" DTS_PC360 .TEXT "5.25\" 360KB - 9 SECTORS, 2 SIDES, 40 TRACKS, DOUBLE DENSITY$" ; MDB_PC120 .DW DTL_PC120 ; ADDRESS OF MEDIA LABEL .DW DTS_PC120 ; ADDRESS OF MEDIA DESCRIPTION .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) FCB_PC120 .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 ; HLT = 16ms .DB DORA_BR500 ; OPERATIONS REGISTER VALUE .DB DORB_BR500 ; OPERATIONS REGISTER VALUE .DB DORC_BR500 ; OPERATIONS REGISTER VALUE .DB DCR_BR500 ; CONTROL REGISTER VALUE .IF (($ - MDB_PC120) != MDB_LEN) .ECHO "*** FCB SIZE ERROR!!! ***\n" .ENDIF DTL_PC120 .TEXT "1.2MB $" DTS_PC120 .TEXT "5.25\" 1.2MB - 15 SECTORS, 2 SIDES, 80 TRACKS, HIGH DENSITY$" ; MDB_PC111 .DW DTL_PC111 ; ADDRESS OF MEDIA LABEL .DW DTS_PC111 ; ADDRESS OF MEDIA DESCRIPTION .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) FCB_PC111 .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 ; HLT = 50ms .DB DORA_BR500 ; OPERATIONS REGISTER VALUE .DB DORB_BR500 ; OPERATIONS REGISTER VALUE .DB DORC_BR500 ; OPERATIONS REGISTER VALUE .DB DCR_BR500 ; CONTROL REGISTER VALUE .IF (($ - MDB_PC111) != MDB_LEN) .ECHO "*** FCB SIZE ERROR!!! ***\n" .ENDIF DTL_PC111 .TEXT "1.11MB$" DTS_PC111 .TEXT "8\" 1.11MB - 15 SECTORS, 2 SIDES, 77 TRACKS, DOUBLE DENSITY$" ; ;=============================================================================== ; FLOPPY DISK CONTROL MENU (DIRECT MENU INTERFACE TO FDC & RELATED HARDWARE) ;=============================================================================== ; ; FDC MENU DATA ; FM_TABLE .DB 'R' \ .DW FMS_READ, FM_READ FM_ENTSIZ .EQU $ - FM_TABLE .DB 'D' \ .DW FMS_READDEL, FM_READDEL .DB 'W' \ .DW FMS_WRITE, FM_WRITE .DB 'E' \ .DW FMS_WRITEDEL, FM_WRITEDEL .DB 'T' \ .DW FMS_READTRK, FM_READTRK .DB 'I' \ .DW FMS_READID, FM_READID .DB 'F' \ .DW FMS_FMTTRK, FM_FMTTRK .DB 'Q' \ .DW FMS_SCANEQ, FM_SCANEQ .DB 'L' \ .DW FMS_SCANLOEQ, FM_SCANLOEQ .DB 'H' \ .DW FMS_SCANHIEQ, FM_SCANHIEQ .DB 'C' \ .DW FMS_RECAL, FM_RECAL .DB 'N' \ .DW FMS_SENSEINT, FM_SENSEINT .DB 'P' \ .DW FMS_SPECIFY, FM_SPECIFY .DB 'V' \ .DW FMS_DRVSTAT, FM_DRVSTAT .DB 'S' \ .DW FMS_SEEK, FM_SEEK .DB 'O' \ .DW FMS_VERSION, FM_VERSION .DB 'U' \ .DW FMS_PULSETC, FM_PULSETC .DB 'A' \ .DW FMS_DOR, FM_DOR .DB 'M' \ .DW FMS_MOTOR, FM_MOTOR .DB 'Z' \ .DW FMS_FDCRESET, FM_FDCRESET .DB 'X' \ .DW FMS_EXIT, 0000H FM_COUNT .EQU (($ - FM_TABLE) / FM_ENTSIZ) ; # ENTRIES IN TABLE FM_INFO: .DW FM_DRAW .DW FM_TABLE .DB FM_ENTSIZ .DB FM_COUNT ; ; FDC COMMAND MENU STRINGS ; STR_FDCMENU: ; .TEXT "===================<< FDU FDC COMMAND MENU >>======= [MSR=XX] ==\r\n" .TEXT "===================<< FDU FDC COMMAND MENU >>======= [MSR=" MV_MSR .TEXT "XX" .TEXT "] ==\r\n" .TEXT "(R)EAD READ (D)EL (W)RITE WRITE D(E)L\r\n" .TEXT "READ (T)RK READ (I)D (F)ORMAT SCAN E(Q)\r\n" .TEXT "SCAN (L)O/EQ SCAN (H)I/EQ RE(C)AL SE(N)SE INT\r\n" .TEXT "S(P)ECIFY DRI(V)E STAT (S)EEK VERSI(O)N\r\n" ; .TEXT "P(U)LSE TC L(A)TCH:XX (M)OTOR:XXX FDC RESET (Z)\r\n" .TEXT "P(U)LSE TC L(A)TCH:" MV_LAT .TEXT "XX" .TEXT " (M)OTOR:" MV_MOT .TEXT "XXX" .TEXT " FDC RESET (Z)" .TEXT "\r\n" .TEXT "E(X)IT\r\n" .TEXT "=== OPTION ===> $" ; FMS_NOP .TEXT "NOP$" FMS_READ .TEXT "READ$" FMS_READDEL .TEXT "READ DELETED$" FMS_WRITE .TEXT "WRITE$" FMS_WRITEDEL .TEXT "WRITE DELETED$" FMS_READTRK .TEXT "READ TRACK$" FMS_READID .TEXT "READ ID$" FMS_FMTTRK .TEXT "FORMAT TRACK$" FMS_SCANEQ .TEXT "SCAN EQUAL$" FMS_SCANLOEQ .TEXT "SCAN LOW OR EQUAL$" FMS_SCANHIEQ .TEXT "SCAN HIGH OR EQUAL$" FMS_RECAL .TEXT "RECALIBRATE$" FMS_SENSEINT .TEXT "SENSE INTERRUPT$" FMS_SPECIFY .TEXT "SPECIFY$" FMS_DRVSTAT .TEXT "DRIVE STATUS$" FMS_SEEK .TEXT "SEEK$" FMS_VERSION .TEXT "VERSION$" FMS_PULSETC .TEXT "PULSE TC$" FMS_DOR .TEXT "DOR$" FMS_MOTOR .TEXT "MOTOR$" FMS_FDCRESET .TEXT "FDC RESET$" FMS_EXIT .TEXT "EXIT$" ; ; ENTRY POINT FOR FDCMENU ; FDCMENU: LD A,01H LD (FCD_TRACE),A ; FORCE TRACING OF ALL FDC COMMANDS LD HL,FM_INFO CALL RUNMENU RET ; ; FDCMENU DRAW PROCEDURE ; FM_DRAW: CALL NEWLINE CALL NEWLINE ; UPDATE MOTOR STATUS LD HL,FDCBM LD A,(HL) AND _DIO JR NZ,FM_DRAW0A LD A,(HL) AND _ZETA | _DIO3 JR NZ,FM_DRAW0B LD A,(HL) AND _DIDE | _N8 | _ZETA2 JR NZ,FM_DRAW0C JR FM_DRAW0D FM_DRAW0A: ; DIO LD A,(FST_DOR) AND 00000010B XOR 00000010B JR FM_DRAW0D FM_DRAW0B: ; ZETA, DIO3 LD A,(FST_DOR) AND 00000010B JR FM_DRAW0D FM_DRAW0C: ; DIDE, N8, ZETA2 LD A,(FST_DOR) AND 11110000B JR FM_DRAW0D FM_DRAW0D: LD DE,STR_ON JP NZ,FM_DRAW1 LD DE,STR_OFF FM_DRAW1: LD HL,MV_MOT CALL STRCPY ; UPDATE MSR VALUE LD DE,MV_MSR LD C,(IY+CFG_MSR) IN A,(C) CALL HEXSTRBYTE ; UPDATE FST_DOR VALUE LD DE,MV_LAT LD A,(FST_DOR) CALL HEXSTRBYTE ; DISPLAY THE MENU LD DE,STR_FDCMENU CALL WRITESTR RET ; ; FDCMENU FUNCTIONS ; FM_READ: CALL FM_GETTHS CALL FC_READ RET FM_READDEL: CALL FM_GETTHS CALL FC_READDEL RET FM_WRITE: CALL FM_GETTHS CALL FC_WRITE RET FM_WRITEDEL: CALL FM_GETTHS CALL FC_WRITEDEL RET FM_READTRK: CALL FM_GETTHS CALL FC_READTRK RET FM_READID: CALL FM_GETHEAD CALL FC_READID RET FM_FMTTRK: CALL FM_GETTRK CALL FM_GETHEAD CALL FC_FMTTRK RET FM_SCANEQ: JP FM_NOTIMPL ; NOT IMPLEMENTED! CALL FM_GETTHS CALL FC_SCANEQ RET FM_SCANLOEQ: JP FM_NOTIMPL ; NOT IMPLEMENTED! CALL FM_GETTHS CALL FC_SCANLOEQ RET FM_SCANHIEQ: JP FM_NOTIMPL ; NOT IMPLEMENTED! CALL FM_GETTHS CALL FC_SCANHIEQ RET FM_RECAL: LD A,0 ; UPDATE CYLINDER FOR FUTURE CALLS LD (FCD_C),A ; FIX: NOT IN THE RIGHT PLACE CALL FC_RECAL RET FM_SENSEINT: CALL FC_SENSEINT RET FM_SPECIFY: CALL FC_SPECIFY RET FM_DRVSTAT: CALL FM_GETHEAD CALL FC_DRVSTAT RET FM_SEEK: CALL FM_GETTRK CALL FC_SEEK RET FM_VERSION: CALL FC_VERSION RET FM_PULSETC: CALL NEWLINE CALL FC_PULSETC RET FM_DOR: CALL FM_GETDOR CALL FC_SETDOR RET FM_MOTOR: ; TOGGLE MOTOR STATE LD HL,FDCBM LD A,(HL) AND _DIO JR NZ,FM_MOTOR0A LD A,(HL) AND _ZETA | _DIO3 JR NZ,FM_MOTOR0B LD A,(HL) AND _DIDE | _N8 | _ZETA2 JR NZ,FM_MOTOR0C JR FM_MOTOR0D FM_MOTOR0A: ; DIO LD A,(FST_DOR) AND 00000010B XOR 00000010B JR FM_MOTOR0D FM_MOTOR0B: ; ZETA, DIO3 LD A,(FST_DOR) AND 00000010B JR FM_MOTOR0D FM_MOTOR0C: ; DIDE, N8, ZETA2 LD A,(FST_DOR) AND 11110000B JR FM_MOTOR0D FM_MOTOR0D: JP Z,FC_MOTORON JP FC_MOTOROFF FM_FDCRESET: CALL NEWLINE LD A,(FDCID) CP FDC_DIO ; RESET NOT POSSIBLE ON DIO JP NZ,FC_RESETFDC LD DE,FCS_NORES JP WRITESTR FM_NOTIMPL: CALL PC_SPACE ; NOT IMPLEMENTED LD DE,STR_NOTIMPL CALL WRITESTR RET FM_GETTHS: CALL FM_GETTRK CALL FM_GETHEAD CALL FM_GETSEC RET FM_GETTRK: LD DE,FCPP_TRK LD HL,FCD_C CALL GETHEXBYTE RET FM_GETHEAD: LD DE,FCPP_HEAD LD HL,FCD_H CALL GETHEXBYTE RET FM_GETSEC: LD DE,FCPP_SEC LD HL,FCD_R CALL GETHEXBYTE RET FM_GETDOR: LD DE,FCPP_DOR LD HL,FST_DOR CALL GETHEXBYTE RET ; FCS_NORES .TEXT "\r\n*** RESET NOT SUPORTED BY HARDWARE ***$" ; ;=============================================================================== ; FLOPPY DISK CONTROL SERVICES (PHYSICAL DEVICE CONTROL FOR FDC HARDWARE) ;=============================================================================== ; ; 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 FRC_MISMATCH .EQU -16H ; EA ; ; 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 ; ; FDC COMMAND PHASE ; FCP_CMD .DB 000H ; INPUT: COMMAND CODE FCP_BUFLEN .DB 00H FCP_BUF .DS 10H FCP_BUFSIZ .EQU $-FCP_BUF FCP_XFRCNT .DW 00H ; BYTES TRANSFERRED DURING COMMAND PHASE ; ; FDC EXECUTION PHASE ; FXP_XR .DW 00H ; INPUT: ADDRESS OF EXECUTION ROUTINE TO INVOKE FXP_TO .DW 00H ; TIMEOUT COUNTDOWN TIMER USED IN SOME VARIATIONS FXP_XFRCNT .DW 00H ; BYTES TRANSFERRED DURING EXECUTION FXP_A .DB 00H ; LAST VALUE OF REG A RECORDED DURING EXECUTION FXP_BC .DW 00H ; LAST VALUE OF REG BC RECORDED DURING EXECUTION FXP_DE .DW 00H ; LAST VALUE OF REG DE RECORDED DURING EXECUTION FXP_HL .DW 00H ; LAST VALUE OF REG HL RECORDED DURING EXECUTION FXP_BUFLEN .DB 00H FXP_BUF .DS 50H ; USED FOR CERTAIN EXEC ROUTINES (FORMAT TRACK) FXP_BUFSIZ .EQU $-FXP_BUF ; ; FDC STATUS ; FST_RC .DB 00H FST_MSR .DB 00H FST_DOR .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 .FILL 10H ; ALLOWS EXTRA CHARACTERS TO BE RETREIEVED FRB_SIZ .EQU $-FRB ; ; FDC COMMANDS ; CMD_READ .EQU 00000110B ; ST0,ST1,ST2,C,H,R,N CMD_READDEL .EQU 00001100B ; ST0,ST1,ST2,C,H,R,N CMD_WRITE .EQU 00000101B ; ST0,ST1,ST2,C,H,R,N CMD_WRITEDEL .EQU 00001001B ; ST0,ST1,ST2,C,H,R,N CMD_READTRK .EQU 00000010B ; ST0,ST1,ST2,C,H,R,N CMD_READID .EQU 00001010B ; ST0,ST1,ST2,C,H,R,N CMD_FMTTRK .EQU 00001101B ; ST0,ST1,ST2,C,H,R,N CMD_SCANEQ .EQU 00010001B ; ST0,ST1,ST2,C,H,R,N CMD_SCANLOEQ .EQU 00011001B ; ST0,ST1,ST2,C,H,R,N CMD_SCANHIEQ .EQU 00011101B ; ST0,ST1,ST2,C,H,R,N CMD_RECAL .EQU 00000111B ; CMD_SENSEINT .EQU 00001000B ; ST0,PCN CMD_SPECIFY .EQU 00000011B ; CMD_DRVSTAT .EQU 00000100B ; ST3 CMD_SEEK .EQU 00001111B ; CMD_VERSION .EQU 00010000B ; ST0 ; ; FDC COMMAND DATA ; FCD: ; FLOPPY CONFIGURATION DATA (PUBLIC) MANAGED AS A "BLOCK", SEE FCB BELOW 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_HLT .DB 000H ; HEAD LOAD TIME, IBM PS/2 CALLS FOR 15ms 08H = 16ms HUT FCD_DORA .DB 000H ; DEFAULT DOR VALUE FOR MEDIA FCD_DORB .DB 000H ; DEFAULT DOR VALUE FOR MEDIA FCD_DORC .DB 000H ; DEFAULT DOR VALUE FOR MEDIA FCD_DCR .DB 000H ; DOR VALUE FOR MEDIA 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 ; FORMAT DATA FILL BYTE FCD_X .DB 002H ; FORMAT INTERLEAVE FACTOR (1...N) FCD_ND .DB 000H ; DMA, 0=DMA, 1=NON-DMA ; STATIC CONFIGURATION, NEVER CHANGES (PRIVATE) FCD_SOT .DB 001H ; STARTING SECTOR NUMBER OF TRACK FCD_MT .DB 000H ; MULTI-TRACK, WE DON'T USE, SET TO 0 FCD_MFM .DB 001H ; MFM, 0=FM, 1=MFM, WE USE MFM ALWAYS FCD_SK .DB 000H ; SKIP MODE, WE DON'T USE, SET TO 0 FCD_N .DB 002H ; SECTOR SIZE, N=2 FOR 512 BYTES FCD_DTL .DB 0FFH ; DATA LENGTH (WHEN N=0, SET TO FF OTHERWISE) FCD_STP .DB 001H ; SECTOR SCAN TYPE, 1=CONTIG, 2=ALTERNATING ; CONTROL STUFF (PUBLIC) FCD_TRACE .DB 00H ; TRACE LEVEL ; ; ; FDC CMD PARM PROMPTS ; FCPP_TRK .TEXT "TRACK$" FCPP_HEAD .TEXT "HEAD$" FCPP_SEC .TEXT "SECTOR$" FCPP_DOR .TEXT "DOR$" ; ; FDC EXECUTION ROUTINE JUMP TABLE ; FXRJ_NOP: JP FXR_NOP FXRJ_READ: JP FXR_READ FXRJ_READDEL: JP FXR_READDEL FXRJ_WRITE: JP FXR_WRITE FXRJ_WRITEDEL: JP FXR_WRITEDEL FXRJ_READTRK: JP FXR_READTRK FXRJ_READID: JP FXR_READID FXRJ_FMTTRK: JP FXR_FMTTRK FXRJ_SCANEQ: JP FXR_SCANEQ FXRJ_SCANLOEQ: JP FXR_SCANLOEQ FXRJ_SCANHIEQ: JP FXR_SCANHIEQ FXRJ_RECAL: JP FXR_RECAL FXRJ_SENSEINT: JP FXR_SENSEINT FXRJ_SPECIFY: JP FXR_SPECIFY FXRJ_DRVSTAT: JP FXR_DRVSTAT FXRJ_SEEK: JP FXR_SEEK FXRJ_VERSION JP FXR_VERSION ; ; FDC COMMAND STRINGS ; FCS_NOP: .TEXT "NOP$" FCS_READ: .TEXT "READ$" FCS_READDEL: .TEXT "READ DELETED$" FCS_WRITE: .TEXT "WRITE$" FCS_WRITEDEL: .TEXT "WRITE DELETED$" FCS_READTRK: .TEXT "READ TRACK$" FCS_READID: .TEXT "READ ID$" FCS_FMTTRK: .TEXT "FORMAT TRACK$" FCS_SCANEQ: .TEXT "SCAN EQUAL$" FCS_SCANLOEQ: .TEXT "SCAN LOW OR EQUAL$" FCS_SCANHIEQ: .TEXT "SCAN HIGH OR EQUAL$" FCS_RECAL: .TEXT "RECALIBRATE$" FCS_SENSEINT: .TEXT "SENSE INTERRUPT$" FCS_SPECIFY: .TEXT "SPECIFY$" FCS_DRVSTAT: .TEXT "DRIVE STATUS$" FCS_SEEK: .TEXT "SEEK$" FCS_VERSION: .TEXT "VERSION$" ; ; FDC COMMAND TABLE ; FCT .DB CMD_READ \ .DW FCS_READ, FXRJ_READ FCT_ENTSIZ .EQU $ - FCT .DB CMD_READDEL \ .DW FCS_READDEL, FXRJ_READDEL .DB CMD_WRITE \ .DW FCS_WRITE, FXRJ_WRITE .DB CMD_WRITEDEL \ .DW FCS_WRITEDEL, FXRJ_WRITEDEL .DB CMD_READTRK \ .DW FCS_READTRK, FXRJ_READTRK .DB CMD_READID \ .DW FCS_READID, FXRJ_READID .DB CMD_FMTTRK \ .DW FCS_FMTTRK, FXRJ_FMTTRK .DB CMD_SCANEQ \ .DW FCS_SCANEQ, FXRJ_SCANEQ .DB CMD_SCANLOEQ \ .DW FCS_SCANLOEQ, FXRJ_SCANLOEQ .DB CMD_SCANHIEQ \ .DW FCS_SCANHIEQ, FXRJ_SCANHIEQ .DB CMD_RECAL \ .DW FCS_RECAL, FXRJ_RECAL .DB CMD_SENSEINT \ .DW FCS_SENSEINT, FXRJ_SENSEINT .DB CMD_SPECIFY \ .DW FCS_SPECIFY, FXRJ_SPECIFY .DB CMD_DRVSTAT \ .DW FCS_DRVSTAT, FXRJ_DRVSTAT .DB CMD_SEEK \ .DW FCS_SEEK, FXRJ_SEEK .DB CMD_VERSION \ .DW FCS_VERSION, FXRJ_VERSION FCT_COUNT .EQU (($ - FCT) / FCT_ENTSIZ) ; # ENTRIES IN TABLE ; ; ENTRY POINTS FOR FDC COMMANDS ; FC_READ: LD A,CMD_READ LD B,0FFH ; MT & MFM & SK & CMD BITS LD (FCP_CMD),A LD HL,FXR_READ LD (FXP_XR),HL CALL FC_SETUPIO CALL FC_CMDPROC RET FC_READDEL: LD A,CMD_READDEL LD B,0FFH ; MT & MFM & SK & CMD BITS LD (FCP_CMD),A LD HL,FXR_READDEL LD (FXP_XR),HL CALL FC_SETUPIO CALL FC_CMDPROC RET FC_WRITE: LD A,CMD_WRITE LD B,0DFH ; MT & MFM & CMD BITS LD (FCP_CMD),A LD HL,FXR_WRITE LD (FXP_XR),HL CALL FC_SETUPIO CALL FC_CMDPROC RET FC_WRITEDEL: LD A,CMD_WRITEDEL LD B,0DFH ; MT & MFM & CMD BITS LD (FCP_CMD),A LD HL,FXR_WRITEDEL LD (FXP_XR),HL CALL FC_SETUPIO CALL FC_CMDPROC RET FC_READTRK: LD A,CMD_READTRK LD B,07FH ; MFM & SK & CMD BITS LD (FCP_CMD),A LD HL,FXR_READTRK LD (FXP_XR),HL CALL FC_SETUPIO CALL FC_CMDPROC RET FC_READID: LD A,CMD_READID LD B,05FH ; MFM & CMD BITS LD (FCP_CMD),A LD HL,FXR_READID LD (FXP_XR),HL CALL FC_SETUPCMD CALL FC_CMDPROC RET FC_FMTTRK: LD A,CMD_FMTTRK LD B,05FH ; MFM & CMD BITS LD (FCP_CMD),A LD HL,FXR_FMTTRK LD (FXP_XR),HL CALL FC_SETUPFMT CALL FC_CMDPROC RET FC_SCANEQ: LD A,CMD_SCANEQ LD B,0FFH ; MT & MFM & SK & CMD BITS LD (FCP_CMD),A LD HL,FXR_SCANEQ LD (FXP_XR),HL CALL FC_SETUPSCAN CALL FC_CMDPROC RET FC_SCANLOEQ: LD A,CMD_SCANLOEQ LD B,0FFH ; MT & MFM & SK & CMD BITS LD (FCP_CMD),A LD HL,FXR_SCANLOEQ LD (FXP_XR),HL CALL FC_SETUPSCAN CALL FC_CMDPROC RET FC_SCANHIEQ: LD A,CMD_SCANHIEQ LD B,0FFH ; MT & MFM & SK & CMD BITS LD (FCP_CMD),A LD HL,FXR_SCANHIEQ LD (FXP_XR),HL CALL FC_SETUPSCAN CALL FC_CMDPROC RET FC_RECAL: LD A,CMD_RECAL LD B,01FH ; CMD BITS ONLY LD (FCP_CMD),A LD HL,FXR_RECAL LD (FXP_XR),HL CALL FC_SETUPSEEK ; SPECIALIZATION OF SEEK LD A,2 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE LD (FCP_BUFLEN),A CALL FC_CMDPROC RET FC_SENSEINT: LD A,CMD_SENSEINT LD B,01FH ; CMD BITS ONLY LD (FCP_CMD),A LD HL,FXR_SENSEINT LD (FXP_XR),HL CALL FC_SETUPCMD LD A,1 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE LD (FCP_BUFLEN),A CALL FC_CMDPROC RET FC_SPECIFY: LD A,CMD_SPECIFY LD B,01FH ; CMD BITS ONLY LD (FCP_CMD),A LD HL,FXR_SPECIFY LD (FXP_XR),HL CALL FC_SETUPSPECIFY CALL FC_CMDPROC RET FC_DRVSTAT: LD A,CMD_DRVSTAT LD B,01FH ; CMD BITS ONLY LD (FCP_CMD),A LD HL,FXR_DRVSTAT LD (FXP_XR),HL CALL FC_SETUPCMD CALL FC_CMDPROC RET FC_SEEK: LD A,CMD_SEEK LD B,01FH ; CMD BITS ONLY LD (FCP_CMD),A LD HL,FXR_SEEK LD (FXP_XR),HL CALL FC_SETUPSEEK CALL FC_CMDPROC RET FC_VERSION: LD A,CMD_VERSION LD B,01FH ; CMD BITS ONLY LD (FCP_CMD),A LD HL,FXR_VERSION LD (FXP_XR),HL CALL FC_SETUPCMD LD A,1 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE LD (FCP_BUFLEN),A CALL FC_CMDPROC RET ; ; HELPER FUNCTIONS TO SETUP CMDBUF ; FC_SETUPCMD: PUSH BC ; B CONTAINS BIT MASK TO USE FOR FIRST BYTE ; SO THAT WE CAN MASK OFF BITS THAT ARE NOT ; USED FOR CERTAIN COMMANDS LD DE,FCP_BUF AND 01FH ; REMOVE ANY EXTRANEOUS BITS FROM COMMAND BYTE LD C,A ; SAVE THE COMMAND LD A,(FCD_MT) ; GET MT BIT AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY RLA ; MAKE ROOM FOR MFM LD B,A ; SAVE WHAT WE HAVE SO FAR IN B LD A,(FCD_MFM) ; GET MFM BIT AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY OR B ; COMBINE WITH SAVED RLA ; MAKE ROOM FOR SK LD B,A ; SAVE WHAT WE HAVE SO FAR IN B LD A,(FCD_SK) ; GET SK BIT AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY OR B ; COMBINE WITH SAVED RLA ; MAKE ROOM FOR THE COMMAND BITS RLA RLA RLA RLA LD B,C ; RECOVER THE COMMAND VALUE OR B ; COMBINE WITH SAVED POP BC ; GET THE BIT MASK FOR FIRST BYTE AND B ; APPLY IT LD (DE),A ; SAVE THE BYTE INC DE LD A,(FCD_H) ; START WITH HDS AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY RLA ; MAKE ROOM FOR DS BITS RLA ; 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 ; THIS IS A 2 BYTE COMMAND STRING LD (FCP_BUFLEN),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_BUFLEN),A RET FC_SETUPFMT: CALL FC_SETUPCMD LD A,(FCD_N) LD (DE),A INC DE LD A,(FCD_SC) LD (DE),A INC DE LD A,(FCD_GPLF) LD (DE),A INC DE LD A,(FCD_D) LD (DE),A INC DE LD A,6 LD (FCP_BUFLEN),A ; SETUP FORMAT BUFFER WITH SECTOR ID INFO FOR ENTIRE TRACK - (C,H,S,N) FOR EACH SECTOR ; INTERLEAVE AS REQUESTED ; ; B = CURRENT INTERLEAVE PASS STARTING SECTOR ID ; C = CURRENT SECTOR ID ; H = INTERLEAVE STEP ; L = LAST SECTOR ID + 1 ; DE = BUFFER POINTER ; PUSH DE PUSH HL ; COMPUTE INTERLEAVE STEP LD H,-1 LD A,(FCD_X) LD B,A LD A,(FCD_SC) LOOP: INC H SUB B JR NC,LOOP LD DE,FXP_BUF ; DE POINTS TO START OF BUFFER LD A,(FCD_SOT) ; GET FIRST SECTOR ID LD B,A ; B = FIRST SECTOR ID FOR CURRENT INTERLEAVE PASS LD C,A ; C = SECTOR ID LD A,(FCD_SC) ; NUM SECTORS TO SET UP ADD A,C LD L,A ; L = LAST SECTOR ID + 1 FC_SETUPFMT1: ; CYLINDER LD A,(FCD_C) LD (DE),A INC DE ; HEAD LD A,(FCD_H) LD (DE),A INC DE ; SECTOR ID LD A,C LD (DE),A INC DE ; SECTOR SIZE LD A,(FCD_N) LD (DE),A INC DE ; INC SECTOR ID BY INTERLEAVE STEP LD A,H ADD A,C LD C,A ; LOOP IF WE HAVE NOT GOTTEN PAST LAST SECTOR ID CP L JP M,FC_SETUPFMT1 ; SETUP FOR NEXT INTERLEAVE PASS INC B LD C,B ; LOOP IF WE ARE NOT DONE LD A,H CP C JP P,FC_SETUPFMT1 ; DONE, FINALIZE BUFFER LD A,(FCD_SC) ; GET SECTOR COUNT RLCA ; MULTIPLY BY 4 RLCA LD (FXP_BUFLEN),A ; STORE AS BUFFER LENGTH POP HL POP DE RET FC_SETUPSCAN: CALL FC_SETUPIO ; START WITH GENERIC IO CMD LD DE,FCP_BUF+8 ; REPLACE DTL WITH STP LD A,(FCD_STP) LD (DE),A RET FC_SETUPSEEK: CALL FC_SETUPCMD ; START WITH GENERIC IO CMD LD A,(FCD_C) LD (DE),A INC DE LD DE,FCP_BUF ; REMOVE EXTRANEOUS BITS FROM CC0 LD A,(DE) AND 0FH LD (DE),A LD A,3 LD (FCP_BUFLEN),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_HLT) AND 07FH RLA LD B,A LD A,(FCD_ND) AND 01H OR B LD (DE),A ; SAVE THE BYTE INC DE LD A,3 LD (FCP_BUFLEN),A RET ; ; MAIN FDC COMMAND PROCESSOR ; FC_CMDPROC: CALL FOP CALL FC_PRTRESULTS RET ; ; FDC SEQUENCE INITIALIZATION ; FC_INIT: LD HL,FDCBM LD A,(HL) AND _DIO JR NZ,FC_INIT1 LD A,(HL) AND _ZETA | _DIO3 JR NZ,FC_INIT2 LD A,(HL) AND _DIDE | _N8 | _ZETA2 JR NZ,FC_INIT3 JR FC_INIT4 FC_INIT1: ; DIO LD A,(FCD_DORA) JR FC_INIT4 FC_INIT2: ; ZETA, DIO3 LD A,(FCD_DORB) JR FC_INIT4 FC_INIT3: ; DIDE, N8, ZETA2 LD A,(FCD_DORC) JR FC_INIT4 FC_INIT4: LD (FST_DOR),A CALL FC_SETDOR RET ; ; SET FST_DOR ; FC_SETDOR PUSH AF LD A,(FST_DOR) LD C,(IY+CFG_DOR) OUT (C),A POP AF RET ; ; RESET FDC BY PULSING BIT 7 OF DOR LOW ; FC_RESETFDC: LD C,(IY+CFG_DOR) LD HL,FDCBM LD A,(HL) AND _ZETA | _DIO3 JR NZ,FC_RESETFDC1 LD A,(HL) AND _DIDE | _N8 | _ZETA2 JR NZ,FC_RESETFDC2 JR FC_RESETFDC3 FC_RESETFDC1: ; ZETA, DIO3 LD A,(FST_DOR) RES 7,A OUT (C),A PUSH AF ; SAVE AF BECAUSE DELAY TRASHES IT CALL DELAY POP AF ; RESTORE AF SET 7,A OUT (C),A JR FC_RESETFDC3 FC_RESETFDC2: ; DIDE, N8, ZETA2 LD A,0 OUT (C),A LD A,(FST_DOR) OUT (C),A JR FC_RESETFDC3 FC_RESETFDC3: LD DE,156 ; DELAY: 16us * 156 = 2.5ms CALL VDELAY RET ; ; PULSE TERMCT TO TERMINATE ANY ACTIVE EXECUTION PHASE ; FC_PULSETC: LD A,(FDCBM) AND _DIDE | _N8 | _ZETA2 JR NZ,FC_PULSETC1 ; NOT DIDE, N8, ZETA2 LD C,(IY+CFG_DOR) LD A,(FST_DOR) SET 0,A OUT (C),A RES 0,A OUT (C),A JR FC_PULSETC2 FC_PULSETC1: ; DIDE, N8, ZETA2 LD C,(IY+CFG_TC) IN A,(C) JR FC_PULSETC2 FC_PULSETC2: RET ; ; SET FST_DOR FOR MOTOR CONTROL ON ; FC_MOTORON: LD HL,FDCBM LD A,(HL) AND _DIO JR NZ,FC_MOTORON1 LD A,(HL) AND _ZETA | _DIO3 JR NZ,FC_MOTORON2 LD A,(HL) AND _DIDE | _N8 | _ZETA2 JR NZ,FC_MOTORON3 JR FC_MOTORON4 FC_MOTORON1: ; DIO LD HL,FST_DOR ; POINT TO FDC_DOR RES 1,(HL) ; SET MOTOR ON JR FC_MOTORON4 FC_MOTORON2: ; ZETA, DIO3 LD HL,FST_DOR ; POINT TO FDC_DOR SET 1,(HL) JR FC_MOTORON4 FC_MOTORON3: ; DIDE, N8, ZETA2 LD HL,FST_DOR ; POINT TO FDC_DOR LD A,(HL) ; START WITH CURRENT DOR 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_MOTORON3A: RLA ; SHIFT LEFT DJNZ FC_MOTORON3A ; DS TIMES OR C ; COMBINE WITH SAVED LD (HL),A ; COMMIT THE NEW VALUE TO FST_DOR JR FC_MOTORON4 FC_MOTORON4: CALL FC_SETDOR ; OUTPUT TO CONTROLLER CALL LDELAY ; WAIT 1/2 SEC ON MOTOR START FOR SPIN-UP LD A,(FDCBM) AND _DIDE | _N8 | _ZETA2 JR Z,FC_MOTORON5 LD A,(FCD_DCR) LD C,(IY+CFG_DCR) OUT (C),A FC_MOTORON5: RET ; ; SET FST_DOR FOR MOTOR CONTROL OFF ; FC_MOTOROFF: LD HL,FDCBM LD A,(HL) AND _DIO JR NZ,FC_MOTOROFF1 LD A,(HL) AND _ZETA | _DIO3 JR NZ,FC_MOTOROFF2 LD A,(HL) AND _DIDE | _N8 | _ZETA2 JR NZ,FC_MOTOROFF3 JR FC_MOTOROFF4 FC_MOTOROFF1: ; DIO LD HL,FST_DOR ; POINT TO FDC_DOR SET 1,(HL) ; SET MOTOR OFF JR FC_MOTOROFF4 FC_MOTOROFF2: ; ZETA, DIO3 LD HL,FST_DOR ; POINT TO FDC_DOR RES 1,(HL) JR FC_MOTOROFF4 FC_MOTOROFF3: ; DIDE, N8, ZETA2 LD HL,FST_DOR ; POINT TO FDC_DOR LD A,DORC_INIT LD (HL),A JR FC_MOTOROFF4 FC_MOTOROFF4: CALL FC_SETDOR ; OUTPUT TO CONTROLLER RET ; ;=============================================================================== ; FDC OPERATIONS ;=============================================================================== ; FOP: ; ; INITIALIZATION ; LD A,0 LD (FRB_LEN),A LD A,FRC_OK LD (FST_RC),A LD B,0 ; B IS LOOP COUNTER LD C,(IY+CFG_MSR) ; SET C TO MSR PORT FOP_CLR1: CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR IN A,(C) ; GET STATUS LD (FST_MSR),A ; SAVE IT FOR POTENTIAL LATER DIAGNOSIS AND 0C0H ; ISOLATE HIGH NIBBLE, RQM/DIO/EXM/CB CP 0C0H ; LOOKING FOR RQM=1, DIO=1, BYTES PENDING JP NZ,FOP_CMD1 ; NO BYTES PENDING, GO TO NEXT PHASE INC C ; SWITCH TO DATA PORT IN A,(C) ; GET THE PENDING BYTE AND DISCARD DEC C ; SWITCH BACK TO MSR PORT DJNZ FOP_CLR1 ; KEEP CHECKING TILL COUNTER EXHAUSTER JP FOP_TOFDCRDY ; OTHERWISE, TIMEOUT ; ; SEND COMMAND ; FOP_CMD1: LD HL,FCP_BUF LD A,(FCP_BUFLEN) 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 ; TYPICAL MSR TRANSITIONS: 80 10 90 90 LD C,(IY+CFG_MSR) ; SET C TO MSR PORT IN A,(C) ; READ MAIN STATUS REGISTER LD (FST_MSR),A ; SAVE IT FOR POTENTIAL LATER DIAGNOSIS AND 0C0H ; ISOLATE RQM/DIO CP 080H ; LOOKING FOR RQM=1, DIO=0 (FDC READY FOR A BYTE) JP 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 LD C,(IY+CFG_DATA) ; SET C TO DATA PORT OUT (C),A ; PUSH IT TO FDC INC HL ; INCREMENT POINTER FOR NEXT TIME DEC D ; DECREMENT NUM BYTES LEFT TO SEND JP NZ,FOP_CMD2 ; DO NEXT BYTE ; ; EXECUTION PHASE ; FOP_X1: LD HL,(FXP_XR) ; LOAD THE EXECUTION ROUTINE ADDRESS CALL JPHL ; CALL INDIRECTLY VIA HL ; FIX: NEED TO CHECK REG A FOR STATUS, DEPENDING ON STATUS ; IT MAY BE NECESSARY TO DO SOMETHING SPECIAL? ; JP FOP_RES ; CONTINUE WITH GETRESULTS ; ; RESULTS PHASE ; FOP_RES: LD B,0 ; D = BYTES RECEIVED LD HL,FRB_LEN ; POINT TO BUFFER LENGTH LD (HL),D ; UPDATE NUMBER OF BYTES RECEIVED LD HL,FRB ; POINT TO RECEIVE BUFFER FOP_RES0: LD DE,0 ; DE IS LOOP COUNTER LD C,(IY+CFG_MSR) ; SET C TO MSR PORT FOP_RES1: CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR IN A,(C) ; READ MAIN STATUS REGISTER LD (FST_MSR),A ; SAVE IT FOR POTENTIAL LATER DIAGNOSIS AND 0F0H ; ISOLATE RQM/DIO CP 0D0H ; LOOKING FOR RQM=1, DIO=1, BUSY=1 (FDC BYTE PENDING) JP Z,FOP_RES2 ; GOOD, GO TO RECEIVE BYTE CP 080H ; CHECK FOR RQM=1, DIO=0 (NOTHING LEFT) JP Z,FOP_EVAL ; IF NOTHING, ALL DONE, LEFT GO TO EOD/EXIT DEC DE LD A,D OR E JP NZ,FOP_RES1 JP FOP_TOGETRES ; OTHERWISE TIMEOUT ERROR FOP_RES2: ; PROCESS NEXT PENDING BYTE LD A,FRB_SIZ ; GET BUF SIZE CP B ; REACHED MAX? JP Z,FOP_BUFMAX ; HANDLE BUF MAX/EXIT INC C ; SWITCH TO DATA PORT IN A,(C) ; GET THE BYTE DEC C ; SWITCH TO MSR PORT LD (HL),A ; SAVE VALUE INC HL ; INCREMENT BUF POS INC B ; INCREMENT BYTES RECEIVED PUSH HL ; SAVE HL LD HL,FRB_LEN ; POINT TO BUFFER LENGTH LD (HL),B ; UPDATE NUMBER OF BYTES RECEIVED POP HL ; RESTORE HL JR FOP_RES0 ; CONTINUE READ LOOP ; ; EXIT POINTS ; FOP_NOTIMPL: LD A,FRC_NOTIMPL JP FOP_ERR FOP_CMDERR: LD A,FRC_CMDERR JP FOP_ERR FOP_ERROR: LD A,FRC_ERROR JP FOP_ERR FOP_ABORT: LD A,FRC_ABORT JP FOP_ERR FOP_BUFMAX: LD A,FRC_BUFMAX JP FOP_ERR FOP_TOFDCRDY: LD A,FRC_TOFDCRDY JP FOP_ERR FOP_TOSNDCMD: LD A,FRC_TOSNDCMD JP FOP_ERR FOP_TOGETRES: LD A,FRC_TOGETRES JP FOP_ERR FOP_TOEXEC: LD A,FRC_TOEXEC JP FOP_ERR FOP_ERR: LD (FST_RC),A FOP_EVAL: LD A,(FCP_CMD) ; DRVSTAT IS WEIRD, HAS ONLY ST3, NOTHING TO EVAL CP CMD_DRVSTAT JP 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 CMD_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 A,FRC_ABTERM JP FOP_SETFST FOP_INVCMD: LD A,FRC_INVCMD JP FOP_SETFST FOP_DSKCHG: LD A,FRC_DSKCHG JP FOP_SETFST FOP_EVALST1: LD A,(FRB_ST1) BIT 7,A JP NZ,FOP_ENDCYL BIT 5,A JP NZ,FOP_DATAERR BIT 4,A JP NZ,FOP_OVERRUN BIT 2,A JP NZ,FOP_NODATA BIT 1,A JP NZ,FOP_NOTWRIT BIT 0,A JP NZ,FOP_MISADR JP FOP_EXIT FOP_ENDCYL: LD A,FRC_ENDCYL JP FOP_SETFST FOP_DATAERR: LD A,FRC_DATAERR JP FOP_SETFST FOP_OVERRUN: LD A,FRC_OVERRUN JP FOP_SETFST FOP_NODATA: LD A,FRC_NODATA JP FOP_SETFST FOP_NOTWRIT: LD A,FRC_NOTWRIT JP FOP_SETFST FOP_MISADR: LD A,FRC_MISADR JP FOP_SETFST FOP_SETFST: LD (FST_RC),A FOP_EXIT: RET ; ; EXECUTION ROUTINES ; FXR_READID: JP FXRX FXR_READ: LD HL,BUFFER ; POINT TO SECTOR BUFFER START LD DE,(FCD_SECSZ) LD A,(DCD_MD) ; FIX: SHOULD NOT BE USING DCD HERE CP MD_POLL JP Z,FXRR CP MD_INT JP Z,IFXRR CP MD_INTFAST JP Z,FFXRR CP MD_INTWAIT JP Z,WFXRR CP MD_DRQWAIT JP Z,WFXRR JP FXR_NOTIMPL FXR_WRITE: LD HL,BUFFER ; POINT TO SECTOR BUFFER START LD DE,(FCD_SECSZ) LD A,(DCD_MD) ; FIX: SHOULD NOT BE USING DCD HERE CP MD_POLL JP Z,FXRW CP MD_INT JP Z,IFXRW CP MD_INTFAST JP Z,FFXRW CP MD_INTWAIT JP Z,WFXRW CP MD_DRQWAIT JP Z,WFXRW JP FXR_NOTIMPL FXR_FMTTRK: LD HL,FXP_BUF ; POINT TO BUFFER START LD D,0 LD A,(FXP_BUFLEN) ; GET BYTE COUNT TO WRITE LD E,A LD A,(DCD_MD) ; FIX: SHOULD NOT BE USING DCD HERE CP MD_POLL JP Z,FXRW CP MD_INT JP Z,IFXRW CP MD_INTFAST JP Z,IFXRW ; CAN'T USE FFXRW BECAUSE IT IS NOT 512 BYTES CP MD_INTWAIT JP Z,WFXRW CP MD_DRQWAIT JP Z,WFXRW JP FXR_NOTIMPL FXR_NOTIMPL: FXR_READDEL: FXR_WRITEDEL: FXR_READTRK: FXR_SCANEQ: FXR_SCANLOEQ: FXR_SCANHIEQ: LD A,FRC_NOTIMPL LD (FST_RC),A ; FALL THROUGH TO RET BELOW FXR_NOP: FXR_RECAL: FXR_SENSEINT: FXR_SPECIFY: FXR_DRVSTAT: FXR_SEEK: FXR_VERSION: RET ; ; NULL EXECUTION, NO DATA TO READ/WRITE (USED BY READID) ; FXRX: LD DE,1000H ; DE IS LOOP COUNTER, 4096 ITERATIONS OF 25ms LD C,(IY+CFG_MSR) ; SET C TO MSR PORT FXRX1: CALL DELAY IN A,(C) ; GET MSR AND 0E0H ; ISOLATE RQM/DIO/EXM CP 0C0H ; WE WANT RQM=1,DIO=1,EXM=0 (READY TO READ A BYTE W/ EXEC INACTIVE) JP Z,FXR_END ; GOT IT, EXIT CLEAN DEC DE ; DECREMENT COUNTER (16 BIT) LD A,D ; CHECK FOR ZERO OR E ; " JR NZ,FXRX1 ; NOT ZERO YET, KEEP CHECKING JP FXR_TO ; OTHERWISE, TIMEOUT ERROR RET ; ; READ DATA ; FXRR: DI ; DISABLE INTERRUPTS TO AVOID TIMEOUTS LD A,(CPUFREQ + 3) / 4 ; HIGH BYTE OF OUTER LOOP COUNTER LD (FXP_TO+1),A ; SAVE HIGH BYTE OF COUNTER XOR A ; LOW BYTE IS ZERO LD (FXP_TO),A ; SAVE LOW BYTE LD B,A ; INIT INNER LOOP COUNTER LD C,(IY+CFG_MSR) ; SET C TO MSR PORT FXRR1: LD B,0 ; SETUP FOR 256 ITERATIONS (INNER LOOP) ; INNER LOOP FXRR2: IN A,(C) ; GET MSR CP 0F0H ; WE WANT RQM=1,DIO=1,EXM=1,BUSY=1 (READY TO RECEIVE A BYTE W/ EXEC ACTIVE) JP Z,FXRR3 ; GOT IT, DO BYTE READ DJNZ FXRR2 ; NOT READY, LOOP IF COUNTER NOT ZERO ; OUTER LOOP AND 0E0H ; MASK TO ISOLATE RQM, DIO, EXM CP 0C0H ; IF RQM=1, DIO=1, EXM=0 (EXECUTION ABORTED) JP Z,FXR_ABORT ; IF ZERO, BAIL OUT TO ERR ROUTINE, FIX: GO TO SPECIFIC ROUTINE FOR THIS??? PUSH HL ; SAVE HL LD HL,(FXP_TO) ; GET DOWN COUNTER DEC HL ; DECREMENT LD (FXP_TO),HL ; SAVE IT LD A,L ; CHECK COUNTER OR H ; ... FOR ZERO POP HL ; RESTORE HL JR NZ,FXRR1 ; LOOP IF NOT ZERO JP FXR_TO ; OTHERWISE, TIMEOUT FXRR3: ; READ A PENDING BYTE INC C ; SET C TO DATA PORT INI ; READ PORT C TO (HL) AND INC HL DEC C ; SET C BACK TO STATUS PORT DEC DE ; DECREMENT BYTE COUNT LD A,D ; TEST COUNTER OR E ; ... FOR ZERO JP NZ,FXRR1 ; IF NOT ZERO, REPEAT LOOP JP FXR_END ; CLEAN EXIT ; ; INT READ DATA - SAFE VERSION ; HANDLES FDC ERRORS, BUT NO TIMEOUT ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) .ORG (($ & 0FF00H) + 40H) .ENDIF ; IFXRR: DI LD C,(IY+CFG_MSR) ; SET C TO MSR PORT IFXRR2: EI IFXRRX .EQU $ - IFXRR HALT IN A,(C) BIT 5,A JP Z,FXR_ABORT INC C ; SWITCH C TO DATA PORT INI DEC C ; SWITCH C BACK TO MSR PORT DEC DE LD A,E OR D JP NZ,IFXRR2 JP FXR_END ; ; INT READ DATA - FAST VERSION ; FIXED SECTOR SIZE OF 512 BYTES ; HANGS ON FDC ERRORS, NO TIMEOUT ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) .ORG (($ & 0FF00H) + 40H) .ENDIF ; FFXRR: DI LD C,(IY+CFG_DATA) ; SET C TO DATA PORT FOR INI FFXRR2 EI HALT FFXRRX1 .EQU $ - FFXRR INI JP NZ,FFXRR2 FFXRR3 EI HALT FFXRRX2 .EQU $ - FFXRR INI JP NZ,FFXRR3 JP FXR_END ; ; WAIT READ DATA ; HANGS ON FDC ERRORS, NO TIMEOUT ; WFXRR: DI LD C,(IY+CFG_DMA) WFXRR2: INI ; GET PENDING BYTE DEC DE ; DECREMENT BYTE COUNT LD A,D OR E JP NZ,WFXRR2 ; IF NOT ZERO, REPEAT LOOP JP FXR_END ; CLEAN EXIT ; ; WRITE DATA ; FXRW: DI ; DISABLE INTERRUPTS TO AVOID TIMEOUTS LD A,(CPUFREQ + 3) / 4 ; HIGH BYTE OF OUTER LOOP COUNTER LD (FXP_TO+1),A ; SAVE HIGH BYTE OF COUNTER XOR A ; LOW BYTE IS ZERO LD (FXP_TO),A ; SAVE LOW BYTE LD B,A ; INIT INNER LOOP COUNTER LD C,(IY+CFG_MSR) ; SET C TO MSR PORT FXRW1: LD B,0 ; SETUP FOR 256 ITERATIONS ; INNER LOOP FXRW2: IN A,(C) ; GET MSR CP 0B0H ; WE WANT RQM=1,DIO=0,EXM=1,BUSY=1 (READY TO SEND A BYTE W/ EXEC ACTIVE) JR Z,FXRW3 ; GOT IT, DO BYTE WRITE DJNZ FXRW2 ; NOT READY, LOOP IF COUNTER NOT ZERO ; OUTER LOOP AND 0E0H ; MASK TO ISOLATE RQM, DIO, EXM CP 0C0H ; IF RQM=1, DIO=1, EXM=0 (EXECUTION ABORTED) JP Z,FXR_ABORT ; IF ZERO, BAIL OUT TO ERR ROUTINE, FIX: GO TO SPECIFIC ROUTINE FOR THIS??? PUSH HL ; SAVE HL LD HL,(FXP_TO) ; GET DOWN COUNTER DEC HL ; DECREMENT LD (FXP_TO),HL ; SAVE IT LD A,L ; CHECK COUNTER OR H ; ... FOR ZERO POP HL ; RESTORE HL JR NZ,FXRW1 ; LOOP IF NOT ZERO JP FXR_TO ; OTHERWISE, TIMEOUT FXRW3: ; READ A PENDING BYTE INC C ; SET C TO DATA PORT OUTI ; WRITE (HL) TO PORT C AND INC HL DEC C ; SET C BACK TO STATUS PORT DEC DE ; DECREMENT LOOP COUNTER LD A,D ; TEST COUNTER OR E ; ... FOR ZERO JP NZ,FXRW1 ; IF NOT ZERO, REPEAT LOOP JP FXR_END ; CLEAN EXIT ; ; INT WRITE DATA - SAFE VERSION ; HANDLES FDC ERRORS, BUT NO TIMEOUT ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) .ORG (($ & 0FF00H) + 40H) .ENDIF ; IFXRW: DI LD C,(IY+CFG_MSR) ; SET C TO MSR PORT IFXRW2: EI HALT IN A,(C) BIT 5,A JP Z,FXR_ABORT INC C ; SWITCH TO DATA PORT OUTI DEC C ; SWITCH BACK TO MSR PORT DEC DE LD A,E OR D JP NZ,IFXRW2 JP FXR_END ; ; INT WRITE DATA - FAST VERSION ; FIXED SECTOR SIZE OF 512 BYTES ; HANGS ON FDC ERRORS, NO TIMEOUT ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) .ORG (($ & 0FF00H) + 40H) .ENDIF ; FFXRW: DI LD C,(IY+CFG_DATA) ; SET C TO DATA PORT FFXRW2 EI HALT OUTI JP NZ,FFXRW2 FFXRW3 EI HALT OUTI JP NZ,FFXRW3 JP FXR_END ; ; WAIT WRITE DATA ; HANGS ON FDC ERRORS, NO TIMEOUT ; WFXRW: DI LD C,(IY+CFG_DMA) WFXRW2: OUTI ; WRITE IT 16ts DEC DE ; DECREMENT BYTE COUNT 6ts LD A,D ; 4ts OR E ; 4ts JP NZ,WFXRW2 ; IF NOT ZERO, REPEAT LOOP 10ts = 40 JP FXR_END ; CLEAN EXIT ; ; COMMON COMPLETION CODE FOR ALL EXECUTION ROUTINES ; FXR_TO: LD A,FRC_TOEXEC JP FXR_ERR FXR_ABORT: LD A,FRC_ABORT JP FXR_ERR FXR_ERR: LD (FST_RC),A JP FXR_END2 FXR_END: ; DO NOT PULSE TC AT END OF FORMAT LD A,(FCP_CMD) CP CMD_FMTTRK JR Z,FXR_END2 ; DO NOT PULSE TC AT END OF READID CP CMD_READID JR Z,FXR_END2 CALL FC_PULSETC FXR_END2: LD (FXP_A),A LD (FXP_BC),BC LD (FXP_DE),DE LD (FXP_HL),HL EI ; I/O FINISHED, INTS BACK ON RET ; ;=============================================================================== ; 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 JP 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 JP 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 JP 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 JP 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 JP NZ,FCPR2 ; 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 CMD_SENSEINT JP NZ,FCPR2 LD A,(FST_RC) CP FRC_INVCMD JP Z,FCPR_EXIT CP FRC_DSKCHG JP Z,FCPR_EXIT CP FRC_ABTERM JP Z,FCPR_EXIT JP FCPR_EXIT FCPR2: CALL NEWLINE CALL FC_PRTCMD CALL PC_COLON LD A,(FCP_BUFLEN) LD DE,FCP_BUF CALL PRTHEXBUF LD DE,STR_ARROW CALL WRITESTR LD A,(FRB_LEN) LD DE,FRB CALL PRTHEXBUF LD A,(FDCBM) AND _ZETA | _DIO3 JR Z,FCPR3 LD DE,STR_DSKCHG CALL WRITESTR LD C,(IY+CFG_DIR) IN A,(C) AND 01H CALL PRTHEXBYTE FCPR3: LD A,(FST_RC) CALL FC_PRTFST FCPR_EXIT: RET ; ; DUMP EXECUTION INFO ; FXR_DUMP: CALL NEWLINE LD DE,STR_OP CALL WRITESTR CALL PC_COLON LD DE,STR_MSR CALL WRITESTR LD A,(FST_MSR) CALL PRTHEXBYTE LD DE,STR_BC CALL WRITESTR LD BC,(FXP_BC) LD A,B CALL PRTHEXBYTE LD A,C CALL PRTHEXBYTE LD DE,STR_DE CALL WRITESTR LD BC,(FXP_DE) LD A,B CALL PRTHEXBYTE LD A,C CALL PRTHEXBYTE LD DE,STR_HL CALL WRITESTR LD BC,(FXP_HL) LD A,B CALL PRTHEXBYTE LD A,C CALL PRTHEXBYTE RET ; ; 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 ; DORA_BR250 .EQU 10100110B ; 250KBPS DORA_BR500 .EQU 11010010B ; 500KBPS ; DORA_INIT .EQU DORA_BR250 ; ; 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 ; DORB_BR250 .EQU 11100100B ; 250KBPS DORB_BR500 .EQU 10100000B ; 500KBPS ; DORB_INIT .EQU DORB_BR250 ; ; *** DIDE/N8/ZETA2 *** ; DORC_INIT .EQU 00001100B ; SOFT RESET INACTIVE, DMA ENABLED ; DORC_BR250 .EQU DORC_INIT DORC_BR500 .EQU DORC_INIT ; ; DCR (ONLY APPLIES TO DIDE, N8, AND ZETA2) ; DCR_BR250 .EQU 01H ; 250KBPS DCR_BR500 .EQU 00H ; 500KBPS ; ;=============================================================================== ; GENERAL UTILITY ROUTINES ;=============================================================================== ; ; INITIALIZE BUFFER WITH FILLER BYTE ; HL = ADDRESS OF BUFFER ; DE = SIZE OF BUFFER ; B = FILLER BYTE VALUE ; FILL_BUFFER: LD A,B LD (HL),A INC HL DEC DE LD A,D OR E JP NZ,FILL_BUFFER RET ; ; INITIALIZE BUFFER WITH PATTERN ; HL = ADDRESS OF BUFFER ; DE = SIZE OF BUFFER ; B = STARTING BYTE VALUE ; PAT_BUFFER: LD A,B LD (HL),A INC HL DEC DE INC B LD A,D OR E JP NZ,PAT_BUFFER RET ; ; PRINT A BLOCK OF MEMORY NICELY FORMATTED ; DUMP_BUFFER: CALL NEWLINE ; BLKRD: CALL PHL ; PRINT START LOCATION LD C,16 ; SET FOR 16 LOCS PUSH HL ; SAVE STARTING HL NXTONE: LD A,(HL) ; GET BYTE CALL PRTHEXBYTE ; PRINT IT CALL PC_SPACE ; UPDH: INC HL ; POINT NEXT DEC C ; DEC. LOC COUNT JR NZ,NXTONE ; IF LINE NOT DONE ; NOW PRINT 'DECODED' DATA TO RIGHT OF DUMP PCRLF: CALL PC_SPACE ; SPACE IT LD C,16 ; SET FOR 16 CHARS POP HL ; GET BACK START PCRLF0: LD A,(HL) ; GET BYTE AND 060H ; SEE IF A 'DOT' LD A,(HL) ; O.K. TO GET JR NZ,PDOT ; DOT: LD A,2EH ; LOAD A DOT PDOT: CALL COUT ; PRINT IT INC HL ; LD A,D ; CP H ; JR NZ,UPDH1 ; LD A,E ; CP L ; JP Z,DUMP_END ; ; ;IF BLOCK NOT DUMPED, DO NEXT CHARACTER OR LINE UPDH1: DEC C ; DEC. CHAR COUNT JR NZ,PCRLF0 ; DO NEXT CONTD: CALL NEWLINE ; JP BLKRD ; DUMP_END: RET ; ; ; UTILITY PROCS TO PRINT SINGLE CHARACTERS ; PC_SPACE: PUSH AF ; Store AF LD A,' ' ; LOAD A "SPACE" CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_HYPHEN: PUSH AF ; Store AF LD A,'-' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_COLON: PUSH AF ; Store AF LD A,':' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_EQUAL: PUSH AF ; Store AF LD A,'=' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_CR: PUSH AF ; Store AF LD A,CHR_CR ; LOAD A CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_LF: PUSH AF ; Store AF LD A,CHR_LF ; LOAD A CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_LBKT: PUSH AF ; Store AF LD A,'[' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_RBKT: PUSH AF ; Store AF LD A,']' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_LPAREN: PUSH AF ; Store AF LD A,'(' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_RPAREN: PUSH AF ; Store AF LD A,')' ; LOAD A COLON CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE PC_BS: PUSH AF ; Store AF LD A,CHR_BS ; LOAD A CALL COUT ; SCREEN IT POP AF ; RESTORE AF RET ; DONE NEWLINE_USED .DB 1 NEWLINE: CALL PC_CR CALL PC_LF LD A,1 PUSH AF LD (NEWLINE_USED),A POP AF ; RESTORE AF RET ; DONE COPYSTR: LD A,(HL) CP '$' RET Z LDI JR COPYSTR ; ;__COUT_________________________________________________________________________________________________________________________ ; ; PRINT CONTENTS OF A ;________________________________________________________________________________________________________________________________ ; COUT_BUFFER .TEXT " $" ; COUT: PUSH BC ; PUSH AF ; PUSH HL ; PUSH DE ; LD (COUT_BUFFER),A ; LD DE,COUT_BUFFER ; LD C,09H ; CP/M WRITE START STRING TO CONSOLE CALL CALL 0005H POP DE ; POP HL ; POP AF ; POP BC ; RET ; DONE ; ;__PHL_________________________________________________________________________________________________________________________ ; ; PRINT THE HL REG ON THE SERIAL PORT ;________________________________________________________________________________________________________________________________ ; PHL: LD A,H ; GET HI BYTE CALL PRTHEXBYTE ; DO HEX OUT ROUTINE LD A,L ; GET LOW BYTE CALL PRTHEXBYTE ; HEX IT CALL PC_SPACE ; RET ; DONE ; ; GET A LINE BUFFER WITH 2 HEX CHARS, HL=ADDRESS OF LINE BUFFER ; EXIT WITH C = NUMBER OF CHARS, C=0 MEANS NOTHING ENTERED ; GETLNHEX: LD C,0 ; C = CHAR COUNT GLH_LOOP: CALL GETKEYUC OR A JP Z,GLH_LOOP CP CHR_CR JP Z,GLH_CHK CP CHR_BS JP Z,GLH_BS CP '0' JP M,GLH_LOOP CP '9' + 1 JP M,GLH_APPEND CP 'A' JP M,GLH_LOOP CP 'F' + 1 JP M,GLH_APPEND JP GLH_LOOP GLH_BS: LD A,C OR A JP Z,GLH_LOOP CALL PC_BS CALL PC_SPACE CALL PC_BS DEC C DEC HL JP GLH_LOOP GLH_APPEND: LD B,A LD A,C CP 2 JP P,GLH_LOOP LD A,B CALL COUT LD (HL),A INC C INC HL JP GLH_LOOP GLH_CHK: LD A,C CP 2 JP Z,GLH_EXIT CP 0 JP Z,GLH_EXIT JP GLH_LOOP GLH_EXIT: RET ; ;__HEXIN__________________________________________________________________________________________________________________________ ; ; GET ONE BYTE OF HEX DATA FROM BUFFER IN HL, RETURN IN A ;________________________________________________________________________________________________________________________________ ; HEXIN: PUSH BC ; SAVE BC REGS. CALL NIBL ; DO A NIBBLE RLC A ; MOVE FIRST BYTE UPPER NIBBLE RLC A ; RLC A ; RLC A ; LD B,A ; SAVE ROTATED BYTE CALL NIBL ; DO NEXT NIBBLE ADD A,B ; COMBINE NIBBLES IN ACC. POP BC ; RESTORE BC RET ; DONE NIBL: LD A,(HL) ; GET K.B. DATA INC HL ; INC KB POINTER CP 40H ; TEST FOR ALPHA JR NC,ALPH ; AND 0FH ; GET THE BITS RET ; ALPH: AND 0FH ; GET THE BITS ADD A,09H ; MAKE IT HEX A-F RET ; ; ; COPY A $ TERMINATED STRING FROM ADDRESS IN DE TO ADDRESS IN HL ; DE = ADDRESS OF SOURCE STRING ; LH = ADDRESS OF TARGET LOCATION ; STRCPY: LD A,(DE) CP '$' JP Z,STRCPYX LD (HL),A INC HL INC DE JP STRCPY STRCPYX: RET ; ; GET A HEX BYTE VALUE FROM THE USER ; WILL UPDATE STORED VALUE, EMPTY RESPONSE LEAVES VALUE ALONE ; DE = ADDRESS OF PROMPT STRING ; HL = ADDRESS OF VALUE ; BC = HI/BO ALLOWABLE RANGE OF VALID VALUES ; GHB_PROMPTP .DW 0 GHB_RANGE .DW 0 GHB_VALUEP .DW 0 GHB_CLEAR .DB " \b\b$" GETHEXBYTE: LD BC,000FFH GETHEXBYTERNG: LD (GHB_PROMPTP),DE LD (GHB_RANGE),BC LD (GHB_VALUEP),HL CALL NEWLINE GHB_LOOP: CALL PC_CR LD DE,STR_ENTER CALL WRITESTR CALL PC_SPACE LD DE,(GHB_PROMPTP) CALL WRITESTR CALL PC_SPACE LD BC,(GHB_RANGE) CALL PC_LBKT LD A,B CALL PRTHEXBYTE CALL PC_HYPHEN LD A,C CALL PRTHEXBYTE CALL PC_RBKT CALL PC_SPACE CALL PC_LPAREN LD HL,(GHB_VALUEP) LD A,(HL) CALL PRTHEXBYTE CALL PC_RPAREN CALL PC_COLON CALL PC_SPACE LD DE,GHB_CLEAR CALL WRITESTR LD HL,KEYBUF CALL GETLNHEX LD A,C CP 0 ; RETAIN CURRENT VALUE JP Z,GHB_EXIT CP 2 ; INPUT LOOKS OK, UPDATE IT JP Z,GHB_CHK JP GHB_LOOP ; ANYTHING ELSE, BAD INPUT, DO OVER GHB_CHK: LD HL,KEYBUF CALL HEXIN LD BC,(GHB_RANGE) CP B JP C,GHB_LOOP CP C JP Z,GHB_OK JP NC,GHB_LOOP GHB_OK: LD HL,(GHB_VALUEP) LD (HL),A GHB_EXIT: RET ; ; PRINT THE HEX BYTE VALUE IN A ; HEXSTRBUF .TEXT "XX$" ; PRTHEXBYTE: PUSH AF PUSH DE LD DE,HEXSTRBUF CALL HEXSTRBYTE LD A,'$' LD (DE),A LD DE,HEXSTRBUF CALL WRITESTR POP DE POP AF RET ; ; PRINT THE HEX WORD VALUE IN BC ; PRTHEXWORD: PUSH AF LD A,B CALL PRTHEXBYTE LD A,C CALL PRTHEXBYTE POP AF RET ; ; CONVERT VALUE IN A TO A 2 CHARACTER HEX STRING AT DE ; HEXCHR .TEXT "0123456789ABCDEF" ; HEXSTRBYTE: PUSH BC PUSH HL PUSH AF LD BC,0 RRA RRA RRA RRA AND 0FH LD C,A LD HL,HEXCHR ADD HL,BC LD A,(HL) LD (DE),A INC DE POP AF PUSH AF LD BC,0 AND 0FH LD C,A LD HL,HEXCHR ADD HL,BC LD A,(HL) LD (DE),A INC DE POP AF POP HL POP BC RET ; ; CONVERT VALUE IN BC TO A 4 CHARACTER HEX STRING AT DE ; HEXSTRWORD: LD A,B CALL HEXSTRBYTE LD A,C CALL HEXSTRBYTE RET ; ; PRINT A BYTE BUFFER IN HEX POINTED TO BY DE ; REGISTER A HAS SIZE OF BUFFER ; PRTHEXBUF: CP 0 ; EMPTY BUFFER? JP Z,PRTHEXBUF2 LD B,A PRTHEXBUF1: CALL PC_SPACE LD A,(DE) CALL PRTHEXBYTE INC DE DJNZ PRTHEXBUF1 JP PRTHEXBUFX PRTHEXBUF2: CALL PC_SPACE LD DE,STR_EMPTY CALL WRITESTR PRTHEXBUFX: RET ; ; JP TO ADDRESS IN HL IN HEX POINTED TO BY DE ; MOSTLY USEFUL TO PERFORM AN INDIRECT CALL LIKE: ; LD HL,xxxx ; CALL JPHL ; JPHL JP (HL) ; ; GENERATE A RANDOM BYTE ; ; RETURNS PSEUDO RANDOM 8 BIT NUMBER IN A. ONLY AFFECTS A. ; (SEED) IS THE BYTE FROM WHICH THE NUMBER IS GENERATED AND MUST BE ; INITIALIZED TO A NON ZERO VALUE OR THIS FUNCTION WILL ALWAYS RETURN ; ZERO. ; RB_SEED .DB 1 ; RNDBYTE SEED (MUST NOT BE ZERO) ; RNDBYTE: LD A,(RB_SEED) ; GET SEED AND 0B8H ; MASK NON-FEEDBACK BITS SCF ; SET CARRY JP PO,RB_NC ; SKIP CLEAR IF ODD CCF ; COMPLEMENT CARRY (CLEAR IT) RB_NC LD A,(RB_SEED) ; GET SEED BACK RLA ; ROTATE CARRY INTO BYTE LD (RB_SEED),A ; SAVE BACK FOR NEXT RET ; DONE ; ; ADD HL,A ; ; A REGISTER IS DESTROYED! ; ADDHLA: ADD A,L LD L,A RET NC INC H RET ; ; OUTPUT A '$' TERMINATED STRING ; WRITESTR: PUSH AF PUSH BC PUSH DE PUSH HL LD C,09H CALL 0005H POP HL POP DE POP BC POP AF RET ; ; READ A KEY, RETURN VALUE IN A ; GETKEY: PUSH BC PUSH DE PUSH HL LD C,06H LD E,0FFH CALL 0005H POP HL POP DE POP BC CP 03 JP Z,EXIT RET GETKEYUC: CALL GETKEY CP 'a' JP M,GETKEYUC_EXIT CP 'z' + 1 JP M,GETKEYUC_FIX JP GETKEYUC_EXIT GETKEYUC_FIX: AND 11011111B GETKEYUC_EXIT: RET ; ; DELAY 16US (CPU SPEED COMPENSATED) INCUDING CALL/RET INVOCATION ; REGISTER A AND FLAGS DESTROYED ; NO COMPENSATION FOR Z180 MEMORY WAIT STATES ; THERE IS AN OVERHEAD OF 3TS PER INVOCATION ; IMPACT OF OVERHEAD DIMINISHES AS CPU SPEED INCREASES ; ; CPU SCALER (CPUSCL) = (CPUHMZ - 2) FOR 16US + 3TS DELAY ; NOTE: CPUSCL MUST BE >= 1! ; ; EXAMPLE: 8MHZ CPU (DELAY GOAL IS 16US) ; LOOP = ((6 * 16) - 5) = 91TS ; TOTAL COST = (91 + 40) = 131TS ; ACTUAL DELAY = (131 / 8) = 16.375US ; ; --- TOTAL COST = (LOOP COST + 40) TS -----------------+ DELAY: ; 17TS (FROM INVOKING CALL) | LD A,(CPUSCL) ; 13TS | ; | DELAY1: ; | ; --- LOOP = ((CPUSCL * 16) - 5) TS ------------+ | DEC A ; 4TS | | #IFDEF CPU_Z180 ; | | OR A ; +4TS FOR Z180 | | #ENDIF ; | | JR NZ,DELAY1 ; 12TS (NZ) / 7TS (Z) | | ; ----------------------------------------------+ | ; | RET ; 10TS (RETURN) | ;-------------------------------------------------------+ ; ; DELAY 16US * DE (CPU SPEED COMPENSATED) ; REGISTER DE, A, AND FLAGS DESTROYED ; NO COMPENSATION FOR Z180 MEMORY WAIT STATES ; THERE IS A 27TS OVERHEAD FOR CALL/RET PER INVOCATION ; IMPACT OF OVERHEAD DIMINISHES AS DE AND/OR CPU SPEED INCREASES ; ; CPU SCALER (CPUSCL) = (CPUHMZ - 2) FOR 16US OUTER LOOP COST ; NOTE: CPUSCL MUST BE > 0! ; ; EXAMPLE: 8MHZ CPU, DE=6250 (DELAY GOAL IS .1 SEC OR 100,000US) ; INNER LOOP = ((16 * 6) - 5) = 91TS ; OUTER LOOP = ((91 + 37) * 6250) = 800,000TS ; ACTUAL DELAY = ((800,000 + 27) / 8) = 100,003US ; ; --- TOTAL COST = (OUTER LOOP + 27) TS ------------------------+ VDELAY: ; 17TS (FROM INVOKING CALL) | ; | ; --- OUTER LOOP = ((INNER LOOP + 37) * DE) TS ---------+ | LD A,(CPUSCL) ; 13TS | | ; | | VDELAY1: ; | | ; --- INNER LOOP = ((CPUSCL * 16) - 5) TS ------+ | | #IFDEF CPU_Z180 ; | | | OR A ; +4TS FOR Z180 | | | #ENDIF ; | | | DEC A ; 4TS | | | JR NZ,VDELAY1 ; 12TS (NZ) / 7TS (Z) | | | ; ----------------------------------------------+ | | ; | | DEC DE ; 6TS | | #IFDEF CPU_Z180 ; | | OR A ; +4TS FOR Z180 | | #ENDIF ; | | LD A,D ; 4TS | | OR E ; 4TS | | JP NZ,VDELAY ; 10TS | | ;-------------------------------------------------------+ | ; | RET ; 10TS (FINAL RETURN) | ;---------------------------------------------------------------+ ; ; DELAY ABOUT 0.5 SECONDS ; 500000US / 16US = 31250 ; LDELAY: PUSH AF PUSH DE LD DE,31250 CALL VDELAY POP DE POP AF RET ; ; HANDLE USER INPUT FOR A MENU BASED ON TABLE OF MENU DATA. DISPATCH MENU FUNCTIONS. ; ON INPUT, HL=ADDRESS OF MENU TABLE, B=COUNT OF MENU ENTRIES ; .module MenuInfo MenuInfo ; TRANSIENT STORAGE FOR CURRENT MENU _DrawProc .dw 0 ; ADDRESS OF MENU DRAW ROUTINE _TableAdr .dw 0 ; ADDRESS OF MENU TABLE DATA _EntryInfo .dw 0 ; ENTRY COUNT / ENTRY SIZE _Size .equ $ - MenuInfo ; RUNMENU: push hl ; save address of menu info data _Run: pop hl ; restore/resave menu init address push hl ld de,MenuInfo ld bc,_Size ldir ld hl,(_DrawProc) call JPHL ; call menu draw routine _GetKey: call GETKEYUC ; GET USER KEYPRESS ld hl,(_TableAdr) ld bc,(_EntryInfo) ; B=COUNT, C=ENTRY SIZE ld d,0 ; put entry size in de ld e,c ; " ; a=key pressed, hl=menu table address, b=entry count, de=entry size _Loop: ld c,(hl) cp c jp z,_Match ; found code add hl,de ; point to next entry djnz _Loop ; check next entry till count is zero jp _GetKey ; keep trying _Match: inc hl ; load string ld e,(hl) inc hl ld d,(hl) call WRITESTR ; display it inc hl ; load code address ld e,(hl) inc hl ld d,(hl) ld a,d ; check for zero or e jp z,_Exit ; zero means exit ex de,hl call JPHL ; indirect call to menu function jp _Run _Exit: pop hl ret ; ; CONTROL CHARACTERS ; CHR_CR .EQU 0DH CHR_LF .EQU 0AH CHR_BS .EQU 08H CHR_ESC .EQU 1BH ; STR_DRIVERESET .TEXT "RESET DRIVE...$" STR_EXECUTE .TEXT "EXECUTION$" STR_OP .TEXT "OPERATION$" STR_FORMAT .TEXT "FORMAT$" STR_SENDCMD .TEXT "SEND COMMAND$" STR_GETRESULTS .TEXT "GET RESULTS$" STR_SEEKWAIT .TEXT "SEEK WAIT$" STR_DOR .TEXT "SET DOR$" STR_PROGRESS .TEXT "PROGRESS$" STR_MISMATCH .TEXT "DATA MISMATCH AT $" STR_RESET .TEXT "FDCRESET...$" STR_NOTIMPL .TEXT "*** NOT IMPLEMENTED ***$" STR_NORESP .TEXT "*** DRIVE NOT RESPONDING ***$" STR_EOD .TEXT "$" STR_EMPTY .TEXT "$" STR_TIMEOUT .TEXT "$" STR_ARROW .TEXT " -->$" STR_ENTER .TEXT "ENTER$" STR_ON .TEXT "ON $" STR_OFF .TEXT "OFF$" STR_DRV720 .TEXT "720KB $" STR_DRV144 .TEXT "1.44MB$" STR_MODEPOLL .TEXT "POLLING $" STR_MODEINT .TEXT "INTERRUPT $" STR_MODEDMA .TEXT "DMA $" STR_CC0 .TEXT " CC0=$" STR_CC1 .TEXT " CC1=$" STR_CC2 .TEXT " CC2=$" STR_N .TEXT " N=$" STR_SC .TEXT " SC=$" STR_GPL .TEXT " GPL=$" STR_D .TEXT " D=$" STR_ST0 .TEXT " ST0=$" STR_ST1 .TEXT " ST1=$" STR_ST2 .TEXT " ST2=$" STR_CYL .TEXT " CYL=$" STR_UNIT .TEXT " UNIT=$" STR_HEAD .TEXT " HD=$" STR_REC .TEXT " SEC=$" STR_NUM .TEXT " NUM=$" STR_DENS .TEXT " DENS=$" STR_EOTSEC .TEXT " EOTSEC=$" STR_GAP .TEXT " GAP=$" STR_DTL .TEXT " DTL=$" STR_SN .TEXT " SN=$" STR_NCN .TEXT " NCN=$" STR_PCN .TEXT " PCN=$" STR_MSR .TEXT " MSR=$" STR_BC .TEXT " BC=$" STR_DE .TEXT " DE=$" STR_HL .TEXT " HL=$" STR_DSKCHG .TEXT " DC=$" ; KEYBUFLEN .EQU 80 KEYBUF .FILL KEYBUFLEN,' ' ; STACKSAV .DW 0 STACKSIZ .EQU 40H ; WE ARE A STACK PIG .FILL STACKSIZ,0 STACK .EQU $ ; BUFFER .EQU 4000H VFYBUF .EQU 5000H BUFSIZ .EQU 0200H .END