;WARNING this bios will not work as a regualar CPM2.2 bios
; it works as a boot loader bios, because only drive A ever gets used
;CPM 2.2 BIOS modified to boot load cpm3
	public	boot2
	public	const2,conin2,conout2,list2,auxout2,auxin2	
	public	home2,seldsk2,settrk2,setsec2,setdma2	
	public	read2,write2,listst2,sectrn2	
true	equ	-1
false	equ	not true

	maclib	z80

;macros for defining disk defintion data for the NDI
Set8	macro	?Register,?Value
	db	1,?Register,?Value
	endm
Set16	macro	?Register,?Value
	db	2,?Register
	dw	?Value
	endm

Banked22	equ	false
;Banked22	equ	true

nk$sys	equ	62		;system size in  kBytes
td	equ	nk$sys/10	;decimal tens digit
od	equ	nk$sys MOD 10	;decimal ones digit
td$asc	equ	td+30h		;ascci tens digit
od$asc	equ	od+30h		;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+3400h	;addres of ccp
bdos	equ	cpm$bs+3c00h	;addres of bdos
bios	equ	cpm$bs+4a00h	;addres of bios
;bios$r	equ	1000h-bios	;DDT offset load?
tpa	equ	100h		;addres of TPA
initIOB	equ	0ffh		;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	0fc00h		;3 256 blocks end feff
;ui$data	equ	eob		;3 256 blocks end of bios

df$drv	equ	0		;default drive
sec$sz	equ	80h		;sector size
fmt$sz	EQU	100h		;format buffer size
n$drvs	equ	4		;number of drives(1-4)

dsk$cmd equ 0D0h 
dsk$sts equ 0D0h 
dsk$drv		equ	0D1h ;drv
dsk$trkL	equ	0D2h ;trkLow
dsk$trkH	equ	0D3h ;trkHigh
dsk$secL	equ	0D4h ;secLow
dsk$secH	equ	0D5h ;secHigh
dsk$dmaL	equ	0D6h ;dmaLow
dsk$dmaH	equ	0D7h ;dmaHigh
dsk$sptL	equ	0D8h ;sptLow
dsk$sptH	equ	0D9h ;sptHigh
dsk$dmaB	equ	0dah ;Disk DMA Bank
dsk$order	equ	0dbh ;track order

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

;	org	bios

;bios jump vector table

boot2		jmp	init	;cold start
		jmp	warm	;warm start
const2	jmp	cns$ck	;get console status
conin2	jmp	cns$in	;console input
conout2
outCH:	jmp	cns$ot	;console output
list2		jmp	list	;printer output
auxout2	jmp	punch	;punch output
auxin2	jmp	reader	;reader input
home2		jmp	home	;home selected drive
seldsk2	jmp	seldsk	;select drive
settrk2	jmp	settrk	;set track
setsec2	jmp	setsec	;set sector
setdma2	jmp	setdma	;set tranfer address
read2		jmp	diskrd	;read disk
write2	jmp	diskwr	;write disk
listst2	jmp	listst	;getr printer status
sectrn2	jmp	sectrn	;translate sector
		jmp	dsk$ok	;format a track

;bios variable storage

bt$cmd	db	0	;dcm command
bt$drv	db	0	;drive number
bt$trk	dw	0	;track number
bt$sec	dw	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	0fb00h
log$buf	dw	log$buff
log$dpb	equ	20h		;offset to dpb in buffer
log$ddf	equ	31h		;id sec - flags	

init:	
	mvi	a,0ffh
	out	dsk$dmab
	mvi	a,0
	out	0c0h	;set bank=0
warm:	
	ret

;disk executive

mag$trk	equ	0
mag$sec	equ	5

log$NDI
	set16	10h,log$buff
	set8	11h,0ffh
	set16	22h,mag$trk
	set16	24h,mag$sec
	set8	00h,cmd$log	;cmd$read
	db	0ffh

log$ex


	mvi	a,018h	;select Drive
	out	0cah	;disk reg select
	lda	bt$drv
	out	0c8h	;dsk$drv

	lxi	h,log$NDI
	call	feedNDI

	mvi	a,08h	;select status
	out	0cah	;disk reg select
	in	0c8h	;dsk$sts

	ana	a		;set ccr
	ret

