; title 'CP/M 2.2 BIOS RSX' ; ; 18Jan84 By Mike Griswold ; ; This RSX will provide CP/M 2.2 compatible BIOS support ; for CP/M 3.x. Primarily it performs logical sector ; blocking and deblocking needed for some programs. ; All actual I/O is done by the CP/M 3.0 BIOS. ; ; Typed in from the Dr. Dobb's Journal article in the July 84 ; issue. mabry ; ; Modified 9 July 1984 to run on an 8085 rather than just a Z80 ! ; Also added trap for BDOS call 12 (version number) and returns ; the indication that the calling program is running under ; version 2.2 of CP/M. This is necessary for programs that ; are written to trap a CP/M Plus environment but not able to ; handle the physical sector I/O of a CP/M Plus BIOS. ; mabry ; cseg ; ; This equate is the only hardware dependent value. ; It should be set to the largest sector size that ; will be used. ; max$sector$size: equ 256 cr equ 0dh lf equ 0ah ; ; ; RSX prefix structure ; db 0,0,0,0,0,0 entry: jmp boot next: db jmp ; jump dw 0 ; next module in line prev: dw 0 ; previous module remove: db 0ffh ; remove flag nonbmk: db 0 db 'BIOS2.21' db 0,0,0 ; ; The CP/M 3.0 BIOS jump table is copied here ; to allow easy access to 1st routines. The disk ; I/O routines are potentially in banked memory ; so they cannot be called directly. ; xwboot: jmp 0 ; warm boot xconst: jmp 0 xconin: jmp 0 xconout:jmp 0 xlist: jmp 0 xauxout:jmp 0 xauxin: jmp 0 jmp 0 jmp 0 jmp 0 jmp 0 jmp 0 jmp 0 jmp 0 xlistst:jmp 0 ; ; Signon message ; signon: db cr,lf,'BIOS ver 2.21 ACTIVE',cr,lf,0 ; ; Cold boot ; boot: push psw ; a BDOS call is in progress, save cpu state push h ; so save cpu state push d push b lda first ; is this the first time through ? ora a jz boot10 ; jump if no xra a ; reset first time through flag sta first call init ; initialize BIOS variables lhld 1 ; save the CP/M 3.0 BIOS jump shld old$addr ; at location 0 lxi d,xwboot ; set up to move jump table lxi b,15*3 ; byte count ; ldir boot05: mov a,m stax d inx h inx d dcr c ; This is cheating, but 15*3 < 256 jnz boot05 lxi h,wbt ; substitute new jump address shld 1 lxi h,signon ; sound off call prmsg ; ; We check to see if CP/M version number is requested via BDOS ; function 12. If so, return 2.2. ; boot10: mov a,c ; BDOS function number cpi 12 ; version call ? jnz boot15 ; if no, then continue pop b pop d pop h pop psw lxi h,0022h ; flag CP/M and version 2.2 ret boot15: pop b ; restore BDOS call state pop d pop h pop psw jmp next ; carry on ; ; Warm boot ; wboot: lhld old$addr shld 1 ; restore normal BIOS address jmp 0 ; jump to CP/M 3.0 warm boot ; ; Initialize BIOS internal variables for cold boot ; init: xra a sta hstwrt ; host buffer written sta hstact ; host buffer inactive lxi h,80h shld dmaadr ret ; ; Routine to call banked BIOS routines via BDOS ; function 50. All disk I.O calls are made through ; here. ; xbios: sta biospb ; set BIOS function mvi c,50 ; direct BIOS call function lxi d,biospb ; BIOS parameter block jmp next ; jump to BDOS ; biospb: db 0 ; BIOS function areg: db 0 ; A register bcreg: dw 0 ; BC register dereg: dw 0 ; DE register hlreg: dw 0 ; HL register ; ; Home disk. ; home: lda hstwrt ; check if pending write ora a cnz writehst ; dump buffer to disk xra a sta hstwrt ; buffer written sta hstact ; buffer inactive sta unacnt ; zero alloc count sta sektrk ; zero track count sta sektrk+1 ret ; ; Set track. ; settrk:;sbcd sektrk mov l,c ; track number to HL mov h,b shld sektrk ret ; ; Align jump table on next page boundary. This is needed ; for programs that cheat when getting the addresses of ; BIOS jump table entries. ; ds 16 ; ; BIOS Jump Table ; cbt: jmp wboot ; cold boot entry wbt: jmp wboot ; warm boot entry jmp xconst ; console status jmp xconin ; console input jmp xconout ; console output jmp xlist ; list output jmp xauxout ; aux device output jmp xauxin ; aux device input jmp home ; home disk head jmp seldsk ; select drive jmp settrk ; select track jmp setsec ; select sector jmp setdma ; set dma address jmp read ; read a sector jmp write ; write a sector jmp xlistst ; list status jmp sectran ; sector translation ; ; Select disk. Create a fake DPH for programs ; that might use it. ; seldsk: mov a,c ; requested drive number sta sekdsk sta bcreg ; set C reg in BIOSPB mvi a,9 ; BIOS function number call xbios ; CP/M 3.0 select mov a,h ora l ; check for HL=0 rz ; select error mov e,m ; get address of xlat table inx h mov d,m xchg shld xlat ; save xlat address lxi h,11 ; offset to dpb address dad d mov e,m ; fetch address of dpb inx h mov d,m xchg shld dpb ; address of dpb mov a,m ; cpm sectors per track sta spt inx h inx h ; point to block shift mask inx h mov a,m sta bsm ; save block shift mask lxi d,12 ; offset to psh dad d mov a,m sta psh ; save physical shift factor lxi h,dph ; return DPH address ret ; ; This fake DPH holds the adresses of the actual ; DPB. The CP/M 3.0 DPH is *not* understood ; by CP/M 2.2 programs. ; dph: equ $ dw 0 ; no translation ds 6 ; scratch words ds 2 ; directory buffer dpb: ds 2 ; DPB ds 2 ; CSV ds 2 ; ALV ; ; Set dma. ; setdma:;sbcd dmaadr mov l,c ; dma number to HL mov h,b shld dmaadr ret ; ; Translate sectors. Sectors are not translated yet. ; Wait until we know the physical sector number. ; This works fine as long as the program trusts ; the BIOS to do the translation. Some programs ; access the XLAT table directly to do their own ; translation. These programs will get the wrong ; idea about the disk skew but it should cause no ; harm. ; sectran:mov l,c ; return sector in HL mov h,b ret ; ; Set sector number. ; setsec: mov a,c sta seksec ret ; ; Read the selected CP/M sector. ; read: mvi a,1 sta readop ; read operation inr a ; a=2 (wrual) sta wrtype ; treat as unalloc jmp alloc ; perform read ; ; Write the selected CP/M sector. ; write: xra a sta readop ; not a read operation mov a,c sta wrtype ; save write type cpi 2 ; unalloc block? jnz chkuna ; ; Write to first sector of unallocated block. ; lda bsm ; get block shift mask inr a ; adjust value sta unacnt ; unalloc record count lda sekdsk ; set up values for sta unadsk ; writing to an unallocated lda sektrk ; block sta unatrk lda seksec sta unasec ; chkuna: lda unacnt ; any unalloc sectors ora a ; in this block? jz alloc ; skip if not dcr a ; unacnt = unacnt - 1 sta unacnt lda sekdsk lxi h,unadsk cmp m ; sekdsk = unadsk ? jnz alloc ; skip if not lda sektrk lxi h,unatrk cmp m ; sektrk = unatrk ? jnz alloc ; skip if not lda seksec lxi h,unasec cmp m ; seksec = unasec ? jnz alloc ; skip if not inr m ; move to next sector mov a,m lxi h,spt ; addr of spt cmp m ; sector > spt ? jc noovf ; skip if no overflow lhld unatrk inx h shld unatrk ; bump rack xra a sta unasec ; reset sector count noovf: xra a sta rsflag ; don't pre-read jmp rwoper ; perform write ; alloc: xra a ; requires pre-read sta unacnt inr a sta rsflag ; force pre-read ; rwoper: xra a sta erflag ; no errors yet lda psh ; get physical shift factor ora a ; set flags mov b,a lda seksec ; logical sector lxi h,hstbuf ; addr of buffer lxi d,128 jz noblk ; no blocking xchg ; shuffle registers shift: xchg rrc jnc sh1 dad d ; bump buffer address sh1: xchg dad h ; double offset ani 07fh ; zero high bit ; djnz shift dcr b jnz shift xchg ; HL=buffer addr noblk: sta sekhst shld sekbuf lxi h,hstact ; buffer active flag mov a,m mvi m,1 ; set buffer active ora a ; was it already? jz filhst ; fill buffer if not lda sekdsk lxi h,hstdsk ; same disk ? cmp m jnz nomatch lda sektrk lxi h,hsttrk ; same track ? cmp m jnz nomatch lda sekhst ; same buffer ? lxi h,hstsec cmp m jz match ; nomatch: lda hstwrt ; buffer changed? ora a cnz writehst ; clear buffer filhst: lda sekdsk sta hstdsk lhld sektrk shld hsttrk lda sekhst sta hstsec lda rsflag ; need to read ? ora a cnz readhst ; yes xra a sta hstwrt ; no pending write ; match: lhld dmaadr xchg lhld sekbuf lda readop ; which way to move ? ora a jnz rwmove ; skip if read mvi a,1 sta hstwrt ; mark buffer changed xchg ; hl=dma de=buffer ; rwmove: lxi b,128 ; byte count ; ldir ; block move rwmv05: mov a,m stax d inx h inx d dcr c ; This is cheating, but 128 < 256 jnz rwmv05 lda wrtype ; write type cpi 1 ; to directory ? jnz exit ; done lda erflag ; check for errors ora a jnz exit ; don't write dir if so xra a sta hstwrt ; show buffer written call writehst ; write buffer exit: lda erflag ret ; ; Disk read. Call CP/M 3.0 BIOS to fill the buffer ; with one physical sector. ; readhst: call rw$init ; init CP/M 3.0 BIOS mvi a,13 ; read function number call xbios ; read sector sta erflag ret ; ; Disk write. Call CP/M 3.0 BIOS to write one ; physical sector from buffer. ; writehst: call rw$init ; init CP/M 3.0 BIOS mvi a,14 ; write function number call xbios ; write sector sta erflag ret ; ; Translate sector. Set CP/M 3.0 track, sector, ; DMA buffer and DMA bank. ; rw$init: lda hstsec ; physical sector number mov l,a mvi h,0 shld bcreg ; sector number in BC lhld xlat ; address of xlat table shld dereg ; xlat address in DE mvi a,16 ; sectrn function number call xbios ; get skewed sector number mov a,l sta actsec ; actual sector shld bcreg ; sector number in BC mvi a,11 ; setsec function number call xbios ; set CP/M 3.0 sector lhld hsttrk ; physical track number shld bcreg ; track number in BC mvi a,10 ; settrk function number call xbios lxi h,hstbuf ; sector buffer shld bcreg ; buffer address in BC mvi a,12 ; setdma function number call xbios mvi a,1 ; DMA bank number sta areg ; bank number in A mvi a,28 ; setbnk function number call xbios ; set DMA bank ret ; ; Print message at HL until null. ; prmsg: mov a,m ora a rz mov c,m push h call xconout pop h inx h jmp prmsg ; ; disk i/o buffer ; hstbuf: ds max$sector$size ; ; variable storage area ; sekdsk: ds 1 ; logical disk number sektrk: ds 2 ; logical track number seksec: ds 1 ; logical sector number ; hstdsk: ds 1 ; physical disk number hsttrk: ds 2 ; physical track number hstsec: ds 1 ; physical sector number ; actsec: ds 1 ; skewed physical sector sekhst: ds 1 ; temp physical sector hstact: ds 1 ; buffer ative flag hstwrt: ds 1 ; buffer changed flag ; unacnt: ds 1 ; unallocated sector cunt unadsk: ds 1 ; unalloc disk number unatrk: ds 2 ; unalloc track number unasec: ds 1 ; unalloc sector number sekbuf: ds 2 ; logical sector address in buffer ; spt: ds 1 ; cpm sectors per track xlat: ds 2 ; xlat address bsm: ds 1 ; block shift mask psh: ds 1 ; physical shift factor ; erflag: ds 1 ; error reporting rsflag: ds 1 ; force sector read readop: ds 1 ; 1 if read operation rwflag: ds 1 ; physical read flag wrtype: ds 1 ; write operation type dmaadr: ds 2 ; last dma address oldaddr:ds 2 ; address of old BIOS ; first: db 0ffh ; first time through flag, set on load end