;         * * * * * * * * * * * * * * * * * * * * * * *
;         *                                           *
;         *        Hard Disk Auto-Boot Installer      *
;         *                                           *
;         *  Copyright (C) 1985 AMPRO Computers, Inc. *
;         *             All rights reserved.          *
;         *                                           *
;         * * * * * * * * * * * * * * * * * * * * * * *
;
;
;  Revision history:
;
;  Ver  Date      Who     Description
;  ---  --------  ---     -----------------------------------------
;
;  1.2  1/7/85    fsw     Installs users HD$ALV$AVAIL: information
;                         in bios at boot time.
;
;  1.0  9/20/85   fsw	  Initial release.
;
;
VERS	EQU	12		; version 1.2
;
; This version saves all the setup disk parameters that the user
; has defined by running SYSGEN, HINIT, and SWAP to the hard disk.
; This user defined information is stored on the last sector (sector 31)
; of the system track, and is used to "patch" the standard BIOS on boot.
; 
; The information on this sector will be read somewhere into memory 
; by the hard disk boot prom with an offset of 15872 bytes (3E00 hex) 
; from the boot dma address.  The user's dpbase (256 bytes), the 64 byte 
; PHYTAB with the user's logical/physical configuration, the user's
; hard disk DBP's (176 bytes), the users HD$ALV$AVAIL block (12 bytes),
; and the users hard disk byte/block flag (1 byte) are saved in that order. 
; (total 509 bytes)
;
; The boot loader is modified to find this offset and install the users
; changes in bios before jumping to cold boot. 
;
; NOTES:
;
;    Only the system track of the hard disk with SCSI id 0 and LUN 0 will 
;    be patched.
;
;    Only Little Board's jumpered for SCSI initiator id 7 can run this 
;    utility.
;
;    Sector 31 (1F hex) of the hard disk system track is reserved.
;
;    Requires special SCSI boot prom .
;
;------------------------------------------------------------------------
;
; The following equates are entry points into the boot prom
;
SETDMA	EQU	800FH
READTRK	EQU	8012H
;
BOOT	EQU	9000h		; location of boot loader
TPA	EQU	100H
OFFSET	EQU	(BOOT-TPA)-3	; -3 for jmp start before patch area
WBOOT	EQU	0		; location of bios warm boot
;
CONTROL	EQU	0		; board control port
;
SREAD	EQU	08H		; scsi read data
SWRITE	EQU	0AH		; scsi write data
;
CR	EQU	0DH
LF	EQU	0AH
;-----------------------------------------------------------------
;
	ORG	TPA		; cp/m tpa
	JMP	START
;
;******************************************************************
;************************Boot loader*******************************
; values set at 0 are patched to users addresses by the program.
;
PATCH$START:
	MVI	A,40H		; must turn off rom
	out	control
DMA:	lxi	H,0		; set dmaadr address
	call	setdma
	call	readtrk		; read first track
	call	readtrk		; read second track
;
; find the user disk information that was loaded in to memory
; from sector 15 of the hard disk
;
	lhld	(dma+1)+offset	; get load address
	lxi	d,15872		; offset to where hard disk sector 31
				; landed
	dad	d		; sorce address in 'hl'
;
; move the users dpbase into bios 
;
DDPBAS:	lxi	d,0		; patched to destination address
	lxi	b,256		; number of bytes to move
	dw	0b0edh		; z80 ldir
;
; move the users phytab into bios
;
DOPHY:	lxi	d,0		; patched to address of phytbl
	lxi	b,64
	dw	0b0edh		; move the 4 bytes with z80 ldir
;
; move the users dpb's into bios
;
DODPB:	lxi	d,0		; patched to destination address
	lxi	b,176		; size hard disk DPB's
	dw	0b0edh		; z80 ldir
;
; move the users HD$ALV$AVAIL info into bios
;
HDAVL:	lxi	d,0		; patched to destination address
	lxi	b,12		; bytes to move
	dw	0b0edh		; z80 ldir
