	title	'C128 Format Program  08 June 1987'

; modified  08June87  by V.Ertwine & F.Bowen:	add 1581 support
; modified  07May86   by V.Ertwine:		add more MFM formats
; original  15May85   by V.Ertwine:		original release

    	maclib	z80
	maclib	cxequ
	maclib	x6502

format$1581	equ	true

$*MACRO

boot$code$02	equ	00B00h
boot$code$80	equ	0E000h

;
;	RAM loader equates
;
rownum	macro	row,col
	db	row+80h,col
    endm

sys$name	equ	000EDh		;
cmp$hl$de	equ	000FAh		;
read$sector	equ	0044Fh		;
ext$error	equ	00480h		;
tell$user	equ	004FFh		;
table$error	equ	0051Bh		;
prt$msg$both	equ 	00526h		;
prt$HL$both	equ	00534h		;
install$ASCII	equ	005BDh		;
wait		equ	00945h		;

;BIOS$65$code	equ	00D22h		;
;BIOS$size	equ	001C3h		;
;F$rd$count	equ	031BDh		;

swap$code	equ	00EE5h		;
swap$size	equ	0001Fh		;

MMU$init$data	equ	00FAAh		;

	page
;"""""""""""""
;E Q U A T E S
;"""""""""""""

cret:		equ	01	; matrix position of return key
disk$ready$key:	equ	11	; matrix position of $
ctrlc:		equ	20	; matrix position of c key
yes$key:	equ	25	; yes - matrix position of 'y' key
no$key:		equ	39	; 'no' - matrix position of n key
cursor$up$key:	equ	53h	; matrix position of up arrow
cursor$dn$key:	equ	54h	; matrix position of down arrow

top$line:	equ	0	; screen top row for signon message
use$line:	equ	2	; screen row for "using source" message
keynam$line:	equ	4	; screen row for showing keyname
lots$line:	equ	6	; screen row for showing big messages
prompt$line:	equ	10	; screen row for showing menus
spec$line:	equ	23	; status line row for special messages

crlf:		equ	0ffh	; code to do cr,lf	
do$6502$fun:	equ	4
scrsz40:	equ	20h	; flag value that this is a 40 column screen
scrsz80:	equ	40h	; flag value that this is an 80 column screen 
both:		equ	scrsz40+scrsz80		; both of the above
user$offset:	equ	3*(30-1)

	page
;"""""""""""""""""""
;CP/M function calls
;"""""""""""""""""""
bdos:		equ	05	;bdos call entry point
conin:		equ	01	;char returned in a
conout:		equ	02	;char sent in e
constat:	equ	11	;console status	
cur$dsk:	equ	25	;get current disk to A
getscb:		equ	49	;get system control block

;"""""""""""""""""""""""""
; 	1581 Drive equates
;"""""""""""""""""""""""""
;	constants
buf$alc		equ	081h	; allocate buffer 0 (no buf 7)

;	memory location
jobs		equ	002h	; list of jobs to be done
hdrs		equ	00Bh	; header info (track and sector)
buf$use		equ	06Dh	; bit X set allocates buffer X
next$job	equ	083h	; pointer to next job to be done
hdrjob		equ	099h
vtrans$ts	equ	1B8h	; sector x-lat vector
drv$buf		equ	300h	; buffer 0 start adr
;boot$ret	equ	0d35Ah	; will change with next ROM
boot$ret	equ	0FF5Ah	; return from drive boot code

	page
;
pointer	equ	20h		; DMA pointer used by 8502
cmdchn	equ	15		; use comand channel #15
;
;	KERNAL EQUATES
;
serial		equ	00A1Ch
D2PRA		equ	0DD00h
D1SDR		equ	0DC0Ch
D1ICR		equ	0DC0Dh
clk$bit		equ	10h

K$set$bnk	equ	0FF68h	;
K$setlfs	equ	0FFBAh	; setup a logical file
K$setnam	equ	0FFBDh	; set up file name for OPEN
K$open		equ	0FFC0h	; open a logical file (after SETLFS & SETNAM)
K$chkout	equ	0FFC9h	; open a channel for output
K$clrchn	equ	0FFCCh	; clears ALL I/O channel 
K$chkin		equ	0FFC6h	; open a channel for input
K$chrin		equ	0FFCFh	; get a character from input channel
K$chrout	equ	0FFD2h	; output a character to output channel
K$clall		equ	0FFE7h	; close all open files
K$close		equ	0FFC3h	; close a logical file
K$readst	equ	0FFB7h	; read status byte

	page
;""""""""""""""""""""""""""""""""""
;m a c r o    d e f i n i t i o n s
;""""""""""""""""""""""""""""""""""
define	macro	label,for
	org	ramalc
label	equ	$
ramalc	set	ramalc+for
	endm

save$top$bottom	macro	toprow,number
	mvi	a,toprow+number-1
	sta	bottom$choice
	mvi	a,toprow
	sta	top$choice
	endm

skip$and$destroy$hl	macro
	db	21h
	endm

zero?	macro	r1,r2
	mov	a,r1
	ora	r2
	endm

fmt	macro	fmtdef1,fmtdef2
	DW	fmtdef1
	IF	NOT NUL fmtdef2
	DW	fmtdef2
	ELSE
	DW	0
	ENDIF
	ENDM

wrdef	macro	?trk,?sect,?pointer
	db	?trk,?sect
	dw	?pointer
	endm

defstart	macro	fillbyte
	IF	NOT NUL fillbyte
	db	fillbyte
	ELSE
	db	0
	ENDIF
	ENDM

defbuf	macro	?count,?offset,?pointer
	dw	?count
	dw	?offset
	dw	?pointer
	endm

definline	macro	?count,?offset
	db	?count,-1
	dw	?offset
	endm

defexec	macro	?adr
	db	0,-1
	dw	?adr
	endm

defend	macro
	dw	-1
	endm

	page
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;S T A R T        H E R E
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

	org	100h
;""""""""""""""
;initialization
;""""""""""""""
	lxi	sp,stack	;set up stack	
	lhld	0001		;read jump vector for i/o
	lxi	d,user$offset	;..add this
	dad	d
	shld	go$user$fun+1	;install vector to user BIOS functions
	
	lxi	d,scbpbrd	;point to my parameter block
	mvi	c,getscb	;..CP/M function to determine screen size
	call	bdos
	mov	a,h
	ani	both		;..save whether 40,80 or both
	sta	scrsiz
				;NOW  we are ready to start

;"""""""""
;SAY$HELLO:
;"""""""""
	call	clear$screen		;set up to
	call	posit$top		;..print signon message	
	call	print$msg
	db	'C128 CP/M FORMAT PROGRAM' 
	db	crlf
	db	'     08 JUNE 1987'
	db	eom

	page
	mvi	c,cur$dsk		; RETURN CURRENT DISK
	call	bdos			; ..in A
	cpi	4
	jrc	select$new
	xra	a			; if not A,B,C or D set A

	page

;----------
select$new:
;----------
	mov	b,a		; save in B for later
	mov	c,a		; A=B=C= selection # (0-3)
	inr	c		; (1-4)
	mvi	a,80h		; rlc will give 01
select$next:
	rlc			; drive byte  0000dcba binary
	dcr	c
	jrnz	select$next

	sta	fmt$drv
	sta	vic$drv
	mov	a,b		; get current drive select (0-3)
	sta	sel$drv
	adi	'A'
	sta	dsp$drv$1
	sta	dsp$drv$2
	sta	dsp$drv$3
;
;*****	login requested drive
;
	call	get$disk$info

	ani	0Eh
	cpi	0Eh
	jz	select$drv
	page
;--------------
;show$disk$type:
;--------------
	lda	fast			;first tell user what type disk
	lxi	h,fmt$drv		;point to format drive
	ana	m			;..drive a= lst bit
	lxi	h,3134h			; 0=1541
	jrz	show$slow$fast		;

