;		NEW BIOS BOOT MAINTENENCE PROGRAM
;
;PURPOSE
;		THIS PROGRAM PROVIDES THE MEANS TO SYSGEN A NEW
;	BIOS CP/M SYSTEM.  IT WRITES THE BOOT, BDOS AND BIOS TO
;	THE SYSTEM TRACKS OF DISK B.  IT CAN BE USED FOR MULTIPLE
;	GENERATIONS WITH AN OPTIONAL SYSTEM OBTAINED FROM DRIVE
;	A.
;DATE WRITTEN
;	MAY 23, 1980 WITH THE HELP OF S.J. SINGER'S PGEN PROGRAM
;OUTLINE
;	TO GENERATE A NEW SYSTEM, DO THE FOLLOWING:
;		DDT CPMXX.COM<RETURN>   (XX = MEMORY SIZE)
;		-M1180,1F80,980
;		-I21BIOS.HEX
;		-H1780,NNNN		(NNNN = START OF BIOS,
;						SEE ASSEMBLY LISTING.)
;		  XXXX YYYY
;		-RYYYY			(LOADS BIOS)
;		-^C
;		NPGEN<RETURN>
;		GET SYSTEM (Y/N)? N
;		PUT SYSTEM (Y/N)? Y
;	     ETC.
;	TOGENERATE FROM AN OLD SYSTEM, JUST RUN NPGEN TELLING
;	IT TO GET THE SYSTEM FROM DRIVE A.
;
;
	MACLIB	MACRO			;INCLUDE MACROS
FALSE	EQU	00H
TRUE	EQU	NOT FALSE
;
SPOOL	EQU	FALSE			;TRUE FOR KLH SPOOLER
;
;
MSIZE	EQU	61			;MEMORY SIZE IN KBYTES
CBASE	EQU	(MSIZE-20)*1024
CPMB	EQU	CBASE+3400H		;START OF CP/M
BDOS	EQU	CPMB+0800H		;START OF BDOS (ROUNDED TO
;					 EVEN SECTOR BOUNDARY)
BIOS	EQU	CPMB+1600H		;START OF BIOS
	IF	SPOOL
JMPDSP	EQU	033H+9	;DISPLACEMENT TO SPECIAL BIOS JUMPS
	ENDIF
	IF	NOT SPOOL
JMPDSP	EQU	033H	;DISPLACEMENT TO SPECIAL BIOS JUMPS
	ENDIF
TPA	EQU	100H
RDCON	EQU	1
WRBUF	EQU	9
START	EQU	900H
FBYTE	EQU	97FH			;DISK FORMAT BYTE LOCATION
;
;
;
	ORG	TPA
;
;
	LXI	H,0			;ZERO HL
	DAD	SP			;GET OLD STACK POINTER
	SHLD	OLDSTK			;AND SAVE IT
	LHLD	1
	SHLD	SAVADR
	LXI	SP,STACK
MAIN:	PRINT	<CR,LF,LF,'      NEW BIOS SYSGEN PROGRAM VERS 2.0'>
	PRINT	<CR,LF,LF>
	CALL	GET$BOOTER
	CALL	PUT$BOOTER
	JMP	REBOOT
;
;
SELDSK:				;SELECT DISK
	PUSH	H
	LHLD	1
	MVI	L,00H+JMPDSP
	XTHL
	RET
;
;
HOME:				;HOME DISK
	PUSH	H
	LHLD	1
	MVI	L,03H+JMPDSP
	XTHL
	RET
;
;
SEEK:				;SEEK TRACK
	PUSH	H
	LHLD	1
	MVI	L,06H+JMPDSP
	XTHL
	RET
;
;
READ:				;READ A SECTOR
	PUSH	H
	LHLD	1
	MVI	L,09H+JMPDSP
	XTHL
	RET
;
WRITE:				;WRITE A SECTOR
	PUSH	H
	LHLD	1
	MVI	L,0CH+JMPDSP
	XTHL
	RET
;
;
GET$BOOTER:			;MAYBE A BOOTER COMES IN
	PRINT	<CR,LF,LF,'GET SYSTEM (Y/N) '>
	CHARIN
	CPI	'Y'
	RNZ			;RETURN IF NOT YES
	PRINT	<CR,LF,'READING SYSTEM FROM DRIVE A, TYPE RETURN '>
	CHARIN
	CPI	3		;IS IT CONTROL C
	JZ	MAIN
	MVI	A,0FFH
	STA	RDFLG		;SET FLAG
	CALL	DO$READ
	RET			;DONE
