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 dispatch 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