;
;*****	test for 1581 or 1571
;
;
  if	format$1581
	mvi	l,'8'			;assume 1581
	lda	vic$data
	cpi	0A3h			;horrible hack for *blank* disks!
	jrz	show$slow$fast		; it is an empty 3.5" drive
	cpi	0A9h
	jrz	show$slow$fast		; it is a virgin 3.5" diskette
	ani	0B0h			; 
	cpi	020h			;Resident fmt with 512 byte sectors?
	jrz	show$slow$fast		; yes, gotta be a 1581
  endif

	mvi	l,'7'			;none of the above, set as a 1571

show$slow$fast:
	shld	a$15xx			;..store the type in message
	call	clear$lots		;
	call	posit$using
	call	print$msg
	db	'Drive '
dsp$drv$1:
	db	'A is a 15'
a$15xx:	db	'x1',eom
	mvi	a,use$line
	call	highlight$line


re$start:
	lxi	sp,stack	; set up stack	
	lda	a$15xx
	cpi	'4'
	jz	get$1541$choices
  if	format$1581
	cpi	'7'
	jz	get$1571$choices
;	cpi	'8'
;	jz	get$1581$choices
;
	page
;---------------
;get$1581$choices:
;---------------
	call	show$prompt		;
	db	'Please select disk type to format'
	db	crlf,'Commodore 1581 (Double Sided)'
	db	crlf,'Write system track only'
	db	crlf,'Select Different Drive'
	db	eom

	save$top$bottom	promptline+1,3
	call	menu$response
	dw	c128$ds$81
	dw	c128$sys$81
	dw	select$drv

  endif
	page
;---------------
get$1571choices:
;---------------
	call	show$prompt
	db	'Please select disk type to format'
	db	crlf,'C128 Double sided'
	db	crlf,'C128 Single sided'
	db	crlf,'C64 Single sided'
	db	crlf,'IBM Single Sided'
	db	crlf,'IBM Double Sided'
	db	crlf,'KAYPRO II'
	db	crlf,'KAYPRO IV'
	db	crlf,'EPSON QX10'
	db	crlf,'OSBORNE DD'
	db	crlf,'Select Different Drive'
	db	eom

	save$top$bottom	promptline+1,10
	call	menu$response
	dw	c128$ds
	dw	c128$ss
	dw	c64$ss			;
	dw	c1571$ibm$ss		;
	dw	c1571$ibm$ds		;
	dw	c1571$kp$2		;
	dw	c1571$kp$4		;
	dw	c1571$epson		;
	dw	c1571$osborne		;
	dw	select$drv		;

	page
;----------------
get$1541$choices:
;----------------
	call	show$prompt		;
	db	'Please select disk type to format'
	db	crlf,'C128 single sided'
	db	crlf,'C64 single sided'
	db	crlf,'Select Different Drive'
	db	eom

	save$top$bottom promptline+1,3
	call	menu$response
	dw	c128$ss
	dw	c64$ss
	dw	select$drv

;----------
select$drv:
;----------
	call	show$prompt		;
	db	'Please select drive you wish to format with'
	db	crlf,'Drive A'
	db	crlf,'Drive B'
	db	crlf,'Drive C'
	db	crlf,'Drive D'
	db	eom

	save$top$bottom	promptline+1,4
	call	menu$response
	dw	0			; continue execute inline
	jmp	select$new

	page
;
;
  if	format$1581
c128$ds$81:
	lxi	h,c1581$dsk$def
	jr	format$str
c128$sys$81:
	lxi	h,c1581$sys$def
	jr	format$str
  endif
;
;
c128$ds:
	lxi	h,c128$ds$dsk$def
	jr	format$str
;
;
c128$ss:
	lxi	h,c128$ss$dsk$def
	jr	format$str
;
;
c64$ss:
	lxi	h,c64$ss$dsk$def
	jr	format$str
;
;
c1571$epson:
	lxi	h,epson$dsk$def
	jr	format$str
;
;
c1571$osborne:
	lxi	h,osborne$dsk$def
	jr	format$str
;
;
c1571$kp$2:
	lxi	h,kp$2$dsk$def
	jr	format$str
;
;
c1571$kp$4:
	lxi	h,kp$4$dsk$def
	jr	format$str
;
;
c1571$ibm$ss:
	lxi	h,ibm$dsk$def$ss
	jr	format$str
;
;
C1571$ibm$ds:
	lxi	h,ibm$dsk$def$ds
;	jr	format$str

	page
;
;
;
format$str:
;	call	print$msg
;	db	'Formatting',crlf,eom
	call	print$what
	shld	fmt$tbl$ptr
	call	clear$prompt
	call	print$msg
	db	'Insert diskette TO BE FORMATTED'
	db	crlf,'in drive '
dsp$drv$2:
	db	'A.  Type $ when ready,'
	db	crlf,'any other key to abort ',eom
	call	scan$for$key
	mov	a,b
	cpi	disk$ready$key
	jnz	user$panics
	call	clear$prompt
	call	print$msg
	db	'..formatting diskette in drive '
dsp$drv$3:
	db	'A',crlf,eom

	lhld	fmt$tbl$ptr
; do the format
	call	get$DE$from$HL	; get format table pointer (side 1)
	cnz	format$DE$tbl
	call	get$DE$from$HL	; get format table pointer (side 2)
	cnz	format$DE$tbl
	call	set$dsk$type

	page
;
;
;
format$1:
	call	get$DE$from$HL	; get track & sect
	mov	a,d		; DE=-1 ?
	ana	e
	inr	a
	jz	format$another?
	mov	a,e
	sta	trk
	mov	a,d
	sta	sect

	call	print$msg
	db	'..writing sector',cr,eom

	call	get$DE$from$HL	; get DEF pointer
	xchg			; HL=DEF pointer,  DE=MAIN pointer
	push	d		; save MAIN pointer
;
;
;
	mov	a,m		; get fill byte
	inx	h
	call	init$buf$with$A	; (HL saved)
next$fill:
	call	get$DE$from$HL	; get word count to DE
	jm	count$minus
	mov	b,d		; save count in BC
	mov	c,e
	call	get$DE$from$HL	; get offset in buffer
	push	d
	call	get$DE$from$HL	; get pointer to data
	xthl			; save DEF pointer get offset
	push	d		; save DATA pointer
	lxi	d,sect$buf
	dad	d		; HL= buffer+offset
	pop	d		; DE= DATA pointer
	xchg			; HL= DATA ptr, DE= buffer+offset
	ldir			; move data to buffer
	pop	h		; recover DEF pointer
	jr	next$fill

	page
;
;
;
count$minus:
	inr	e		; test for -1
	jrnz	cnt$not$minus$1

	call	write$data
	pop	h		; recover MAIN pointer
	jmp	format$1

cnt$not$minus$1:
	dcr	e		; test for 0
	jrnz	data$inline

	call	get$DE$from$HL
	xchg			; HL=code adr,  DE=DEF pointer
	push	d		; save DEF pointer
	call	ipchl
	pop	h		; recover DEF pointer
	jmp	next$fill

data$inline:
	mvi	b,0
	mov	c,e		; BC=number of bytes to move
	call	get$DE$from$HL	; get offset
	push	h		; save DEF pointer
	lxi	h,sect$buf
	dad	d		; HL=buffer+offset
	xchg			; DE=buffer+offset
	pop	h		; recover DEF pointer
	ldir
	jmp	next$fill

	page
;
;
;
format$DE$tbl:
	push	h		; save MAIN pointer
	xchg			; HL=format table pointer
	mov	c,m		; get count
	mvi	b,0		; count will be 1 to 255
	inr	c		; move count also
	lxi	d,@buffer	; point to command buffer area
	ldir			; move format data to buffer
	lda	fmt$drv		;..bit 0==drive a, MSB=unit 0
	sta	vic$drv
	mvi	a,do$6502$fun
	mvi	l,vic$frmt
	call	go$user$fun	;call,ret
	ora	a
	jrnz	format$error
	pop	h
	ret

	page
;------------
format$error:
;------------
	call	print$msg
	db	'    format error',cr,lf,eom
user$panics:
	pop	h

;---------------
format$another?:
;---------------
	call	print$msg
	db	crlf,lf,lf
	db	'Do you want to format another disk?'
	db	eom
	call	get$yes$or$no
	jc	re$start