;
;
PUT$BOOTER:			;DOES THE BOOTER GO OUT?
	PRINT	<CR,LF,'PUT SYSTEM (Y/N) '>
	CHARIN
	CPI	3		;CONTROL C
	JZ	MAIN
	CPI	'Y'
	RNZ			;EXIT IF NO
PUT$AGAIN:			;ELSE START TO PUT
	PRINT	<CR,LF,'WRITING SYSTEM TO DRIVE B, TYPE RETURN '>
	CHARIN
	CPI	3		;CHECK FOR CONTROL C
	JZ	MAIN
	CALL	READ$FMT		;READ T 0 S 1 FROM DESTINATION DISK
	CALL	MOVE$BOOT
	PRINT	<CR,LF,'THE DESTINATION DISK FORMAT IS - '>
	CALL	WRITE$FMT
	LDA	FMFLG		;CHECK IF KNOWN FORMAT
	ORA	A
	CNZ	GET$FORMAT
;		*** TEMPORARY ***
	LDA	FBYTE		;MUST BE SINGLE DENSITY
	CPI	020H		;128-BYTE SECTORS.
	JNZ	FMT$ERR		
;		*** END OF TEMPORARY ***
	CALL	DO$WRITE	;NOW PUT IT OUT
	PRINT	<CR,LF,LF,'AGAIN (Y/N) '>
	CHARIN
	CPI	'Y'
	JZ	PUT$AGAIN	;WRITE IT AGAIN
	RET			;ALL DONE
;		*** TEMPORARY ***
FMT$ERR:
	PRINT	<CR,LF,'*** FORMAT MUST BE SD 128... ***'>
	RET
;		*** TEMPORARY ***
;
;
REBOOT:				;PULL BACK CP/M
	PRINT	<CR,LF,LF,'REBOOTING CP/M, TYPE RETURN '>
	CHARIN
	MVI	C,0
	CALL	SELDSK		;SELECT DRIVE 0
	LHLD	SAVADR
	SHLD	1
	LHLD	OLDSTK		;RECOVER ORIGINAL STACK POINTER
	SPHL			;RESTORE IT
	JMP	0



;************************************************
;*	READ IN ALL SYSTEM TRACKS ASSUMING	*
;*	SD 128-BYTE SECTORS FOR ALL.		*
;************************************************

DO$READ:			;READ IN THE BOOTER
	MVI	C,0		;SELECT DISK
	CALL	SELDSK
	CALL	TIME
	CALL	HOME		;MAKE SURE IT'S INITIALIZED
	LXI	B,1		;B=TRK C=SECTOR
	LXI	H,START		;START OF AREA.
DR$LOOP:				;THEN PUT IT OUT
	PUSH	B
	PUSH	H
	PUSH	B
	MOV	C,B		;SEEK TRACK.
	CALL	SEEK
	POP	B
	POP	H		;RESTORE BUFFER PTR.
	PUSH	H
	CALL	READ		;READ A SECTOR.
	POP	H
	POP	B
	LXI	D,128		;BUMP BUFFER PTR.
	DAD	D
	INR	C		;BUMP SECTOR PTR.
	MOV	A,C		;END OF TRACK?
	CPI	26+1
	JC	DR$LOOP		;...NO.
	MVI	C,1		;RESET SECTOR.
	INR	B		;BUMP TRACK PTR.
	MOV	A,B		;END OF SYSTEM TRACKS?
	CPI	1+1
	JC	DR$LOOP		;...NO.
	RET



;************************************************
;*	WRITE OUT ALL SYSTEM TRACKS ASSUMING	*
;*	SD 128-BYTE SECTORS FOR ALL.		*
;************************************************

DO$WRITE:			;READ IN THE BOOTER
	MVI	C,1		;SELECT DISK
	CALL	SELDSK
	CALL	TIME
	CALL	HOME		;MAKE SURE IT'S INITIALIZED
	LXI	B,1		;B=TRK C=SECTOR
	LXI	H,START		;START OF AREA.
