;	Skeletal CBIOS for first level of CP/M 2.0 alteration
;
msize	equ	20	;cp/m version memory size in kilobytes
;
;	"bias" is address offset from 3400H for memory systems
;	than 16K (referred to as "b" throughout the text).
;
bias	equ	(msize-20)*1024
ccp	equ	3400H+bias	;base of ccp
bdos	equ	ccp+806h	;base of bdos
bios	equ	ccp+1600h	;base of bios
cdisk	equ	0004H	;current disk number 0=A,...,15=P
iobyte	equ	0003h	;intel i/o byte
;
	org	bios	;origin of this program
nsects	equ	($-ccp)/128	;warm start sector count
;
;	jump vector for individual subroutines
	jmp	boot		;cold start
wboote:	jmp	wboot		;warm start
	jmp	const		;console status
	jmp	conin		;console character in
	jmp	conout		;console character out
	jmp	list		;list character out
	jmp	punch		;punch character out
	jmp	reader		;reader character out
	jmp	home		;move head to home position
	jmp	seldsk		;select disk
	jmp	settrk		;set track number
	jmp	setsec		;set sector number
	jmp	setdma		;set dma address
	jmp	read		;read disk
	jmp	write		;write disk
	jmp	listst		;return list status
	jmp	sectran		;sector translate
;
;	fixed data tables for four-drive standard
;	IBM-compatible 8" disks
;	disk parameter header for disk 00
dpbase:	dw	trans,0000H
	dw	0000H,0000H
	dw	dirbf,dpblk
	dw	chk00,all00
;	disk parameter header for disk 01
	dw	trans,0000H
	dw	0000H,0000H
	dw	dirbf,dpblk
	dw	chk01,all01
;	disk parameter header for disk 02
	dw	trans,0000H
	dw	0000H,0000H
	dw	dirbf,dpblk
	dw	chk02,all02
;	disk parameter header for disk 03
	dw	trans,0000H
	dw	0000H,0000H
	dw	dirbf,dpblk
	dw	chk03,all03
;
;	sector translate vector
trans:	db	1,7,13,19	;sectors 1,2,3,4
	db	25,5,11,17	;sectors 5,6,7,8
	db	23,3,9,15	;sectors 9,10,11,12
	db	21,2,8,14	;sectors 13,14,15,16
	db	20,26,6,12	;sectors 17,18,19,20
	db	18,24,4,10	;sectors 21,22,23,24
	db	16,22		;sectors 25,26
;
dpblk:	;disk parameter block, common to all disks
	dw	26		;sectors per track
	db	3		;block shift factor
	db	7		;block mask
	db	0		;null mask
	dw	242		;disk size-1
	dw	63		;directory max
	db	192		;alloc 0
	db	0		;alloc 1
	dw	16		;check size
	dw	2		;track offset
;
;	end of fixed tables
;
;	individual subroutines to perform each function
boot:	;simplest case is to just perform parameter initialization
	xra	a		;zero in the accum
	sta	iobyte		;clear the iobyte
	sta	cdisk		;select disk zero
	jmp	gocpm		;initialize and go to cp/m
;
wboot:	;simplest case is to read the disk until all sectors loaded
	lxi	sp,80h		;use space below buffer for stack
	mvi	c,0		;select disk 0
	call	seldsk
	call	home		;go to track 00
;
	mvi	b,nsects	;b counts # of sectors to load
	mvi	c,0		;c has the current track number
	mvi	d,2		;d has the next sector to read
;	note that we begin by reading track 0, sector 2 since sector 1
;	contains the cold start loader, which is skipped in a warm start
	lxi	h,ccp		;base of cp/m (initial load point)
load1:	;load one more sector
	push	b	;save sector count, current track
	push	d	;save next sector to read
	push	h	;save dma address
	mov	c,d	;get sector address to register c
	call	setsec	;set sector address from register c
	pop	b	;recall dma address to b,c
	push	b	;replace on stack for later recall
	call	setdma	;set dma address from b,c
;
;	drive set to 0, track set, sector set, dma address set
	call	read
	cpi	00h	;any errors?
	jnz	wboot	;retry the entire boot if an error occurs
;
;	no error, move to next sector
	pop	h	;recall dma address
	lxi	d,128	;dma=dma+128
	dad	d	;new dma address is in h,l
	pop	d	;recall sector address
	pop	b	;recall number of sectors remaining, and current trk
	dcr	b	;sectors=sectors-1
	jz	gocpm	;transfer to cp/m if all have been loaded
;
;	more sectors remain to load, check for track change
	inr	d
	mov	a,d	;sector=27?, if so, change tracks
	cpi	27
	jc	load1	;carry generated if sector<27
;
;	end of current track, go to next track
	mvi	d,1	;begin with first sector of next track
	inr	c	;track=track+1
;
;	save register state, and change tracks
	push	b
	push	d
	push	h
	call	settrk	;track address set from register c
	pop	h
	pop	d
	pop	b
	jmp	load1	;for another sector
