; ============================================================================ ; * CP/M 2.2 BIOS EMULATOR FOR CP/M 3.x * ; ============================================================================ ; This program, by Mike Griswold, is a Resident System Extension (RSX) which ; runs under CP/M-Plus. This RSX intercepts certain BIOS function calls, ; and resolves differences between CP/M 2.2 and CP/M 3.x. It allows many ; programs that do not normally work with CP/M-Plus to work correctly. ; So far, I have installed this RSX on DU2, which normally does not work ; at all, and with the RSX, it works perfectly. ; Installation instructions: ; 1. RMAC CPM22 $pz $sz ; 2. LINK CPM22[OP] ; 3. REN CPM22.RSX=CPM22.PRL ; 4. GENCOM CPM22 ; NOTE: is a .COM file which is the CP/M 2.2 command ; file. ; ; Program typed from Doctor Dobbs Journal #93, July 1984. ; Typed & Instructions written by Charles Foreman. ; Uploaded to CURA RCP/M - (212) 625-5931 - by Charles Foreman. ; ; 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. ; maclib z80 ; Z80 opcode equates cseg ; ; This equate is the only hardware dependant value. ; It should be set to the largest sector size that ; will be used. ; max$sector$size: equ 512 ; ; ; RSX prefix structure ; db 0,0,0,0,0,0 entry: jmp boot next: db jmp ; jump dw 0 ; next module in line prev: db 0 ; previous module remove: db 00fh ; remove flag nonbnk: db 0 db 'BIOS2.21' db 0,0,0 ; ; Align jump table on next page boundary. This is needed ; for programs that cheat when getting the addresses of ; BIOS jump table entries. Optimization freaks could move ; some code up here. With a 60k TPA though its hard to ; get excited. ; ds 229 ; ; 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 ; ; The CP/M 3.0 BIOS jump table is copied here ; to allow easy access to its 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 0dh,0ah,'BIOS ver 2.21 ACTIVE',0dh,0ah,0 ; ; Cold boot ; boot: push psw ; a BDOS call is in progress push h ; so save CPU state push d push b lxi h,next ; now bypass this RSX on shld entry+1 ; all subsequent BDOS calls 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 lxi h,wbt ; substitute new jump address shld 1 lxi h,signon ; sound off call prmsg 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 reguster 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 ; ; 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 to 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 addresses 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 track. ; settrk: sbcd sektrk ret ; ; Set dma. ; setdma: sbcd 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 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? jrnz 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 jrz alloc ; skip if not dcr a ; --unacnt sta unacnt lda sekdsk lxi h,unadsk cmp m ; sekdsk = unadsk ? jrnz alloc ; skip if not lda sektrk cmp m ; sektrk = unatrk ? jrnz alloc ; skip if not lda seksec lxi h,unasec cmp m ; sektrk = unasec ? jrnz alloc ; skip if not inr m ; move to next sector mov a,m lxi h,spt ; addr of spt cmp m ; sector > spt ? jrc noovf ; skip if no overflow lhld unatrk inx h shld unatrk ; bump track xra a sta unasec ; reset sector count noovf: xra a sta rsflag ; don't pre-read jr 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 jrz noblk ; no blocking xchg ; shuffle registers shift: xchg rrc jrnc sh1 dad d ; bump buffer address sh1: xchg dad h ani 07fh ; zero high bit djnz 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? jrz filhst ; fill buffer if not lda sekdsk lxi h,hstdsk ; same disk ? cmp m jrnz nomatch lda sektrk lxi h,hsttrk ; same track ? cmp m jrnz nomatch lda sekhst ; same buffer ? lxi h,hstsec cmp m jrz 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 jrnz 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 lda wrtype ; write type cpi 1 ; to directory ? jrnz exit ; done lda erflag ; check for errors ora a jrnz 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 active flag hstwrt: ds 1 ; buffer changed flag ; unacnt: ds 1 ; unallocated sector count 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 ; end