;-----------
exit$format:
;-----------
	call	clear$screen
	call	posit$prompt
	call	print$msg
	db	'.....exiting format program',cr,lf,lf,eom
exit$clean:
	mvi	c,13
	call	bdos			; reset disk system
	mvi	c,constat
	call	bdos
	ana	a
	jz	0
	mvi	c,conin
	call	bdos
	jmp	0

;---------
quit$prog:
;---------
	push	psw
	push	b
	push	d
	push	h
	call	posit$spec		;for special messages 
	call	print$msg
	db	'Are you sure? [ ]',bs,bs,eom
	call	get$yes$or$no
	jrc	exit$clean
	call	posit$spec
	call	clear$eol
	pop	h
	pop	d
	pop	b
	pop	psw
	ret

	page
;--------------
get$DE$from$HL:
;--------------
	mov	e,m
	inx	h
	mov	d,m
	inx	h
	mov	a,d
	ora	e
	rz
	mov	a,d
	ana	a
	ret

ipchl:
	pchl

;---------------
init$buf$with$A:
;---------------
	push	h
	lxi	h,sect$buf
	shld	dma
	mov	m,a
	lxi	d,sect$buf+1
	lxi	b,1024
	ldir
	pop	h
	ret
;
;	set disk type to TYPE 0  (GCR direct sector I/O)
;------------
set$dsk$type:
;------------
	push	h
	mvi	c,13
	call	bdos			; reset disk system
	lxi	d,sel$pb
	call	BIOS$ind
	dcx	h
	call	get$disk$info
	bit	a,7			; MFM disk?
	jrnz	size$ok			; yes, then size is ok
	ani	30h			; no,  1571 GCR reports
	jrnz	size$ok			; ...128 byte sectors
	mvi	a,10h			; set 256 byte sectors
size$ok:
	ani	00110000b 		; set disk type to DSK$none
	mov	m,a			; this will allow direct sector access
	pop	h
	ret

;-------------
get$disk$info:
;-------------
	push	h
	mvi	a,do$6502$fun
	mvi	l,vic$test
	call	go$user$fun	;call,ret
	mov	b,a		;save status from drive in B also
	pop	h
	ret

	page
;----------
write$data:
;----------
	lxi	d,dma$bnk$pb
	call	BIOS$ind
	lxi	d,dma$pb
	call	BIOS$ind
	lxi	d,trk$pb
	call	BIOS$ind
	lxi	d,sect$pb
	call	BIOS$ind
	lxi	d,wr$pb
	call	BIOS$ind
	ana	a
	rz
	pop	h
	call	posit$spec
	call	print$msg
	db	'Disk write error',crlf,eom
	jmp	exit$format

	page
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;Í É Ó Ã Å Ì Ì Á Î Å Ï Õ Ó   Õ Ó Å Æ Õ Ì   Õ Ô É Ì É Ô É Å Ó							
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
beep:
	mvi	e,7
	jr	do$con$out

do$cr$lf:
	mvi	e,cr
	call	do$con$out
	mvi	e,lf
do$con$out:
	mvi	c,conout
	jmp	bdos

BIOS$ind:
	mvi	c,50
	jmp	bdos

print$what:
	push	h
	call	clear$lots
	pop	h
	call	print$hl
	push	h
	mvi	a,lots$line
	call	highlight$line
	pop	h
	ret

show$prompt:
	call	clear$lots		;
	call	posit$prompt
;-----------------------------------------------------
print$msg:		;print a message on the screen
;-----------------------------------------------------
	xthl
	call	print$hl
	xthl
	ret

print$hl:
	push	h
print$lp:
	pop	h
	mov	a,m		;..get the message
	mov	e,a		;in a for call to bdos
	inx	h		;..update pointer
	ana	a		;if char was 0, pointer = return address
	rz			;..end message
	push	h		;..restore pointer
	inr	a		;..FF==cr,lf
	jrz	char$eq$cr$lf	
	call	do$con$out
	jr	print$lp	;..loop

char$eq$cr$lf:
	call	do$cr$lf
	jr	print$lp

clear$prompt:
	call	posit$prompt
	call	clear$eos
posit$prompt:
	lxi	d,prompt$line*256+0		; B=row C=col
	jr	set$fx$cp$cmd

clear$edit:
	call	posit$editing
	call	clear$eos
posit$editing:
	lxi	d,keynam$line*256+0
	jr	set$fx$cp$cmd

posit$top:
	lxi	d,top$line*256
	jr	set$fx$cp$cmd

posit$using:
	lxi	d,use$line*256		; B=row C=col
	jr	set$fx$cp$cmd

clear$lots:
	call	posit$lots
	call	clear$eos
posit$lots:
	lxi	d,lots$line*256
	jr	set$fx$cp$cmd

posit$spec:		
	lxi	d,spec$line*256
set$fx$cp$cmd:				;set up to position cursor
	mvi	l,FR$cursor$pos
	jr	go$screen$fun

clear$eos:
	mvi	l,FR$CES
	jr	go$screen$fun

clear$eol:
	mvi	l,FR$CEL
go$screen$fun:
	push	b
	lda	scr$siz
	mvi	c,FR$40
	cpi	scrsz40
	jrnz	set$fx$eighty
set$fx$forty:
	mov	a,l
	add	c
	mov	l,a
set$fx$eighty:
	mvi	a,3		; direct to screen fun #
	pop	b
go$user$fun:
	jmp	0000		; address filled in

	page
;"""""""""""""""""""""""""""""""""""""""""""""""""
;s c a n   f o r   y e s   o r   n o   a n s w e r
;"""""""""""""""""""""""""""""""""""""""""""""""""
get$yes$or$no:
	call	scan$for$key
	mov	a,b
	cpi	yes$key
	stc
	rz
	sui	no$key
	ana	a		;clear carry for no
	rz
	call	beep
	jr	get$yes$or$no

;"""""""""""""""""""""""""""""""""""""""""""""
;s c a n   f o r   k e y b o a r d   i n p u t
;""""""""""""""""""""""""""""""""""""""""""""" 
scan$for$key:
	mvi	a,2
	call	go$user$fun
	inr	b
	jrz	scan$for$key
	dcr	b
	ret

	page
;"""""""""""""""""""""""""""""""
;c l e a r   t h e   s c r e e n
;"""""""""""""""""""""""""""""""
clear$screen:
	call	print$msg
	db	1ah,eom
	ret

;"""""""""""""""""""""""""""""""""""""""""
;g e t    r e s p o n s e   t o    m e n u
;""""""""""""""""""""""""""""""""""""""""" 
menu$response:
	sta	current$choice
	call	highlight$line
wait$ans:
	call	scan$for$key
	mov	a,b
	cpi	cret
	jz	made$selection
	cpi	ctrlc
	cz	quit$prog
	call	up$or$down?
	cz	scroll$values
	jr	wait$ans 	

made$selection:
	lda	top$choice
	mov	b,a
	lda	current$choice		
	sub	b			;a=0-x for choices
	mov	b,a			;..save it
	pop	d
	mov	l,a
	mvi	h,0
				;first test if de is pointing to a 00
	dad	h		;*2
	xchg			;(00--> just return)
	mov	a,m
	inx	h
	ora	m
	inx	h
	jrz	back$to$??
	dcx	h
	dcx	h
	dad	d		;+ beginning of jump table
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a	
back$to$??:
	mov	a,b		;a=0-x
	pchl	

	page
;"""""""""""""""""""""""""""""""""""""""""""""""""""""
;s c r o l l  u p  &  d o w n   t h r o u g h  m e n u
;"""""""""""""""""""""""""""""""""""""""""""""""""""""
scroll$values:
	push	psw
	lda	current$choice
				;unhighlight the current menu line
	mvi	h,0		;..dont set any additional attributes
	call	write$hi$line	;..common routine for writing current line
	pop	psw		;get flags back
	jrc	scroll$up
	lda	bottom$choice
	mov	b,a
	lda	current$choice
	cmp	b		;current==bottom && scrolldown??
	jrz	force$top
	inr	a			
	sta	current$choice
	jr	highlight$line	;call,ret

