;****************************************************************************** ;* B N K B I O S * C P M S Y S 5 * T h o m a s H o l t e * 8 4 1 1 1 6 * ;****************************************************************************** ;* * ;* R O O T M O D U L E O F R E L O C A T A B L E B I O S F O R * ;* ===================================================================== * ;* * ;* C P / M 3 . 0 O N T H E G E N I E I I I * ;* ================================================= * ;* * ;* M I C R O C O M P U T E R S Y S T E M * ;* ======================================= * ;* * ;* * ;* Thomas Holte Version 1.0 * ;* * ;****************************************************************************** .Z80 TITLE 'ROOT MODULE OF RELOCATABLE BIOS FOR CP/M 3.0' ;version 1.0 15 Sept 82 ; 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. ;ASCII control codes BEL EQU 07H ;bell DC1 EQU 11H ;device control 1 DC3 EQU 13H ;device control 3 ESC EQU 1BH ;escape CCP EQU 0100H ;Console Command Processor gets loaded ;into the TPA CSEG ;GENCPM puts CSEG stuff in common memory ;Variables in system data page EXTERNAL @COVEC,@CIVEC ;I/O redirection vectors EXTERNAL @AOVEC,@AIVEC,@LOVEC EXTERNAL @MXTPA ;addr of system entry point ;Initialization EXTERNAL ?INIT ;general initialization and signon EXTERNAL ?LDCCP,?RLCCP ;load & reload CCP for BOOT & WBOOT ;User defined character I/O routines EXTERNAL ?CI,?CO,?CIST ;each take device in reg. B EXTERNAL ?COST EXTERNAL ?CINIT ;(re)initialize device in reg. C EXTERNAL @CTBL ;physical character device table ;Disk communication data items EXTERNAL @DTBL ;table of pointers to XDPHs GLOBAL @ADRV,@RDRV,@TRK ;parameters for disk I/O GLOBAL @SECT,@DMA,@DBNK ;Memory control GLOBAL @CBNK ;current bank EXTERNAL ?XMOVE,?MOVE ;select move bank, and block move EXTERNAL ?BANK ;select CPU bank EXTERNAL ?USERF ;call hardware drivers ;Clock support EXTERNAL ?TIME ;signal time operation ;General utility routines GLOBAL ?PMSG,?PDEC ;print message, print number from 0 to 65535 GLOBAL ?PDERR ;print BIOS disk error message header INCLUDE MODEBAUD ;define mode bits ;External names for BIOS entry points GLOBAL ?BOOT,?WBOOT,?CONST,?CONIN,?CONO,?LIST,?AUXO,?AUXI,?HOME,?SLDSK GLOBAL ?SLDSK,?STTRK,?STSEC,?STDMA,?READ,?WRITE,?LISTS,?SCTRN,?CONOS GLOBAL ?AUXIS,?AUXOS,?DVTBL,?DEVIN,?DRTBL,?MLTIO,?FLUSH,?MOV,?TIM GLOBAL ?BNKSL,?STBNK,?XMOV *EJECT ;BIOS jump vector. ;All BIOS routines are invoked by calling these entry points. ?BOOT: JP BOOT ;initial entry on cold start ?WBOOT: JP WBOOT ;reentry on program exit, warm start ?CONST: JP CONST ;return console input status ?CONIN: JP CONIN ;return console input character ?CONO: JP CONOUT ;send console output character ?LIST: JP LIST ;send list output character ?AUXO: JP AUXOUT ;send auxiliary output character ?AUXI: JP AUXIN ;return auxiliary input character ?HOME: JP HOME ;set disks to logical home ?SLDSK: JP SELDSK ;select disk drive, return disk parameter info ?STTRK: JP SETTRK ;set disk track ?STSEC: JP SETSEC ;set disk sector ?STDMA: JP SETDMA ;set disk I/O memory address ?READ: JP READ ;read physical block(s) ?WRITE: JP WRITE ;write physical blocks ?LISTS: JP LISTST ;return list device status ?SCTRN: JP SECTRN ;translate logical to physical sector ?CONOS: JP CONOST ;return console output status ?AUXIS: JP AUXIST ;return aux input status ?AUXOS: JP AUXOST ;return aux output status ?DVTBL: JP DEVTBL ;return address of device def table ?DEVIN: JP ?CINIT ;change baud rate of device ?DRTBL: JP GETDRV ;return address of disk drive table ?MLTIO: JP MULTIO ;set multiple record count for disk I/O ?FLUSH: JP FLUSH ;flush BIOS maintained disk caching ?MOV: JP ?MOVE ;block move memory to memory ?TIM: JP ?TIME ;signal time and date operation ?BNKSL: JP BNKSEL ;select bank for code execution and default DMA ?STBNK: JP SETBNK ;select different bank ;for disk I/O DMA operations ?XMOV: JP ?XMOVE ;set source and destination banks ;for one operation ?USER: JP ?USERF ;call hardware drivers (user function) JP DUMMY ;reserved for future expansion JP DUMMY *EJECT DSEG ;this part can be banked BOOT: ;==== ;Initial entry point for system startup. LD SP,BOOT$STACK LD C,15 ;initialize all 16 character devices C$INIT$LOOP: PUSH BC CALL ?CINIT POP BC DEC C JP P,C$INIT$LOOP CALL ?INIT ;perform any additional system initialization ;and print signon message JP BOOT$1 CSEG ;following in resident memory BOOT$1: CALL SET$JUMPS CALL ?LDCCP ;fetch CCP for first time JP CCP WBOOT: ;===== ;Entry for system restarts. LD SP,BOOT$STACK CALL SET$JUMPS ;initialize page zero CALL ?RLCCP ;reload CCP JP CCP ;then reset jmp vectors and exit to CCP SET$JUMPS: DI ;disable interrupts IM 1 ;non maskable interrupt (RST 7) LD A,1 CALL ?BNKSL LD HL,0038H ;^interrupt vector --> reg. HL LD (HL),0F3H ;0038 DI INC HL LD (HL),0C9H ;0039 RET LD A,0C3H LD (0000H),A ;set up jumps in page zero LD (0005H),A LD HL,?WBOOT ;BIOS warm start entry LD (0001H),HL LD HL,(@MXTPA) ;BDOS system call entry LD (0006H),HL RET DEFS 64 BOOT$STACK EQU $ *EJECT CONST: ;===== ;Console input status. Return true if any selected console input device has an ;available character. LD HL,(@CIVEC) ;get console input vector JP IST$SCAN CONIN: ;===== ;Console input. Return character from first ready console input device. LD HL,(@CIVEC) JR IN$SCAN CONOUT: ;====== ;Console output. Send character in reg. C to all selected devices. LD HL,(@COVEC) ;fetch console output bit vector JR OUT$SCAN LIST: ;==== ;List output. Send character in reg. C to all selected devices. LD HL,(@LOVEC) ;fetch list output bit vector OUT$SCAN: LD B,0 ;start with device 0 CO$NEXT:ADD HL,HL ;shift out next bit JR NC,NO$OUT$DEVICE PUSH HL ;save the vector PUSH BC ;save the count and character NOT$OUT$READY: CALL COSTER OR A JR Z,NOT$OUT$READY POP BC ;restore and resave the character and device PUSH BC CALL ?CO ;if device selected, print it POP BC ;recover count and character POP HL ;recover the rest of the vector NO$OUT$DEVICE: INC B ;next device number LD A,H ;see if any devices left OR L JR NZ,CO$NEXT ;and go find them ... RET AUXOUT: ;====== ;Auxiliary output. Send character in reg. C to all selected devices. LD HL,(@AOVEC) ;fetch aux output bit vector JR OUT$SCAN AUXIN: ;===== ;Auxiliary input. Return character from first ready auxiliary input device. LD HL,(@AIVEC) IN$SCAN:PUSH HL ;save bit vector LD B,0 CI$NEXT:ADD HL,HL ;shift out next bit LD A,0 ;insure zero a (nonexistent device not ready) CALL C,CISTL ;see if the device has a character OR A JR NZ,CI$RDY ;this device has a character INC B ;else, next device LD A,H ;see if any more devices OR L JR NZ,CI$NEXT ;go look at them POP HL ;recover bit vector JR IN$SCAN ;loop til we find a character CI$RDY: POP HL ;discard extra stack JP ?CI *EJECT ;Utility subroutines ;print message (^ in reg. HL) up to a null ?PMSG: PUSH BC ;saves reg. BC & reg. DE PUSH DE PMSG$LOOP: LD A,(HL) OR A JR Z,PMSG$EXIT LD C,A PUSH HL CALL ?CONO POP HL INC HL JR PMSG$LOOP PMSG$EXIT: POP DE POP BC RET ;print binary number 0-65535 from reg. HL ?PDEC: LD BC,TABLE10 LD DE,-10000 NEXT: LD A,'0'-1 PDEC1: PUSH HL INC A ADD HL,DE JR NC,STOPLOOP INC SP INC SP JR PDEC1 STOPLOOP: PUSH DE PUSH BC LD C,A CALL ?CONO POP BC POP DE NEXTDIGIT: POP HL LD A,(BC) LD E,A INC BC LD A,(BC) LD D,A INC BC LD A,E OR D JR NZ,NEXT RET TABLE10:DEFW -1000,-100,-10,-1,0 ?PDERR: LD HL,DRIVE$MSG ;error header CALL ?PMSG LD A,(@ADRV) ;drive code ADD A,'A' LD C,A CALL ?CONO LD HL,TRACK$MSG ;track header CALL ?PMSG LD HL,(@TRK) ;track number CALL ?PDEC LD HL,SECTOR$MSG ;sector header CALL ?PMSG LD HL,(@SECT) ;sector number JR ?PDEC XOFFLIST: DEFB -1,-1,-1,-1 ;CTL-S clears to zero DEFB -1,-1,-1,-1 DEFB -1,-1,-1,-1 DEFB -1,-1,-1,-1 *EJECT DSEG ;following resides in banked memory HOME: ;==== ;Home selected drive. Treated as SETTRK (0). LD BC,0 ;same as set track zero JR SETTRK SELDSK: ;====== ;Select disk drive. Drive code in reg. C. Invoke login procedure for drive if ;this is first select. Return address of disk parameter header in reg. HL. LD A,C ;save drive select code LD (@ADRV),A LD L,C ;create index from drive code LD H,0 ADD HL,HL LD BC,@DTBL ;get pointer to dispatch table ADD HL,BC LD A,(HL) ;point at disk descriptor INC HL LD H,(HL) LD L,A RET ;if no entry in table, no disk SETTRK: ;====== ;Set track. Saves track address from reg. BC in @TRK for further operations. LD (@TRK),BC RET SETSEC: ;====== ;Set sector. Saves sector number from reg. BC in @SECT for further operations. LD (@SECT),BC RET SETDMA: ;====== ;Set disk memory address. Saves DMA address from reg. BC in @DMA. LD (@DMA),BC RET READ: ;==== ;Read physical record from currently selected drive. Finds address of proper ;read routine from extended disk parameter header (XDPH). LD HL,(@ADRV) ;get drive code and double it LD H,0 ADD HL,HL LD DE,@DTBL ;make address of table entry ADD HL,DE LD A,(HL) ;fetch table entry INC HL LD H,(HL) LD L,A PUSH HL ;save address of table LD DE,-8 ;point to read routine address ADD HL,DE JR 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). LD HL,(@ADRV) ;get drive code and double it LD H,0 ADD HL,HL LD DE,@DTBL ;make address of table entry ADD HL,DE LD A,(HL) ;fetch table entry INC HL LD H,(HL) LD L,A PUSH HL ;save address of table LD DE,-10 ;point to write routine address ADD HL,DE RW$COMMON: LD A,(HL) ;get address of routine INC HL LD H,(HL) LD L,A POP DE ;recover address of table DEC DE ;point to relative drive DEC DE LD A,(DE) ;get relative drive code and post it LD (@RDRV),A JP (HL) ;leap to driver *EJECT CSEG LISTST: ;====== ;List output status. Return true if all selected list output devices are ready. LD HL,(@LOVEC) ;get list output bit vector OST$SCAN: LD B,0 ;start with device 15 COS$NEXT: ADD HL,HL ;check next bit PUSH HL ;save the vector PUSH BC ;save the count LD A,0FFH ;assume device ready CALL C,COSTER ;check status for this device POP BC ;recover count POP HL ;recover bit vector OR A ;see if device ready RET Z ;if any not ready, return false INC B ;drop device number LD A,H ;see if any more selected devices OR L JR NZ,COS$NEXT LD A,0FFH ;all selected were ready, return true RET ;check for output device ready, including optional XON/XOFF support COSTER: LD L,B ;make device code 16 bits LD H,0 PUSH HL ;make it in stack ADD HL,HL ;create offset into device characteristics tbl ADD HL,HL ADD HL,HL LD DE,@CTBL+6 ;make address of mode byte ADD HL,DE LD A,(HL) AND MB$XON$XOFF POP HL ;recover console number in reg. HL JP Z,?COST ;not a XON device, go get output status direct LD DE,XOFFLIST ;make pointer to proper XON/XOFF flag ADD HL,DE CALL CISTL ;see if this keyboard has character LD A,(HL) ;get flag or read key if any CALL NZ,CIL CP DC1 ;if its a CTL-Q, JR NZ,NOT$Q LD A,0FFH ;set the flag ready NOT$Q: CP DC3 ;if its a CTL-S, JR NZ,NOT$S XOR A ;clear the flag NOT$S: LD (HL),A ;save the flag CALL COSTL ;get the actual output status AND (HL) ;and mask with CTL-Q/CTL-S flag RET ;return this as the status CISTL: PUSH BC ;get input status with regs. BC and HL saved PUSH HL CALL ?CIST POP HL POP BC OR A RET COSTL: PUSH BC ;get output status, saving reg. BC & reg. HL PUSH HL CALL ?COST POP HL POP BC OR A RET CIL: PUSH BC ;get input, saving reg. BC & reg. HL PUSH HL CALL ?CI POP HL POP BC RET *EJECT DSEG SECTRN: ;====== ;Sector translate. Indexes skew table in reg. DE with sector in reg. BC. ;Returns physical sector in reg. HL. If no skew table (reg. DE = 0) then ;returns physical = logical. LD L,C LD H,B LD A,D OR E RET Z EX DE,HL ADD HL,BC LD L,(HL) LD H,0 RET *EJECT CSEG CONOST: ;====== ;Console output status. Return true if all selected console output devices are ;ready. LD HL,(@COVEC) ;get console output bit vector JR OST$SCAN AUXIST: ;====== ;Auxiliary input status. Return true if any selected auxiliary input device has ;an available character. LD HL,(@AIVEC) ;get aux input bit vector IST$SCAN: LD B,0 ;start with device 15 CIS$NEXT: ADD HL,HL ;check next bit LD A,0 ;assume device not ready CALL C,CISTL ;check status for this device OR A ;if any ready, return true RET NZ INC B ;drop device number LD A,H ;see if any more selected devices OR L JR NZ,CIS$NEXT XOR A ;all selected devices were not ready, RET ;return false AUXOST: ;====== ;Auxiliary output status. Return true if all selected auxiliary output devices ;are ready. LD HL,(@AOVEC) ;get aux output bit vector JP OST$SCAN DEVTBL: ;====== ;Return address of character device table. LD HL,@CTBL RET *EJECT GETDRV: ;====== ;Return address of drive table. LD HL,@DTBL MULTIO: ;====== ;Set multiple sector count (not implemented). DUMMY: RET DSEG FLUSH: ;===== ;BIOS deblocking buffer flush. Not implemented. XOR A ;return with no error RET *EJECT CSEG BNKSEL: ;====== ;Bank select. Select CPU bank for further execution. LD (@CBNK),A ;remember current bank JP ?BANK ;and go exit through users ;physical bank select routine DSEG SETBNK: ;====== ;Set disk memory bank. Saves bank number in @DBNK for future disk data ;transfers. LD (@DBNK),A RET ;error message components: DRIVE$MSG: DEFB ESC,'E',BEL DEFM 'BIOS Error on ' DEFB 0 TRACK$MSG: DEFM ': T-' DEFB 0 SECTOR$MSG: DEFM ', S-' DEFB 0 @ADRV: DEFS 1 ;currently selected disk drive @RDRV: DEFS 1 ;controller relative disk drive @TRK: DEFS 2 ;current track number @SECT: DEFS 2 ;current sector number @DMA: DEFS 2 ;current DMA address @DBNK: DEFB 0 ;bank for DMA operations CSEG ;common memory @CBNK: DEFB 0 ;bank for processor operations END