;
;	end of load operation, set parameters and go to cp/m
gocpm:
	mvi	a,0c3h	;c3 is a jmp instruction
	sta	0	;for jmp to wboot
	lxi	h,wboote	;wboot entry point
	shld	1	;set address field for jmp at 0
;
	sta	5	;for jmp to bdos
	lxi	h,bdos	;bdos entry point
	shld	6	;address field of jump at 5 to bdos
;
	lxi	b,80h	;default dma address is 80h
	call	setdma
;
	ei		;enable the interrupt system
	lda	cdisk	;get current disk number
	mov	c,a	;send to the ccp
	jmp	ccp	;go to cp/m for further processing
;
;
;	simple i/o handlers (must be filled in by user)
;	in each case, the entry point is provided, with space reserved
;	to insert your own code
;
const:	;console status, return 0ffh if character ready, 00h if not
	ds	10h	;space for status subroutine
	mvi	a,00h
	ret
;
conin:	;console character into register a
	ds	10h	;space for input routine
	ani	7fh	;strip parity bit
	ret
;
conout: ;console character output from register c
	mov	a,c	;get to accumulator
	ds	10h	;space for output routine
	ret
;
list:	;list character from register c
	mov	a,c	;character to register a
	ret		;null subroutine
;
listst:	;return list status (0 if not ready, 1 if ready)
	xra	a	;0 is always ok to return
	ret
;
punch:	;punch character from register c
	mov	a,c	;character to register a
	ret		;null subroutine
;
;
reader: ;read character into register a from reader device
	mvi	a,1ah	;enter end of file for now (replace later)
	ani	7fh	;remember to strip parity bit
	ret
;
;
;	i/o drivers for the disk follow
;	for now, we will simply store the parameters away for use
;	in the read and write subroutines
;
home:	;move to the track 00 position of current drive
;	translate this call into a settrk call with parameter 00
	mvi	c,0	;select track 0
	call	settrk
	ret		;we will move to 00 on first read/write
;
seldsk:	;select disk given by register C
	lxi	h,0000h	;error return code
	mov	a,c
	sta	diskno
	cpi	4	;must be between 0 and 3
	rnc		;no carry if 4,5,...
;	disk number is in the proper range
	ds	10	;space for disk select
;	compute proper disk parameter header address
	lda	diskno
	mov	l,a	;L=disk number 0,1,2,3
	mvi	h,0	;high order zero
	dad	h	;*2
	dad	h	;*4
	dad	h	;*8
	dad	h	;*16 (size of each header)
	lxi	d,dpbase
	dad	d	;HL=.dpbase(diskno*16)
	ret
;
settrk:	;set track given by register c
	mov	a,c
	sta	track
	ds	10h	;space for track select
	ret
;
setsec:	;set sector given by register c
	mov	a,c
	sta	sector
	ds	10h	;space for sector select
	ret
;
sectran:
	;translate the sector given by BC using the
	;translate table given by DE
	xchg		;HL=.trans
	dad	b	;HL=.trans(sector)
	mov	l,m	;L = trans(sector)
	mvi	h,0	;HL= trans(sector)
	ret		;with value in HL
;
setdma:	;set dma address given by registers b and c
	mov	l,c	;low order address
	mov	h,b	;high order address
	shld	dmaad	;save the address
	ds	10h	;space for setting the dma address
	ret
;
read:	;perform read operation (usually this is similar to write
;	so we will allow space to set up read command, then use
;	common code in write)
	ds	10h	;set up read command
	jmp	waitio	;to perform the actual i/o
;
write:	;perform a write operation
	ds	10h	;set up write command
;
waitio:	;enter here from read and write to perform the actual i/o 
;	operation.  return a 00h in register a if the operation completes
;	properly, and 01h if an error occurs during the read or write
;
;	in this case, we have saved the disk number in 'diskno' (0,1)
;			the track number in 'track' (0-76)
;			the sector number in 'sector' (1-26)
;			the dma address in 'dmaad' (0-65535)
	ds	256	;space reserved for I/O drivers
	mvi	a,1	;error condition
	ret		;replaced when filled-in
;
;	the remainder of the CBIOS is reserved uninitialized
;	data area, and does not need to be a part of the
;	system memory image (the space must be available,
;	however, between "begdat" and "enddat").
;
track:	ds	2	;two bytes for expansion
sector:	ds	2	;two bytes for expansion
dmaad:	ds	2	;direct memory address
diskno:	ds	1	;disk number 0-15
;
;	scratch ram area for BDOS use
begdat	equ	$	;beginning of data area
dirbf:	ds	128	;scratch directory area
all00:	ds	31	;allocation vector 0
all01:	ds	31	;allocation vector 1
all02:	ds	31	;allocation vector 2
all03:	ds	31	;allocation vector 3
chk00:	ds	16	;check vector 0
chk01:	ds	16	;check vector 1
chk02:	ds	16	;check vector 2
chk03:	ds	16	;check vector 3
;
enddat	equ	$	;end of data area
datsiz	equ	$-begdat;size of data area
	end