scroll$up:
	lda	top$choice
	mov	b,a
	lda	current$choice
	cmp	b		;current==top && scroll up??
	jrz	force$bottom
	dcr	a
	sta	current$choice
	jr	highlight$line		;call,ret

force$top:
	lda	top$choice		;to scroll down from bottom==top
	sta	current$choice
	jr	highlight$line		;call,ret

force$bottom:
	lda	bottom$choice		;to scroll up from top==bottom
	sta	current$choice
highlight$line:				;a=row # to be highlighted 
	call	read$this$row		;to highlight, read current row and
	mvi	h,40h			;..write it out with rev vid attribute
	jr	write$hi$line

	page

read$this$row:
	push	psw		;save the row number for others
	mov	d,a		;d=row
	mvi	e,0		;column (e) = 0
	lxi	h,save$buffer	;a place to read into
	shld	temp$ptr	;save a pointer
	xra	a		;..initialize count to 0
	sta	read$count
read$row$lp:	
	push	d		;..save it to loop
	mvi	l,FR$rd$chr$atr	;read char and attribute from (de)
	call	go$screen$fun	;go read a character
	lhld	temp$ptr	;get buffer pointer
	mov	m,b		;buffer the character 
	inx	h
	mov	m,c		;..and the attribute
	inx	h		;to next character
	shld	temp$ptr	;save the pointer
	pop	d		;current row,column
	inx	d		;next row,column
	mov	a,e		;when count(column)=, we have read maximum
	cpi	40
	jrnz	read$row$lp
	dcx	h		;point to last character written
	mov	b,e		;# of characters to scan for trailing spaces
	mvi	a,' '		;..space character to scan for
scan$space$lp:
	dcx	h		;char, not attribute
	cmp	m		;..is char a space??
	jrnz	end$scan	;if not, we are done
	dcx	h		;..else decrement to previous attribute
	djnz	scan$space$lp	;keep looking
end$scan:
	mov	a,b	
	inr	a
	sta	read$count	;store # of non-white chars to restore
	pop	psw		;restore a=row #
	ret

	page

write$hi$line:
	mov	d,a		;current row passed in a
	mvi	e,0		;..start at column 0
	mov	a,h		;..additional attribute bits passed in h
	sta	temp		;save attribute value to be ored
	lxi	h,save$buffer	;where the current line was read into
	shld	temp$ptr	;..save pointer to it 
	lda	read$count	;number of bytes actually in line (no trailing spaces)

	mov	b,a		;..count in b
wr$hi$li$loop:
	push	b		;save the counter
	push	d		;save the cursor position
	lhld	temp$ptr	;get pointer
	mov	b,m		;get char in b
	inx	h		;point to attribute
	mov	a,m		;..get attribute
	inx	h		;update to next character
	shld	temp$ptr	;..save the pointer
	mov	h,a		;..put attribute here for a moment
	lda	temp		;get the attribute passed (either rev vid or none)
	ora	h		;..set it
	mov	c,a		;..attribute must be passed in c
	mvi	l,FR$wr$chr$atr	;write character and attribute functions
	call	go$screen$fun	;go do it
	pop	d		;cursor position back
	inx	d		;..next
	pop	b		;get counter back
	djnz	wr$hi$li$loop	;..loop	
	ret			;...line is rewritten

up$or$down?:
	mov	a,b		;get matrix row in a	
	cpi	cursor$up$key	;is it cursor up?
	stc
	rz			;..return if cursor up
	sui	cursor$dn$key
	ana	a		;clear carry flag
	ret			;return something to caller

	page
;-------------------------------
;p a r a m e t e r   b l o c k s
;-------------------------------
scbpbrd:
	db	24h		;set offset for conout
	db	0		;..get (read)scb (hl =returnedword,a=byte)
	dw	0		;just for the hell of it

wr$pb:			;write parameter block	
	db	14		;write sector
	db	0
	dw	0
	dw	0
	dw	0

sel$pb:			;select parameter block
	db	9		; select drive function
	db	0		; a  
sel$drv:
	dw	0		; bc drive #
	dw	1		; de lsb=0 means 1st time select
	dw	0		; hl

trk$pb:			;track parameter block
	db	10		; set track
	db	0
trk:	dw	0001h		; track #
	dw	0
	dw	0

sect$pb:		;sector parameter block	
	db	11		; set sector #
	db	0
sect:	dw	0001h		; sector #
	dw	0
	dw	0

dma$pb:			;dma parameter block
	db	12
	db	0
dma:	dw	4000h		; dma pointer
	dw	0
	dw	0

dma$bnk$pb:		;dma bank parameter block
	db	28
	db	1		; dma bank 1
	dw	0
	dw	0
	dw	0

	page
