	z80
;CPM 2.2 BIOS for ZEMU

true	equ	0ffh
false	equ	0


;Banked22	equ	false
Banked22	equ	true


tPrim	equ	3		;3 inch disks on primary
;tPrim	equ	8		;8 inch disks on primary
tSec	equ	3		;3 inch disks on secondary
;tSec	equ	8		;8 inch disks on secondary

nk.sys	equ	62		;system size in  kBytes
td	equ	nk.sys/10	;decimal tens digit
od	equ	nk.sys%10	;decimal ones digit
td.asc	equ	td+&h30		;ascci tens digit
od.asc	equ	od+&h30		;ascii ones digit
k.byte	equ	1024		;bytes in a kilobyte
cpm.sz	equ	nk.sys*k.byte	;top system addres
v.20k	equ	20*k.byte	;twenty kByte 
cpm.bs	equ	cpm.sz-v.20k	;cpm bias value
ccp	equ	cpm.bs+&H3400	;addres of ccp
bdos	equ	cpm.bs+&H3c00	;addres of bdos
bios	equ	cpm.bs+&H4a00	;addres of bios
;bios.r	equ	&H1000-bios	;DDT offset load?
tpa	equ	&H100		;addres of TPA
iobyte	equ	&Hff		;initial iobyte value

io.byte	equ	3		;address of iobyte
c.disk	equ	4		;address of current logged disk

;un-initialized data definition

ui.data	equ	&Hfc00		;3 256 blocks end feff
;ui.data	equ	eob		;3 256 blocks end of bios

df.drv	equ	0		;default drive
sec.sz	equ	&H80		;sector size
fmt.sz	EQU	&h100		;format buffer size
n.drvs	equ	4		;number of drives(1-4)

dsk.cmd equ &HD0 
dsk.sts equ &HD0 
dsk.drv		equ	&HD1 	;drv
dsk.trkL	equ	&HD2 	;trkLow
dsk.trkH	equ	&HD3 	;trkHigh
dsk.secL	equ	&HD4 	;secLow
dsk.secH	equ	&HD5 	;secHigh
dsk.dmaL	equ	&HD6 	;dmaLow
dsk.dmaH	equ	&HD7	;dmaHigh
dsk.sptL	equ	&HD8 	;sptLow
dsk.sptH	equ	&HD9 	;sptHigh
dsk.dmaB	equ	&hda 	;Disk DMA Bank
dsk.order	equ	&hdb 	;track order

cmd.read	equ	1
cmd.write	equ	2
cmd.log		equ	4
cmd.fmt		equ	8

	org	bios

;bios jump vector table

	jmp	init	;cold start
	jmp	warm	;warm start - reload ccp + bdos
	jmp	cns.ck	;get console status
	jmp	cns.in	;console input
outCH:	jmp	cns.ot	;console output
	jmp	list	;printer output
	jmp	punch	;punch output
	jmp	reader	;reader input
	jmp	home	;home selected drive
	jmp	seldsk	;select drive
	jmp	settrk	;set track
	jmp	setsec	;set sector
	jmp	setdma	;set tranfer address
	jmp	diskrd	;read disk
	jmp	diskwr	;write disk
	jmp	listst	;getr printer status
	jmp	sectrn	;translate sector
	jmp	format	;format a track

;bios variable storage

bt.cmd	db	0	;dcm command
bt.drv	db	0	;drive number
bt.trk	db	0	;track number
bt.sec	db	0	;sector number
bt.sp0	db	0	;spare
bt.sp1	db	0	;spare
bt.mod	db	0	;mode controls
bt.sts	db	0	;command status

bt.lad	dw	0	;load address
bt.lng	dw	0	;load length
bt.dma	dw	0	;system tranfer address

dt.ptr	dw	0	;drive table pointer
log.rq	db	0	;log on request

;log.buf	dw	&hfb00
log.buf	dw	eoui
log.dpb	equ	&h20		;offset to dpb in buffer
log.ddf	equ	&H31		;id sec - flags	


;init stuff is overlayed by directory buffer

dir.bf	equ	$

;get system size in ascii

init	lxi	sp,&H0080	;set initial stack

