Files
Digital-Research-Source-Code/CONTRIBUTIONS/z80em86/bios/zbios.mac
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

721 lines
15 KiB
Plaintext

title 'Root module of relocatable BIOS for CP/M 3.0'
;
; code differs from cp/m 3 book marked with ^^^
;
; ^^^0 : book has, mvi a,jmp
; ^^^1 : book has, dcr b
; ^^^2 : book has not, mvi a,1 | sta @cnt
; ^^^3 : book has not, mov a,c
;
maclib ASMTYPE.LIB
.z80
;
; 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.
;
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 ; I/O re-direction vectors
extrn @aivec,@lovec
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 <B>
extrn ?cinit ; (re)initialize device in <C>
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.LIB ; 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: 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 auxilliary output character
?auxi: jp auxin ; return auxilliary 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 block(s)
?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 ; set 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
jp 0 ; reserved for system implementor
jp 0 ; reserved for future expansion
jp 0 ; reserved for future expansion
;
;
; BOOT
; Initial entry point for system startup.
;
dseg ; this part can be banked
;
boot:
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 init.
; and print signon message
ld bc,16*256+0
ld hl,@dtbl ; init all 16 logical disk drives
d$init$loop:
push bc ; save remaining count and abs drive
ld e,(hl)
inc hl
ld d,(hl)
inc hl ; grab @drv entry
ld a,e
or d
jp z,d$init$next ; if null, no drive
push hl ; save @drv pointer
ex de,hl ; XDPH address in <HL>
dec hl
dec hl
ld a,(hl)
ld (@rdrv),a ; get relative drive code
ld a,c
ld (@adrv),a ; get absolute drive code
dec hl ; point to init pointer
ld d,(hl)
dec hl
ld e,(hl) ; get init pointer
ex de,hl
call ipchl ; call init routine
pop hl ; recover @drv pointer
d$init$next:
pop bc ; recover counter and drive #
inc c
dec b
jp nz,d$init$loop ; and loop for each drive
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.
;
wboot: 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:
if banked
ld a,1
call ?bnksl
endif
ld a,0c3h ;^^^0
ld (0000h),a
ld (0005h),a ; set up jumps in page zero
ld hl,?wboot
ld (0001h),hl ; BIOS warm start entry
ld hl,(@mxtpa)
ld (0006h),hl ; BDOS system call entry
ret
;
ds 64
;
boot$stack equ $
;
; DEVTBL
; Return address of character device table.
;
devtbl:
ld hl,@ctbl
ret
;
; GETDRV
; Return address of drive table.
;
getdrv:
ld hl,@dtbl
ret
;
; CONOUT
; Console Output. Send character in <C>
; to all selected devices
;
conout:
ld hl,(@covec) ; fetch console output bit vector
jp out$scan
;
; AUXOUT
; Auxiliary Output. Send character in <C>
; to all selected devices
;
auxout:
ld hl,(@aovec) ; fetch aux output bit vector
jp out$scan
;
; LIST
; List output. Send character in <C>
; to all selected devices.
;
list:
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
jp nc,not$out$device
push hl ; save the vector
push bc ; save the count and character
not$out$ready:
call coster
or a
jp z,not$out$ready
pop bc
push bc ; restore and resave the character and device
call ?co ; if device selected, print it
pop bc ; recover count and character
pop hl ; recover the rest of the vector
not$out$device:
inc b ; next device number
ld a,h
or l ; see if any devices left
jp nz,co$next ; and go find them...
ret
;
; CONOST
; Console Output Status. Return true if
; all selected console output devices
; are ready.
;
conost:
ld hl,(@covec) ; get console output bit vector
jp ost$scan
;
; AUXOST
; Auxiliary Output Status. Return true if
; all selected auxiliary output devices
; are ready.
;
auxost:
ld hl,(@aovec) ; get aux output bit vector
jp ost$scan
;
; LISTST
; List output status. Return true if
; all selected list output devices
; are ready.
;
listst:
ld hl,(@lovec) ; get list output bit vector
ost$scan:
ld b,0 ; start with device 0
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 ; ^^^1 next device number
ld a,h
or l ; see if any more selected devices
jp nz,cos$next
or 0ffh ; all selected were ready, return true
ret
;
coster: ; check for output device ready, including optional
; xon/xoff support
;
ld l,b
ld h,0 ; make device code 16 bits
push hl ; save it in stack
add hl,hl
add hl,hl
add hl,hl ; create offset into device characteristics tbl
ld de,@ctbl+6
add hl,de ; make address of mode byte
ld a,(hl)
and mb$xonxoff
pop hl ; recover console number in <HL>
jp z,?cost ; not a xon device, go get output status direct
ld de,xofflist
add hl,de ; make pointer to proper xon/xoff flag
call cist1 ; see if this keyboard has character
ld a,(hl)
call nz,ci1 ; get flag or read key if any
cp ctlq
jp nz,not$q ; if its a ctl-Q,
ld a,0ffh ; set the flag ready
not$q:
cp ctls
jp nz,not$s ; if its a ctl-S,
ld a,00h ; clear the flag
not$s:
ld (hl),a ; save the flag
call cost1 ; get the actual output status,
and (hl) ; and mask with ctl-q/ctl-s flag
ret ; return this as the status
;
cist1: ; get input status with <BC> and <HL> saved
push bc
push hl
call ?cist
pop hl
pop bc
or a
ret
;
cost1: ; get output status, saving <BC> & <HL>
push bc
push hl
call ?cost
pop hl
pop bc
or a
ret
;
ci1: ; get input, saving <BC> & <HL>
push bc
push hl
call ?ci
pop hl
pop bc
ret
;
; CONST
; Console Input Status. Return true if
; any selected console input device
; has an available character.
;
const:
ld hl,(@civec) ; get console input bit vector
jp ist$scan
;
; AUXIST
; Auxiliary Input Status. Return true if
; any selected auxiliary input device
; has an available character.
;
auxist:
ld hl,(@aivec) ; get aux input bit vector
ist$scan:
ld b,0 ; start with device 0
cis$next:
add hl,hl ; check next bit
ld a,0 ; assume device not ready
call c,cist1 ; check status for this device
or a
ret nz ; if any ready, return true
inc b ; next device number
ld a,h
or l ; see if any more selected devices
jp nz,cis$next
xor a ; all selected were not ready, return false
ret
; CONIN
; Console Input. Return character from first
; ready console input device.
;
conin:
ld hl,(@civec)
jp in$scan
;
; AUXIN
; Auxiliary Input. Return character from first
; ready auxiliary input device.
;
auxin:
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 (nonexistant device not ready).
call c,cist1 ; see if the device has a character
or a
jp nz,ci$rdy ; this device has a character
inc b ; else, next device
ld a,h
or l ; see if any more devices
jp nz,ci$next ; go look at them
pop hl ; recover bit vector
jp in$scan ; loop till we find a character
ci$rdy: pop hl ; discard extra stack
jp ?ci
;
; Utility Subroutines
;
ipchl: ; vectored call point
jp (hl)
;
?pmsg: ; print message @<HL> up to a null
; saves <BC> & <DE>
;
push bc
push de
pmsg$loop:
ld a,(hl)
or a
jp z,pmsg$exit
ld c,a
push hl
call ?cono
pop hl
inc hl
jp pmsg$loop
pmsg$exit:
pop de
pop bc
ret
;
?pdec: ; print binary number 0-65535 from <HL>
;
ld bc,table10
ld de,-10000
next:
ld a,'0'-1
pdecl:
push hl
inc a
add hl,de
jp nc,stoploop
inc sp
inc sp
jp pdecl
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
jp nz,next
ret
;
table10:
dw -1000,-100,-10,-1,0
;
?pderr:
ld hl,drive$msg
call ?pmsg ; error header
ld a,(@adrv)
add a,'A'
ld c,a
call ?cono ; drive code
ld hl,track$msg
call ?pmsg ; track header
ld hl,(@trk)
call ?pdec ; track number
ld hl,sector$msg
call ?pmsg ; sector header
ld hl,(@sect)
call ?pdec ; sector number
ret
;
; BNKSEL
; Bank Select. Select CPU bank for further execution.
;
bnksel:
ld (@cbnk),a ; remember current bank
jp ?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 <C>.
; Invoke login procedure for drive
; if this is first select. Return
; address of disk parameter header
; in <HL>
;
seldsk: ld a,1 ;^^^2
ld (@cnt),a ;^^^2 clear multi counter
ld a,c
ld (@adrv),a ; save drive select code
ld l,c
ld h,0
add hl,hl ; create index from drive code
ld bc,@dtbl
add hl,bc ; get pointer to dispatch table
ld a,(hl)
inc hl
ld h,(hl)
ld l,a ; point at disk descriptor
or h
ret z ; if no entry in table, no disk
ld a,e
and 1
jp nz,not$first$select ; examine login bit
push hl
ex de,hl ; put pointer in stack & <DE>
ld hl,-2
add hl,de
ld a,(hl)
ld (@rdrv),a ; get relative drive
ld hl,-6
add hl,de ; find login addr
ld a,(hl)
inc hl
ld h,(hl)
ld l,a ; get address of login routine
call ipchl ; call login
pop hl ; recover dph pointer
not$first$select:
ret
;
; HOME
; Home selected drive. Treated as settrk(0).
;
home:
ld bc,0 ; same as set track zero
;
; SETTRK
; Set Track. Saves track address from <BC>
; in @trk for further operations.
;
settrk:
ld l,c
ld h,b
ld (@trk),hl
ret
;
; SETSEC
; Set Sector. Saves sector number from <BC>
; in @sect for further operations.
;
setsec:
ld l,c
ld h,b
ld (@sect),hl
ret
;
; SETDMA
; Set Disk Memory Address. Saves DMA address
; from <BC> in @DMA and sets @dbnk to @cbnk
; so that further disk operations take place
; in current bank.
;
setdma:
ld l,c
ld h,b
ld (@dma),hl
ld a,(@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:
ld (@dbnk),a
ret
;
; SECTRN
; Sector Translate. Indexes skew table in <DE>
; with sector in <BC>. Returns physical sector
; in <HL>. If no skew table (<DE>=0) then
; returns physical=logical.
;
sectrn:
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
;
; READ
; Read physical record from currently selected drive.
; Finds address of proper read routine from
; extended disk parameter header (XDPH).
;
read:
ld hl,(@adrv)
ld h,0
add hl,hl ; get drive code and double it
ld de,@dtbl
add hl,de ; make address of table entry
ld a,(hl)
inc hl
ld h,(hl)
ld l,a ; fetch table entry
push hl ; save address of table
ld de,-8
add hl,de ; point to read routine address
jp 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:
ld hl,(@adrv)
ld h,0
add hl,hl ; get drive code and double it
ld de,@dtbl
add hl,de ; make address of table entry
ld a,(hl)
inc hl
ld h,(hl)
ld l,a ; fetch table entry
push hl ; save address of table
ld de,-10
add hl,de ; point to write routine address
rw$common:
ld a,(hl)
inc hl
ld h,(hl)
ld l,a ; get address of routine
pop de ; recover address of table
dec de
dec de ; point to relative drive
ld a,(de)
ld (@rdrv),a ; get relative drive code and post it
inc de
inc de ; point to dph again
jp (hl) ; leap to driver
;
; MULTIO
; Set multiple sector count. Saves passed count in
; @CNT
;
multio:
ld a,c ;^^^3 passed in c
ld (@cnt),a
ret
;
; FLUSH
; BIOS deblocking buffer flush. Not implemented.
;
flush:
xor 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