title KAYPRO CBIOS for CP/M 2.2
;###############################################################
;##      KAYPRO 10      CBIOS for CP/M 2.2                    ##
;##      Copyright (C) 1982 By Non-Linear Systems, Inc.       ##
;##      No warranty is made, expressed or implied.           ##
;###############################################################
;##	Last Update: 10/20/83      	[01]		      ##
;###############################################################
;
;History:
;	Add secondary xlate table and build in ability to have
;	function keys.
;				Steven R. Fabian	
;	Initialization of modem port on a cold boot.
;				Steven R. Fabian
;	Add Parallel port driver using a time delay.
;				Steven R. Fabian
;
true	equ 	0ffh
false	equ	0
;
;****************************************************************
;*								*
;*  This BIOS can support versions D, F and G versions of the	*
;*  Kaypro 10 CP/M system, if the proper equates are set.  Set	*
;*  one and only one of the following three equates to true;	*
;*  be sure the other two are false.				*
;*								*
versd	equ	false	; for version 2.2D			*
versf	equ	false	; for version 2.2F			*
versg	equ	true	; for version 2.2G			*
;*								*
;****************************************************************
;
;
;****************************************************************
;*								*
;*  Set the following equate to false if a standard CP/M BIOS	*
;*  is desired.  Set it to true for ZCPR3 buffer initialization *
;*								*
zcpr3	equ	true	;					*
;*								*
;****************************************************************
;
;	Version 3.0
;	Initial version customized for ZCPR3.
;	John C. Smith		July 23, 1984
;
;	Version 3.1
;	Added code to support ZCPR3 from floppy.  Requires
;	LDR SYS.RCP,SYS.ENV,SYS.FCP,NAMES.NDR be executed
;	from the boot floppy.  Path is changed to B$,B0
;	when booted from floppy to be consistent with
;	hard disk path of A$,A0.  Also, added code and
;	conditional equates to support CP/M versions 2.2D
;	and 2.2G.
;	John C. Smith		August 5, 1984
;
zvers	equ	31		; ZCPR version number
;
;
	.Z80			; Z80 CPU
hdisk	equ	-1		; -1 for hard disk, 0 for floppy

	if	zcpr3
msize	equ	58		; system memory size in k (ZCPR3)
	else
msize	equ	60		; system memory size in k (CP/M)
	endif

vers	equ	22		; CP/M version number
bias	equ	(msize-20)*1024	; bias for systems larger than 20k
ccp	equ	3400H+bias	; start of CCP
bdos	equ	ccp+806H	; start of BDOS (The resident portion of CP/M)
bios	equ	ccp+1600H	; start of Basic I/O Subsystem (BIOS)
cpml	equ	bios-ccp	; length of CP/M system in bytes (less BIOS)
nsects	equ	cpml/128	; length of CP/M system in sectors (less BIOS)
	if	hdisk
trksec	equ	68		; sectors/track
	else
trksec	equ	40
	endif
