	title	'CP/M BDOS Interface, BDOS, Version 3.0 Dec, 1982'
;*****************************************************************
;*****************************************************************
;**                                                             **
;**   B a s i c    D i s k   O p e r a t i n g   S y s t e m    **
;**								**
;**            I n t e r f a c e   M o d u l e                  **
;**                                                             **
;*****************************************************************
;*****************************************************************
;
;	Copyright (c) 1978, 1979, 1980, 1981, 1982
;	Digital Research
;	Box 579, Pacific Grove
;	California
;
;       December 1982
;
on	equ	0ffffh
off	equ	00000h
MPM	equ	off
BANKED	equ	on

;
;	equates for non graphic characters
;

ctla	equ	01h	; control a
ctlb	equ	02h	; control b
ctlc	equ	03h	; control c
ctle	equ	05h	; physical eol
ctlf	equ	06h	; control f
ctlg	equ	07h	; control g
ctlh	equ	08h	; backspace
ctlk	equ	0bh	; control k
ctlp	equ	10h	; prnt toggle
ctlq	equ	11h	; start screen
ctlr	equ	12h	; repeat line
ctls	equ	13h	; stop screen
ctlu	equ	15h	; line delete
ctlw	equ	17h	; control w
ctlx	equ	18h	; =ctl-u
ctlz	equ	1ah	; end of file
rubout	equ	7fh	; char delete
tab	equ	09h	; tab char
cr	equ	0dh	; carriage return
lf	equ	0ah	; line feed
ctl	equ	5eh	; up arrow

		org	0000h
base		equ	$

; Base page definitions

bnkbdos$pg	equ	base+0fc00h
resbdos$pg	equ	base+0fd00h
scb$pg		equ	base+0fb00h
bios$pg		equ	base+0ff00h

; Bios equates

bios		equ	bios$pg
bootf		equ	bios$pg 	; 00. cold boot function

if BANKED

wbootf		equ	scb$pg+68h	; 01. warm boot function
constf		equ	scb$pg+6eh  	; 02. console status function
coninf		equ	scb$pg+74h	; 03. console input function
conoutf		equ	scb$pg+7ah	; 04. console output function
listf		equ	scb$pg+80h	; 05. list output function

else

wbootf		equ	bios$pg+3	; 01. warm boot function
constf		equ	bios$pg+6	; 02. console status function
coninf		equ	bios$pg+9	; 03. console input function
conoutf		equ	bios$pg+12	; 04. console output function
listf		equ	bios$pg+15	; 05. list output function

endif

punchf		equ	bios$pg+18	; 06. punch output function
readerf		equ	bios$pg+21	; 07. reader input function
homef		equ	bios$pg+24	; 08. disk home function
seldskf		equ	bios$pg+27	; 09. select disk function
settrkf		equ	bios$pg+30	; 10. set track function
setsecf		equ	bios$pg+33	; 11. set sector function
setdmaf		equ	bios$pg+36	; 12. set dma function
readf		equ	bios$pg+39	; 13. read disk function
writef		equ	bios$pg+42	; 14. write disk function
liststf		equ	bios$pg+45	; 15. list status function
sectran		equ	bios$pg+48	; 16. sector translate
conoutstf	equ	bios$pg+51	; 17. console output status function
auxinstf	equ	bios$pg+54	; 18. aux input status function
auxoutstf	equ	bios$pg+57	; 19. aux output status function
devtblf		equ	bios$pg+60	; 20. retunr device table address fx
devinitf	equ	bios$pg+63	; 21. initialize device function
drvtblf		equ	bios$pg+66	; 22. return drive table address
multiof		equ	bios$pg+69	; 23. multiple i/o function
flushf		equ	bios$pg+72	; 24. flush function
movef		equ	bios$pg+75	; 25. memory move function
timef		equ	bios$pg+78	; 26. system get/set time function
selmemf		equ	bios$pg+81	; 27. select memory function
setbnkf		equ	bios$pg+84	; 28. set dma bank function
xmovef		equ	bios$pg+87	; 29. extended move function

if BANKED

; System Control Block equates

olog		equ	scb$pg+090h
rlog		equ	scb$pg+092h

SCB		equ	scb$pg+09ch

; Expansion Area - 6 bytes