c.logo	lxi	h,msg.so	;sign on message
	call	msg.ot		;out message
	lhld	bios+4		;get warm address
	shld	bios+1		;make cold point to warm

	ift 	Banked22
	 mvi	a,1		;bank	1
	 out	&hc0		;emu bank select
	endif

	xra	a		;clear accumulator
	sta	io.byte		;store at iobyte
	sta	c.disk		;current logged disk =0

	ift 	Banked22
	 xra	a		;bank	0
	 out	&hc0		;emu bank select

	 mvi	e,0	;1	;log on request
	 mvi	c,0		;drive 0
	 call	seldsk		;select drive
	 lxi	b,&h8000	;cpm ccp address in bank 0
	 call	setdma		;set dma
	 mvi	c,29		;first ccp sector(128)
	 call	setsec		;set sector
	 mvi	c,0		;ccp/bdos track
	 call	settrk		;set track
w.read	 call	diskrd		;read one sector
	 ana	a		;set flags(ccr)
	 jrnz	w.eror		;exit if error
	 lda	bt.sec		;get sector number
	 cmpi	72		;last SECTOR(128)
	 jz	warm		;go init page zero
	 inr	a		;next sector
	 sta	bt.sec		;store next sector
	 lxi	d,sec.sz	;sector size
	 lhld	bt.dma		;inc buffer pointer to next sec
 	 dad	d		;new buffer address 
	 shld	bt.dma		;store new buffer address
	 mvi	c,&h2a		;
	 call	outCH		;splash progress bar
	 jmpr	w.read		;again

w.eror	 lxi	h,msg.le	;display error msg
	 call	msg.ot		;and
	 hlt			;halt

	else

	 jmp	warm		;do warm boot

	endif

eoinit	equ	$
;fill out directory buffer dir.buf

	org	dir.bf+&H80

msg.so	db	cr,lf
sys.td	db	td.asc		;tens digit
sys.od	db	od.asc		;ones digit
	text	"K CPM 2.2"
	ift 	Banked22
	text	"-Banked"
	endif
	db	cr,lf+&H80	;sign bit set on last byte

;disk executive

dsk.log
	lda	log.buf
	out	dsk.dmaL
	lda	log.buf+1	
	out	dsk.dmaH
	lda	bt.drv
	out	dsk.drv
	mvi	a,cmd.log
	out	dsk.cmd
	in	dsk.sts
	ana	a		;set ccr
	jnz	dsk.er		;exit error
	jmpr	dsk.ok		;normal exit

;disk executive

dsk.ex
	call	dpb.ad		;de points to dpb
	xchg			;hl ->dpb
	mov	a,m		;sptLow
	out	dsk.sptL
	inx	hl
	mov	a,m		;sptHigh
	out	dsk.sptH
	xra	a
	out	dsk.order	;sides
	lda	bt.dma
	out	dsk.dmaL
	lda	bt.dma+1	
	out	dsk.dmaH
	lda	bt.drv
	out	dsk.drv
	lda	bt.trk
	out	dsk.trkL
	lda	bt.sec
	out	dsk.secL
	lda	bt.cmd
	out	dsk.cmd
	in	dsk.sts
	ana	a		;set ccr
	ret

;read sector

diskrd
	mvi	a,cmd.read
	sta	bt.cmd
	call	dsk.ex		;perform operation
	jnz	dsk.er		;exit error
	jmpr	dsk.ok		;normal exit

;write sector

diskwr	
	mvi	a,cmd.write
	sta	bt.cmd
	call	dsk.ex		;perform operation
	jrz	dsk.ok		;normal exit if write ok
	jmpr	dsk.er		;exit error

;disk read write format exits and errors

dsk.ok	
	xra	a		;clear a to indicate OK
	ret

dsk.er	
	mvi	a,&HFF		;set a to indicate ERROR
	ret

format	
	mvi	a,cmd.fmt
	sta	bt.cmd
	call	dsk.ex		;perform operation
	jrz	dsk.ok		;normal exit if write ok
	jmpr	dsk.er		;exit error

seldsk	lxi	h,0		;hl = zero if error
	mov	a,c		;put drive number in a
	sta	bt.drv		;store drive number
	cmpi	n.drvs		;check if legal drive number
	rnc			;ret if illegal

	mov	a,e		;chk if need log on
	sta	log.rq		;save log on req in log.rq
	
retdsk	lda	bt.drv		;get drv number
	mov	l,a		;l=drive number
	mvi	h,0		;clear h
	dad	h		;*2
	dad	h		;*4
	dad	h		;*8
	dad	h		;*16
	lxi	d,d0.dph	;first dph
	dad	d		;hl=dph of drv number in c
	shld	dt.ptr		;store drive table pointer

;log on set dpb