DW$LOOP:				;THEN PUT IT OUT
	PUSH	B
	PUSH	H
	PUSH	B
	MOV	C,B		;SEEK TRACK.
	CALL	SEEK
	POP	B
	POP	H		;RESTORE BUFFER PTR.
	PUSH	H
	CALL	WRITE		;READ A SECTOR.
	POP	H
	POP	B
	LXI	D,128		;BUMP BUFFER PTR.
	DAD	D
	INR	C		;BUMP SECTOR PTR.
	MOV	A,C		;END OF TRACK?
	CPI	26+1
	JC	DW$LOOP		;...NO.
	MVI	C,1		;RESET SECTOR.
	INR	B		;BUMP TRACK PTR.
	MOV	A,B		;END OF SYSTEM TRACKS?
	CPI	1+1
	JC	DW$LOOP		;...NO.
	RET
;
;
MOVE$BOOT:			;MOVE BOOT CODE TO 900H
	LXI	H,900H		;POINT TO MEMORY
	LXI	D,BOOT		;POINT TO BOOT CODE
	MVI	C,ENDBOOT-BOOT	;LENGTH OF CODE
MLOOP:	LDAX	D		;GET A BYTE
	MOV	M,A		;STORE IT
	INX	H
	INX	D		;INCR POINTERS
	DCR	C		;BYTE COUNT
	JNZ	MLOOP
	MVI	A,0C7H		;RESTART ZERO INSTRUCTION
	STA	97DH		;PUT IT IN BUFFER TOO
	RET
;
GET$FORMAT:			;READ DISK FORMAT FROM CONSOLE AND SET FBYTE
	PRINT	<CR,LF,'DO YOU WISH TO WRITE A FMT CODE ON THE DISK? (Y/N) '>
	CHARIN
	CPI	'Y'
	RNZ
GF1:	PRINT	<CR,LF,'IS DISK SINGLE DENSITY? (Y/N) '>
	CHARIN
	CPI	'Y'
	MVI	A,20H		;SINGLE DENSITY CODE
	JZ	GF2
	MVI	A,10H		;DOUBLE DENSITY CODE
GF2:	STA	FBYTE		;SAVE IN FBYTE
	PRINT	<CR,LF,'DOES DISK HAVE 128 BYTE SECTORS? (Y/N) '>
	CHARIN
	CPI	'Y'
	MVI	B,0		;128 BYTE CODE
	JZ	GFE
	PRINT	<CR,LF,'DOES DISK HAVE 256 BYTE SECTORS? (Y/N) '>
	CHARIN
	CPI	'Y'
	MVI	B,1		;256 BYTE CODE
	JZ	GFE
	PRINT	<CR,LF,'DOES DISK HAVE 512 BYTE SECTORS? (Y/N) '>
	CHARIN
	CPI	'Y'
	MVI	B,2		;512 BYTE CODE
	JZ	GFE
	PRINT	<CR,LF,'DOES DISK HAVE 1024 BYTE SECTORS? (Y/N) '>
	CHARIN
	CPI	'Y'
	MVI	B,3		;1024 BYTE CODE
	JZ	GFE
	PRINT	<CR,LF,'*** DEFAULTING TO 128 BYTE SECTORS.'>
	MVI	B,0
GFE:	LDA	FBYTE
	ORA	B		;OR IN THE BYTE CODE
	STA	FBYTE
	RET
;
READ$FMT:			;READ FORMAT CODE FROM DESTINATION DISK
	MVI	C,1		;SELECT THE DISK
	CALL	SELDSK
	CALL	TIME
	CALL	HOME
	CALL	TIME
	LXI	H,START
	MVI	C,1
	CALL	READ		;READ DRIVE B TRACK 0 SECTOR 1
	RET
;
WRITE$FMT:			;DECODE FORMAT BYTE AND DISPLAY
	MVI	A,0FFH
	STA	FMFLG		;SET FLAG TO KNOWN FORMAT
	LDA	FBYTE
	CPI	20H
	JNZ	WF2
	PRINT	<'SINGLE DENSITY 128 BYTE SECTORS',CR,LF>
	RET
WF2:	CPI	22H
	JNZ	WF3
	PRINT	<'SINGLE DENSITY 512 BYTE SECTORS',CR,LF>
	RET
WF3:	CPI	23H
	JNZ	WF4
	PRINT	<'SINGLE DENSITY 1024 BYTE SECTORS',CR,LF>
	RET