hashl		equ	scb$pg+09ch
hash		equ	scb$pg+09dh
version		equ	scb$pg+0a1h

; Utilities Section - 8 bytes

util$flgs	equ	scb$pg+0a2h
dspl$flgs	equ	scb$pg+0a6h

; CLP Section - 4 bytes

clp$flgs	equ	scb$pg+0aah
clp$errcde	equ	scb$pg+0ach

; CCP Section - 8 bytes

ccp$comlen	equ	scb$pg+0aeh
ccp$curdrv	equ	scb$pg+0afh
ccp$curusr	equ	scb$pg+0b0h
ccp$conbuff	equ	scb$pg+0b1h
ccp$flgs	equ	scb$pg+0b3h

; Device I/O Section - 32 bytes

conwidth	equ	scb$pg+0b6h
column		equ	scb$pg+0b7h
conpage		equ	scb$pg+0b8h
conline		equ	scb$pg+0b9h
conbuffadd	equ	scb$pg+0bah
conbufflen	equ	scb$pg+0bch
conin$rflg	equ	scb$pg+0beh
conout$rflg	equ	scb$pg+0c0h
auxin$rflg	equ	scb$pg+0c2h
auxout$rflg	equ	scb$pg+0c4h
lstout$rflg	equ	scb$pg+0c6h
page$mode	equ	scb$pg+0c8h
pm$default	equ	scb$pg+0c9h
ctlh$act	equ	scb$pg+0cah
rubout$act	equ	scb$pg+0cbh
type$ahead	equ	scb$pg+0cch
contran		equ	scb$pg+0cdh
conmode		equ	scb$pg+0cfh
outdelim	equ	scb$pg+0d3h
listcp		equ	scb$pg+0d4h
qflag		equ	scb$pg+0d5h

; BDOS Section - 42 bytes

scbadd		equ	scb$pg+0d6h
dmaad		equ	scb$pg+0d8h
olddsk		equ	scb$pg+0dah
info		equ	scb$pg+0dbh
resel		equ	scb$pg+0ddh
relog 		equ	scb$pg+0deh
fx		equ	scb$pg+0dfh
usrcode		equ	scb$pg+0e0h
dcnt		equ	scb$pg+0e1h
;searcha	equ	scb$pg+0e3h
searchl		equ	scb$pg+0e5h
multcnt		equ	scb$pg+0e6h
errormode	equ	scb$pg+0e7h
searchchain	equ	scb$pg+0e8h
temp$drive	equ	scb$pg+0ech
errdrv      	equ	scb$pg+0edh
media$flag	equ	scb$pg+0f0h
bdos$flags	equ	scb$pg+0f3h
stamp		equ	scb$pg+0f4h
commonbase	equ	scb$pg+0f9h
error		equ	scb$pg+0fbh	;jmp error$sub
bdosadd		equ	scb$pg+0feh

; Resbdos equates

resbdos		equ	resbdos$pg
move$out	equ	resbdos$pg+9	; a=bank #, hl=dest, de=srce
move$tpa	equ	resbdos$pg+0ch	; a=bank #, hl=dest, de=srce
srch$hash	equ	resbdos$pg+0fh	; a=bank #, hl=hash table addr
hashmx		equ	resbdos$pg+12h	; max hash search dcnt
rd$dir$flag	equ	resbdos$pg+14h	; directory read flag
make$xfcb	equ	resbdos$pg+15h	; make function flag
find$xfcb	equ	resbdos$pg+16h	; search function flag
xdcnt		equ	resbdos$pg+17h	; dcnt save for empty fcb, 
					; user 0 fcb, or xfcb
xdmaad		equ	resbdos$pg+19h	; resbdos dma copy area addr
curdma		equ	resbdos$pg+1bh	; current dma
copy$cr$only	equ	resbdos$pg+1dh	; dont restore fcb flag
user$info	equ	resbdos$pg+1eh	; user fcb address
kbchar		equ	resbdos$pg+20h  ; conbdos look ahead char
qconinx		equ	resbdos$pg+21h	; qconin mov a,m routine

ELSE

move$out	equ	movef
move$tpa	equ	movef

ENDIF

;
serial: db	'654321'
;
;	Enter here from the user's program with function number in c,
;	and information address in d,e
;