;
; move the users byte, block mode value to bios
;
CTLBYT:	lxi	d,0		; patched to destination address
	mov	a,m		; 'hl' has location of byte
	stax	d		; store at 'de' location
;
GOBIOS:	JMP	0		; go to cold boot
;
SIZE$PATCH:	equ	$-patch$start
;
;********************End of Boot loader****************************
;******************************************************************
;
START:	lxi	h,0		; init new stack and save old
	dad	sp
	shld	oldstk
	lxi	sp,stack
;
; clear the buffer area to zeros
;
	lxi	h,btsec
	lxi	b,1024		; do 1024 bytes
CLEAR:	xra	a
	mov	m,a
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	clear
;
; move the bios jump table to use it's io, easier
;
	lhld	wboot+1		; get address of bios
	mvi	l,0		; begining of bios jmp table
	lxi	d,bj$tbl	; bios jmp table
	lxi	b,64		; move 64 bytes
	dw	0b0edh		; z80 ldir
;
; check id of board
;
	in	29h		; get board id
	ani	07h		; mask board id
	cpi	7		; must be 7
	jz	signon
	adi	'0'
	sta	shid		; show id setting
	call	puts
	db	cr,lf,'Little Board ID = '
SHID:	db	'  , must = 7 to run this utility.',0
	jmp	exit
;
; bios jmp table moved, give signon
;
SIGNON:	call	puts
	db	cr,lf,lf,lf
	db	'                      AMPRO Hard Disk Auto-Boot Installer ',cr,lf
	db	'                    Copyright (C) 1985 AMPRO Computers, Inc. ',cr,lf
	db	'                                 Version ',VERS/10+'0','.',VERS MOD 10+'0'
	db	cr,lf,lf

	db	'This utility will install your current hard disk '
	db	'configuration on the system ',cr,lf
	db	'tracks of the SCSI hard disk drive located at '
        db	'SCSI ID 0, LUN 0. ',cr,lf,lf

	db	'Prior to using this program you must use SYSGEN '
	db	'to write the proper size ',cr,lf
        db      'CP/M system to the system tracks of the SCSI hard '
        db      'disk drive from which you ',cr,lf
        db      'will boot.  In addition, you must have already '
        db      'used the HINIT and SWAP ',cr,lf
        db      'utilities to customize your system. ',cr,lf,lf

	db	'NOTE:  The CP/M system you install on the hard '
        db      'disk must identical to ',cr,lf
        db      'the system you are running now. ',cr,lf,lf

	db	'WARNING!!! ',cr,lf
	db	'As a safety measure, be sure to backup your hard '
        db      'disk before you perform ',cr,lf
        db      'this function or attempt hard disk auto-booting! ',cr,lf,lf

        db	cr,lf,'Do you wish to continue? (Y/N) ',0

        call	conin
	ani	5fh		; make it upper case
	cpi	'Y'
	jnz     exit            ; exits if input not Y 
;
; read the current boot sector into memory
;
DOIT:	mvi	a,sread		; scsi read command
	sta	scsicmd
	lxi	h,scsicmd	; point to scsi command
	lxi	d,btsec		; data area
	mvi	a,1		; target address
	call	scsi
	jnz	scsierr		; not zero results=error
;
; we have the boot sector in memory lets see if sysgen has been
; done on the hard disk. two choices are present for the first byte
; of the AMPRO boot sector either mvi a,xx or lxi h,xxxx , check for 
; both.
;
	ldax	d		; call to scsi returns data pointer
	cpi	3eh		; see if mvi a,x
	jz	next
	cpi	21h		; could be lxi h,xxx
	jz	next
	call	puts		; neither
	db	cr,lf,'Valid Boot sector not present on hard disk, '
	db	'do SYSGEN, HINIT, and SWAP, then retry.',0
	jmp	exit
;
; figure out the dma address to load track 0 at
; the rest if the info we will find in the bios jmp table
;
NEXT:	ldax	d		; find the lxi h,xxx instruction for
	cpi	21h		; dma address
	inx	d
	jnz	next
	xchg			; location of dma address in 'hl'
	mov	a,m
	inx	h
	mov	h,m		; dma address in 'hl'
	mov	l,a
	shld	dma+1		; set dma address in new boot loader
