;****************************************************************************
;
;  CP/M cold boot loader for Altair CP/M for 5.25" HD Floppy
;
;    This code is at the start of track 0 and is loaded into RAM by 
;    the cold boot loader. This loader, in turn, loads the CP/M image
;    into high RAM. 
;
;    Since the cold boot loader reads a full track into memory and loading
;    at zero would clobber the first 10K of RAM, this code is loaded at
;    4000h to avoid overwriting anything in the first 16K of RAM.
;
;    Version History
;
;    Ver     Date	  Description
;    ---   --------	---------------------
;    1.0   06/17/20	Original, Mike Douglas     
;
;--------------------------------------------------------------------------
;
;    To patch changes made to this loader into a CP/M image saved from
;    MOVCPM (e.g., CPMxx.COM), use the following commands:
;
;	A>DDT CPMxx.COM
;	-IBOOT.HEX
;	-Rxxxx      	(where xxxx = BIAS computed below)
;	-G0	    	(Go zero, not "oh")
;	A>SYSGEN
;
;****************************************************************************
MEMSIZE	equ	56			;set memory size in K bytes
BIOSLEN	equ	3000h			;length of this BIOS
LOADER	equ	4000h			;loader base address
TRKLEN	equ	10240			;track length to read

;-----------------------------
; CP/M size & locations
;-----------------------------
CCPLEN	equ	0800h			;CPM 2.2 fixed length
BDOSLEN	equ	0E00h			;CPM 2.2 fixed length

BIOSBAS	equ	MEMSIZE*1024-BIOSLEN	;BIOS base address
BDOSBAS	equ	BIOSBAS-BDOSLEN		;BDOS base address
CCPBASE	equ	BDOSBAS-CCPLEN		;CCP base address
SYSGEN	equ	0900h			;SYSGEN image base address
BOOTLEN	equ	128		        ;length of boot loader
BIAS	equ	SYSGEN-LOADER		;bias in DDT to patch in this loader

;-----------------------------
; Altair disk controller 
;-----------------------------
DRVSLCT	equ	08h		;drive select register (out)
cDSLCT	equ	80h		;deselect drive

DRVSTAT	equ	08h		;drive status register (in)
sENWD	equ	01h		;enter new write data flag
sMOVEOK	equ	02h		;OK to move head
sHDSTAT	equ	04h		;head status flag
sDSKEN	equ	08h		;disk is selected and enabled
sXFRDON	equ	10h		;sector transfer complete
sINTEN	equ	20h		;processor interrupts enabled
sTRACK0	equ	40h		;on track zero flag
sNRDA	equ	80h		;new read data available

DRVCMD	equ	09h		;drive command register (out)
cSTEPI	equ	01h		;step in
cSTEPO	equ	02h		;step out
cHDLOAD	equ	04h		;head load
cRESTMR	equ	04h		;restart motor-off timer (MINIDSK)
cHDUNLD	equ	08h		;head unload
cREADEN	equ	10h		;read request enable
cHCSON	equ	40h		;reduce head current switch
cWRTEN	equ	80h		;write enable

DRVSEC	equ	09h		;drive sector position (in)
sNEWSEC	equ	01h		;new sector flag (sector true)

DRVDATA	equ	0Ah		;drive read/write data (in/out)

DRVTRK	equ	0Bh		;OUT to provide cylinder to FDC+

DRVIOS	equ	0Bh		;IN for I/O operation status
sCRCERR	equ	01h		;1=checksum error
sTRKERR	equ	02h		;1=track number error
sEOSERR	equ	04h		;1=unexpected end of sector
sDONE	equ	80h		;1=physical disk transfer complete
sERRMSK	equ	(NOT sDONE) AND 0FFh

;--------------------------------------------------------------------------
;  Entry point
;--------------------------------------------------------------------------
	org	LOADER

start	di
	lxi	sp,CCPBASE-BOOTLEN  ;init stack pointer

; Deselect and reselect the drive in case this loader was loaded
;    from somewhere other than drive 0

	mvi	a,cDSLCT	;deselect current drive
	out	DRVSLCT	
	xra	a		;select drive 0
	out	DRVSLCT
	mvi	a,cHDLOAD	;load head
	out	DRVCMD
	
; Restore to track zero

seek0	in	DRVSTAT		;loop until OK to move the head 
	ani	sMOVEOK
	jnz	seek0

	in	DRVSTAT		;see if we are at track 0
	ani	sTRACK0		;at track zero?
	jz	load		;yes, start the load process

	mvi	a,cSTEPO	;issue step out
	out	DRVCMD
	jmp	seek0
	
; Load CP/M image

load	xra	a		;give FDC+ the track number
	out	DRVTRK

waitSec	in	DRVSEC		;read sector position register
	rar			;wait for sector true (0=true)
	jc	waitSec	
	
	lxi	h,CCPBASE-BOOTLEN  ;HL->where to put the track
	lxi	b,TRKLEN/2	;BC=transfer counter
	mvi	a,cREADEN	;issue read request
	out	DRVCMD

wtData	in	DRVSTAT		;wait for data to be ready
	ora	a		;test for NRDA flag true (zero)
	jm	wtData

	in	DRVDATA		;do dummy read to start transfer

; rdFifo - Read data without handshake. Data should be read slower than
;    the physical transfer rate of 16us/byte. This loop averages 17us/byte.

rdFifo	in	DRVDATA		;(10) get and store a byte
	mov	m,a		;(7)
	inx	h		;(5)

	in	DRVDATA		;(10) get and store abyte
	mov	m,a		;(7)
	inx	h		;(5)

	dcx	b		;(5) decrement transfer counter
	mov	a,b		;(5)
	ora	c		;(4)
	jnz	rdFifo		;(10)

	in	DRVIOS		;get the I/O status
	ani	sERRMSK		;test the error bits
	jnz	start		;error, start over
	
	jmp	BIOSBAS		;enter BIOS
	
	end