bdose:	; Arrive here from user programs
	xchg! shld info! xchg ; info=de, de=info

	mov a,c! sta fx! cpi 14! jc bdose2
	lxi h,0! shld dircnt ; dircnt,multnum = 0
	lda olddsk! sta seldsk ; Set seldsk

if BANKED
	dcr a! sta copy$cr$init
ENDIF

	; If mult$cnt ~= 1 then read or write commands
	; are handled by the shell
	lda mult$cnt! dcr a! jz bdose2
	lxi h,mult$fxs
bdose1:
	mov a,m! ora a! jz bdose2
	cmp c! jz shell
	inx h! jmp bdose1
bdose2:
	mov a,e! sta linfo ; linfo = low(info) - don't equ
	lxi h,0! shld aret ; Return value defaults to 0000
	shld resel ; resel,relog = 0
	; Save user's stack pointer, set to local stack
	dad sp! shld entsp ; entsp = stackptr

if not BANKED
	lxi sp,lstack ; local stack setup
ENDIF

	lxi h,goback ; Return here after all functions
	push h ; jmp goback equivalent to ret
	mov a,c! cpi nfuncs! jnc high$fxs ; Skip if invalid #
	mov c,e ; possible output character to c
	lxi h,functab! jmp bdos$jmp
	; look for functions 98 ->
high$fxs:
	cpi 128! jnc test$152
	sui 98! jc lret$eq$ff ; Skip if function < 98
	cpi nfuncs2! jnc lret$eq$ff
	lxi h,functab2
bdos$jmp:
	mov e,a! mvi d,0 ; de=func, hl=.ciotab
	dad d! dad d! mov e,m! inx h! mov d,m ; de=functab(func)
	lhld info ; info in de for later xchg	
	xchg! pchl ; dispatched

;	   CAUTION: In banked systems only,
;          error$sub is referenced indirectly by the SCB ERROR
; 	   field in RESBDOS as (0fc7ch).  This value is converted
; 	   to the actual address of error$sub by GENSYS.  If the offset
; 	   of error$sub is changed, the SCB ERROR value must also
; 	   be changed.

;
;	error subroutine
;

error$sub:
	mvi b,0! push b! dcr c
	lxi h,errtbl! dad b! dad b
	mov e,m! inx h! mov d,m! xchg
	call errflg
	pop b! lda error$mode! ora a! rnz
	jmp reboote

mult$fxs:	db	20,21,33,34,40,0

	maclib	makedate
if BANKED
	@LCOPY
	@BDATE
	ds	5
else
	@SCOPY
	@BDATE

	;	31 level stack

	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
lstack:

endif

;	dispatch table for functions

functab:
	dw	rebootx1, func1, func2, func3
	dw	punchf, listf, func6, func7
	dw	func8, func9, func10, func11
diskf	equ	($-functab)/2	; disk funcs
	dw	func12,func13,func14,func15
	dw	func16,func17,func18,func19
	dw	func20,func21,func22,func23
	dw	func24,func25,func26,func27
	dw	func28,func29,func30,func31
	dw	func32,func33,func34,func35
	dw	func36,func37,func38,func39
	dw	func40,lret$eq$ff,func42,func43
	dw	func44,func45,func46,func47
	dw	func48,func49,func50
nfuncs	equ	($-functab)/2

functab2:
	dw	func98,func99
	dw	func100,func101,func102,func103
	dw	func104,func105,func106,func107
	dw	func108,func109,func110,func111
	dw	func112

nfuncs2	equ	($-functab2)/2

errtbl:
	dw	permsg
	dw	rodmsg
	dw	rofmsg
	dw	selmsg
	dw	0
	dw	0
	dw	passmsg
	dw	fxstsmsg
	dw	wildmsg

test$152:
	cpi 152! rnz

