;****************************************************************************
;
;   Cold boot loader for CP/M 2.2 for Mostek Development System computers.
;
;   The monitor PROMs on the SDB-80 CPU board provide disk I/O support
;   routines that are called by this BIOS to do disk I/O.
;
;   The boot ROM searches sectors from track 0 and track 1 in the order
;   specified by a boot order table. The ROM looks for the file "OS.BIN"
;   using a directory format from FLP-80DOS. If OS.BIN is found in a sector,
;   the bytes at offset 0Eh and 0Fh in the directory entry contain the
;   starting sector and track of the file to load (this boot loader).
;
;   The file loaded (this code) consists of 124 data bytes per sector
;   followed by backward and forward (sector/track) byte pairs. The
;   backward link isn't used. A forward link of (FFh,FFh) indicates
;   this is the last sector of the file. This loader fits in 1 sector
;   and has the (FFh,FFh) link defined at offset 7Eh, 7Fh.
;
;   Based on the order sectors are searched, placing the code in sector
;   one and the directory entry in sector 2 results in the fastest
;   boot time. This file is organized with the 128 bytes of boot code
;   first followed by the 128 byte fake directory sector 2nd. The
;   CP/M image then starts at sector 3.
;
;   To patch an update to this BIOS into a CP/M image created and saved
;   after running MOVCPM, use the following commands:
;
;	A>DDT CPM56.COM    (specify the saved CP/M image file)
;	-IBOOT.HEX
;	-R800	     (puts loader at 900, directory at 980, sectors 1 & 2)
;	-G0	     (Go zero, not "oh")
;
;	Ver	  Date		     Description
;	---	----------	---------------------
;	1.0 	03/05/2016	Original, Mike Douglas
;
;****************************************************************************

	maclib	z80		;include Z80 opcodes

;  CP/M location equates

MEMSIZE	equ	56		;memory size in K bytes

BIOSLEN	equ	0600h		;length of BIOS in default MOVCPM
CCPLEN	equ	0800h		;CPM 2.2 fixed length
BDOSLEN	equ	0e00h		;CPM 2.2 fixed length

CPMSIZE	equ	CCPLEN+BDOSLEN+BIOSLEN
CCPBASE	equ	MEMSIZE*1024 - CPMSIZE
BIOSBAS	equ	(CCPBASE+CCPLEN+BDOSLEN) ;base address of the BIOS

;  ROM monitor equates

DSKDRV	equ	0EC00h		;disk driver entry point
MONITOR	equ	0E11Dh		;ROM monitor command loop
DISPMSG	equ	0E3C7h		;ROM monitor display message

;  Disk information equates

SECLEN	equ	128		;128 bytes per sector
NUMSECS	equ	26		;26 sectors per track
LOADCNT	equ	CPMSIZE/SECLEN	;number of sectors to load
STRTSEC	equ	3		;starting sector to load from

RETRIES	equ	4		;max retries

dcREAD	equ	11h		;read sector command
dcRESTR	equ	14h		;restore to track 0 command

STACK	equ	80h		;stack pointer used during boot

;------------------------------------------------------------------------
;  Start of code
;------------------------------------------------------------------------
	org	100h

loader	lxi	sp,STACK	;init stack pointer

; Clear DCB

	lxi	hl,dcb		;HL->DCB
	mvi	b,DCBLEN	;B=bytes to clear
	xra	a		;A=0

clrDcb	mov	m,a		;clear location
	inx	h
	dcr	b
	jnz	clrDcb

; Restore to track 0

	lxiy	dcb		  ;point IY to the DCB
	mviy	STRTSEC,oDCBSEC	  ;set starting sector in DCB
	mviy	dcRESTR,oDCBCMD	  ;set restore command in DCB
	call	DSKDRV
	jnz	fail

; Load CP/M into memory

doLoad	mviy	dcREAD,oDCBCMD	;set read command in DCB
	lxi	hl,CCPBASE	;HL->load address
	mvi	c,LOADCNT	;C=number of sectors to load
	mvi	b,RETRIES	;B=retry counter

readLp	shld	dcbDma		;set load address for this sector
	call	DSKDRV
	jz	goodRd

	dcr	b		;decrement retry counter
	jnz	readLp	

; fail - Display failure message and exit to monitor

fail	lxi	h,mFAIL		;hl->fail message
	call	DISPMSG		;display message
	jmp	MONITOR		;exit to monitor

; goodRd - Sector read successful. The last four bytes of the 
;    sector are in the DCB instead of in the destination
;    buffer. Move them to the destination.

goodRd	mvi	b,4		;copy last four bytes of sector
	lxi	d,dcbLst4	;which are in the DCB
	lhld	dcbDma		;hl->end of 124 bytes read

move4	ldax	d		;move from DCB to (HL)
	mov	m,a
	inx	d
	inx	h
	dcr	b
	jnz	move4

; Decrement sector counter, enter BIOS if all sectors read.
;    Otherwise, increment and wrap the sector count and
;    repeat for next sector.

	dcr	c		;decrement sector counter
	jz	BIOSBAS		;jump to BIOS if done

	lda	dcbSec		;increment and wrap sector
	inr	a
	sta	dcbSec
	cpi	NUMSECS+1	;past last sector?
	jc	readLp		;no, go read another sector

	inry	oDCBTRK		;increment track number in DCB
	mvi	a,1		;restart at sector 1
	sta	dcbSec
	jmp	readLp

;-----------------------------------------------------------------------
;  Data Constants
;-----------------------------------------------------------------------
mFail	db	'BOOT FAIL',03h

; Program counter at this point must be <= 17Ch or the boot code
;    is too long

pgmEnd	equ	$		;make sure <= 17Ch

	org	17Ch		;last four bytes of sector
	db	0,0
	db	0FFh,0FFh	;sector,track link of FF = end
	
;------------------------------------------------------------------------
;  Fake directory sector that contains the file "OS.BIN" that points
;     to the sector containing the boot loader code. This sector is
;     written to sector 2. The boot code is written to sector 1
;------------------------------------------------------------------------
	org	180h			;start of sector 2

	db	'OS    BIN',0,0FFh	;file name boot ROM looks for
	db	0,0,1			;expected values
	db	1			;boot code in sector 1
	db	0			;track 0
	db	0,0			;expected values
	dw	loader			;load address of boot loader
	
;----------------------------------------------------------------------
; dcb - disk control block. This structure is used to pass parameters
;    to the firmware disk driver. The DCB is allocated in scratchpad
;    RAM in high memory
;----------------------------------------------------------------------
	org	0FF60h		;put DCB in scratchpad RAM

DCBLEN	equ	30h		;total length of a DCB

dcb	equ	$
	ds	3		;0-2 not used
dcbDrv	ds	1		;drive number (0-3) at offset 3
dcbFn	ds	11		;file name (not used) at offset 04-0E
dcbCmd	ds	1		;command at offset 0F
	ds	9
dcbDma	ds	2		;transfer buffer address at 19,1A
	ds	2
dcbWSec	ds	1		;number of sectors for file dump to disk
dcbSec	ds	1		;sector to read/write at offset 1E
dcbTrk	ds	1		;track to read/write at offset 1F
dcbLst4	ds	4		;last four bytes of track are placed here

oDCBTRK	equ	dcbTrk-dcb	;offset of track entry in DCB
oDCBSEC	equ	dcbSec-dcb	;offset of track entry in DCB
oDCBCMD	equ	dcbCmd-dcb	;offset of track entry in DCB

	end