log.on	lda	log.rq		;check log on req
	ani	&H01		;log on bit test
	jrnz	dsk.ok		;already logged on

;read id sector

	call	dsk.log
	jrz	log.ck		;id sector got read
	lxi	h,0		;bad log on hl=0(no Drive table)
	jmp	dsk.er		;exit error

;check if a JADE disk


log.ck	
	lhld	log.buf		;get address of log buffer
	lxi	d,jadeid	;de points to id value
	mvi	b,id.sze	;label size
..loop	ldax	d		;get label character
	cmp	m		;match?
	jrnz	ck1440		;no - go chk for 1440
;	inx	h		;point to next chars
;	inx	d		;
	dcr	b		;dec count of characters
	jrnz	..loop		;again
	jmpr	yes.id

;check for pc1440 id (3.5 1440)

ck1440	lhld	log.buf	;hl points to sector(id) buffer
	lxi	d,id1440	;de points to id value
	mvi	b,id.sze	;label size
..loop	ldax	d		;get label character
	cmp	m		;match?
	jrnz	lg.type		;no - go chk drive type(no id)
	inx	h		;point to next chars
	inx	d		;
	dcr	b		;dec count of characters
	jrnz	..loop		;again


;diskette contains JADE id or 3.5 1440 id

yes.id	call	dpb.ad		;get dpb address in de
	lxi	b,log.dpb	;offset to dpb in buffer
	lhld	log.buf		;get address of buffer
	dad	b		;hl points to dpb in sector(id) buffer
	lxi	b,dpb.sz	;size of dpb
;	call	block		;move dpb on dd to bios dpb
	ldir
	lxi	d,log.ddf	;
	lhld	log.buf		;get address of dd
	dad	d		;hl points to dpb in sector(id) buffer
	mov	a,m		;get density flags on dd
	ani	&H04		;test trk1 density
	cz	tr3740		;if single density use 3740
	cnz	trnone		;no sector translation
	lhld	dt.ptr		;reload ptr to dph table
	jmp	dsk.ok		;return with drv ptr in hl

; chk drive type - 0 or 1 can be 1440's 2 or 3 can be 3740's

lg.type	
	ifz 	tSec-8		;if tSec is 8 assemble this else assume 1440
	 lda	bt.drv		;3740 can be in 2 or 3
	 ani	&h02		;test if drive 2 0r 3
	 jrnz	lg3740		;must be 2 or 3 
	endif

;assume 1440

lg1440	call	trnone		;no translate 
	call	dpb.ad		;get dpb address in de
	lxi	b,dpb.sz	;size of dpb
	lxi	h,pc.pbk	;hl points to pc 1440 dpb
;	call	block		;move pc 1440 dpb  to bios dpb
	ldir
	lhld	dt.ptr		;reload ptr to dph table
	jmp	dsk.ok		;return with drv ptr in hl

;assume 3740

lg3740	call	tr3740		;translate for 3740
	call	dpb.ad		;get dpb address in de
	lxi	b,dpb.sz	;size of dpb
	lxi	h,sd.pbk	;hl points to single density dpb 3740
;	call	block		;move single dens dpb  to bios dpb
	ldir
	lhld	dt.ptr		;reload ptr to dph table
	jmp	dsk.ok		;return with drv ptr in hl

;set 3740 sector translation

tr3740	lxi	d,sdtran	;single density translation table
	lhld	dt.ptr		;ptr to dph table
	mov	m,e		;put xlat table pointer into dph at 0
	inx	h		;
	mov	m,d		;
	ret

;set no xlation

trnone	xra	a		;zero
	lhld	dt.ptr		;ptr to dph table
	mov	m,a		;put null xlat table pointer into dph at 0
	inx	h		;
	mov	m,a		;
	ret


;get drv par block address

dpb.ad	lhld	dt.ptr		;ptr to dph table
	lxi	d,10		;
	dad	d		;d points to dpb in dph
	mov	e,m		;get dpb from dph at offset 10
	inx	h		;
	mov	d,m		;
	ret


;home drive

home	mvi	c,0		;set track to 0 fall into settrk

;set track

settrk	mov	a,c		;track no in a from c
	sta	bt.trk		;store track number
	ret

;set sector

setsec	mov	a,c		;sector no in a from c
	sta	bt.sec		;store sector number
	ret

;set dma

setdma	mov	l,c		;just save in bt.dma
	mov	h,b
	shld	bt.dma
	ret

;sector translation