;
;	PARSE version 3.0b  Oct 08 1982 - Doug Huskey
;
;
      	; DE->.(.filename,.fcb)
	;
	; filename = [d:]file[.type][;password]
	;             
	; fcb assignments
	;
	;   0     => drive, 0 = default, 1 = A, 2 = B, ...
	;   1-8   => file, converted to upper case,
	;            padded with blanks (left justified)
	;   9-11  => type, converted to upper case,
	;	     padded with blanks (left justified)
	;   12-15 => set to zero
	;   16-23 => password, converted to upper case,
	;	     padded with blanks
	;   24-25 => 0000h
	;   26    => length of password (0 - 8)
	;
	; Upon return, HL is set to FFFFH if DE locates
	;            an invalid file name;
	; otherwise, HL is set to 0000H if the delimiter
	;            following the file name is a 00H (NULL)
	; 	     or a 0DH (CR);
	; otherwise, HL is set to the address of the delimiter
	;            following the file name.
	;
	lxi 	h,sthl$ret
	push	h
	lhld 	info
	mov	e,m		;get first parameter
	inx	h
	mov	d,m
	push	d		;save .filename
	inx	h
	mov	e,m		;get second parameter
	inx	h
	mov	d,m
	pop	h		;DE=.fcb  HL=.filename
	xchg
parse0:
	push	h		;save .fcb
	xra	a
	mov	m,a		;clear drive byte
	inx	h
	lxi	b,20h*256+11
	call	pad		;pad name and type w/ blanks
	lxi	b,4
	call	pad		;EXT, S1, S2, RC = 0
	lxi	b,20h*256+8
	call	pad		;pad password field w/ blanks
	lxi	b,12
	call	pad		;zero 2nd 1/2 of map, cr, r0 - r2
;
;	skip spaces
;
	call	skps
;
;	check for drive
;
	ldax	d
	cpi	':'		;is this a drive?
	dcx	d
	pop	h
	push	h		;HL = .fcb
	jnz	parse$name
;
;	Parse the drive-spec
;
parsedrv:
	call 	delim
	jz	parse$ok
	sui	'A'
	jc	perror1
	cpi	16
	jnc	perror1
	inx	d
	inx	d		;past the ':'
	inr	a		;set drive relative to 1
	mov	m,a		;store the drive in FCB(0)
;
;	Parse the file-name
;
parse$name:
	inx	h		;HL = .fcb(1)
	call	delim
	jz	parse$ok
	lxi	b,7*256

parse6:	ldax	d		;get a character
	cpi	'.'		;file-type next?
	jz	parse$type	;branch to file-type processing
	cpi	';'
	jz	parse$pw
	call	gfc		;process one character
	jnz	parse6		;loop if not end of name
	jmp	parse$ok
;
;	Parse the file-type
;
parse$type:	
	inx	d		;advance past dot
	pop	h
	push	h		;HL =.fcb
	lxi	b,9
	dad	b		;HL =.fcb(9)
	lxi	b,2*256

parse8:	ldax	d
	cpi	';'
	jz	parsepw
	call	gfc		;process one character
	jnz	parse8		;loop if not end of type
;
parse$ok:
	pop	b
	push	d
	call	skps		;skip trailing blanks and tabs
	dcx	d
	call	delim		;is next nonblank char a delim?
	pop	h
	rnz			;no
	lxi	h,0
	ora	a
	rz			;return zero if delim = 0
	cpi	cr
	rz			;return zero if delim = cr
	xchg
	ret
;
;	handle parser error
;
perror:
	pop	b			;throw away return addr
perror1:
	pop	b
	lxi	h,0ffffh
	ret
;
;	Parse the password
;
parsepw:
	inx	d
	pop	h
	push	h
	lxi	b,16
	dad	b
	lxi	b,7*256+1
parsepw1:
	call	gfc
	jnz	parsepw1
	mvi	a,7
	sub	b
	pop	h
	push	h
	lxi	b,26
	dad	b
	mov	m,a
	ldax	d			;delimiter in A
	jmp	parse$ok
;
;	get next character of name, type or password
;
gfc:	call	delim		;check for end of filename
	rz			;return if so
	cpi	' '		;check for control characters
	inx	d
	jc	perror		;error if control characters encountered
	inr	b		;error if too big for field
	dcr	b
	jm	perror
	inr	c
	dcr	c
	jnz	gfc1
	cpi	'*'		;trap "match rest of field" character
	jz	setmatch
gfc1:	mov	m,a		;put character in fcb
	inx	h
	dcr	b		;decrement field size counter
	ora	a		;clear zero flag
	ret
