	title	'Root module of relocatable BIOS for CP/M 3.0 28 Aug 85'

; version 1.0	5 Sept 84


	maclib	cxequ			; C128 equates lib

	maclib	modebaud		; define mode bits

	maclib	Z80

;		  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.

bell	equ	7
ctlQ	equ	'Q'-'@'
ctlS	equ	'S'-'@'

ccp	equ	0100h			; Console Command Processor
					; gets loaded into the TPA

	page

	cseg				; GENCPM puts CSEG stuff in
					; common memory

; variables in system data page

	extrn	@covec,@civec
	extrn	@aovec
	extrn	@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 <B>
	extrn	?out80,?kbin,?kbstat	; shortcut
	extrn	?pt$o$1,?pt$s$1,?in$RS232
	extrn	?in$st$RS232
	extrn	?cinit			; (re)initialize device in <C>
	extrn	@ctbl			; physical character device table

; disk communication data items

	extrn	@dtbl			; table of pointers to XDPHs

; memory control

	extrn	?xmove,?move		; select move bank, and block move
	extrn	?bank			; select CPU bank

; clock support

	extrn	?time			; signal time operation

;; user function

	extrn	?user			; special functions

; general utility routines

	public	?pmsg			; print message
	public	?pdec 			; print number from 0 to 65535
	public	?pderr			; print BIOS disk error message header
	
	page

; 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	?user		; reserved for future expansion
	jmp	0		; reserved for future expansion
	jmp	0		; reserved for future expansion


	page


;
; BOOT
;	Initial entry point for system startup.

	dseg			; this part can be banked
boot:
	lxi	sp,bobo$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
	jrz	d$init$next	; if null, no drive

	push	h		; save @drv pointer 
	xchg                    ; XDPH address in <HL>
	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
	djnz	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

	page

; WBOOT
;	Entry for system restarts.

wboot:
	lxi	sp,bobo$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

	page
;
; 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 <C>
;			to all selected devices
conout:	
	lhld	@covec		; fetch console output bit vector
	mvi	a,40h
	cmp	h
	jrnz	out$scan        ; 80 column only ? detour for time saving

shrt$o:
	sspd	sp$o+1		; shortcut, device output - save old stack
	lxi	sp,bios$stack	; our own stack
	lda	force$map	; get current bank
	push	psw		; save on stack
	sta	bank$0		; set bank 0 as current
call$o:
	call	?out80
	pop	psw
	sta	force$map	; set old bank back
sp$o:
	lxi	sp,0		; set old stack back, modified above
	ret
;
; AUXOUT
;	Auxiliary Output. Send character in <C>
;			to all selected devices
auxout:
	lhld	@aovec		; fetch aux output bit vector
				; no detour, xon-xoff too complex
out$scan:
	mvi	b,0		; start with device 0
co$next:
	dad	h		; shift out next bit
	jrnc	not$out$device
	push	h		; save the vector
	push	b		; save the count and character
not$out$ready:
	call	coster
	ora	a
	jrz	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
	jrnz	co$next		; and go find them...
	ret

;
; LIST
;	List Output.  Send character in <C>
;		to all selected devices.
list:
	lhld	@lovec		; fetch list output bit vector
	mvi	a,10h
	cmp	h
	jrnz	out$scan	; printer #1 only ? detour for time saving
	lxi	h,?pt$o$1	; if printer not present, char vanishes
	shld	call$o+1	; contrary to the spec but better than
	call	shrt$o		; waiting forever.
	lxi	h,?out80
	shld	call$o+1
	ret

	page
;
; CONOST
;	Console Output Status.  Return true if
;		all selected console output devices
;		are ready.
conost:
	lhld	@covec		; get console output bit vector
	mvi	a,0ffh-40h-20h
	ana	h
	jrnz	ost$scan        ; some screen only ? detour for time saving
	ori	0ffh            ; screen is always ready.
	ret



;
; LISTST
;	List Output Status.  Return true if
;		all selected list output devices
;		are ready.
listst:
	lhld	@lovec		; get list output bit vector
	mvi	a,10h
	cmp	h
	jrnz	ost$scan	; printer #1 only ? detour for time saving
	lxi	h,?pt$s$1
	shld	call$no+1
	jmp	shrt$no

;
; AUXOST
;	Auxiliary Output Status.  Return true if
;		all selected auxiliary output devices
;		are ready.
auxost:
	lhld	@aovec		; get aux output bit vector
				; no detour, too complicated (xon-xoff)
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
	jrnz	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		; create offset into device
	dad	h		; 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 <HL>
	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
	jrnz	not$q		; if its a ctl-Q,
	mvi	a,0FFh 		;	set the flag ready