sectrn	mov	a,d		;testing tbl addr
	ora	e		;chk if 0
	jrz	notran		;no xlation
	xchg			;hl=xlat tbl
	dad	b		;hl + sec offset
	mov	l,m		;
	mvi	h,0		;assumed 8 bit sector numbers
	ret	
notran	lxi	h,1		;add one to sector number and ret
	dad	b		
	ret	

;define console io ports

cni.sp	equ	&H7d
cni.sb	equ	&H02
cni.si	equ	&H00
cni.dp	equ	&H7c

cno.sp	equ	&H7d
cno.sb	equ	&H01
cno.si	equ	&H00
cno.dp	equ	&H7c

io.rdy	equ	&HFF	;ready
asc.msk	equ	&H7F	;ascii mask

lf	equ	&H0a
cr	equ	&H0d
cntl.z	equ	&H1A

;console input status


cns.ck	in	cni.sp		;in status
	xri	cni.si		;adjust polarity
	ani	cni.sb		;mask status bit
	rz			;return zero if not ready
	mvi	a,io.rdy	;set ready
	ret			;return

;console input

cns.in	call	cns.ck		;check status
	jrz	cns.in		;repeat untill ready
	in	cni.dp		;in character
	ani	asc.msk		;mask to 7 bit ascii
	ret			;return

;console output

cns.ot	in	cno.sp		;in status
	xri	cno.si		;adjust polarity
	ani	cno.sb		;mask status bit
	jrz	cns.ot		;repeat untill ready
	mov	a,c		;get character in a
	ani	asc.msk		;mask to 7 bit ascii
	out	cno.dp		;out character
	ret			;return

;reader and punch

reader	mvi	a,cntl.z	;return eof
	ret			;return

punch	ret			;throw away

;printer

lst.dp	equ	7eH		;list data port
lst.st	equ	7fH		;list status port
lst.ctl	equ	lst.st		;control port
lst.msk	equ	00000001B	;status mask (ORPLY)
ctl.msk	equ	00000001b	;control mask (OSTB)
mem.msk	equ	00000010b	;sbc mem mask 10=out 00=in

listst	in	lst.st		;
	ani	lst.msk		;
	rz			;not ready
	mvi	a,&Hff		;ready
	ret
	
list	call	listst		;get status
	jrz	list		;wait for ready
	mov	a,c		;get char from c
	out	lst.dp		;out char to data port
	mvi	a,ctl.msk		;strobe data
	out	lst.ctl		;strobe
	ret			;return

;display string

msg.ot	push	psw		;save ccr and a
..rept	mov	c,m		;get char fron where hl points
	call	outCH		;output char
	mov	a,m		;get char again to test sign
	inx	hl		;inc to next char
	ani	&H80		;test sign bit
	jrz	..rept		;repeat until sign bit set
	pop	psw		;restore ccr and a
	ret			;;return

block	mov	a,m		;
	stax	d		;
	inx	h		;
	inx	d		;
	dcx	b		;
	mov	a,b		;
	ora	c		;
	jnz	block		;
	ret			;

;warm boot loads ccp + bdos sets up page zero

warm	lxi	sp,&H0080	;set up stack in def buffer
	
	ift	Banked22

	 mvi	a,0		;bank	0
	 out	&hc0		;expandoram ii bank select
	mvi	a,&hc3		;jmp xxxx = (c3 xx xx)
	sta	0		;Fix warm vector in bank 0 for reset
	lxi	h,bios+&H03	;jmp bios+3(warm)
	shld	1
	 lxi	hl,&h8000	;loc of ccp and bdos in bnk0
	 lxi	de,ccp		;loc of ccp and bdos in bnk1
	 lxi	bc,44*128	;size of ccp and bdos
	 ldir
;	 call 	block
	 mvi	a,1		;bank	1
	 out	&hc0		;expandoram ii bank select

	else

	 mvi	e,0	;1	;no log on request
	 mvi	c,0		;drive 0
	 call	seldsk		;select drive
	 lxi	b,ccp		;cpm ccp address
	 call	setdma		;set dma
	 mvi	c,29		;first ccp sector(128)
	 call	setsec		;set sector
	 mvi	c,0		;ccp/bdos track
	 call	settrk		;set track
	
;read ccp and bdos
	 mvi	c,&hd		;
	 call	outCH		;cr
	 mvi	c,&ha		;
	 call	outCH		;lf