;;
setmatch:
	mvi	m,'?'		;set match one character
	inx	h
	dcr	b
	jp	setmatch
	ret
;	
;	check for delimiter
;
;	entry:	A = character
;	exit:	z = set if char is a delimiter
;
delimiters:	db	cr,tab,' .,:;[]=<>|',0

delim:	ldax	d		;get character
	push	h
	lxi	h,delimiters
delim1:	cmp	m		;is char in table
	jz	delim2
	inr	m
	dcr	m		;end of table? (0)
	inx	h
	jnz	delim1
	ora	a		;reset zero flag
delim2:	pop	h
	rz
	;
	;	not a delimiter, convert to upper case
	;
	cpi	'a'
	rc
	cpi	'z'+1
	jnc	delim3
	ani 	05fh
delim3:	ani	07fh	
	ret			;return with zero set if so
;
;	pad with blanks or zeros
;
pad:	mov	m,b
	inx	h
	dcr	c
	jnz	pad
	ret
;
;	skip blanks and tabs
;
skps:	ldax	d
	inx	d
	cpi	' '		;skip spaces & tabs
	jz 	skps
	cpi	tab
	jz	skps
	ret
;
;	end of PARSE
;

errflg:
	; report error to console, message address in hl
	push h! call crlf ; stack mssg address, new line
	lda adrive! adi 'A'! sta dskerr ; current disk name
	lxi b,dskmsg

if BANKED
	call zprint ; the error message
else
	call print
endif

	pop b

if BANKED
	lda bdos$flags! ral! jnc zprint
	call zprint ; error message tail
	lda fx! mvi b,30h
	lxi h,pr$fx1
	cpi 100! jc errflg1
	mvi m,31h! inx h! sui 100
errflg1:
	sui 10! jc errflg2
	inr b! jmp errflg1
errflg2:
	mov m,b! inx h! adi 3ah! mov m,a
	inx h! mvi m,20h
	lxi h,pr$fcb! mvi m,0
	lda resel! ora a! jz errflg3
	mvi m,20h! push d
	lhld info! inx h! xchg! lxi h,pr$fcb1
	mvi c,8! call move! mvi m,'.'! inx h
	mvi c,3! call move! pop d
errflg3:
	call crlf
	lxi b,pr$fx! jmp zprint

zprint:
	ldax b! ora a! rz
	push b! mov c,a
	call tabout
	pop b! inx b! jmp zprint

pr$fx:	db	'BDOS Function = '
pr$fx1:	db	'   '
pr$fcb:	db	' File = '
pr$fcb1:ds	12
	db	0

else
	jmp	print
endif

reboote: 
	lxi h,0fffdh! jmp rebootx0 ; BDOS error
rebootx:
;;;	lxi h,0fffeh ; CTL-C error
	call patch$1e25	;[JCE] DRI Patch 13
rebootx0:
	shld clp$errcde
rebootx1:
	jmp wbootf

entsp:	ds	2	; entry stack pointer

shell:
	lxi h,0! dad sp! shld shell$sp

if not BANKED
	lxi sp,shell$stk
endif

	lxi h,shell$rtn! push h
	call save$rr! call save$dma
	lda mult$cnt
mult$io:
	push a! sta mult$num! call cbdos
	ora a! jnz shell$err
	lda fx! cpi 33! cnc incr$rr
	call adv$dma
	pop a! dcr a! jnz mult$io
	mov h,a! mov l,a! ret

shell$sp:	dw	0

		dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h

shell$stk:		; shell has 5 level stack
hold$dma:	dw	0

cbdos:
	lda fx! mov c,a
cbdos1:
	lhld info! xchg! jmp bdose2

adv$dma:
	lhld dmaad! lxi d,80h! dad d! jmp reset$dma1

save$dma:
	lhld dmaad! shld hold$dma! ret

reset$dma:
	lhld hold$dma
reset$dma1:
	shld dmaad! jmp setdma

shell$err:
	pop b! inr a! rz
	lda mult$cnt! sub b! mov h,a! ret

shell$rtn:
	push h! lda fx! cpi 33! cnc reset$rr
	call reset$dma
	pop d! lhld shell$sp! sphl! xchg
	mov a,l! mov b,h! ret

	page


