
;CPM 2.2 BIOS Dynamic Disk Config version 2.2.1

; changes to 2.2.1
;   modified "select sector" to mask upper 8 bits for drives with less
;     than 256 sectors---this fixes programs like copysys,
;         which send garbage in upper byte of sector number
;     (hopefully there's no 8bit track errors, track numbers,
;         like sector numbers are supposed to be 16bit)
;      (fixed here so not have to fix many original sources)

;This bios does not detect logins of different types of disks
; HOWEVER, this bios will allow you to write new values into 
; DPBs and XLTs to define varieous types of disks
;
;It supports 16 bit values for track and sector
;  but only supports 8 bit sector translations
;
;each drive A:-P: has its own DPH, DPB, Allocation tables, 
;directory check vectors and translate tables (kept in 16 upper banks)
;
;All drives A:-P:(at cold boot, set for PC1440 disks)
;   Allocation = 512 bytes (enough for 8MB drive)
;   Check=512 bytes (enough for 2048 directory entries)
;   XLT=256 bytes (not used for pc 1440 but there if needed)
;
;   	XLTs at fA00 for 62 k system
; 
; use the DC.com,DCI.com and DCJ.com Version 2 program to change drive A: and P: parameters
;(use caution when changing drive A:)
;
;1K at f600-f9ff (62k system) is unused(reserved for XBIOS) 
; 

true	equ	-1
false	equ	not true

;AS8080		equ	false
AS8080		equ	true

	if 	AS8080
		z80
	else
		maclib	z80
	endif

IBM$3740	equ	false

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	0fA00h		;6 256 blocks 

df$drv	equ	0		;default drive
sec$sz	equ	80h		;sector size

n$drvs	equ	16		;number of drives(1-16)

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

	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	dsk$er	; claim JaddeDD format failed
; magic is required in next three bytes... 
	db	076h,'2','2'	;flag for Disk Config Program

msg$so	db	cr,lf
sys$td	db	td$asc		;tens digit
sys$od	db	od$asc		;ones digit
		db	'K CPM 2.2-D V2.2.1'
		db	'-Banked'
	db	cr,lf+80h	;sign bit set on last byte

;bios variable storage

bt$cmd	db	0	;ndi 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

;init stuff is overlayed by directory buffer

dir$bf	equ	$


init	lxi	sp,0080h	;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

	call	init$disks

	mvi	a,1		;bank	1
	out	0c0h		;emu bank select

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

	xra	a		;bank	0
	out	0c0h		;emu bank select

	mvi	e,0	;1	; log on request
	mvi	c,0		;drive 0
	call	seldsk		;select drive
	lxi	b,8000h	;cpm ccp address in bank 0
	call	setdma		;set dma
	lxi	b,29		;first ccp sector(128)
	call	setsec		;set sector
	lxi	b,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
	cpi	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,2ah		;
	call	outCH		;splash progress bar
	jr	w$read		;again

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

;load error message

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

eoinit	equ	$	;end of init stuff

;fill out directory buffer dir$buf
	if eoinit lt (dir$bf+80h)
	org	dir$bf+80h
	endif

;read disk definition sector into Log buffer

mag$trk	equ	0
mag$sec	equ	5

log$ex

	mvi	a,010h	;select DMA
	out	0cah	;disk reg select
	mvi	a,low(log$buff)
	out	0c8h	;dsk$dmaL
	mvi	a,high(log$buff)	
	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
	mvi	a,low(mag$trk)
	out	0c8h	;dsk$trkL
	mvi	a,high(mag$trk)
	out	0c9h	;dsk$trkH

	mvi	a,024h	;select sector
	out	0cah	;disk reg select
	mvi	a,low(mag$sec)
	out	0c8h	;dsk$secL
	mvi	a,high(mag$sec)	
	out	0c9h	;dsk$secH

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

	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
	jrz	dsk$ok		;normal exit
	jr	dsk$er		;exit error

;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	
	mov	a,c		;put drive number in a
	sta	bt$drv		;store drive number

	out	0xee		;select upper bank

	lxi	h,d0$dph	;dph
	shld	dt$ptr		;store drive table pointer

	mov	a,e		;chk if need log on
	sta	log$rq		;save logon req in log$rq

	ani	01h		;log on bit test
	jrnz	dsk$ok		;already logged on

	call	log$in
	
ret$sel	
	lhld	dt$ptr		;reload ptr to dph table
	jr	dsk$ok

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

log$in
	call	log$ex		;read disk definition from drive into log buffer
	rnz			;do nothing if not readable

;check for magic

	lxi	h,log$buff	;get address of log buffer
	lxi	d,magic		;de points to magic value
	mvi	b,mag$sz	;magic size