w.read	 call	diskrd		;read one sector
	 ana	a		;set flags(ccr)
	 jrnz	w.eror		;exit if error
	 lda	bt.sec		;get sector number
	 cmpi	72		;last SECTOR(128)
	 jrz	w.zrpg		;go init page zero
	 inr	a		;next sector
	 sta	bt.sec		;store next sector
	 lxi	d,sec.sz	;sector size
	 lhld	bt.dma		;inc buffer pointer to next sec
	 dad	d		;new buffer address 
	 shld	bt.dma		;store new buffer address
	 mvi	c,&h2a		;
	 call	outCH		;splash progress bar
	 jmpr	w.read		;again 

w.eror	 lxi	h,msg.le	;display error msg
	 call	msg.ot		;and
	 hlt			;halt
	endif
;init page zero

w.zrpg	mvi	a,&hc3		;jmp xxxx = (c3 xx xx)
	sta	0		;
	lxi	h,bios+&H03	;jmp bios+3(warm)
	shld	1
	sta	5		;jmp instruction
	lxi	h,bdos+&H06	;jmp bdos (ccp+6)
	shld	6
	sta	&H38		;rst 7
	lxi	h,&He003	;jmp sdmon(e003)
	shld	&h39

;last logged drive

	lda	c.disk		;last drive used
	mov	c,a		;ccp expects it in c
	
	lxi	h,&H0080	;set default dma
	shld	bt.dma		;

;jump to ccp

	jmp	ccp		;go to cpm


;3740 parameters

sdtran	db	&H01,&H07,&H0d,&H13,&H19,&H05
	db	&H0b,&H11,&H17,&H03,&H09,&H0f
	db	&H15,&H02,&H08,&H0e,&H14,&H1a
	db	&H06,&H0c,&H12,&H18,&H04,&H0a
	db	&H10,&H16

;default drive parametr block for 3.5" pc1440
;		pc1440
;		IBM PC
pc.pbk	dw	72		;spt
	db	4		;bsh
	db	15		;blm
	db	0		;exm
	dw	710		;dsm
	dw	255		;drm
	db	&hf0		;al0
	db	0		;al1
	dw	64		;cks
	dw	2		;off

;default drive parametr block for 8" IBM3740

sd.pbk	dw	26		;spt
	db	3		;bsh
	db	7		;blm
	db	0		;exm
	dw	242		;dsm
	dw	63		;drm
	db	&hc0		;al0
	db	0		;al1
	dw	16		;cks
	dw	2		;off

;messages

msg.le	db	cr,lf
	text	"LOAD ERRO"
	db	&H52+&H80

;id label

id1440	text	"3.5 1440"

jadeid	text	"Jade ID " ;"JADE ID" ?????
id.sze	equ	$-jadeid

;drive parameter area

d0.dph	dw	0		;xlat table
	dw	0		;scratch area
	dw	0		;scratch area
	dw	0		;scratch area
	dw	dir.bf		;directory buffer
	dw	d0.dpb		;drive parameter block
	dw	d0.chk		;chk for disk change area
	dw	d0.all		;drive allocation table
d1.dph	dw	0
	dw	0
	dw	0
	dw	0
	dw	dir.bf
	dw	d1.dpb
	dw	d1.chk
	dw	d1.all
d2.dph	dw	0
	dw	0
	dw	0
	dw	0
	dw	dir.bf
	dw	d2.dpb
	dw	d2.chk
	dw	d2.all
d3.dph	dw	0
	dw	0
	dw	0
	dw	0
	dw	dir.bf
	dw	d3.dpb
	dw	d3.chk
	dw	d3.all

eob	equ	$

;Un-initialized data area

	org	ui.data

;reserve drive parameter block DPB's

dpb.sz	equ	15	;size of drive parameter block

d0.dpb	ds	dpb.sz	;reserve 15 byte for drive 0
d1.dpb	ds	dpb.sz	;reserve 15 byte for drive 1
d2.dpb	ds	dpb.sz	;reserve 15 byte for drive 2
d3.dpb	ds	dpb.sz	;reserve 15 byte for drive 3

; drive allocation area

all.sz equ	91

d0.all	equ	$
d1.all	equ	d0.all+all.sz
d2.all	equ	d1.all+all.sz
d3.all	equ	d2.all+all.sz

;changed disk area

chk.sz	equ	64

d0.chk	equ	d3.all+all.sz
d1.chk	equ	d0.chk+chk.sz
d2.chk	equ	d1.chk+chk.sz
d3.chk	equ	d2.chk+chk.sz

eoui	equ	d3.chk+chk.sz