;disk executive called by disk read and disk write

dsk$ex

;	New Disk Interface

	mvi	a,010h	;select DMA
	out	0cah	;disk reg select
	lda	bt$dma
	out	0c8h	;dsk$dmaL
	lda	bt$dma+1	
	out	0c9h	;dsk$dmaH

	mvi	a,018h	;select Drive
	out	0cah	;disk reg select
	lda	bt$drv
	out	0c8h	;dsk$drv

	mvi	a,022h	;select track
	out	0cah	;disk reg select
	lda	bt$trk
	out	0c8h	;dsk$trkL
	lda	bt$trk+1	
	out	0c9h	;dsk$trkH

	mvi	a,024h	;select sector
	out	0cah	;disk reg select
	lda	bt$sec
	out	0c8h	;dsk$secL

; the folowing code is added to allow those improperly written programs
; that call bios directly but assume 8 bit sector numbers to work (like-copysys.com)
	call	spt$val	;get SPT value
	mov	a,d	;see if 8bit
	ora	a
	jrz	out$hi	;if 8bit then hi byte =0
; end of fix for 8bit errors

	lda	bt$sec+1	
out$hi	out	0c9h	;dsk$secH

	mvi	a,000h	;select Command
	out	0cah	;disk reg select
	lda	bt$cmd
	out	0c8h	;dsk$cmd

	mvi	a,08h	;select status
	out	0cah	;disk reg select
	in	0c8h	;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
	jr	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
	jr	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,0FFh		;set a to indicate ERROR
	ret

seldsk	lxi	h,0		;hl = zero if error
	mov	a,c		;put drive number in a
	sta	bt$drv		;store drive number
	cpi	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	01h		;log on bit test
	jrnz	dsk$ok		;already logged on

;read id sector

	call	log$ex
	jrz	log$ck		;id sector got read
	lxi	h,0		;bad log on hl=0(no Drive table)
	jmp	dsk$er		;exit error
log$ck	
	call	log$in
ret$sel	
	lhld	dt$ptr		;reload ptr to dph table
	jr	dsk$ok

NDI$NDI	equ	020h
DPB$NDI	equ	08h

DPB$IDT equ	020h

magic	db	'NDI-Z80E'
mag$sz	equ	$-magic

id1440	db	'3.5 1440'
id$sz	equ	$-id1440

DPBsize	equ	15	;17 for CPM3

PC1440P:
	set8	080h,0		;order
	set8	081h,0		;formatType
	set8 	082h,0		;DONT use prepost

	set16	09ah,0		;IDM flag	0

	set16	0a0h,80		;Cylinders
	set8	0a1h,2		;Heads

	set8	0a3h,2		;density	(data) 	Double=2
	set16	0a4h,72		;SPT		(data) 
	set8	0a5h,0		;Sector Size	(data) 	128=0
	set8	0a6h,4		;clock		(data) 	3inch=4

	db	0ffh

log$in

;check for magic for DD (disk Definition)

	lxi	h,log$buff	;get address of log buffer
	lxi	d,magic		;de points to DD magic value
	mvi	b,mag$sz	;magic size
	call	strncmp		;check for Magic DD
	jrz	its$DD		; matched DD id magic

	lxi	h,log$buff	;get address of log buffer
	lxi	d,id1440	;de points to IDT magic value
	mvi	b,id$sz		;magic size
	call	strncmp		;check for Magic DD
	jrz	its$IDT		; matched IDT Magic

; if niether Magic matches, set to PC1440

its$def
	lxi	hl,PC1440P	;hl should be pointer to NDI disk definition data
	call	Def$Disk	;move NDI info to NDI 

	; copy default pc1440 DPB to real DPB

	call	dpb$ad			;de=DPB in bios
	lxi	hl,pc$pbk		;hl=internal DPB
	lxi	b,DPBsize		;size of dpb
	ldir
	jr	CLR$XLT


its$IDT
	lxi	hl,PC1440P	;hl should be pointer to NDI disk definition data
	call	Def$Disk	;move NDI info to NDI 

; copy DPB from log Buffer to real DPB

	call	dpb$ad			;de=DPB in bios
	lxi	hl,log$buff+DPB$IDT	;hl=internal DPB
	lxi	b,DPBsize		;size of dpb
	ldir
	jr	CLR$XLT