..loop	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	..loop		;again

; 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

NDI$dat	equ	0x20
DPB$dat	equ	0x08

;select drive

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

; config the disk

;hl should be pointer to NDI disk definition data
	lxi	hl,log$buff+NDI$dat

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

;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

; copy DPB from log Buffer to real DPB

DPBsize	equ	15	;17 for CPM3

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

; alway clear XLT because Z80 disks don't need it.
	lhld	dt$ptr 	;ptr to dph
	xra	a	;clear a
	mov	m,a
	inx	h
	mov	m,a	;clear xlt
	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	mvi	c,0		;set track to 0 fall into settrk
	mov	b,c

;set track

settrk	
	sbcd	bt$trk		;store track number 16 bits
	ret

;set sector

setsec	
	sbcd	bt$sec		;store sector number 16 bits
	ret

;set dma

setdma	
	sbcd	bt$dma	 	;just save in bt$dma
	ret

;sector translation
; this routine assumes 8 bit sector numbers
; assume that disks with more than 256 SPT are HDs without skew
;  plus--xlt only has room for 256 byte entries

sectrn	mov	a,d		;testing tbl addr
	ora	e		;chk if 0
	jrz	notran		;no xlation
	xchg			;hl=xlat tbl
	mvi	b,0		;assume 8 bit sector numbers
	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			;

;warm boot loads ccp + bdos sets up page zero

warm	lxi	sp,0080h	;set up stack in def buffer
	
	mvi	a,0		;switch to bank 0 where CCP and BDOS is at 0x8000
	out	0c0h		;expandoram ii bank select

; init page zero in bank 0
	mvi	a,0c3h		;jmp xxxx = (c3 xx xx)
	sta	0		;
	lxi	h,bios+03	;jmp bios+3(warm)
	shld	1		;init warm vector in bank 0 so reset works

; move copy of CCP and BDOS to common memory (>0xC000)
	lxi	hl,8000h	;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
	mvi	a,1		;switch back to bank 1
	out	0c0h		;expandoram ii bank select

;init page zero in bank 1

w$zrpg	mvi	a,0c3h		;jmp xxxx = (c3 xx xx)
	sta	0		;
	lxi	h,bios+03	;jmp bios+3(warm)
	shld	1
	sta	5		;jmp instruction
	lxi	h,bdos+06	;jmp bdos (ccp+6)
	shld	6
	sta	38h		;rst 7
	lxi	h,0e003h	;jmp sdmon(e003)
	shld	39h

;last logged drive

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

;jump to ccp
	
	jmp	ccp		;go to cpm

; eobc 
eobc	equ	$	

;log buffer overlays initialization of disk parameters...

log$buff

;default drive parameter area
; these can be overlayed as they are used only during cold boot

df$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

;default drive parametr block for 3$5" pc1440

df$dpb:	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
	db	0		; PSH
	db	0		; PHM

;more init stuff can be overlayed if needed as only use during init cold boot

init$disks
	mvi	a,0xfa
	out	0xef		;set Upper memory bank start to 0xFA00
	
	mvi	a,0
	out	0xe2		;src bnk

	mvi	a,low(df$dph)	
	out	0xe4		;src addr
	mvi	a,high(df$dph)
	out	0xe5
	
	mvi	a,low(d0$dph)
	out	0xe6		;Dst addr
	mvi	a,high(d0$dph)
	out	0xe7

	mvi	a,low(16+17)
	out	0xe8		;count
	mvi	a,high(16+17)
	out	0xe9

	mvi	b,16
..loop	
	dcr	b
	rm	
	mov	a,b
	out	0xe3		;dst bnk
	mvi	a,1
	out	0xe0		;command
	jmp	..loop
	
eoinit2 equ	$	;end of init stuff

;fill out log buffer log$buff
	if eoinit2 lt (log$buff+80h)
	org	log$buff+80h
	endif


; eob must be less than bios + 0x400
eob:	equ	$
bios$p400	equ bios+0x400

;Un-initialized and runtime initialized data area

;everything from here on is in upper memory bank 
;   that gets swapped for each drive.

	org	ui$data

all$sz 	set	512
chk$sz	set 	512
xlt$sz	set	256

; Sector Translate Table
	
d0$xlt	equ	$

; drive allocation area


d0$all	equ	d0$xlt+xlt$sz


;  changed disk area

d0$chk	equ	d0$all+all$sz

;  Dph

d0$dph	equ	d0$chk+chk$sz

; DPB

d0$dpb	equ 	d0$dph+16


eoui	equ	d0$dpb+17
