; BIOSKRNL.ASM FOR MORROW DISK JOCKEY TITLE 'ROOT MODULE OF RELOCATABLE BIOS FOR CP/M 3.0' ; VERSION 1.0 15 SEPT 82 TRUE EQU -1 FALSE EQU NOT TRUE BANKED EQU TRUE ; COPYRIGHT (C), 1982 ; DIGITAL RESEARCH, INC ; P.O. BOX 579 ; PACIFIC GROVE, CA 93950 ; THIS IS THE INVARIANT PORTION OF THE MODULAR BIOS AND IS ; DISTRIBUTED AS SOURCE FOR INFORMATIONAL PURPOSES ONLY. ; ALL DESIRED MODIFICATIONS SHOULD BE PERFORMED BY ; ADDING OR CHANGING EXTERNALLY DEFINED MODULES. ; THIS ALLOWS PRODUCING "STANDARD" I/O MODULES THAT ; CAN BE COMBINED TO SUPPORT A PARTICULAR SYSTEM ; CONFIGURATION. CR EQU 13 LF EQU 10 BELL EQU 7 CTLQ EQU 'Q'-'@' CTLS EQU 'S'-'@' CCP EQU 0100H ; CONSOLE COMMAND PROCESSOR GETS LOADED INTO THE TPA CSEG ; GENCPM PUTS CSEG STUFF IN COMMON MEMORY ; VARIABLES IN SYSTEM DATA PAGE EXTRN @COVEC,@CIVEC,@AOVEC,@AIVEC,@LOVEC ; I/O REDIRECTION VECTORS EXTRN @MXTPA ; ADDR OF SYSTEM ENTRY POINT EXTRN @BNKBF ; 128 BYTE SCRATCH BUFFER ; INITIALIZATION EXTRN ?INIT ; GENERAL INITIALIZATION AND SIGNON EXTRN ?LDCCP,?RLCCP ; LOAD & RELOAD CCP FOR BOOT & WBOOT ; USER DEFINED CHARACTER I/O ROUTINES EXTRN ?CI,?CO,?CIST,?COST ; EACH TAKE DEVICE IN EXTRN ?CINIT ; (RE)INITIALIZE DEVICE IN EXTRN @CTBL ; PHYSICAL CHARACTER DEVICE TABLE ; DISK COMMUNICATION DATA ITEMS EXTRN @DTBL ; TABLE OF POINTERS TO XDPHS PUBLIC @ADRV,@RDRV,@TRK,@SECT ; PARAMETERS FOR DISK I/O PUBLIC @DMA,@DBNK,@CNT ; '' '' '' '' ; MEMORY CONTROL PUBLIC @CBNK ; CURRENT BANK EXTRN ?XMOVE,?MOVE ; SELECT MOVE BANK, AND BLOCK MOVE EXTRN ?BANK ; SELECT CPU BANK ; CLOCK SUPPORT EXTRN ?TIME ; SIGNAL TIME OPERATION ; GENERAL UTILITY ROUTINES PUBLIC ?PMSG,?PDEC ; PRINT MESSAGE, PRINT NUMBER FROM 0 TO 65535 PUBLIC ?PDERR ; PRINT BIOS DISK ERROR MESSAGE HEADER MACLIB MODEBAUD ; DEFINE MODE BITS ; EXTERNAL NAMES FOR BIOS ENTRY POINTS PUBLIC ?BOOT,?WBOOT,?CONST,?CONIN,?CONO,?LIST,?AUXO,?AUXI PUBLIC ?HOME,?SLDSK,?STTRK,?STSEC,?STDMA,?READ,?WRITE PUBLIC ?LISTS,?SCTRN PUBLIC ?CONOS,?AUXIS,?AUXOS,?DVTBL,?DEVIN,?DRTBL PUBLIC ?MLTIO,?FLUSH,?MOV,?TIM,?BNKSL,?STBNK,?XMOV ; BIOS JUMP VECTOR. ; ALL BIOS ROUTINES ARE INVOKED BY CALLING THESE ; ENTRY POINTS. ?BOOT: JMP BOOT ; INITIAL ENTRY ON COLD START ?WBOOT: JMP WBOOT ; REENTRY ON PROGRAM EXIT, WARM START ?CONST: JMP CONST ; RETURN CONSOLE INPUT STATUS ?CONIN: JMP CONIN ; RETURN CONSOLE INPUT CHARACTER ?CONO: JMP CONOUT ; SEND CONSOLE OUTPUT CHARACTER ?LIST: JMP LIST ; SEND LIST OUTPUT CHARACTER ?AUXO: JMP AUXOUT ; SEND AUXILLIARY OUTPUT CHARACTER ?AUXI: JMP AUXIN ; RETURN AUXILLIARY INPUT CHARACTER ?HOME: JMP HOME ; SET DISKS TO LOGICAL HOME ?SLDSK: JMP SELDSK ; SELECT DISK DRIVE, RETURN DISK PARAMETER INFO ?STTRK: JMP SETTRK ; SET DISK TRACK ?STSEC: JMP SETSEC ; SET DISK SECTOR ?STDMA: JMP SETDMA ; SET DISK I/O MEMORY ADDRESS ?READ: JMP READ ; READ PHYSICAL BLOCK(S) ?WRITE: JMP WRITE ; WRITE PHYSICAL BLOCK(S) ?LISTS: JMP LISTST ; RETURN LIST DEVICE STATUS ?SCTRN: JMP SECTRN ; TRANSLATE LOGICAL TO PHYSICAL SECTOR ?CONOS: JMP CONOST ; RETURN CONSOLE OUTPUT STATUS ?AUXIS: JMP AUXIST ; RETURN AUX INPUT STATUS ?AUXOS: JMP AUXOST ; RETURN AUX OUTPUT STATUS ?DVTBL: JMP DEVTBL ; RETURN ADDRESS OF DEVICE DEF TABLE ?DEVIN: JMP ?CINIT ; CHANGE BAUD RATE OF DEVICE ?DRTBL: JMP GETDRV ; RETURN ADDRESS OF DISK DRIVE TABLE ?MLTIO: JMP MULTIO ; SET MULTIPLE RECORD COUNT FOR DISK I/O ?FLUSH: JMP FLUSH ; FLUSH BIOS MAINTAINED DISK CACHING ?MOV: JMP ?MOVE ; BLOCK MOVE MEMORY TO MEMORY ?TIM: JMP ?TIME ; SIGNAL TIME AND DATE OPERATION ?BNKSL: JMP BNKSEL ; SELECT BANK FOR CODE EXECUTION AND DEFAULT DMA ?STBNK: JMP SETBNK ; SELECT DIFFERENT BANK FOR DISK I/O DMA OPERATIONS. ?XMOV: JMP ?XMOVE ; SET SOURCE AND DESTINATION BANKS FOR ONE OPERATION JMP 0 ; RESERVED FOR FUTURE EXPANSION JMP 0 ; RESERVED FOR FUTURE EXPANSION JMP 0 ; RESERVED FOR FUTURE EXPANSION ; BOOT ; INITIAL ENTRY POINT FOR SYSTEM STARTUP. DSEG ; THIS PART CAN BE BANKED BOOT: LXI SP,BOOT$STACK MVI C,15 ; INITIALIZE ALL 16 CHARACTER DEVICES C$INIT$LOOP: PUSH B CALL ?CINIT POP B DCR C JP C$INIT$LOOP CALL ?INIT ; PERFORM ANY ADDITIONAL SYSTEM INITIALIZATION ; AND PRINT SIGNON MESSAGE LXI B,16*256+0 LXI H,@DTBL ; INIT ALL 16 LOGICAL DISK DRIVES D$INIT$LOOP: PUSH B ; SAVE REMAINING COUNT AND ABS DRIVE MOV E,M INX H MOV D,M INX H ; GRAB @DRV ENTRY MOV A,E ORA D JZ D$INIT$NEXT ; IF NULL, NO DRIVE PUSH H ; SAVE @DRV POINTER XCHG ; XDPH ADDRESS IN DCX H DCX H MOV A,M STA @RDRV ; GET RELATIVE DRIVE CODE MOV A,C STA @ADRV ; GET ABSOLUTE DRIVE CODE DCX H ; POINT TO INIT POINTER MOV D,M DCX H MOV E,M ; GET INIT POINTER XCHG CALL IPCHL ; CALL INIT ROUTINE POP H ; RECOVER @DRV POINTER D$INIT$NEXT: POP B ; RECOVER COUNTER AND DRIVE # INR C DCR B JNZ D$INIT$LOOP ; AND LOOP FOR EACH DRIVE JMP BOOT$1 CSEG ; FOLLOWING IN RESIDENT MEMORY BOOT$1: CALL SET$JUMPS CALL ?LDCCP ; FETCH CCP FOR FIRST TIME JMP CCP ; WBOOT ; ENTRY FOR SYSTEM RESTARTS. WBOOT: LXI SP,BOOT$STACK CALL SET$JUMPS ; INITIALIZE PAGE ZERO CALL ?RLCCP ; RELOAD CCP JMP CCP ; THEN RESET JMP VECTORS AND EXIT TO CCP SET$JUMPS: IF BANKED MVI A,1 ! CALL ?BNKSL ENDIF MVI A,JMP STA 0 STA 5 ; SET UP JUMPS IN PAGE ZERO LXI H,?WBOOT SHLD 1 ; BIOS WARM START ENTRY LHLD @MXTPA SHLD 6 ; BDOS SYSTEM CALL ENTRY RET DS 64 BOOT$STACK EQU $ ; DEVTBL ; RETURN ADDRESS OF CHARACTER DEVICE TABLE DEVTBL: LXI H,@CTBL RET ; GETDRV ; RETURN ADDRESS OF DRIVE TABLE GETDRV: LXI H,@DTBL RET ; CONOUT ; CONSOLE OUTPUT. SEND CHARACTER IN ; TO ALL SELECTED DEVICES CONOUT: LHLD @COVEC ; FETCH CONSOLE OUTPUT BIT VECTOR JMP OUT$SCAN ; AUXOUT ; AUXILIARY OUTPUT. SEND CHARACTER IN ; TO ALL SELECTED DEVICES AUXOUT: LHLD @AOVEC ; FETCH AUX OUTPUT BIT VECTOR JMP OUT$SCAN ; LIST ; LIST OUTPUT. SEND CHARACTER IN ; TO ALL SELECTED DEVICES. LIST: LHLD @LOVEC ; FETCH LIST OUTPUT BIT VECTOR OUT$SCAN: MVI B,0 ; START WITH DEVICE 0 CO$NEXT: DAD H ; SHIFT OUT NEXT BIT JNC NOT$OUT$DEVICE PUSH H ; SAVE THE VECTOR PUSH B ; SAVE THE COUNT AND CHARACTER NOT$OUT$READY: CALL COSTER ORA A JZ NOT$OUT$READY POP B PUSH B ; RESTORE AND RESAVE THE CHARACTER AND DEVICE CALL ?CO ; IF DEVICE SELECTED, PRINT IT POP B ; RECOVER COUNT AND CHARACTER POP H ; RECOVER THE REST OF THE VECTOR NOT$OUT$DEVICE: INR B ; NEXT DEVICE NUMBER MOV A,H ORA L ; SEE IF ANY DEVICES LEFT JNZ CO$NEXT ; AND GO FIND THEM... RET ; CONOST ; CONSOLE OUTPUT STATUS. RETURN TRUE IF ; ALL SELECTED CONSOLE OUTPUT DEVICES ; ARE READY. CONOST: LHLD @COVEC ; GET CONSOLE OUTPUT BIT VECTOR JMP OST$SCAN ; AUXOST ; AUXILIARY OUTPUT STATUS. RETURN TRUE IF ; ALL SELECTED AUXILIARY OUTPUT DEVICES ; ARE READY. AUXOST: LHLD @AOVEC ; GET AUX OUTPUT BIT VECTOR JMP OST$SCAN ; LISTST ; LIST OUTPUT STATUS. RETURN TRUE IF ; ALL SELECTED LIST OUTPUT DEVICES ; ARE READY. LISTST: LHLD @LOVEC ; GET LIST OUTPUT BIT VECTOR OST$SCAN: MVI B,0 ; START WITH DEVICE 0 COS$NEXT: DAD H ; CHECK NEXT BIT PUSH H ; SAVE THE VECTOR PUSH B ; SAVE THE COUNT MVI A,0FFH ; ASSUME DEVICE READY CC COSTER ; CHECK STATUS FOR THIS DEVICE POP B ; RECOVER COUNT POP H ; RECOVER BIT VECTOR ORA A ; SEE IF DEVICE READY RZ ; IF ANY NOT READY, RETURN FALSE INR B ; DROP DEVICE NUMBER MOV A,H ORA L ; SEE IF ANY MORE SELECTED DEVICES JNZ COS$NEXT ORI 0FFH ; ALL SELECTED WERE READY, RETURN TRUE RET COSTER: ; CHECK FOR OUTPUT DEVICE READY, INCLUDING OPTIONAL ; XON/XOFF SUPPORT MOV L,B MVI H,0 ; MAKE DEVICE CODE 16 BITS PUSH H ; SAVE IT IN STACK DAD H DAD H DAD H ; CREATE OFFSET INTO DEVICE CHARACTERISTICS TBL LXI D,@CTBL+6 DAD D ; MAKE ADDRESS OF MODE BYTE MOV A,M ANI MB$XONXOFF POP H ; RECOVER CONSOLE NUMBER IN JZ ?COST ; NOT A XON DEVICE, GO GET OUTPUT STATUS DIRECT LXI D,XOFFLIST DAD D ; MAKE POINTER TO PROPER XON/XOFF FLAG CALL CIST1 ; SEE IF THIS KEYBOARD HAS CHARACTER MOV A,M CNZ CI1 ; GET FLAG OR READ KEY IF ANY CPI CTLQ JNZ NOT$Q ; IF ITS A CTL-Q, MVI A,0FFH ; SET THE FLAG READY NOT$Q: CPI CTLS JNZ NOT$S ; IF ITS A CTL-S, MVI A,00H ; CLEAR THE FLAG NOT$S: MOV M,A ; SAVE THE FLAG CALL COST1 ; GET THE ACTUAL OUTPUT STATUS, ANA M ; AND MASK WITH CTL-Q/CTL-S FLAG RET ; RETURN THIS AS THE STATUS CIST1: ; GET INPUT STATUS WITH AND SAVED PUSH B PUSH H CALL ?CIST POP H POP B ORA A RET COST1: ; GET OUTPUT STATUS, SAVING & PUSH B PUSH H CALL ?COST POP H POP B ORA A RET CI1: ; GET INPUT, SAVING & PUSH B PUSH H CALL ?CI POP H POP B RET ; CONST ; CONSOLE INPUT STATUS. RETURN TRUE IF ; ANY SELECTED CONSOLE INPUT DEVICE ; HAS AN AVAILABLE CHARACTER. CONST: LHLD @CIVEC ; GET CONSOLE INPUT BIT VECTOR JMP IST$SCAN ; AUXIST ; AUXILIARY INPUT STATUS. RETURN TRUE IF ; ANY SELECTED AUXILIARY INPUT DEVICE ; HAS AN AVAILABLE CHARACTER. AUXIST: LHLD @AIVEC ; GET AUX INPUT BIT VECTOR IST$SCAN: MVI B,0 ; START WITH DEVICE 0 CIS$NEXT: DAD H ; CHECK NEXT BIT MVI A,0 ; ASSUME DEVICE NOT READY CC CIST1 ; CHECK STATUS FOR THIS DEVICE ORA A RNZ ; IF ANY READY, RETURN TRUE INR B ; DROP DEVICE NUMBER MOV A,H ORA L ; SEE IF ANY MORE SELECTED DEVICES JNZ CIS$NEXT XRA A ; ALL SELECTED WERE NOT READY, RETURN FALSE RET ; CONIN ; CONSOLE INPUT. RETURN CHARACTER FROM FIRST ; READY CONSOLE INPUT DEVICE. CONIN: LHLD @CIVEC JMP IN$SCAN ; AUXIN ; AUXILIARY INPUT. RETURN CHARACTER FROM FIRST ; READY AUXILIARY INPUT DEVICE. AUXIN: LHLD @AIVEC IN$SCAN: PUSH H ; SAVE BIT VECTOR MVI B,0 CI$NEXT: DAD H ; SHIFT OUT NEXT BIT MVI A,0 ; INSURE ZERO A (NONEXISTANT DEVICE NOT READY). CC CIST1 ; SEE IF THE DEVICE HAS A CHARACTER ORA A JNZ CI$RDY ; THIS DEVICE HAS A CHARACTER INR B ; ELSE, NEXT DEVICE MOV A,H ORA L ; SEE IF ANY MORE DEVICES JNZ CI$NEXT ; GO LOOK AT THEM POP H ; RECOVER BIT VECTOR JMP IN$SCAN ; LOOP TIL WE FIND A CHARACTER CI$RDY: POP H ; DISCARD EXTRA STACK JMP ?CI ; UTILITY SUBROUTINES IPCHL: ; VECTORED CALL POINT PCHL ?PMSG: ; PRINT MESSAGE @ UP TO A NULL ; SAVES & PUSH B PUSH D PMSG$LOOP: MOV A,M ORA A JZ PMSG$EXIT MOV C,A PUSH H CALL ?CONO POP H INX H JMP PMSG$LOOP PMSG$EXIT: POP D POP B RET ?PDEC: ; PRINT BINARY NUMBER 0-65535 FROM LXI B,TABLE10 LXI D,-10000 NEXT: MVI A,'0'-1 PDECL: PUSH H INR A DAD D JNC STOPLOOP INX SP INX SP JMP PDECL STOPLOOP: PUSH D PUSH B MOV C,A CALL ?CONO POP B POP D NEXTDIGIT: POP H LDAX B MOV E,A INX B LDAX B MOV D,A INX B MOV A,E ORA D JNZ NEXT RET TABLE10: DW -1000,-100,-10,-1,0 ?PDERR: LXI H,DRIVE$MSG CALL ?PMSG ; ERROR HEADER LDA @ADRV ADI 'A' MOV C,A CALL ?CONO ; DRIVE CODE LXI H,TRACK$MSG CALL ?PMSG ; TRACK HEADER LHLD @TRK CALL ?PDEC ; TRACK NUMBER LXI H,SECTOR$MSG CALL ?PMSG ; SECTOR HEADER LHLD @SECT CALL ?PDEC ; SECTOR NUMBER RET ; BNKSEL ; BANK SELECT. SELECT CPU BANK FOR FURTHER EXECUTION. BNKSEL: STA @CBNK ; REMEMBER CURRENT BANK JMP ?BANK ; AND GO EXIT THROUGH USERS ; PHYSICAL BANK SELECT ROUTINE XOFFLIST DB -1,-1,-1,-1,-1,-1,-1,-1 ; CTL-S CLEARS TO ZERO DB -1,-1,-1,-1,-1,-1,-1,-1 DSEG ; FOLLOWING RESIDES IN BANKED MEMORY ; DISK I/O INTERFACE ROUTINES ; SELDSK ; SELECT DISK DRIVE. DRIVE CODE IN . ; INVOKE LOGIN PROCEDURE FOR DRIVE ; IF THIS IS FIRST SELECT. RETURN ; ADDRESS OF DISK PARAMETER HEADER ; IN SELDSK: MOV A,C STA @ADRV ; SAVE DRIVE SELECT CODE MOV L,C MVI H,0 DAD H ; CREATE INDEX FROM DRIVE CODE LXI B,@DTBL DAD B ; GET POINTER TO DISDPATCH TABLE MOV A,M INX H MOV H,M MOV L,A ; POINT AT DISK DESCRIPTOR ORA H RZ ; IF NO ENTRY IN TABLE, NO DISK MOV A,E ANI 1 JNZ NOT$FIRST$SELECT ; EXAMINE LOGIN BIT PUSH H XCHG ; PUT POINTER IN STACK & LXI H,-2 DAD D MOV A,M STA @RDRV ; GET RELATIVE DRIVE LXI H,-6 DAD D ; FIND LOGIN ADDR MOV A,M INX H MOV H,M MOV L,A ; GET ADDRESS OF LOGIN ROUTINE CALL IPCHL ; CALL LOGIN POP H ; RECOVER DPH POINTER NOT$FIRST$SELECT: RET ; HOME ; HOME SELECTED DRIVE. TREATED AS SETTRK(0). HOME: LXI B,0 ; SAME AS SET TRACK ZERO ; SETTRK ; SET TRACK. SAVES TRACK ADDRESS FROM ; IN @TRK FOR FURTHER OPERATIONS. SETTRK: MOV L,C MOV H,B SHLD @TRK RET ; SETSEC ; SET SECTOR. SAVES SECTOR NUMBER FROM ; IN @SECT FOR FURTHER OPERATIONS. SETSEC: MOV L,C MOV H,B SHLD @SECT RET ; SETDMA ; SET DISK MEMORY ADDRESS. SAVES DMA ADDRESS ; FROM IN @DMA AND SETS @DBNK TO @CBNK ; SO THAT FURTHER DISK OPERATIONS TAKE PLACE ; IN CURRENT BANK. SETDMA: MOV L,C MOV H,B SHLD @DMA LDA @CBNK ; DEFAULT DMA BANK IS CURRENT BANK ; FALL THROUGH TO SET DMA BANK ; SETBNK ; SET DISK MEMORY BANK. SAVES BANK NUMBER ; IN @DBNK FOR FUTURE DISK DATA ; TRANSFERS. SETBNK: STA @DBNK RET ; SECTRN ; SECTOR TRANSLATE. INDEXES SKEW TABLE IN ; WITH SECTOR IN . RETURNS PHYSICAL SECTOR ; IN . IF NO SKEW TABLE (=0) THEN ; RETURNS PHYSICAL=LOGICAL. SECTRN: MOV L,C MOV H,B MOV A,D ORA E RZ XCHG DAD B MOV L,M MVI H,0 RET ; READ ; READ PHYSICAL RECORD FROM CURRENTLY SELECTED DRIVE. ; FINDS ADDRESS OF PROPER READ ROUTINE FROM ; EXTENDED DISK PARAMETER HEADER (XDPH). READ: LHLD @ADRV MVI H,0 DAD H ; GET DRIVE CODE AND DOUBLE IT LXI D,@DTBL DAD D ; MAKE ADDRESS OF TABLE ENTRY MOV A,M INX H MOV H,M MOV L,A ; FETCH TABLE ENTRY PUSH H ; SAVE ADDRESS OF TABLE LXI D,-8 DAD D ; POINT TO READ ROUTINE ADDRESS JMP RW$COMMON ; USE COMMON CODE ; WRITE ; WRITE PHYSICAL SECTOR FROM CURRENTLY SELECTED DRIVE. ; FINDS ADDRESS OF PROPER WRITE ROUTINE FROM ; EXTENDED DISK PARAMETER HEADER (XDPH). WRITE: LHLD @ADRV MVI H,0 DAD H ; GET DRIVE CODE AND DOUBLE IT LXI D,@DTBL DAD D ; MAKE ADDRESS OF TABLE ENTRY MOV A,M INX H MOV H,M MOV L,A ; FETCH TABLE ENTRY PUSH H ; SAVE ADDRESS OF TABLE LXI D,-10 DAD D ; POINT TO WRITE ROUTINE ADDRESS RW$COMMON: MOV A,M INX H MOV H,M MOV L,A ; GET ADDRESS OF ROUTINE POP D ; RECOVER ADDRESS OF TABLE DCX D DCX D ; POINT TO RELATIVE DRIVE LDAX D STA @RDRV ; GET RELATIVE DRIVE CODE AND POST IT INX D INX D ; POINT TO DPH AGAIN PCHL ; LEAP TO DRIVER ; MULTIO ; SET MULTIPLE SECTOR COUNT. SAVES PASSED COUNT IN ; @CNT MULTIO: STA @CNT RET ; FLUSH ; BIOS DEBLOCKING BUFFER FLUSH. NOT IMPLEMENTED. FLUSH: XRA A RET ; RETURN WITH NO ERROR ; ERROR MESSAGE COMPONENTS DRIVE$MSG DB CR,LF,BELL,'BIOS ERROR ON ',0 TRACK$MSG DB ': T-',0 SECTOR$MSG DB ', S-',0 ; DISK COMMUNICATION DATA ITEMS @ADRV DS 1 ; CURRENTLY SELECTED DISK DRIVE @RDRV DS 1 ; CONTROLLER RELATIVE DISK DRIVE @TRK DS 2 ; CURRENT TRACK NUMBER @SECT DS 2 ; CURRENT SECTOR NUMBER @DMA DS 2 ; CURRENT DMA ADDRESS @CNT DB 0 ; RECORD COUNT FOR MULTISECTOR TRANSFER @DBNK DB 0 ; BANK FOR DMA OPERATIONS CSEG ; COMMON MEMORY @CBNK DB 0 ; BANK FOR PROCESSOR OPERATIONS END