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
public	p1, p2, p3, ccp

	.Z80			; Z80 CPU
hdisk	equ	-1		; -1 for hard disk, 0 for floppy
msize	equ	60		; system memory size in k
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

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

	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', '-', ','
	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	0,0,0,0		; - key on numeric pad
	defb	0,0,0,0		; , key on numeric pad 
	defb	0,0,0,0		; <cr>	"	"	
	defb	0,0,0,0		; . key on numeric pad

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
	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 
	nop
	nop
	nop
	nop
	nop
	nop
	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 CP/M Version '
	defb	vers/10+'0', '.', vers mod 10+'0','F'
	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:	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
	xor	a
	ld	(ccp+7),a
	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: 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

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

; 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

	end


	call	ccpco
	ld