title 'Root module of relocatable BIOS for CP/M 3.0' ;ver 1.0 true equ -1 false equ not true ; banked equ true ; cr equ 13 ; lf equ 10 ; bell equ 7 ; ctlQ equ 11h ; ctlS equ 13h ; ccp equ 100h ;CCP gets loaded into the TPA ; cseg ;GENCPM puts cseg in common memory ; ; variables is system data page ; extrn @covec,@civec,@aovec extrn @aivec,@lovec ;I/O redirection vectors extrn @mxtpa ;addr of system entry point extrn @bnkbf ;128 byte scratch buffer ; ; intialization ; extrn ?init ;general intialization and signon extrn ?ldccp,?rlccp ;load and reload cCP for BOOT & WBOOT ; ; user defined character I/O rouines ; 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 ;parameters for disk I/O ; ; memory control ; public @cbnk ;current bank extrn ?xmove,?move ;select move bank, and block move extrn ?bank ;select CPU bank ; ; clock suport ; extrn ?time ;signal time operation ; ; general untility routine ; public ?pmsg,?pdec ;print message,print number 0-65535 public ?pderr ;print BIOS disk error header ; ; ; maclib modebaud ;define mode bits ; ; extrnal 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 vectors ; ?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 ;retun 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 disk to logiacal home ?sldsk: jmp seldsk ;select disk drive, return disk parms. ?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 phyical sector ; ?conos: jmp conost ;console output status ?auxis: jmp auxist ;return aux input status ?auxos: jmp auxost ;return aux output status ?dvtbl: jmp devtbl ;return addres of 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 I/O ?flush: jmp flush ;flush BIOS maintained disk caching ; ?mov: jmp ?move ;block move to memory ?tim: jmp ?time ;signal time and date operation ?bnksl: jmp bnksel ;select bank for code execution and defualt dma ?stbnk: jmp setbnk ;select differnt bank for disk I/O operation ?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 ; dseg ;can be banked ; boot: lxi sp,boot$stack mvi c,15 ;intialize 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 sign on lxi b,16*256+0 ;init 16 logical disk drives lxi h,@dtbl ; d$init$loop: push b ;save remaining count and abs drive mov e,m ;grab @drv entry inx h ; mov d,m ; inx h ; mov a,e ;if null, no drive ora d ; jz d$init$next ; push h ;save @dvr pointer xchg ;xdph address in dcx h ;get reelative drive code dcx h ; mov a,m ; sta @rdrv ; mov a,c ;get absolute drive code sta @adrv ; dcx h ;point to init pointer mov d,m ;get init pointer dcx h ; mov e,m ; xchg ;call init routine call ipchl ; pop h ;recover @drv pointer d$init$next: pop b ;recover counter and drive # inr c ;and loop for each drive dcr b ; jnz d$init$loop ; jmp boot$1 ; ; cseg ;following in resident memory ; boot$1: call set$jumps ; call ?ldccp ;fetch CCP for frist time jmp ccp ; ; WBOOT ; wboot: lxi sp,boot$stack ; call set$jumps ;initailize 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 ;load a jump opcode sta 0 ;set jumps in page 0 sta 5 ; lxi h,?wboot ;load warm boot address shld 0001h ;store a loaction 1 lhld @mxtpa ;get bdos entry point shld 0006h ;get address to store ret ;done ; ds 64 ;boot stack ram boot$stack equ $ ; ; ; ; devtbl return address of character device table ; devtbl: lxi h,@ctbl ;get address ret ;return with it ; ; getdrv return address of drive table ; getdrv: lxi h,@dtbl ;get address ret ;return with it ; ; conout console output ; send character in to all selected devices ; conout: lhld @covec ;get console output bit vector jmp out$scan ; ; ; auxout auxiliary output ; send character in to all selected devices ; auxout: lhld @aovec ;get aux output bit vector jmp out$scan ; ; ; list list output ; send character in to all selected devices ; list: lhld @lovec ;get list output bit vector ; ; do output to all selected devices as per bit vector ; in , character in ; out$scan: mvi b,0 ;start with device 0 co$next: dad h ;shift out next bit jnc not$out$device ; push h ;save vector push b ;save count and character not$out$ready: call coster ; ora a ;set flags jz not$out$ready ;not ready jump pop b ;restore character and device push b ;resave them 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 ;go and 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 auxilary output status ; return ture 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 ; ; get status of all selected devices as per bit vector ; in reg. ; 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 vount mvi a,0ffh ;assume device ready cc coster ;check status for this device pop b ;recove count pop h ;recover bit vector ora a ;see if device ready rz ;if not ready return false inr b ;next device number mov a,h ;see if any more selected devices ora l ;set flags jnz cos$next ; ori 0ffh ;all selected were ready, return ture ret ; ; ; check for output device ready, including optional ; xon/xoff suport ; coster: mov l,b ;make device code 16 bits mvi h,0 ; push h ;save it dad h ;get offset for device chaacteristics tbl dad h ;*4 dad h ;*8 lxi d,@ctbl+6 ;make address mode byte dad d ; mov a,m ;get xon/xoff status ani mb$xonxoff ;mask pop h ;recover device # jz ?cost ;not a xon device get output status direct lxi d,xofflist ;make pointer to proper xon/xoff flag dad d ; call cistl ;see if have recive character mov a,m ;get flag or read character if any cnz cil ; cpi ctlq ;see if ctl-Q jnz not$q ; mvi a,0ffh ;set the flag ready not$q: cpi ctls ;if it ctl-S jnz not$s ; mvi a,00h ;clear the flag not$s: mov m,a ;save the flag call costl ;get actual output status, ana m ;and mask with ctl-q/ctl-s flag ret ;return with status cistl: push b ;get input status with saved push h ; call ?cist ; pop h ; pop b ; ora a ;set flags ret ; costl: push b ;get output status with saved push h ; call ?cost ; pop h ; pop b ; ora a ;set flags ret ; cil: push b ; push h ; call ?ci ;get input with saved pop h ; pop b ; ret ;return with character ; ; const console input status ; return true if any selected console input devices ; has a character available ; const: lhld @civec ;get console input vector jmp ist$scan ; ; ; auxist auxiliary input status ; reture true if any selected console input devices ; has a character available ; auxist: lhld @aivec ;get aux input bit vector ; ; get input status of devices as per bit vector in ; ist$scan: mvi b,0 ;strat with device 0 cis$next: dad h ;check next bit mvi a,0 ;assume device not ready cc cistl ;check status for this device ora a ;set flags rnz ;return true inr b ;next device number mov a,h ;see if any more seleced devices ora l ; jnz cis$next ; xra a ;set flag all not ready ret ;return flase ; ; conin ; console input return character from frist ready ; console input device. ; conin: lhld @civec ; jmp in$scan ; ; ; auxin ; aux ilary input retun character from frist ready ; auxilary 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 a=0 cc cistl ;see if device has a character ora a ; jnz ci$rdy ;this device has a character inr b ;else, next device mov a,h ;see if any more devices ora l ; jnz ci$next ;go look at them pop h ;recover bit vector jmp in$scan ;loop till we find a character ; ci$rdy: pop h ;discard extra stack jmp ?ci ; ; ; utility subroutines ; ipchl: pchl ;vector called point ; ; print message @ up to a null, save ; ?pmsg: push b ; push d ; pmsg$loop: mov a,m ;get character ora a ;set flags jz pmsg$exit ;if 0 we are done printing mov c,a ;character to c to print push h ;save call ?cono ;ouput character pop h ;get inx h ;next character jmp pmsg$loop ;loop till done pmsg$exit: pop d ;recover reg pop b ; ret ; ; ; print binary number 0-65535 from ; ?pdec: lxi b,table10 ;point to table lxi d,-10000 ; next: mvi a,'0'-1 ; pdec1: push h ;save inr a ; dad d ; jnc stoploop ; inx sp ; inx sp ; jmp pdec1 ; 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 ; ; error print routine ; ?pderr: lxi h,drive$msg ;point to message call ?pmsg ;print it lda @adrv ;get drive # adi 'A' ;get letter code mov c,a ;put in c to print call ?cono ;output it lxi h,track$msg ;point to message call ?pmsg ;print it lhld @trk ;get track # call ?pdec ;print it lxi h,sector$msg ;point to message call ?pmsg ;print it lhld @sect ;get sector # call ?pdec ;print it ret ;done ; ; bnksel bank select select memory bank for further execution ; bnksel: sta @cbnk ;remember current bank jmp ?bank ;do switch ; 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 ; ; seldsk select disk drive ; drive code in . Invoke login procedure for drive if this ; is frist select. Return address of disk parameter header in . ; seldsk: mov a,c ;get drive # sta @adrv ;save it mov l,c ;make index from drive code mvi h,0 ;zero h dad h ; lxi b,@dtbl ;get dispatch table pointer dad b ; mov a,m ;point to disk descripter inx h ; mov h,m ; mov l,a ; ora h ;if no entry in table, no disk rz ; mov a,e ;examine login bit ani 1 ; jnz not$frist$select; push h ;put pointer in stack and xchg ; lxi h,-2 ;get reative drive dad d ; mov a,m ; sta @rdrv ; lxi h,-6 ;find login addr dad d ; mov a,m ;get address of login routine inx h ; mov h,m ; mov l,a ; call ipchl ;call login pop h ;recover DPH pointer not$frist$select: ret ;done ; ; home home selected drive ; home: lxi b,0 ;same as set track 0 ; ; settrk set track. save track address from in @trk ; for futher operations ; settrk: mov l,c ; mov h,b ; shld @trk ; ret ;done ; ; setsec set sector. save sector number from in ; @sect for further operations. ; setsec: mov l,c ; mov h,b ; shld @sect ; ret ; ; ; setdma set disk dma address ; Saves DMA address from in @DMA and sets @DBNK to @CBNK ; so that futher disk operations take place in current bank ; setdma: mov l,c ; mov h,b ; shld @dma ; lda @cbnk ;default bank is current bank ;fall through to set dma bank ; ; setbank 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 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. ; Find address of proper read routine from extened disk parameter ; header (XDPH) ; read: lhld @adrv ;get drive code and double it mvi h,0 ; dad h ; lxi d,@dtbl ;make address of table entry dad d ; mov a,m ;fetch table entry inx h ; mov h,m ; mov l,a ; push h ;save address of table lxi d,-8 ;point to read routine address dad d ; jmp rw$common ;use common code ; ; write Write physical sector from currently selected drive ; write: lhld @adrv ;get drive code and double it mvi h,0 ; dad h ; lxi d,@dtbl ;make address of table entry dad d ; mov a,m ;fetch table entry inx h ; mov h,m ; mov l,a ; push h ;save address of table lxi d,-10 ;point to write routine dad d ; rw$common: mov a,m ;get address of routine inx h ; mov h,m ; mov l,a ; pop d ;recover address of table dcx d ;point to relative drive dcx d ; ldax d ;get relative drive code and post it sta @rdrv ; inx d ;point to DPH again inx d ; pchl ;leap to driver ; ; multio Set multiple sector count. Save pass count in @cnt ; multio: sta @cnt ; ret ;done ; ; flush BIOS deblocking buffer. Not implemented. ; flush: xra a ;return no errors ret ; ; ; error messages ; 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