;
;     39-1	|     39-2	|     39-3	|
;"""""""""""""""|"""""""""""""""|"""""""""""""""|
; disk	| BAM	| BAM	| disk	| DRV	| SYS	|
; lable	|  1	|  2	| DIR	| boot	| boot	|
;"""""""|"""""""|"""""""|"""""""|"""""""|"""""""|
; 40-0	| 40-1	| 40-2	| 40-3	| 40-4	| 40-5	|
;
;
;     39-4	|     39-5	|
;"""""""""""""""|"""""""""""""""|
;  LDR	|  LDR	|  LDR	|	|
;  #1	|  #2	|  #3	|	|
;"""""""|"""""""|"""""""|"""""""|
; 40-6	| 40-7	| 40-8	| 40-9	|
;
  if	format$1581
c1581$dsk$def:
	db	'Formatting C128 CP/M on C1581',eom
	fmt	fmt$tbl$c1581
	wrdef	39,1,ST$sect$39$1
	wrdef	39,2,ST$sect$39$2
	wrdef	39,3,ST$sect$39$3
	wrdef	39,4,ST$loader$start$1
	wrdef	39,5,ST$loader$start$2
	defend

c1581$sys$def:
	db	'Formatting C128 CP/M on C1581',eom
	fmt	0
	wrdef	39,1,ST$sect$39$1
	wrdef	39,2,ST$sect$39$2
	wrdef	39,3,ST$sect$39$3
	wrdef	39,4,ST$loader$start$1
	wrdef	39,5,ST$loader$start$2
	defend
  endif
	page
;
;
;
c64$ss$dsk$def:
	db	'C64 single sided',eom
	fmt	fmt$tbl$c64$ss
	wrdef	1,0,ST$c64$1$0		; track, sector, DEF
	wrdef	3,0,ST$E5s		; track, sector, DEF
	wrdef	3,1,ST$E5s		; track, sector, DEF
	wrdef	3,2,ST$E5s		; track, sector, DEF
	wrdef	3,3,ST$E5s		; track, sector, DEF
	wrdef	3,4,ST$E5s		; track, sector, DEF
	wrdef	3,5,ST$E5s		; track, sector, DEF
	wrdef	3,6,ST$E5s		; track, sector, DEF
	wrdef	3,7,ST$E5s		; track, sector, DEF
	defend

	page
;
;
;
c128$ss$dsk$def:
	db	'C128 Single Sided',eom
	fmt	fmt$tbl$c64$ss
	wrdef	1,0,ST$c128$1$0$ss	; track, sector, DEF
	wrdef	1,3,ST$E5s
	wrdef	1,4,ST$E5s
	wrdef	1,9,ST$E5s
	wrdef	1,10,ST$E5s
	wrdef	1,14,ST$E5s
	wrdef	1,15,ST$E5s
	wrdef	1,19,ST$E5s
	wrdef	1,20,ST$E5s
	wrdef	18,0,ST$sect$18$0
	defend

	page
;
;
;
c128$ds$dsk$def:
	db	'C128 Double Sided',eom
	fmt	fmt$tbl$c64$ss
	wrdef	1,0,ST$c128$1$0$ds	; track, sector, DEF
	wrdef	1,1,ST$E5s
	wrdef	1,2,ST$E5s
	wrdef	1,3,ST$E5s
	wrdef	1,4,ST$E5s
	wrdef	1,7,ST$E5s
	wrdef	1,8,ST$E5s
	wrdef	1,9,ST$E5s
	wrdef	1,10,ST$E5s
	wrdef	1,12,ST$E5s
	wrdef	1,13,ST$E5s
	wrdef	1,14,ST$E5s
	wrdef	1,15,ST$E5s
	wrdef	1,17,ST$E5s
	wrdef	1,18,ST$E5s
	wrdef	1,19,ST$E5s
	wrdef	1,20,ST$E5s
	wrdef	18,0,ST$sect$18$0
	wrdef	53,0,ST$sect$18$0
	defend

	page
;
;
;
epson$dsk$def:
	db	'EPSON QX10 512 byte sectors',eom
	fmt	fmt$tbl$epson
	defend
;
;	no sector to be set-up
;
osborne$dsk$def:
	db	'OSBORNE Double Density',eom
	fmt	fmt$tbl$osborne
	defend
;
;
;
kp$2$dsk$def:
	db	'KAYPRO II',eom
	fmt	fmt$tbl$kp$2
	defend

kp$4$dsk$def:
	db	'KAYPRO IV',eom
	fmt	fmt$tbl$kp$2,fmt$tbl$kp$4
	defend

	page
;
;	IBM -- single sided disk definition
;
ibm$dsk$def$ss:
	db	'IBM SS',eom
	fmt	fmt$tbl$ibm$ss
	wrdef	0,1,ST$ibm$ss		; track, sector, DEF
	defend
;
;
;
ibm$dsk$def$ds:
	db	'IBM DS',eom
	fmt	fmt$tbl$ibm$ds
	wrdef	0,1,ST$ibm$ds		; track, sector, DEF
	defend

	page
;
;
;
  if	format$1581
fmt$tbl$c1581:
	db	1
	db	10000110b	;parameter N   (cmd 4)
	db	2		; sector size =512
	db	79		; last track # 79
	db	10		; number sectors/track
	db	0		; starting track #
	db	0E5h		; fill byte
	db	38		; gap 3
  endif
;
;	GCR Format parameters
;
fmt$tbl$c64$ss:
table$ds:	
	db	4
	db	00000110b	;parameter N   (cmd 4)
	db	00000000b	;parameter MD  (Mode GCR)
	db	'8'		;parameter ID low
	db	'0'		;parameter ID high

;
;
;
fmt$tbl$epson:
	db	6
	db	01100110b	; N	cmd 4
	db	10000001b	; MD	Mode MFM (start sector 1)
	db	0		; CP	interleave of 0
	db	2		; SZ	512 byte sectors
	db	39		; LT	last track 39
	db	10		; NS	10 sectors/track
				; ST	Starting track logical (def 0)
				; S	Starting Offset Physical (def 0)
				; FL	Fill byte (def E5)

;
;
;
fmt$tbl$osborne:
	db	6
	db	01000110b	; N	cmd 4
	db	10000001b	; MD	Mode MFM (start sector 1)
	db	0		; CP	interleave of 0
	db	3		; SZ	1024 byte sectors
	db	39		; LT	last track 39
	db	5		; NS	5 sectors/track
				; ST	Starting track logical (def 0)
				; S	Starting Offset Physical (def 0)
				; FL	Fill byte (def E5)
	page
;
;	KAYPRO II (single sided)
;	-- uses this format data to format the top and
;	-- bottom of KAYPRO II and KAYPRO IV disk
;
fmt$tbl$kp$2:
	db	6
	db	01000110b	; N	cmd 4
	db	10000000b	; MD	Mode MFM (start sector 0)
	db	0		; CP	interleave of 0
	db	2		; SZ	512 byte sectors
	db	39		; LT	last track 39
	db	10		; NS	10 sectors/track
				; ST	Starting track logical (def 0)
				; S	Starting Offset Physical (def 0)
				; FL	Fill byte (def E5)
;
;	KAYPRO IV (double sided)
;	-- uses this format data to format the top of the disk (side 1)
;
fmt$tbl$kp$4:
	db	6
	db	01010110b	; N	cmd 4
	db	10001010b	; MD	Mode MFM (start sector 10)
	db	0		; CP	interleave of 0
	db	2		; SZ	512 byte sectors
	db	39		; LT	last track 39
	db	10		; NS	10 sectors/track
				; ST	Starting track logical (def 0)
				; S	Starting Offset Physical (def 0)
				; FL	Fill byte (def E5)
	page
;
;
;
fmt$tbl$ibm$ss:
	db	6
	db	01000110b	; N	cmd 4
	db	10000001b	; MD	Mode MFM (start sector 1)
	db	0		; CP	interleave of 0
	db	2		; SZ	512 byte sectors
	db	39		; LT	last track 39
	db	8		; NS	8 sectors/track
				; ST	Starting track logical (def 0)
				; S	Starting Offset Physical (def 0)
				; FL	Fill byte (def E5)
;
;
;
fmt$tbl$ibm$ds:
	db	6
	db	01100110b	; N	cmd 4 format DS
	db	10000001b	; MD	Mode MFM (start sector 1)
	db	0		; CP	interleave of 0
	db	2		; SZ	512 byte sectors
	db	39		; LT	last track 39
	db	8		; NS	8 sectors/track
				; ST	Starting track logical (def 0)
				; S	Starting Offset Physical (def 0)
				; FL	Fill byte (def E5)
	page
;
;
;
  if	format$1581
ST$sect$39$1:
	defstart	0
	defbuf		29,0,sect$39$1
	definline	8,100h
	db	40,2			; next BAM track and sector
	db	44h,not(44h)		; DISK version #
	db	'80'			; Disk ID
	db	40h			; verify off, check header ID on
	db	80h			; enable auto load
	defend
;
;
;
ST$sect$39$2:
	defstart	0
	definline	7,1
	db	-1		; mark end of BAM
	db	44h,not(44h)	; Disk Version #
	db	'80'		; ID
	db	40h		; verify off, check header ID on
	db	80h		; enable auto load
	definline	20,101h
	db	-1		; next track sector (end)
	db	83h		; type USR
	db	40,4		; logical trk 40 sector 4
	db	'COPYRIGHT CBM 86'
	definline	1,11Eh
	db	1
	defend

	page
;
;
;
ST$sect$39$3:
	defstart	0
	definline	5,0
	db		0,lng$drv$cd+5
	dw		drv$buf			; exec adr in drv
	db		lng$drv$cd
	defbuf		lng$drv$cd,5,drv$code$1581
	defexec		boot$check$sum		; compute Check SUM
	defbuf		0c9h,100h,boot$1581$sector
	defend
;
;
;
ST$loader$start$1:
	defstart	0
	defbuf		512,0,loader$start
	defend
;
;
;
ST$loader$start$2:
	defstart	0
	defbuf		loader$size-512,0,loader$start+512
	defend
  endif

	page
;
;
;
ST$c64$1$0:
	defstart	0E5h
	definline	4,0
	db		'KMBE'
	defend
;
;
;
ST$c128$1$0$ss:
	defstart	0
	defbuf		24h,0,sect$1$0
	defend
;
;
;
ST$c128$1$0$ds:
	defstart	0
	defbuf		24h,0,sect$1$0
	definline	1,255
	db		-1
	defend

	page
;
;
;
ST$sect$18$0:
	defstart	0
	definline	4,0
	db	1,5,41h,80h
	definline	4,48h
	db	11h,0fch,-1,7
	definline	27,90h
	db	'CP/M PLUS'
	db	0a0h,0a0h,0a0h
	db	0a0h,0a0h,0a0h
	db	0a0h,0a0h,0a0h
	db	'65',0a0h,'2A'
	db	0a0h,0a0h
	db	0a0h,0a0h
	defend
;
;
;
ST$E5s:
	defstart	0E5h
	defend
;
;	IBM single sided
;	-- Track 0, Sector 1 last byte =0 
;
ST$ibm$ss:
	defstart	0E5h
	definline	1,512-1		; last byte=0 for IBM single sided
	db	0
	defend

;
;	IBM double sided
;	-- Track 0, Sector 1 last byte =1
;
ST$ibm$ds:
	defstart	0E5h
	definline	1,512-1		; last byte=0 for IBM single sided
	db	1
	defend

	page
;
;	Computes and sets the check SUM for the 1581 Drive Boot sector
;
  if	format$1581
boot$check$sum:
	lxi	h,sect$buf+2
	lxi	d,sect$buf+2+lng$drv$cd+3
	mvi	b,0		; start SUM =0
sum$next:
	mov	a,m
	add	b
	aci	0		; add carry to LSB
	mov	b,a		; return sum to B
	inx	h
	mov	a,l
	cmp	e
	jrnz	sum$next
	mov	a,h
	cmp	d
	jrnz	sum$next
	mov	m,b		; save sum to end of buffer
	ret
  endif

	page
;
;		1541 and 1571 BOOT sector
;	track 1 sector 0 sector definition (256 bytes)
;
sect$1$0:
	db	'CBM',0,0,0,0,0,0	; bytes 0 to 8

	@sei				; byte 9
	@jsr	0ff84h
	@lda	3eh,#			; set MMU to bank 0
	@sta	0ff00h			; .. RAM and IO only
	@lda	0c3h,#
	@sta	0FFEEh
	@lda	08,#
	@sta	0FFEFh
	@lda	0,#
	@sta	0FFF0h
	@jmp	0ffd0h			; swap to Z80 (RST1 location)

	page

  if	format$1581
CV		equ	drv$buf-$
my$trans$hi	set	high(my$trans+CV)
my$trans$low	set	low(my$trans+CV)
;
;
drv$code$1581:
	@sei
	@lda	VTRANS$TS		; get old vector and save it
	@sta	CV+code$exit$2+1
	@lda	VTRANS$TS+1
	@sta	CV+code$exit$2+2

	@lda	my$trans$low,#		; install new vector
	@sta	VTRANS$TS
	@lda	my$trans$hi,#
	@sta	VTRANS$TS+1
	@lda	buf$alc,#
	@sta	buf$use			; protect this code
	@jmp	boot$ret

my$trans:
	@ldx	next$job
	@lda	jobs,x			; get current job
	@cmp	80h,#			; is it a READ
	@bne	code$exit$1

	@ldy	hdrjob			; get index

	@ldx	hdrs+1,y		; get logical sector
	@bne	code$exit$1

	@ldx	hdrs,y			; get logical track
	@dex				; was it one
	@bne	code$exit$1

	@ldx	40,#
	@stx	hdrs,y			; change to logical TRK=40
	@ldx	5,#
	@stx	hdrs+1,y		; change to logical SECT=5
code$exit$1:
	@lda	CV+code$exit$2+1
	@sta	VTRANS$TS		; get old vector and save it
	@lda	CV+code$exit$2+2
	@sta	VTRANS$TS+1
code$exit$2:
	@jmp	0000

lng$drv$cd	equ	$-drv$code$1581

	page
;
;		1581 Directory Sector
;
sect$39$1:
	db	40,3		; directory track & sector (logical)
	db	44h,0		; Disk versions and null flag
	db	'CP/M PLUS',0a0h,0a0h,0a0h
	db	0a0h,0a0h,0a0h,0a0h,0a0h,0a0h
	db	'80',0a0h,'3D',0a0h,0a0h

	page
;		1581 Boot sector
;	track 1 sector 0 sector definition (256 bytes)
;
BB	equ	boot$code$02-$
;
boot$1581$sector:
	db	'CBM',0,0,0,0,0,0	; bytes 0 to 8
	@sei				; (9)
	@jsr	0ff84h			; (A-C)  reset I/O
;
;	Clear Bank 0 memory
;
	@lda	3Fh,#			; (D,E)   Enable RAM 0
	@sta	0FF00h			; (F-11)
	@lda	10h,#			; (12,13)   high byte of page pointer
	@sta	21h			; (14,15)  save hiagh byte of pointer
	@lda	0,#			; (16,17) low byte of page pointer
	@sta	20h			; (18,19) save low byte of pointer
	@ldx	0EFh,#			; (1A,1B) from 1000h to FEFFh
	@tay				; (1Ch)   start at 0
era$loop:
	@sta	(20h),y			; (1D,1E)
	@iny				; (1Fh)
	@bne	era$loop		; (20,21)
	@inc	21h			; (22,23) point to next page
	@dex				; (24)
	@bne	era$loop		; (25,26)
	@sta	0FF00h			; (27-29) re-enable kernal and I/O
;
;  load sectors that contain CP/M boot code (RAM version)
;
;	open command chanel to drive 8
;
	@lda	15,#			; (2A,2B)
	@ldx	8,#			; (2C,2D)
	@tay				; (2Eh)
	@jsr	0FFBAh			; (2F-31) SETLFS
	@lda	0,#			; (32,33)
	@tax				; (34)
	@jsr	0FF68h			; (35-37) SETBNK
	@lda	4,#			; (38,39)
	@ldx	low(u0point+BB),#	; (3A,3B)
	@ldy	high(u0point+BB),#	; (3C,3D)
;;;***
	@jsr	0FFBDh			; (3E-40) SETNAM
	@jsr	0FFC0h			; (41-43) OPEN
;
;	read sector 39-4
;
dma0	equ	high(boot$code$80)

	@lda	39,#
	@ldx	4,#
	@ldy	dma0,#
	@jsr	rd$sector+BB
;
;	read sector 39-5
;
	@lda	39,#
	@ldx	5,#
	@jsr	rd$sect$buf+BB
;
;	install JMP BOOT$CP/M
;
low$start	equ	low(boot$code$80)
hi$start	equ	high(boot$code$80)

	@lda	0c3h,#
	@sta	0FFEEh
	@lda	low$start,#
	@sta	0FFEFh
	@lda	hi$start,#
	@sta	0FFF0h
	@lda	3eh,#			; set MMU to bank 0 
	@sta	0ff00h			; .. RAM and IO only
	@jmp	0FFD0h			; swap to Z80 (RST1 location)

rd$sector:
	@sty	21h			; hi byte of DMA adr
rd$sect$buf:
	@sta	trk02+BB
	@stx	sect02+BB

	@ldx	15,#			; CMD chan #
	@jsr	0FFC9h			; CHKOUT
	@ldy	6,#
sendf:
	@lda	R$cmd+BB,y
	@jsr	0FFD2h			; CHROUT
	@dey
	@bne	sendf

	@jsr	0FFCCh			; CLRCHN
	@bit	0DC0Dh			; D1ICR
	@jsr	read$byte+BB		; read status
	@ldx	2,#			; 2 256 byte blocks/sector
	@ldy	0,#
	@sty	20h			; low byte of DMA adr
rd$buff:
	@jsr	read$byte+BB
	@sta	(20h),y			; at dma adr
	@iny
	@bne	rd$buff
	@inc	21h			; advance DMA pointer
	@dex
	@bne	rd$buff

	@lda	0DD00h			; D2PRA
	@and	0EFh,#			;
	@sta	0DD00h			; set clk hi
	@rts				;

read$byte:
	@sei				; disable interrupts
	@lda	0DD00h			; D2PRA
	@eor	10h,#			; clock bit
	@sta	0DD00h
	@lda	8,#
in$1:
	@bit	0DC0Dh			; D1ICR
	@beq	in$1
	@lda	0DC0Ch			; D1SDR
	@rts

R$cmd	equ	$-1
	db	1			; count
sect02:	db	0			; sector #
trk02:	db	0			; track #
	db	0,'0'			; read comand =00h

u0point:
	db	'U0',4Ch,0		; Inquire Status

	page
;
ZZ	equ	boot$code$80-$
LOADER$START:
;
;	setup the MMU for booting CP/M
;
	lxi	sp,boot$stack
	mvi	a,3Fh
	sta	force$map
;
;	move bios and swap code into ram
;
	lxi	h,bios$65$code+ZZ
	lxi	d,bios$02
	lxi	b,bios$size
	ldir

	lxi	h,swap$code
	lxi	d,enable$z80
	lxi	b,swap$size
	ldir

	mvi	a,RET			; get z80 return adr
	sta	return$6502		; store the RET
;
;	initilize the 8502 bios
;
	xra	a				; cleared by memory fill
	sta	vic$cmd
	call	enable$6502

	page
;
;	set MMU registers to a known state (for CP/M to use)
;
	lxi	h,mmu$init$data+11-1	; start at the End
	lxi	b,mmu$start+11-1	; and work forward
	mvi	d,11			; for all 11 bytes

INIT$MMU$CPM:
	mov	a,m			; get table value
	outp	a			; send to MMU
	dcx	h
	dcr	c
	dcr	d
	jrnz	INIT$MMU$CPM
					; re-enabled RAM bank 0 (no I/O)
;
;	set 80 column colors and set up Video memory with ASCII char set
;
	mvi	a,26
	call	wait
	mvi	a,90h			; foreground red background black
	outp	a
	mvi	a,85h			; set attr and color
	sta	current$atr		; ..for 80 column display
	mvi	a,green			; set color
	sta	attr$40			; ..for 40 column display
	call	install$ASCII		; convert char set to true ASCII

 mvi	a,25			; number of lines on the 40 col display
 sta	paint$size

	page
;
;	Let the user know we are booting CP/M
;
	call	prt$msg$both
	db	-1			; clear both screens
	rownum	1,10
	db	'BOOTING CP/M PLUS',0
;
;	point 40 column screen to CP/M screen area
;
	lxi	b,VIC+24
	mvi	a,vic$screen*4/256+6	; upper and lower case set (+6)
	outp	a
;
;
;
	mvi	a,64
	sta	block$size		; 64 dir entries/2K allocation

	lxi	h,DIR$PTRS+ZZ
	shld	ld$blk$ptr
	call	SCAN$DIR+ZZ		; check 1st block
	call	SCAN$DIR+ZZ		; check 2nd block (1K or 2K)

	lhld	block$ptrs		; 1st pointer <>0 if file
	mov	a,h			; name exist
	ora	l			; pointer = 0
	jz	tell$user		; yes, inform user there is a error
					; no, file FOUND, process it
	page
;----------------------------
; load 1st group to 1K buffer
;----------------------------
FILE$FOUND:
	lxi	h,block$ptrs
	shld	ld$blk$ptr
	call	UPDATE$BUFFER+ZZ

;--------------
;GET$BOOT$INFO:
;--------------
	lxi	h,block$buffer
	lxi	d,info$buffer
	lxi	b,12
	ldir

	call	prt$msg$both
	rownum	10,0
	db	0			; end of string marker
	lxi	h,block$buffer+80h
	call	prt$hl$both

	lxi	h,block$buffer+256	; set scan pointer
	shld	blk$unld$ptr

	page
;---------------------------------
; load keyboard data to system RAM
;---------------------------------
	call	prt$msg$both
	rownum	3,12
	db	'DATA TABLES',0
	lhld	info$buffer+10
	shld	key$tbl			; install keyboard translation pointer

	lxi	h,info$buffer+9
	call	GET$SIZE$ADR+ZZ		; HL=adr DE=# (128 btye) records 
	shld	fun$tbl

LOAD$NEXT$FORWARD:
	call	LOAD$RECORD+ZZ		; HL =load address (in and out)
	lxi	d,128			; move pointer back to buf start
	dad	d
	jrnz	LOAD$NEXT$FORWARD

	page
;-----------------------------------
; transfer CP/M code to load address
;-----------------------------------
	call	prt$msg$both
	rownum	4,12
	db	'COMMON CODE',0
	lxi	h,info$buffer+1		; load common code
	call	LOAD$REVERSE+ZZ

	call	prt$msg$both
	rownum	5,12
	db	'BANKED CODE',0
	lxi	h,info$buffer+3		; load banked code
	call	LOAD$REVERSE+ZZ

	page
;---------------------------
; now load the bios8502 code
;---------------------------
	call	prt$msg$both
	rownum	6,12
	db	'BIOS8502 CODE',0
	lxi	h,info$buffer+7		; load banked code
	call	LOAD$REVERSE+ZZ

	lda	info$buffer+7		; get code size (in 256 byte blocks)
	mov	b,a
	lda	info$buffer+6		; get page pointer (pointer to end)
	sub	b			; find the start
					; install jmp adr to BIOS02
	sta	return$z80+2		; (jmp) (low) (high)
	xra	a
	sta	return$z80+1		; (jmp) (low) (high)
;------------------------------------
; now let's start executing CP/M Plus
;------------------------------------
	lhld	info$buffer+4		; get start address
	pchl				; transfer control to CP/M

	page
;-------------;
; SUBROUTINES ;
;-------------;
;	scan buffer for CPM+.SYS file
;
SCAN$DIR:
	call	UPDATE$BUFFER+ZZ	; returns HL=block$buffer
	mvi	a,64			; 64 for 2K block
					; ..number director entries/sector
CHECK$NEXT:
	shld	@dma
	lxi	d,sys$name		; point to system name
	push	psw
	call	name$match+ZZ
	cz	FOUND+ZZ
	pop	psw
	lhld	@dma			; get current buffer pointer
	lxi	d,32
	dad	d
	dcr	a
	jrnz	CHECK$NEXT
	ret

	page
;
;
;
name$match:
	mvi	b,12
	xchg
match$next:
	ldax	d
	ani	7fh
	cmp	m
	rnz
	inx	h
	inx	d
	djnz	match$next
	ldax	d
	sta	ext$num
	xra	a
	ret

	page
;----------------------------
; save CPM+.SYS group numbers
;----------------------------
;	found a dir entry that has the right name
;	add block pointers to list
;
FOUND:
	lxi	d,block$ptrs		; point to start of block pointers
	lda	ext$num
	ora	a
	jrz	EXT$NUM$0

	lxi	d,block$ptrs+16
	dcr	a
	jnz	ext$error

EXT$NUM$0:
	lhld	@dma			; get current pointer

	lxi	b,16			; number of bytes to move
	dad	b			; also advance to block pointers
	ldir

	lhld	block$ptrs
	mov	a,h
	ora	l			; 1st block present ?
	rz				; no, read more dir.

	lhld	block$ptrs+14		; extent full?
	mov	a,h
	ora	l
	jrz	GO$BOOT$IT		; no, this is it then

	lhld	block$ptrs+16		; 2nd block present ?
	mov	a,h
	ora	l
	rz				; no, read more dir.

GO$BOOT$IT:
	jmp	FILE$FOUND+ZZ		; two parms are still on the stack
					; but at this point who cares
	page
;
;
;
LOAD$REVERSE:
	call	GET$SIZE$ADR+ZZ		; HL=adr DE=# records (128 byte)

LOAD$NEXT:
	lxi	d,-128			; move pointer back to buf start
	dad	d
	call	LOAD$RECORD+ZZ
	jrnz	LOAD$NEXT
	ret
;
;
;
GET$SIZE$ADR:
	mov	e,m
	mvi	d,0			; get buffer size (#256 byte)
	mov	a,e			; get size to A
	ora	a
	jz	table$error		; exit if count=0

	xchg
	dad	h			; HL=#128 byte blocks
	shld	load$count
	xchg
	dcx	h
	mov	h,m
	mvi	l,0
	ret

	page
;
;
;
LOAD$RECORD:
	push	h			; save to address
	lxi	h,block$buffer+2048
	xchg
	lhld	blk$unld$ptr
	call	cmp$hl$de
	cz	UPDATE$BUFFER+ZZ
	xchg
	lxi	h,128
	dad	d
	shld	blk$unld$ptr
	pop	h			; recover save address
	push	h
	xchg				; HL=source  DE=dest.
	lxi	b,128			; size of move
	ldir
	lhld	load$count
	dcx	h
	shld	load$count
	mov	a,l
	ora	h
	pop	h
	ret

	page
;
;
;
UPDATE$BUFFER:
	lxi	h,block$buffer
	shld	@dma
	push	h			; save block buffer adr for ret
	lhld	ld$blk$ptr		; get the current block pointer
	mov	e,m			; get LSB of block pointer
	inx	h			; advance pointer
	mov	d,m			; zero MSB of block pointer
	inx	h
	shld	ld$blk$ptr
	xchg				; get block number to HL
;
;	read the block pointed to by the HL
;	into the data buffer
;
	dad	h			; 2X (1k blocks)
	dad	h			; 4x (512 byte blocks)
	push	h
	call	read$1K+ZZ		; read 1st 1K block
	lda	@dma+1
	adi	4
	sta	@dma+1
	pop	h
	inx	h
	inx	h
	call	read$1K+ZZ		; read 2nd 1K block
	pop	h			; recover block buffer adr
	ret
;
;	convert block number (in HL) to sector and track
;
read$1K:
	mvi	a,2
	sta	F$rd$count+BZ		; 
	lxi	d,10			; 10 sectors per side
	lxi	b,-1
compute$trk:
	inx	b
	ora	a			; clear the carry
	dsbc	d			; negative if <10
	jrnc	compute$trk		; jump if still positive >=10
	dad	d			; add it back
	mov	a,c			; get trk # (0 to 159)
	cpi	39*2
	jrc	bump$trk
	inr	a
	inr	c
bump$trk:
	rrc				; move bit 0 to carry
	mvi	a,1			; sector numbers are 1 to 10
	jrnc	side$0
	mvi	a,11			; or 11 to 20
side$0:
	add	l			; get sector # (0-9)
	sta	VIC$sect
	mov	a,c			; get trk # (0 to 159)
	rar				; divide by 2 (carry was cleared)
	sta	VIC$trk
	jmp	read$sector		; read the sector to the buffer

DIR$PTRS:
	dw	0,1

	page
;
;
; **** THIS IS THE COMMAND LOOP ****
;
BZ	equ	bios$02-$	; BIOS BIAS

;
bios$65$code:
	@sei
	@lda	0,#		;-K
	@sta	vic$data	;-K
	@JSR	VICIO+BZ	;-K  go find and do requested operation
CMDLP:
	@sei
	@lda	3eh,#		;?K  set up Z80 memory map as required
	@sta	force$map	;-K
	@jmp	enable$z80	;-K

;
; **** IO COMMAND DISPATCH ROUTINE ****
;
VICIO:
	@CLD			;-K  clear to binary mode
	@LDA	vic$cmd		;-K  get the command
	@bne	read		;-K   0=initialize
				;     1=read
	page
;
;
;
initilize:			;   initialize the 8502
	@ldx	0,#		;-K
	@stx	force$map	;-K  enable the kernal
	@stx	VIC+26		;+K  turn off VIC interrupts

	@ldx	low(irqs+BZ),#
	@ldy	high(irqs+BZ),#
	@stx	314h		;+K  IRQ vector
	@sty	315h
	@stx	316h		;+K  BRK vector
	@sty	317h
	@stx	318h		;+K  NMI vector
	@sty	319h
;
; **** OPEN DISK COMMAND CHANNEL ****
;
	@LDA	cmdchn,#	;+K
	@CLC			;+K
	@JSR	K$close		;+K
	@LDA	cmdchn,#	;+K
	@LDX	8,#		;+K
	@TAY			;+K
	@JSR	K$setlfs	;+K

	@LDA	0,#		;+K
	@TAX			;+K
	@JSR	K$set$bnk	;+K

	@LDA	4,#			;+K
	@LDX	low(U0PT+BZ),#		;+K
	@LDY	high(U0PT+BZ),#		;+K
	@JSR	K$setnam		;+K
	@JSR	K$open			;+K
	@bcs	misdsk

	@jsr	K$readst
	@ROL	A		;+K  GET MSB TO CARRY
	@BCS	MISDSK		;+K  DEVICE MISSING IF CARRY SET
	@rts

;
;  * DEVICE MISSING, CLEAN UP ERROR *
;
MISDSK:
	@LDA	00fh,#		;+K  SET ERROR CODE
	@skip2

fst$error:
	@lda	0dh,#		;?K
	@skip2

DISK$CHANGED:
	@lda	0FFh,#		;?K
	@STA	vic$data	;?K  writes to RAM under ROM
	@JMP	CMDLP+BZ	;?K

	PAGE
;
; **** DISK SECTOR READ ****
;
READ:
	@ldx	0,#		;-K get read command (side 0)
	@lda	VIC$sect
	@cmp	11,#
	@bmi	on$side$0
	@ldx	10h,#		; get read command (side 1)
	@sbc	10,#		; remove bias of 10
on$side$0:
	@stx	F$cmd+BZ
	@sta	F$rd$sect+BZ
	@lda	VIC$trk
	@sta	F$rd$trk+BZ

	@lda	@dma		;-K
	@sta	pointer		;-K
	@lda	@dma+1		;-K
	@sta	pointer+1	;-K
	@lda	0,#		;-K
	@sta	force$map	;-K
	@ldx	cmdchn,#	;+K
	@jsr	K$chkout	;+K
	@bcs	fst$error	;+K
	@ldy	Fcmd$lng,#	;+K
sendf$rd:
	@lda	Fcmd$buf+BZ-1,y	;+K
	@jsr	K$chrout	;+K
	@dey			;+K
	@bne	sendf$rd	;+K
	@jsr	K$clrchn	;+K
	@bit	D1ICR		;+K
	@ldx	F$rd$count+BZ	;+K
rd$sect:
	@sei			;+K
	@jsr	rd$byte+BZ	;+K
	@and	0eh,#		;+K  mask off error bits
	@bne	fst$error	;+K
	@ldy	0,#		;+K start index at buffer start
	@jsr	read$256+BZ	;+K read 1st 256 byte block
	@jsr	read$256+BZ	;+K read 2nd 256 byte block
	@dex			;+K
	@bne	rd$sect		;+K
clk$hi:
	@lda	d2pra		;+K
	@and	0ffh-clk$bit,#	;+K
	@sta	d2pra		;+K
	@rts			;+K

	PAGE
;
;
read$256:
	@jsr	rd$byte+BZ	;+K
	@sta	(pointer),y	;+K
	@iny			;+K
	@bne	read$256	;+K
	@inc	pointer+1	;+K
	@rts
;
rd$byte:
	@lda	d2pra
	@eor	clk$bit,#
	@sta	d2pra
	@lda	8,#
rd$1:
	@bit	d1icr
	@beq	rd$1
	@lda	d1sdr
	@rts

	page
;
;	handle all interrupts in BIOS 8502 (throw them away)
;
irqs:
	@lda	CIA$1+int$ctrl
	@lda	CIA$2+int$ctrl
	@lda	0fh,#
	@sta	VIC+25
;
;	system saved memory config, Y, X and A before getting here
;
	@pla
	@sta	force$map
	@pla
	@tay
	@pla
	@tax
	@pla
	@rti

	PAGE

F$cmd$buf:
F$rd$count:	db	2		; 5
F$rd$sect:	db	0		; 4 filled in
F$rd$trk:	db	0		; 3 filled in
F$cmd:		db	0		; 2 read=0
		db	'0'
U0PT:		db	'U'		; 1
Fcmd$lng	equ	$-F$cmd$buf
		db	'0',4Ch,0	; reset disk change status (open)
bios$size	equ	$-bios$65$code
loader$size	equ	$-loader$start
  if	loader$size > 2048
	' loader size error'
  endif
  endif

	page

endofcode:
ramalc	set	endofcode

define	top$choice,1		; top row of these choices
define	bottom$choice,1		; bottom row of these choices
define	current$choice,1	; current row of these choices
define	fmt$drv,1		; drive to do format on
define	fmt$tbl$ptr,2		; pointer to current format table
define	save$buffer,80*2	; save a row here
define	scrsiz,1		; current screen size flag
define	temp,1			; miscellaneous			
define	temp$ptr,2		; temporary ptr for moving tables
define	read$count,1		; # of characters in saved line

define	top$of$stack,300
define	stack,0
define	sect$buf,1024		; largest sector is 1024

	end