not$q:
	cpi	ctls
	jrnz	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 <BC> and <HL> saved
	push	b
	push	h 
	call	?cist
	pop	h
	pop	b
	ora	a
	ret

cost1:				; get output status, saving <BC> & <HL>
	push	b
	push	h
	call	?cost
	pop	h
	pop	b
	ora	a
	ret

ci1:				; get input, saving <BC> & <HL>
	push	b
	push	h
	call	?ci
	pop	h
	pop	b
	ret

	page
;
; CONST
;	Console Input Status.  Return true if
;		any selected console input device
;		has an available character.
const:
	lhld	@civec		; get console input bit vector
	mvi	a,80h
	cmp	h
	jrnz	ist$scan	; keyboard only ? detour for time saving
	lxi	h,?kbstat
	shld	call$no+1

shrt$no:
	sspd	sp$no+1		; shortcut, non-output device i/o
	lxi	sp,bios$stack	; our own stack
	lda	force$map	; get current bank
	push	psw		; save on stack
	sta	bank$0		; set bank 0 as current
call$no:
	call	?kbstat
	mov	c,a
	pop	psw
	sta	force$map	; set old bank back
sp$no:
	lxi	sp,0		; set old stack back, modified above
	mov	a,c
	ora	a
	ret


;
; AUXIST
;	Auxiliary Input Status.  Return true if
;		any selected auxiliary input device
;		has an available character.
auxist:
	lhld	@aivec		; get aux input bit vector
	mvi	a,2h
	cmp	h
	jrnz	ist$scan	; RS232 only ? detour for time saving
	lxi	h,?in$st$RS232
	shld	call$no+1
	jmp	shrt$no

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
	jrnz	cis$next
	xra	a		; all selected were not ready, return false
	ret

	page
;
; CONIN
;	Console Input.  Return character from first
;		ready console input device.
conin:
	lhld	@civec
	mvi	a,80h
	cmp	h
	jrnz	in$scan		; keyboard only ? detour for time saving
	lxi	h,?kbin
	shld	call$no+1
	jmp	shrt$no

; AUXIN
;	Auxiliary Input.  Return character from first
;		ready auxiliary input device.
auxin:
	lhld	@aivec
	mvi	a,2h
	cmp	h
	jrnz	in$scan		; RS232 only ? detour for time saving
	lxi	h,?in$RS232
	shld	call$no+1
	jmp	shrt$no

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
	jrnz	ci$rdy		; this device has a character
	inr	b		; else, next device
	mov	a,h
	ora	l		; see if any more devices
	jrnz	ci$next		; go look at them
	pop	h		; recover bit vector
	jr	in$scan		; loop til we find a character

ci$rdy:
	pop	h		; discard extra stack
	jmp	?ci

	page

;	Utility Subroutines


?pmsg:				; print message @<HL> up to a null
				; saves <BC> & <DE>
	push	b
	push	d
pmsg$loop:
	mov	a,m
	ora	a
	jrz	pmsg$exit
	mov	c,a
	push	h
	call	?cono
	pop	h
	inx	h
	jr	pmsg$loop
pmsg$exit:
	pop	d
	pop	b
	ret

?pdec:				; print binary number 0-65535 from <HL>
;	lxi	b,table10
;	lxi	d,-10000
next:
;	mvi	a,'0'-1
pdecl:
;	push	h
;	inr	a
;	dad	d
;	jrnc	stoploop
;	inx	sp
;	inx	sp
;	jr	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
;	jrnz	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
;	jr	?pdec			; sector number (call/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 <C>.
;		Invoke login procedure for drive
;		if this is first select.  Return
;		address of disk parameter header
;		in <HL>
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
	jrnz	not$first$select	; examine login bit
	push	h
	xchg				; put pointer in stack & <DE>
	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

	page
;
; HOME
;	Home selected drive.  Treated as SETTRK(0).
home:
	lxi	b,0			; same as set track zero


;
; SETTRK
;	Set Track. Saves track address from <BC> 
;		in @TRK for further operations.
settrk:
	sbcd	@trk
	ret


;
; SETSEC
;	Set Sector.  Saves sector number from <BC>
;		in @sect for further operations.
setsec:
	sbcd	@sect
	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:
	sbcd	@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

	page
;
;	
; 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:
	mov	l,c
	mov	h,b
	mov	a,d
	ora	e
	rz
	xchg
	dad	b
	mov	l,m
	mvi	h,0
	ret

	page
;
; 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
	jr	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
ipchl:
	pchl				; leap to driver

	page
;
; 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


	end