WF4:	CPI	10H
	JNZ	WF5
	PRINT	<'DOUBLE DENSITY 128 BYTE SECTORS',CR,LF>
	RET
WF5:	CPI	11H
	JNZ	WF6
	PRINT	<'DOUBLE DENSITY 256 BYTE SECTORS',CR,LF>
	RET
WF6:	CPI	12H
	JNZ	WF7
	PRINT	<'DOUBLE DENSITY 512 BYTE SECTORS',CR,LF>
	RET
WF7:	CPI	13H
	JNZ	WF8
	PRINT	<'DOUBLE DENSITY 1024 BYTE SECTORS',CR,LF>
	RET
WF8:	PRINT	<'NO FORMAT CODE ON DISK',CR,LF>
	XRA	A
	STA	FMFLG		;SET FLAG FOR NO FORMAT
	RET
;
TIME:	LXI	B,3000H		;TIMING DELAY
DELAY:	DCX	B
	MOV	A,B
	ORA	C
	JNZ	DELAY
	RET
;
OLDSTK:	DW	0		;STORAGE FOR OLD STACK POINTER
SAVADR:	DW	0
RDFLG	DB	0			;INDICATES BOOTER READ FROM DISK
FMFLG	DB	0			;INDICATES FORMAT FLAG ON DEST DISK
;
;
;
;		BDOS/BIOS BOOT LOADER
;
;THIS IS A MODIFICATION OF TARBELL'S BOOT LOADER DESIGNED TO RUN AT 0H
;THE CODE IS MOVED TO 900H BY PGEN WITH THE PROPER FORMAT CODE IN THE
;LAST BYTE FOR WRITING ON THE SYSTEM TRACK OF A PASCAL DISK.
;THE PROGRAM LOADS 51 SECTORS FROM DISK WHEN EXECUTED STARTING WITH
;TRACK 0 SECTOR 2.
;
;
DISK	EQU	0F8H		;BASE ADDR FOR DISK I/O PORTS
DCOM    EQU	DISK
DSTAT   EQU	DISK
TRACK   EQU	DISK+1
SECT    EQU	DISK+2
DDATA   EQU	DISK+3
WAIT    EQU	DISK+4
NS	EQU	51		;NUMBER OF SECTORS TO LOAD
;
OFFSET	EQU	00H		;BOOT OFFSET 
;
;
;
BOOT:
	MVI	E,10
BLOOP:
	LXI	SP,100H
 	MVI	D,NS		;D CONTAINS SECTOR COUNT
	LXI	H,BDOS		;STARTING LOAD ADDR FOR PROGRAM
	MVI	C,2		;SECTOR NUMBER.
RNTRK:	MVI	B,4		;FOR HEAD LOAD.
RNSEC:	CALL	BREAD-BOOT+OFFSET ;READ A SECTOR.
	DCR	D		;IF DONE.
	JZ	BIOS		;GO TO CP/M.
	MVI	B,0		;FOR NO HEAD LOAD.
	INR	C		;INCR TRACK COUNT.
	MOV	A,C		;DONE WITH
	CPI	26+1		;THIS TRACK?
	JC	RNSEC-BOOT+OFFSET ;...NO.
	MVI	A,053H		;ISSUE STEP COMMAND.
	OUT	DCOM
	IN	WAIT		;WAIT UNTIL DONE.
	MVI	C,1		;RESET SECTOR NUMBER.
	JMP	RNTRK-BOOT+OFFSET
;
BREAD:
	MOV	A,C		;SET SECTOR.
	OUT	SECT
	MVI	A,088H		;GET READ CMD.
	ORA	B		;GET HEAD LOAD BIT.
	OUT	DCOM		;ISSUE IT.
RLOOP:
	IN	WAIT		;WAIT FOR DRQ
	ORA	A
	JP	CHECK-BOOT+OFFSET ;JUMP IF DONE
	IN	DDATA
	MOV	M,A
	INX	H
	JMP	RLOOP-BOOT+OFFSET
;
CHECK:
	IN	DSTAT		;READ STATUS.
	ANI	09DH
	RZ
	DCR	E
	JNZ	BLOOP-BOOT+OFFSET
	STA	EC-BOOT+OFFSET
HERE:
	JMP	HERE-BOOT+OFFSET
;
EC:	DS	1
ENDBOOT:
	DS	128		;LOTS OF SPACE FOR STACK
STACK:	EQU	$
	END