its$DD
	lxi	hl,log$buff+NDI$NDI	;hl should be pointer to NDI disk definition data
	call	Def$Disk		;move NDI info to NDI 

; copy DPB from log Buffer to real DPB

	call	dpb$ad			;de=DPB in bios
	lxi	hl,log$buff+DPB$NDI	;hl=internal DPB
	lxi	b,DPBsize		;size of dpb
	ldir

; alway clear XLT because PCxxxx Z80 disks don't need it.
CLR$XLT
	lhld	dt$ptr 	;ptr to dph
	xra	a	;clear a
	mov	m,a
	inx	h
	mov	m,a	;clear xlt
	ret


; load disk config from disk definition table
; this routine writes new disk config data from login buffer
;   to the Z80 Emulator's Disk Controller config registers

Def$Disk:

;select drive

	mvi	a,018h	;select
	out	0cah	;out to select port
	lda	bt$drv
	out	0c8h	; out to low reg

; hl should be pointer to NDI disk definition data
	call 	feedNDI	; config the disk

;set parameters
	mvi	a,000h	;select command Reg
	out	0cah	;out to select port
	mvi	a,07fh	;command is NULL but causes parameters to be set
	out	0c8h	; out to low reg
	ret

; enter with HL pointing to NDI data
feedNDI
another	mov	a,m	;got size 1=low 2=low/high and FF=finished
	cpi	0ffh
	jz	finished
	cpi	2
	jz	do2
do1	inx	hl
	mov	a,m	;got register selector in a
	out	0cah	;out to select port
	inx	hl
	mov	a,m	;got value to out to register
	out 	0c8h	; out to low reg
	inx	hl
	jmp 	another
do2	inx	hl
	mov	a,m	;got register selector in a
	out	0cah	;out to select port
	inx	hl
	mov	a,m	;got value to out to low register
	out 	0c8h	; out to low reg
	inx	hl
	mov	a,m	;got value to out to high register
	out 	0c9h	; out to high reg
	inx	hl
	jmp 	another
finished
	ret

; routine to compare to strings 

strncmp
scloop	ldax	d		;get magic character
	cmp	m		;match?
	rnz			;no - return doing nothing
	inx	h		;point to next chars
	inx	d		;
	dcr	b		;dec count of characters
	jrnz	scloop		;again
	ret

;get DPB address returned in DE

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

;get SPT returned in DE

spt$val	call	dpb$ad
	xchg
	mov	e,m
	inx	h
	mov	d,m
	ret

;home drive

home	lxi	bc,0		;set track to 0 fall into settrk

;set track

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

;set sector

setsec
	sbcd	bt$sec
;	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
	sbcd	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	7dh
cni$sb	equ	02h
cni$si	equ	00h
cni$dp	equ	7ch

cno$sp	equ	7dh
cno$sb	equ	01h
cno$si	equ	00h
cno$dp	equ	7ch

io$rdy	equ	0FFh	;ready
asc$msk	equ	7Fh	;ascii mask

lf	equ	0ah
cr	equ	0dh
cntl$z	equ	1Ah

;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,0ffh		;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
loop3	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	80h		;test sign bit
	jrz	loop3		;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			;


;3740 parameters

;sdtran:	db	01h,07h,0dh,13h,19h,05h
;		db	0bh,11h,17h,03h,09h,0fh
;		db	15h,02h,08h,0eh,14h,1ah
;		db	06h,0ch,12h,18h,04h,0ah
;		db	10h,16h

;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	0f0h		;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	0c0h		;al0
;	db	0		;al1
;	dw	16		;cks
;	dw	2		;off

;messages

msg$le:	db	cr,lf,'LOAD ERRO'
		db	52h+80h

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

;Un-initialized data area

;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	128 	;91

d0$all	ds	all$sz
d1$all	ds	all$sz
d2$all	ds	all$sz
d3$all	ds	all$sz

;changed disk area

chk$sz	equ	64

d0$chk	ds	chk$sz
d1$chk	ds	chk$sz
d2$chk	ds	chk$sz
d3$chk	ds	chk$sz

log$buff
dir$bf	ds	80h