;
; fix the cold boot address
;
	lhld	wboot+1		; get address of warm boot from cp/m
	mvi	l,0		; make it a cold boot
	shld	gobios+1	; patch jmp in boot loader
;
; get users dpbase, always at cold boot+80h in AMPRO bios
;
	mvi	l,80h		; offset from cold boot
	shld	ddpbas+1	; load address in boot loader
	lxi	d,udpbase	; move the dpbase to save area
	lxi	b,256
	dw	0b0edh		; move it, ldir
	push	d		; save buffer destination address
;
; get address of phytbl
;
	call	gettbl		; get address of next table
	lxi	d,6		; offset to phtbac
	dad	d		; 'hl'=address of phtbac routine
	shld	gtbl+1
	lxi	h,0		; if 'hl'=00 returns address of phytbl
GTBL:	call	0		; call phtbac
	shld	dophy+1		; patch location of phytab in users bios
	pop	d		; get destination address
	lxi	b,64		; move 64 bytes
	dw	0b0edh		; z80 ldir
	push	d		; save address
;
; the patch area now contains the phytab as designated by the user.
;
; lets find the users DPB's for the hard disk. Test first to see 
; if SWAP has been run
;
	mvi	c,0		; drive 'A'
	mvi	e,1		; not first time select
	call	seldsk		; do select to return dpb header address
	lxi	d,10		; offset to dpb address
	dad	d
	mov	a,m		; get the address of the dpb in 'hl'
	inx	h
	mov	h,m
	mov	l,a
	mov	a,m		; 'hl' has address of DBP
	cpi	40+1		; sectors per track information
	jnc	movdpb		; have hard disk DPB
	call	puts
	db	cr,lf,'Run HINIT and SWAP to set up your system first.'
	db	cr,lf,0
	pop	d		; restore stack for exit
	jmp	exit		;
;
; find address of first hard disk dpb
;
MOVDPB:	lhld	wboot+1		; get address of bios
	mvi	l,80h		; offset to dpbase
	lxi	d,16*5		; offset to first hard disk dpb header
	dad	d		; 'hl'= first hard disk 
	lxi	d,10		; offset to dpb address
	dad	d
	mov	a,m		; get the address of the dpb in 'hl'
	inx	h
	mov	h,m
	mov	l,a
	pop	d		; restore destination address
	shld	dodpb+1		; address of users hd dpb for boot loader
	lxi	b,176		; move 176 bytes, 11 dpb's
	dw	0b0edh		; z80 ldir
;
; find the HD$ALV$AVAIL block to save and the scsi controller burst/byte 
; mode value and save. needed for Shugart and other non-burst controllers
;
	push	d		; save 'de'
	call	gettbl		; get address of next table
	lxi	d,3		;
	dad	d		; 
	shld	brst+1
BRST:	CALL	0		; call get hd$info

	pop	d		; get table address back
	push	h		; save address of HD$ALV$AVAIL
	shld	hdavl+1		; save bios address in boot loader
	lxi	b,12		; bytes to move into table
	dw	0b0edh		; z80 ldir

	pop	h		; get location of HD$ALV$AVAIL back
	push	d		; save table address
	
	lxi	d,4		; 
	dad	d		; 'hl' points to address of byte, burst
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a		; address of byte,burst mode byte
	mov	a,m		; get current value, 0 or 1
	pop	d
	stax	d		; save value
	shld	ctlbyt+1	; save bios address in boot loader
;
; we have all the information from the users bios in the patch area
; read to overlay the first sector and write back to disk.
;
	lxi	h,btsec		; clean the boot sector
	lxi	b,128
CLEAN:	xra	a
	mov	m,a
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	clean
;
; overlay the new boot loader on the boot sector
;
	lxi	h,patch$start	; begining of new boot loader
	lxi	d,btsec		; location of boot sector in ram
	lxi	b,size$patch	; number of patch bytes
	dw	0b0edh		; z80 ldir, movit