;
;	ZCPR3 BUFFERS
;
WHEEL	EQU	3EH		; LOCATION OF WHEEL BYTE
EXTPATH EQU	40H		; START OF EXTERNAL PATH
GRAF	EQU	4FH		; LOCATION OF GRAPHICS BYTE
MCMD	EQU	0F600H		; MUTLI-COMMAND BUFFER
;
;
;
bitport	equ	14H		; status/control bit maped port
baudA	equ	0		; baud rate port (modem)
baudB	equ	8		; baud rate port (printer)
baud30	equ	05H		; 300 baud rate
iobyte	equ	3		; logical to physical map
rom	equ	00000H		; base of rom
time	equ	8000H		; time out rate
fox	equ	0		; set to 0
siokb0	equ	07H		; keyboard channel command/status
siosp0	equ	0EH		; serial printer command/status channel
siom0	equ	06H		; modem channel command/status 
reset	equ	18H		; channel reset 
wr1	equ	01H		; interrupt enable and wait/ready modes
tid	equ	00H		; transmitter interrupt disable
rid	equ	00H		; recieve interrupt disable
wr3	equ	03H		; receiver logic control parameters
re	equ	01H		; receiver enable
autoe	equ	20H		; auto enable (use dcd and cts to enable recv 
				; and xmt 
rbits8	equ	0C0H		; 8 bits/character
wr4	equ	04H		; control bits that affect both xmt and recv
sbits1	equ	04H		; 1 stop bit
cr16	equ	40H		; x16
wr5	equ	05H		; control bits that affect xmt
te	equ	08H		; transmit enable
tbits8	equ	60H		; 8 bits/character
dtr	equ	80H		; DTR output
pfdat	equ	24		; cent out data port (8 bit latch)
pstrob	equ	3               ; bit in bit port 
;	aseg
;	org	bios
	.phase  bios

	jp	boot		; arrive here from cold start
	jp	wboot		; arrive here for warm start
	jp	const		; console status return in A FF=ready, 00=not
	jp	conin		; console char in
	jp	conout		; console char out
	jp	list		; listing char out
	jp	punch		; punch char out
	jp	reader		; reader char in
	jp	home		; move to track 0 on selected disk drive
	jp	seldsk		; select disk drive
	jp	settrk		; set track #
	jp	setsec		; set sector #
	jp	setdma		; set DMA address
	jp	read		; read selected sector
	jp	write		; write selected sector
	jp	listst		; list status (Ready to print a char)
	jp	sectran		; sector translate
ioconfig: defb	10000001B	; initial value for i/o byte (may be patched)
wrtsafe: defb	0		; write safe flage 0=false
vtab:	defb	11, 10, 8, 12	; vector pad xlate table ^k ^j ^h ^l
	defb	'0', '1', '2', '3'
	defb	'4', '5', '6', '7'
	defb	'8', '9',  0 ,  0
	defb	0DH, '.'
baudrt:	defb	baud30		; baud rate (modem)
	defb	baud30		; baud rate (printer)

	defb	(sndtab-vtab)

sioint:				;i/o device initialization table
				;first byte is number of bytes to send
				;second byte is port to send out to
				;third byte is data
	;init sio channel for serial printer
	defb	09H
	defb	siosp0
	defb	reset			;reset sio channel
	defb	wr4	
	defb	sbits1 or cr16		;one stop bit 16x clock
	defb	wr3
	defb	re or rbits8 or autoe	;recv enable, 8bits/char
	defb	wr5
	defb	te or tbits8 or dtr 	;xmt enable, 8bits/char,assert dtr
	defb	wr1
	defb	tid or rid		;xmt & recv interrupts disabled

	;init sio channel for modem
	defb	09H
        defb	siom0
	defb	reset			;reset sio channel
	defb	wr4	
	defb	sbits1 or cr16		;one stop bit 16x clock
	defb	wr3
	defb	re or rbits8 or autoe	;recv enable, 8bits/char
	defb	wr5
	defb	te or tbits8 or dtr 	;xmt enable, 8bits/char,assert dtr
	defb	wr1 
	defb	tid or rid		;xmt & recv interrupts disabled

siotbnd:defb	0			;end of table

sndtab:	defb	0,0,0,0		; up arrow key
	defb	0,0,0,0		; down arrow key
	defb	0,0,0,0		; left arrow key
	defb	0,0,0,0		; right arrow key
	defb	0,0,0,0		; 0 key on numeric pad
	defb	0,0,0,0		; 1 key on numeric pad
	defb	0,0,0,0		; 2 key on numeric pad
	defb	0,0,0,0		; 3 key on numeric pad
	defb	0,0,0,0		; 4 key on numeric pad
	defb	0,0,0,0		; 5 key on numeric pad
	defb	0,0,0,0		; 6 key on numeric pad
	defb	0,0,0,0		; 7 key on numeric pad
	defb	0,0,0,0		; 8 key on numeric pad
	defb	0,0,0,0		; 9 key on numeric pad
	defb	'dir',0DH	; - key on numeric pad
	defb	'swp',0DH	; , key on numeric pad 
	defb	0,0,0,0		; <cr>	"	"	
	defb	0,0,0,0		; . key on numeric pad

	if	versg
mdmflg:	defb	0ffh		;Internal modem flag 0=none present
	endif

subttl Cold and Warm boot entry points	defb
page
; Cold boot entry point, set up system pointers and pass control to the CCP
boot:	call	diskint
	xor	a		; clear system disk number
	ld	(4),a

	if	zcpr3
	LD	(GRAF),A	; reset graphics byte
	CPL
	LD	(WHEEL),A	; set wheel byte
	LD	HL,PATH		; initialize path
	LD	DE,EXTPATH
	LD	BC,7
	LDIR
	LD	HL,CMD		; initialize multi-command line buffer
	LD	DE,MCMD
	LD	BC,0CH
	LDIR
	LD	HL,0E600H	; initialize ZCPR3 RCP buffer by
	LD	A,8		; filling memory with 0
	CALL	ZLOOP
	endif

	if	zcpr3 and not hdisk	; if boot from floppy, initialize
	LD	HL,0F100H	; all other buffer spaces since
	LD	a,5		; there will be no load from the
	CALL	ZLOOP		; "putovl" track.
	endif

	ld	a,(ioconfig)	; init value for i/o byte
	ld	(iobyte),a
	ld	hl,sioint	; initialize i/o devices
iolp:	ld	b,(hl)		; number of bytes to send
	inc	hl
	ld	c,(hl)		; port to send
	inc	hl		; address of byte being sent
	otir
	ld	a,(hl)		; get this byte
	or	a		; clean test
	jr	nz,iolp		; if more tables then do 

	if	versd and hdisk
	IN	A,(BITPORT)	;2.2D PATCH
	OR	4		;2.2D PATCH
	OUT	(BITPORT),A	;2.2D PATCH
	endif

	ld	a,(baudrt)	; set baud rates
	out	(baudA),a
	ld	a,(baudrt+1)
	out	(baudB),a
	call	print
	defb	1AH, 0DH, 0AH
	defb	'KAYPRO 10 '
	defb	msize/10+'0', msize mod 10+'0'
	defb	'K CP/M Version '
	defb	vers/10+'0', '.', vers mod 10+'0'

	if	versd
	defb	'D'
	endif
	if	versf
	defb	'F'
	endif
	if	versg
	defb	'G'
	endif

	if	zcpr3
	defb	' & ZCPR Version ' 
	defb	zvers/10+'0', '.', zvers mod 10+'0'
	defb	' JCS'
	endif

	defb	0DH, 0AH, 00H

goccp:	ld	hl,time		; reset disk time out
	ld	(count),hl

	ld	a,0C3H		; set up CP/M jumps to bdos and wboot

	ld	hl,bios+3	; wboot entry point
	ld	(0),a
	ld	(1),hl

	ld	hl,bdos		; entry point to bdos
	ld	(5),a
	ld	(6),hl

	ld	a,(4)		; last logical disk unit used
	ld	c,a
	and	0FH		; valid disk?
	cp	3
	jp	c,ccp
	ld	a,c		; no, so go to drive 0
	and	0F0H
	ld	c,a		; pass to ccp to select
	jp	ccp		; pass control to ccp

; Warm boot entry point, re-load the CCP and BDOS
wboot:	
	if	versg
	ld	a,(mdmflg)
	cp	0
	call	nz,mdmnit
	endif

	ld	c,0		; select drive A:
	call	seldsk
	call	home
	call	diskint
	call	print
	defb	0DH, 0AH, 'Warm Boot', 0DH, 0AH, 00H
wb0:	ld	sp,100H		; re-set stack
	ld	bc,0		; set track
	call	settrk
	ld	bc,ccp		; first memory location to load
	ld	(dmaadr),bc
	call	setdma
	ld	bc,nsects*256+1
wb1:	push	bc		; save sector count and current sector
	call	setsec		; select sector
	call	read
	pop	bc
	or	a
	jr	nz,wb0		; oops, error on warm boot
	push	bc
	ld	hl,(dmaadr)	; update dma address for next sector
	ld	de,128		; new dma address
	add	hl,de
	ld	b,h
	ld	c,l
	ld	(dmaadr),hl
	call	setdma
	pop	bc

	if	not zcpr3
	xor	a
	ld	(ccp+7),a
	endif

	dec	b
	jp	z,goccp		; done loading
	inc	c		; bump sector count
	if	not hdisk
	ld	a,trksec	; next track?
	cp	c
	jr	nz,wb1
	ld	c,16		; next sector to read
	push	bc
	ld	c,1
	call	settrk		; set track number
	pop	bc
	endif
	jr	wb1

subttl logical to physical devices CON:, PUN:, RDR:, and LST:
page
; logical devices are con: rdr: pun: and lst:
; physical devices are:
;	crt:	video and kbd
;	tty:	serial
;	lpt:	centronics
;	ul1:	serial with cts as busy
;	pun:	same as ul1
;
;con:	tty, crt
;rdr:	tty
;pun:	tty, pun
;lst:	tty, crt, lpt, ul1

const:	ld	hl,(count)	; time out motors?
	dec	hl
	ld	(count),hl
	ld	a,h
	or	l
	call	z,diskoff
	ld	a,(cnt)		;load function counter
	or	a		;clean test
	jr	nz,loadup	;set to do function if not 0
	ld	a,(iobyte)	; get i/o byte
	and	03H		; strip to con bits
	ld	l,rom+33H	; serial status
	jp	z,callrom
	ld	l,rom+2AH	; assume CRT
	jp	callrom

loadup:	ld	a,0ffh		;set for get character
	or	a		;clean test
	ret

conin:	call	const		; key press?
	or	a
	jr	z,conin
	ld	a,(cnt)		;get counter value
	or	a		;clean test
	jr	nz,frjp		;do function if not 0
	ld	a,(iobyte)	; go get character
	and	03H		; check i/o byte
	ld	l,rom+36H	; serial input
	jp	z,callrom
	ld	l,rom+2DH	; assume input from kbd
	call	callrom		; go get char
	or	a
	ret	p		; msb not set
	and	01FH		; form table index to vtab
	ld	hl,vtab
	ld	c,a
	ld	b,0
	add	hl,bc
	ld	a,(hl)		; pick up xlated character
	or	a		; clean test
	ret	nz		; if valid character value return

sectab:	ld	hl,sndtab	;set to new table value
	ld	a,c		;get character position
	sla	a		;shift left to
	sla	a		;mult by 2, again
	ld	c,a		;get new character position
	add	hl,bc		;set hl to character position
	ld	(pntr),hl	;store position in pointer
	ld	a,4		;set to this value
	ld	(cnt),a		;initialize counter to 4
				;continue process of function

frjp:	ld	hl,(pntr)	;get pointer value
	ld	c,(hl)		;load character into c reg
	inc	hl		;increment pointer
	ld	(pntr),hl	;save this value off
	ld	a,(cnt)		;retrieve current counter value
	dec 	a		;decrement this value
	ld	(cnt),a		;save this value off
	ld	a,c		;move character into a
	ret	z		;return if zero
	ld	a,(hl)		;get next byte value
	cp	fox		;compare to 0
	ld	a,c		;move value in c to a
	ret	nz		;return if not zero
	xor	a		;clear accumulator
	ld	(cnt),a		;clear counter value
	ld	a,c		;move value in c to a
	ret

diskoff:ld	l,rom+27H
	jp	callrom

conout: 
	if	zcpr3
	LD	A,(GRAF)	; check graphics byte
	OR	A		; is it set?
	JR	NZ,R1		; allow eighth bit if it is
	LD	A,C		; otherwise, mask it out
	AND	7FH
	LD	C,A
R1:	
	endif
	
	ld	a,(iobyte)	; check i/o byte
	and	03H
	ld	l,rom+39H	; serial output
	jp	z,callrom
	ld	l,rom+45H	; assume video
	jp	callrom

reader:	ld	l, rom+36H	; serial input
	jp	callrom

;punch:	ld	a,(iobyte)	; check i/o byte
;	and	30H
;	ld	l,rom+39H	; serial punch
;	jp	z,callrom
;	ld	l, rom+42H	; serial with cts as busy
;	jp	callrom
punch:
	ld	l,rom+39h	;serial punch
	jp	callrom
;

list:	ld	a,(iobyte)
	and	0C0H		; check i/o byte
	ld	l,rom+39H	; serial
	jp	z,callrom
	cp	80H		; centronics
	jp	z,lstdev	; time delay routine for output
	ld	l,rom+45H	; video
	cp	40H
	jp	z,callrom
;	ld	l, rom+42H	; assume serial with cts as busy
	ld	l, rom+39h	;ul1: default to serial
	jp	callrom

listst:	ld	a,(iobyte)	; check i/o byte
	and	0C0H
	ld	l,rom+42H	; serial
	jp	z,callrom
	ld	l,rom+3CH	; centronics
	cp	80H
	jp	z,callrom
	xor	a		; 0=ready
	ret

lstdev:	call	listst		; is printer busy
	jr	nz,lstdev	; if not return
	ld	a,c		; move character into a
	out	(pfdat),a	; output character to printer
	in	a,(bitport)	; strb. printer
	res	pstrob,a
	out	(bitport),a
	set	pstrob,a
	out	(bitport),a
	ret

	if	versg
mdmnit:	ld	a,0fh		; set up pio
	out	(23h),a
	ld	a,87h		; and interrupt vector
	out	(23h),a
	ld	a,4ah		; set modem on-hook
	out	(21h),a
	endif

subttl Disk I/O and ROM dispatch
page
diskint:ld	l,rom+03H	; re-set disk software sub-system
	jr	callrom

home:	ld	l,rom+0CH	; home disk drive rom routine
	jr	callrom

seldsk:	ld	l,rom+0FH	; select disk drive
	jr	callrom

settrk:	ld	l,rom+12H	; seek track
	jr	callrom

setsec:	ld	l,rom+15H	; set sector number
	jr	callrom

setdma:	ld	l,rom+18H	; set dma address
	jr	callrom

read:	ld	hl,time		; reset time out
	ld	(count),hl
	ld	l,rom+1BH	; read a logical sector
	jr	callrom

write:	ld	hl,time		; reset time out
	ld	(count),hl
	ld	l,rom+1EH	; write a logical sector
	ld	a,(wrtsafe)	; write safe flag
	or	a		; true or false
	jr	z,callrom	; normal operation
	ld	c,1		; directory write code (forces write op)
	jr	callrom

sectran:ld	l,rom+21H	; xlate logical to physical sector
	jr	callrom

callrom:exx			; save cp/m arguments
	in	a,(bitport)	; turn rom on
	set	7,a
	out	(bitport),a	
	ld	(savsp),sp	; save current stack (may be under rom)
	ld	sp,stack	; set a local stack
	ld	de,biosret	; rom to "RET" here
	push	de
	exx			; restore cp/m arguments and call loc
	ld	h,0
	jp	(hl)		; to rom routine specified in hl
biosret:ex	af,af'		; save reg A
	ld	sp,(savsp)	; restore stack
	in	a,(bitport)	; off the rom
	res	7,a
	out	(bitport),a
	ex	af,af'		; restore reg A
	ret			; done with rom routine

print:
	ex	(sp),hl	; pop return address, points to text to print
	ld	a,(hl)	; get a byte of text, stop on zero byte
	inc	hl
	ex	(sp),hl	; save new return address
	or	a	; is it a zero byte?
	ret	z
	ld	c,a	; no, so print it
	call	conout
	jr	print

	if	zcpr3
ZLOOP:	LD	B,0FFH
ZERO:	LD	(HL),0
	INC	HL
	DJNZ	ZERO
	DEC	A
	JR	NZ,ZLOOP
	RET
	endif

	if	hdisk and zcpr3
PATH:	DEFB	1,'$',1,0,0,0,0,0,0,0,0		; search path
	else
PATH:	DEFB	2,'$',2,0,0,0,0,0,0,0,0
	endif

	if	zcpr3
CMD:	DEFB	4,0F6H,0C8H,0,'STARTUP',0	; mcl buffer
pntr:	defw	0	; pointer for function key routine
cnt:	defb	0	; character counter for function routine
count:	defs	2	; disk time out counter
savsp:	defs	2	; current spact pointer during rom call
dmaadr:	defs	2	; dma address for warm boot
	db	'END'	; end of BIOS marker
stack	equ	$+64	; a local stack

	else

; Patch CCP to display user # and search user 0 for a COM file.

openf	equ	15		; open disk function
bdosent	equ	5		; entry point to bdos
ccperr	equ	ccp+7EEH
ccpfcb	equ	ccp+7CDH
ccpco	equ	ccp+8CH
ccpread	equ	ccp+0F9H
ccpp1	equ	ccp+0392H
ccpp2	equ	ccp+00D7H
ccpp3	equ	ccp+06E9H

getusr	macro	x
	.xlist
	ld	e,-1
	ld	c,32
	call	bdosent
	ld	(x),a
	.list
	endm

setusr	macro	x
	.xlist
	ld	a,(x)
	ld	e,a
	ld	c,32
	call	bdosent
	.list
	endm

p1:	ld	c,32		; show user#, get current one
	ld	e,-1		; e=255 is get user code
	call	bdosent
	cp	10		; if a>10 then print first digit
	jr	c,pmt0
	sub	10
	push	af		; save second digit
	ld	a,'1'
	call	ccpco
	pop	af
pmt0:	add	a,'0'		; for ascii digit
	call	ccpco
	ld	a,'>'
pmt2:	jp	ccpco

p2:	getusr	curuser
	ld	(fcbuser),a
	ld	de,ccpfcb
	ld	c,openf		; open a file (check user 0 if not found)
	call	bdosent
	ld	(ccperr),a
	inc	a
	ret	nz		; nz=file opened
	ld	a,(curuser)	; get current user #
	or	a
	jr	z,nogd		; in user 0, file open no good
	xor	a
	ld	(fcbuser),a	; try user 0
	setusr	fcbuser
	ld	de,ccpfcb	; try to open file
	ld	c,openf
	call	bdosent
	ld	(ccperr),a	; ccp error return
	setusr	curuser		; restore user #
nogd:	ld	a,(ccperr)	; was open ok?
	inc	a
	ret			; back to ccp

p3:	setusr	fcbuser		; read a sector
	ld	de,ccpfcb
	call	ccpread
	push	af
	setusr	curuser
	pop	af
	ret
pntr:	defw	0	; pointer for function key routine
cnt:	defb	0	; character counter for function routine
curuser:defs	1	; current user
fcbuser:defs	1	; user # of load file
count:	defs	2	; disk time out counter
savsp:	defs	2	; current spact pointer during rom call
dmaadr:	defs	2	; dma address for warm boot
stack	equ	$+64	; a local stack
	endif

	end