************************************************************* * NAME BIOS * ************************************************************** * I/O DRIVERS FOR CP/M * ************************************************************* * VERSION 1.0 03/17/78 * * RUNS ON Z80, 8080, OR 8085 * * MODIFIED BY M.A. SASS * * VER. 1.1 * * 6 MS STEPPING RATE * * VER. 1.2 * * FORMAT COMPATIBLE WITH 179X FAMILY * ************************************************************* ORG 0F000H ; ; NBYTES EQU 128 ; BYTES PER SECTOR ; ; RBASE EQU 0000H ; START OF RAM TADDR EQU RBASE+40H ; TRANSFER ADDRESS UNIT EQU RBASE+42H ; DRIVE # SCTR EQU RBASE+43H ; SECTOR TRK EQU RBASE+44H ; TRACK NREC EQU RBASE+45H ; # OF SECTORS ERMASK EQU RBASE+46H ; ERROR MASK ERSTAT EQU RBASE+47H ; ERROR STATUS IDSV EQU RBASE+48H ; 4BYTE CMDSV EQU RBASE+4CH ; COMMAND SAVE SPSV EQU RBASE+4DH ; SP SAVE ( 2 BYTES ) SSTACK EQU RBASE+80H ; SYSTEM STACK COLD EQU RBASE+80H ; COLD START ADDRESS ; ; ; PORTS USED BY DISK CONTROLLER ; X EQU 60H ; DRIVE SELECT PORT SELECT EQU X+3 ; DRIVE SELECT PORT STATUS EQU X+4 ; STATUS PORT TRACK EQU X+5 ; TRACK PORT SECTOR EQU X+6 ; SECTOR PORT DATA EQU X+7 ; DATA PORT CMD EQU X+4 ; COMMAND PORT ; ; ; SERIAL I/O PORTS ; ; SDATA EQU 1 ; SERIAL DATA PORT SSTAT EQU 0 ; SERIAL STATUS PORT RXRMSK EQU 1 ; RX RDY MASK TXRMSK EQU 80H ; TX RDY MASK ; ; ; DISK ERROR STATUS BITS (ERSTAT) ; ; ; BIT 7 - DRIVE NOT READY ; BIT 6 - WRITE PROTECT ; BIT 5 - TRACK SEEK ERROR ; BIT 4 - SECTOR NOT FOUND ; BIT 3 - CRC ERROR ; BIT 2 - DATA LOST ; BIT 1 - DRQ BIT ; BIT 0 - WRITE DELETED SECTOR READ ; ; ; DISK CONTROLLER COMMAND CODES ; ; RDACMD EQU 0C4H ; READ ADDRESS CMD RDCMD EQU 88H ; READ SECTOR CMD WRCMD EQU 0A8H ; WRITE SECTOR CMD WRTCMD EQU 0F4H ; WRITE TRACK CMD ; ; ; ; CP/M SYSTEM LINKAGES ; ; BIOS JMP BOOT ; COLD START ENTRY WBOOTE JMP BOOT ; WARM START ENTRY CSE JMP CONST ; CONSOLE STATUS CIE JMP CONIN ; READ CONSOLE COE JMP CONOUT ; WRITE CONSOLE LSTE JMP LIST ; WRITE PRINTER PCHE JMP PUNCH ; WRITE PUNCH RDRE JMP READER ; READ READER HME JMP HOME ; MOVE DISK TO TRK 00 SDSKE JMP SELDSK ; SELECT DISK DRIVE STRKE JMP SETTRK ; SET DISK TRACK SSECE JMP SETSEC ; SET DISK SECTOR SDMAE JMP SETDMA ; SET MEM ADDR FOR READ/WRITE RDE JMP READ ; READ A SECTOR WRE JMP WRITE ; WRITE A SECTOR LDE JMP LOADER ; LOAD 'N' SECTORS SVE JMP SAVER ; SAVE 'N' RECORDS FMATE JMP FMAT ; FORMAT A TRACK SCANE JMP SCAN ; SCAN FOR OPERAND PTXTE JMP PTXT ; PRINT TEXT PACCE JMP PACC ; PRINT ACC ; ; ; MINI / FULL SIZE VARIABLES ; ; SET UP THESE BYTES FOR THE TYPE OF ; DRIVE TO BE USED. THESE ARE THE ONLY ; SOFTWARE CHANGES WITHIN BIOS TO CONVERT ; FROM MINI TO FULL SIZE DRIVES (OR VISA ; VERSA). ; ; ; MINI FULL SIZE NSCTRS DB 1AH ; 12 1A SCTRS/TRK NTRKS DB 4DH ; 23 4D TRACKS/SIDE GAP1 DB 2FH ; 14 2F FORMAT GAP1 GAP2 DB 0BH ; 11 0B FORMAT GAP2 GAP3 DB 1AH ; 0E 1A FORMAT GAP3 RSCMD DB 09H ; 0B 09 RESTORE CMD SKCMD DB 1DH ; 1F 1D SEEK CMD SKNCMD DB 19H ; 1B 19 SEEK W/NO VER HLDLY DB 23H ; 4B 23 HEAD LOAD DELAY ; ; ; DELAY CONSTANTS MUST BE SET UP FOR ; THE CPU CLOCK RATE USED: ; 2MHZ 2.5MHZ 3MHZ 4MHZ ; DLYCON DB 8EH ; 8E B2 D8 00 ; BOOT IN 7FH ; CLEAR H. W. LXI SP,SSTACK LXI H,COLD SHLD TADDR MVI A,1 STA NREC STA SCTR XRA A STA UNIT STA TRK CALL LOADER ; BOOT IN CP/M (EXCEPT CBIOS) JZ COLD ; JUMP TO COLD START HLT ; IF BAD DISK, HALT ; ; NON-DISK I/O DRIVER LINKAGES ; CONST IN SSTAT ; CONSOLE STATUS ANI RXRMSK MVI A,0 RNZ!CMA!RET ; CONSOLE INPUT ; CONIN IN SSTAT ; KEYBOARD STATUS ANI RXRMSK JNZ CONIN IN SDATA ANI 7FH ; STRIP PARITY RET ; RETURN TO CALLER WITH DATA IN A REG ; ; CONSOLE OUTPUT ; CONOUT PUSH PSW ; SAVE STATUS IN SSTAT ANI TXRMSK ; TX BFR EMPTY JNZ CONOUT+1 ; TRY AGAIN TIL EMPTY MOV A,C ; GET FROM 'C' REG THE DATA OUT SDATA POP PSW ; RESTORE RET ; ; READER INPUT ; READER RET ; ; LISTING OUTPUT ; LIST RET ; ; ; PUNCH OUTPUT ; PUNCH RET ; ; ; ; DISK CONTROLLER LINKAGES ; ; HOME CALL DRVSET ; SELECT DRIVE LXI H,0 DAD SP SHLD SPSV LDA RSCMD CALL CMDI XRA A RET ; ; SELECT DISK DRIVE ; SELDSK MOV A,C STA UNIT RET ; ; SET TRACK NUMBER ; SETTRK MOV A,C STA TRK RET ; ; SET SECTOR NUMBER ; SETSEC MOV A,C STA SCTR RET ; ; SET MEMORY TRANSFER ADDRESS FOR DISK READ/WRITE ; SETDMA PUSH B POP H SHLD TADDR RET ; ; READ A SECTOR ; READ MVI B,10 READ1 PUSH B CALL RDSC ; READ A SECTOR POP B RZ ; RETURN IF NO ERROR DCR B JNZ READ1 ; 10 RETRIES XRA A INR A RET ; ERROR RETURN ; ; WRITE A SECTOR ; WRITE MVI B,10 WRITE1 PUSH B CALL WRSC ; WRITE A SECTOR POP B RZ ; RETURN IF NO ERROR DCR B JNZ WRITE1 XRA A INR A RET ; ERROR RETURN ; ; ; ; ; END OF COMMAND ; ; ; END11 IN STATUS ANI 1 JNZ END11 CALL DWAIT IN STATUS MOV D,A LDA ERMASK ANA D ; CHECK FOR ERRORS RZ END1 MOV A,D END2 STA ERSTAT ; SAVE ERROR BITS ORI 1 LHLD SPSV SPHL!RET ; ; ; ; SELECT SIDE AND SEEK TRACK ; SEEK MVI B,0 LDA NTRKS MOV C,A LDA TRK CMP C JC SEEK1 MVI B,10H ; SIDE 2 SUB C SEEK1 MOV C,A ; SAVE NEW TRK # IN TRACK ; PRESENT TRACK CMP C PUSH PSW IN SELECT CMA MOV E,A ANI 10 ; CHECK SIDE SELECT CMP B JZ SEEK3 ; IF SAME SIDE JUMP MOV A,E ANI 0FH ORA B CMA OUT SELECT ; SELECT NEW SIDE MVI A,1 CALL MSEC SEEK3 POP PSW RZ ; IF SAME TRK, RETURN MVI B,3 ; 3 RETRIES SEEK2 MOV A,C ; TRK # OUT DATA ; LDA SKCMD ; SEEK COMMAND PUSH B ; SAVE RETRY COUNT AND TRK # CALL CMDI POP B IN STATUS ANI 10H ; TEST FOR SEEK ERROR RZ ; IF NO ERROR, RETURN LDA RSCMD PUSH B CALL CMDI ; RESTOR DISK POP B DCR B JNZ SEEK2 MVI A,20H JMP END2 ; ; ; CHECK DRIVE SELECT AND CHANGE IF DIFFERENT ; DRVSET IN SELECT CMA ANI 0FH JZ DRVS1 ; IF NONE, JUMP MVI C,0FFH DRVS0 INR C ; CONVERT TO DRV# RAR JNC DRVS0 LDA UNIT ANI 3 CMP C RZ ; IF SAME DRV, RETURN DRVS1 CALL DRSEL XRA A INR A RET ; ; ; ; READ PRESENT DISK ADDRESS (TRK AND SECTOR) ; ; IDRD LXI H,IDSV MVI B,4 MVI A,0F8H STA ERMASK CALL SWEB MVI A,RDACMD ; 'READ ADDRESS' CMD CALL RDSC0 ; READ ID LDA IDSV OUT TRACK RET ; ; ; ; READ SECTOR COMMAND ; ; RDSC LXI H,0 DAD SP SHLD SPSV CALL DRVSET CNZ IDRD ; NEW DRIVE CALL SEEK ; SEEK TRACK MVI A,0FEH STA ERMASK CALL TRINT MVI A,RDCMD ; READ COMM@ND CALL HLCHK ; SET UP HEAD LOAD BIT RDSC0 STA CMDSV OUT CMD ; OUTPUT COMMAND RDSC1 IN DATA MOV M,A INX H DCR B JNZ RDSC1 CALL END11 XRA A RET ; ; ; WRITE SECTOR COMMAND ; WRSC LXI H,0 DAD SP SHLD SPSV CALL DRVSET CNZ IDRD CALL SEEK MVI A,0FCH STA ERMASK CALL TRINT MVI A,WRCMD CALL HLCHK STA CMDSV OUT CMD ; OUTPUT COMMAND WRSC1 MOV A,M ; MEMORY TO A OUT DATA INX H ; BUMP IT DCR B JNZ WRSC1 ; DO IT AGAIN TILL DONE CALL END11 ; CLEAN UP THE ACT XRA A ; CLEAR EVERY THING RET ; GO HOME ; ; ; TYPE I COMMANDS ; ; CMDI STA CMDSV ; SAVE COMMAND MVI A,80H STA ERMASK ; SET UP ERROR MASK CMDII IN STATUS ; GET STATUS ANI 1 ; MASK IT JNZ CMDII ; WAIT FOR NOT BUSY IN SELECT ; NOT BUSY ANI 1FH ORI 60H OUT SELECT ; NO WAIT STATE ENBL LDA CMDSV ; RESTORE COMMAND OUT CMD ; OUTPUT COMMAND MVI A,1 CALL MSEC ; WAIT FOR STATUS JMP END11 ; ; ; ; DISABLE WAIT STATES ; DWAIT IN SELECT ORI 0E0H ; NEG. TRUE AND OUT SELECT RET ; ; ; ENABLE WAIT STATES ; ; SWEB IN SELECT ANI 1FH OUT SELECT RET ; ; ; SELECT DRIVE ; ; DRSEL LDA UNIT ANI 3 ; ONLY 4 DRIVES ORA A ; CHECK FOR ZERO MOV C,A MVI A,1 JZ DRSEL3 ; NO SHIFTING DRSEL2 RLC ; SHIFT LEFT DCR C JNZ DRSEL2 DRSEL3 CMA OUT SELECT LDA HLDLY ; HEAD DELAY CALL MSEC ; DELAY 35 MILLISECS IN STATUS ANI 80H ; MASK JZ DRSEL4 POP PSW MVI A,40H ; IF DRIVE NOT RDY, ERR JMP END2 ; CLEAN IT UP DRSEL4 RET ; ; ; DELAY ( A REG ) * 1 MILLISECOND ; ; MSEC MOV B,A MSEC0 LDA DLYCON MSEC1 DCR A JNZ MSEC1 DCR B JNZ MSEC0 RET ; ; ; INITIALIZE FOR DISK TRANSFER ; ; TRINT LHLD TADDR MVI B,NBYTES LDA SCTR OUT SECTOR ; SELECT SECTOR JMP SWEB ; ENABLE WAIT ; ; ; ; CHECK FOR HEAD LOADED, IF NOT SET ; 'H' BIT IN COMMAND ; ; HLCHK MOV D,A ; SAVE COMMAND IN D MVI A,0D0H OUT CMD ; 'FORCE INTERRUPT' CMD MVI A,4 HLCHK0 DCR A JNZ HLCHK0 IN STATUS ANI 20H MOV A,D ; RESTORE COMMAND RNZ ; IF HEAD LOADED, RET ORI 4 ; SET 'H' BIT RET ; ; ; LOAD # OF SECTORS IN (NREC) ; ; LOADER CALL READ ; READ A SECTOR JNZ ERR CALL INCP ; INC TRK/SCTR JNZ LOADER RET ; ; ; SAVE # OF SECTORS IN (NREC) ; ; SAVER CALL WRITE JNZ ERR CALL INCP JNZ SAVER RET ; ; INC SECTOR AND TRACK ; ; INCP LHLD TADDR LXI D,NBYTES DAD D SHLD TADDR LXI H,NREC ; POINT TO NREC DCR M RZ DCX H DCX H ; POINT TO SCTR INR M ; INC SCTR # LDA NSCTRS INR A CMP M ; LAST SCTR ON TRK? RNZ ; IF NOT, RETURN MVI M,1 ; SET TO SCTR 1 INX H ; POINT TO TRK INR M ; INC TRK # ORA A RET ; ; ; ERROR MESSAGE 'DISK ERR#' ; ; ERR LXI H,ERMSG CALL PTXT LDA ERSTAT CALL PACC MVI C,0AH ; LINE FEED CALL CONOUT ; CONSOLE MVI C,0DH ; CARRIAGE RETURN JMP CONOUT ;CONSOLE RET ; ERMSG DB 'DISK ERR# ',3 ; ; ; PRINT TEXT ; ; PTXT MOV A,M ; FETCH A BYTE CPI 3 ; TERMINATOR RZ MOV C,A CALL CONOUT ; CONSOLE INX H ; BUMP TE BUMP JMP PTXT ; TIL DONE ; ; ; PRINT ACCUMULATOR ; ; PACC PUSH PSW RRC!RRC!RRC!RRC CALL PRVAL POP PSW ; ; PRVAL ANI 0FH ADI 90H DAA ACI 40H DAA MOV C,A JMP CONOUT ; PRINT IT ; ; ; ASCII TO BINARY CONVERSION ; ; ASBIN SUI 30H CPI 10 RM SUI 7 RET ; ; ; CHECK FOR VALID HEX CHARACTER ; ; AORN CPI '0' JC AORN2 ; JUMP IF < 30H CPI '9'+1 JC AORN1 ; JUMP IF < 3AH CPI 'A'-1 JC AORN2 ; JUMP IF < 'A' CPI 'F'+1 JNC AORN2 ; JUMP IF <'G' AORN1 XRA A RET ; VALID DATA RET AORN2 XRA A INR A RET ; NOT HEX CHAR ; ; ; CHECK FOR TERMINATOR ; ; TERMCK CPI ' ' ; SPACE RZ CPI ',' ; COMMA RZ ; THERE CPI '^' ; UP ARROW JZ TCHK0 CPI '.' ; PERIOD JZ TCHK0 CPI 0DH ; CR RNZ TCHK0 PUSH B MVI C,0AH CALL CONOUT POP B XRA A RET ; ; ; SCAN FOR OPERAND FROM KEYBOARD ; ; ; EXIT WITH DATA IN HL, AND TERMINATOR ; IN C. IF VALID DATA, RETURN WITH ; ZERO FLAG SET. B CONTAINS # OF CHARACTERS ENTERED ; ; SCAN LXI H,0 MOV B,L SCAN1 CALL ECHO INR B ; INC CHAR COUNT CALL TERMCK RZ ; IF TERMINATOR, RETURN CALL AORN ; VALID DATA CHEECK RNZ ; IF NOT RETURN MOV A,C CALL ASBIN ; CONVERT TO BINARY DAD H DAD H DAD H DAD H ; P SHIFT 4 BITS ADD L MOV L,A JMP SCAN1 ; ; ; ECHO CALL CONIN MOV C,A JMP CONOUT ; ; ; FORMAT A TRACK ( IBM MINI DISK FORMAT ) ; ; FMAT LDA TRK OUT DATA LDA SKNCMD CALL CMDI MVI L,1 LDA NSCTRS MOV H,A LDA GAP1 MOV B,A XRA A STA ERMASK CALL SWEB MVI A,WRTCMD STA CMDSV OUT CMD ; OUTPUT COMMAND XRA A CMA ; ACC TO FF FMAT3 OUT DATA ; GAP1 DCR B JNZ FMAT3 MVI A,0FCH ; INDEX MARK OUT DATA LDA GAP3 MOV B,A XRA A CMA ; ACC TO ONE'S FMAT4 OUT DATA DCR B JNZ FMAT4 FMAT51 MVI B,6 XRA A ; ACC TO ZERO'S FMAT41 OUT DATA DCR B JNZ FMAT41 FMAT5 MVI A,0FEH ; ID ADDRESS MARK OUT DATA IN TRACK OUT DATA ; TRACK # XRA A OUT DATA ; 0 GAP MOV A,L OUT DATA ; SECTOR # XRA A OUT DATA ; SECTOR LENGTH = 128 BYTES MVI A,0F7H ; SEND CRC ( 2 BYTES ) OUT DATA LDA GAP2 MOV B,A XRA A CMA ; ACC TO ONE'S FMAT6 OUT DATA ; GAP2 DCR B JNZ FMAT6 MVI B,6 XRA A ; SET TO ZERO FMAT61 OUT DATA DCR B JNZ FMAT61 MVI A,0FBH ; DATA ADDRESS MARK OUT DATA MVI A,0E5H MVI B,128D FMAT7 OUT DATA ; 128 E5'S IN DATA FIELD DCR B JNZ FMAT7 MVI A,0F7H OUT DATA ; CRC ( 2 BYTES ) LDA GAP3 MOV B,A XRA A CMA ; SET TO ONES'S FMAT8 OUT DATA ; GAP3 DCR B JNZ FMAT8 INR L ; INC SECTOR # DCR H ; CHECK FOR LAST SECTOR JNZ FMAT51 ; WRITE ALL SECTORS CALL END11 XRA A RET END