;
; now write it back to hard disk
;
	mvi	a,swrite	; scsi write
	sta	scsicmd
	mvi	a,1		; target address
	lxi	h,scsicmd	; the scsi command
	lxi	d,btsec		; data area
	call	scsi		; do the write
	jnz	scsierr		; if error
;
; boot sector written, now do users swap and disk information
;
	mvi	a,swrite	; scsi write
	sta	scsicmd
	mvi	a,31		; sector 31
	sta	lowaddr		; 
	lxi	h,scsicmd
	lxi	d,user$tab	; users bios information
	mvi	a,1
	call	scsi		; do the write
	jnz	scsierr
	call	puts
	db	cr,lf,'Done!  You may now boot from hard disk.',cr,lf,lf,0
	jmp	exit
;
; show any scsi error that may have occured
;
SCSIERR:
	ldax	d		; 'de' has address of sense info
	push	psw		; save it
	ani	0f0h
	rar			; convert upper nibble to ascii
	rar
	rar
	rar
	cpi	10		; alpha or numeric
	jc	num0
	adi	'7'		; alpha offset (A thru F)
	jmp	num0+1
;
NUM0:	adi	'0'		; numeric offset (0 thru 9) 
	sta	errtyp
;
; do second digit
;
	pop	psw
	ani	0fh
	cpi	10
	jc	num1		; numeric
	adi	'7'		; alpha offset (A thru F)
	jmp	num1+2

NUM1:	adi	'0'		; numeric offset (0 thru 9)
	sta	errtyp+1
;
ERRMSG:	call	puts
	db	cr,lf,'SCSI read/write error, Sense byte = '
ERRTYP:	db	'  ',cr,lf,lf,0		; fall thru and exit
;
EXIT:	xra	a
	lhld	oldstk
	sphl
	ret
;
; SEND BYTES TO CONSOLE UNTIL ZERO ENCOUNTERED
;
PUTS:	pop	h		; 'hl' has address of msg
	mov	c,m		; get char
	call	conout		; send to comsole
	mov	a,m
	cpi	lf
	cz	wait		; wait 10 ms after lf for slow terminals
	inx	h		; next byte
	mov	a,m		;
	ana	a		; see if end=0
	jnz	puts+1		; nope
	pchl			; hl has return address
;
WAIT:	mvi	b,4
	lxi	d,0
WAIT1:	inx	d
	mov	a,e
	cmp	d
	jnz	wait1
	dcr	b
	rz
	jmp	wait1
;
; scsi command block
;
SCSICMD	db	sread		;
	db	0		; high address
	db	0		; middle address
LOWADDR	db	0		; low address
	db	1		; number of sectors=1
	db	0		; reserved
;***********************Save area**************************
;
OLDSTK:	ds	2		; storage for old stack pointer
;
BJ$TBL:				; table of bios jumps
CBOOT	ds	3		; cold boot address
	ds	3		; warm boot
CONST:	ds	3		; console status
CONIN:	ds	3		; console character in
CONOUT:	ds	3		; console character out
	ds	3		; list char out
	ds	3		; punch char out
	ds	3		; reader char in
	ds	3		; home
SELDSK:	ds	3		; select disk
SETTRK:	ds	3		; set track number
SETSEC:	ds	3		; set sector number
	ds	3		; set dma address
	ds	3		; read disk
	ds	3		; write disk
	ds	3		; return list status
	ds	3		; sector translate
GETTBL:	ds	3		; point to next jmp table
	ds	3		; getedsk, pointer to 'e' disk prameters
	ds	3		; ioinit
SCSI:	ds	3		; scsi direct driver
;
	ds	32		; 32 byte stack area
STACK:	EQU	$
;
BTSEC:	DS	512
;
USER$TAB:
UDPBASE:	DS	256	; users dpbase
UPHYTAB:	DS	64	; users phytab
UHDDPB:		DS	176	; users hd dpb's
UHDAVAL:	DS	12	; users hd$alv$avail values
BYTEBLK:	DS	1	; scsi controller byte, block value

TBL$SIZE:	EQU	$-user$tab

	END

