;	Figure 7-5.
;
;	Example Put CP/M
;
;	This program writes out the CP/M Cold Boot Loader, the
;	CCP, BDOS and BIOS to a floppy diskette. It runs under
;	CP/M as a normal Transient Program.
;
Version		EQU	'01'	;Equates used in the sign on message
Month		EQU	'07'
Day		EQU	'24'
Year		EQU	'82'
;
;
;	The actual PUTCPMF5.COM program consists of this code,
;	plus the BOOTF5.HEX, the CCP, BDOS and BIOS.
;
;	When this program executes, the memory image should 
;	look like this :
;
;	      Component      Base Address
;		BIOS		1F80H
;		BDOS		1180H
;		CCP		0980H
;		BOOTF5		0780H
;
;	The components are produced as follows :
;
;		BIOS.HEX	By assembling source code
;		BDOS )		From a CPMnn.COM file output
;		CCP  )		by MOVCPM and SAVEd on disk
;		BOOTF5.HEX	By assembling source code
;
;	The components are pieced together using DDT with the
;	following commands :
;
;		DDT CPMnn.COM
;		IPUTCPMF5.HEX
;		R			(Reads in this program)
;		IBOOTF5.HEX
;		R680			(Reads in Boot at 0780H)
;		IBIOS.HEX
;		R2980			(Reads in BIOS at 1F80H)
;		G0			(Exit from DDT)
;		SAVE 40 PUTCPMF5.COM	(Create final .COM file)
;
;	The actual layout of the diskette is as follows :
;								
; Track 0                     Sector
;         1     2     3     4     5     6     7     8     9	
; Head	+-----+-----+-----+-----+-----+-----+-----+-----+-----+	
;  0	|Boot |<======== CCP ========>|<=======  BDOS ========|	
;	+-----+-----+-----+-----+-----+-----+-----+-----+-----+	
;  1	|====== BDOS ====>|<=============  BIOS  ============>|	
;	+-----+-----+-----+-----+-----+-----+-----+-----+-----+	
;	  10    11    12    13    14    15    16    17    18
;                             Sector
;
;	Equates for defining memory size and the base address and
;	length of the system components.
;
Memory$Size	EQU	64	;Number of Kbytes of RAM
;
;	The BIOS Length must match that declared in the BIOS.
;
BIOS$Length	EQU	0900H
;
Boot$Length	EQU	512
CCP$Length	EQU	0800H	;Constant
BDOS$Length	EQU	0E00H	;Constant
;
Length$In$Bytes	EQU	CCP$Length + BDOS$Length + BIOS$Length
;
Start$Image	EQU	980H - Boot$Length	;Address of CP/M Image
Length$Image	EQU	Length$In$Bytes + Boot$Length 
;
;
;	Disk Characteristics
;
;	These equates describe the physical characteristics of
;	the floppy diskette so that the program can move from
;	one sector to the next, updating the track and resetting
;	the sector when necessary.
;
First$Sector$on$Track	EQU	1
Last$Sector$on$Track	EQU	18
Last$Sector$on$Head$0	EQU	9
Sector$Size		EQU	512
;
;
;	Controller Characteristics
;
;	On this computer system, the floppy disk controller can write
;	multiple sectors in a single command. However, in order to
;	to produce a more general example it is shown only reading one
;	sector at a time.
;
Sectors$Per$Write	EQU	1
;
;
;	Cold Boot Characteristics
;
Start$Track		EQU	0	;Initial values for CP/M image
Start$Sector		EQU	1	;= " =
Sectors$To$Write	EQU	(Length$Image + Sector$Size - 1) / Sector$Size
;

;
B$PRINTS	EQU	9	;Print string terminated by $
BDOS		EQU	5	;BDOS Entry Point
;
;

	ORG	100H
Put$CPM:
	JMP	Main$Code	;Enter main code body
				;For reasons of clarity, the main
				;  data structures are shown before the
				;  executable code.
CR	EQU	0DH		;Carriage Return
LF	EQU	0AH		;Line Feed
;
Signon$Message:
	DB	CR,LF,'Put CP/M on Diskette'
	DB	CR,LF
	DB	'Version '
	DW	Version
	DB	' '
	DW	Month
	DB	'/'
	DW	Day
	DB	'/'
	DW	Year
	DB	CR,LF,'$'

;
;	 Disk Control Tables
;
Disk$Control$5	EQU	45H	;5 1/4" Control Byte
Command$Block$5	EQU	46H	;Control Table Pointer
Disk$Status	EQU	43H	;Completion status
;
;
;	The command table Track and DMA$Address can also be used
;	as working storage and updated as the load process
;	continues. The Sector in the command table cannot be
;	used directly as the disk controller requires it to be
;	the sector number on the specified head (1 - 9) rather
;	than the sector number on track. Hence a separate variable
;	must be used.
;
Sector:		DB	Start$Sector
;
Command$Table:	DB	02H		;Command - Write
Unit:		DB	0		;Unit (Drive) number = 0 or 1
Head:		DB	0		;Head number = 0 or 1 
Track:		DB	Start$Track	;Used as working variable
Sector$on$head:	DB	0		;Converted by low-level driver
Byte$Count:	DW	Sector$Size * Sectors$Per$Write
DMA$Address:	DW	Start$Image
Next$Status:	DW	Disk$Status	;Pointer to next Status Block
					;  if commands are chained.
Next$Control:	DW	Disk$Control$5	;Pointer to next Control Byte
					;  if commands are chained.

Main$Code:
	LXI	SP,Put$CPM	;Stack grows down below code

	LXI	D,Signon$Message	;Sign on
	MVI	C,B$PRINTS		;Print string until $
	CALL	BDOS

	LXI	H,Command$Table		;Point the disk controller at
	SHLD	Command$Block$5		;  the command block

	MVI	C,Sectors$To$Write	;Set sector count
Write$Loop:
	CALL	Put$CPM$Write		;Write data onto diskette
	DCR	C			;Downdate sector count
	JZ	0			;Warm Boot

	LXI	H,Sector		;Update sector number
	MVI	A,Sectors$Per$Write	;  by adding on number of sectors
	ADD	M			;  by controller
	MOV	M,A			;Save result
	MVI	A,Last$Sector$On$Track + 1	;Check if at end of track
	CMP	M
	JNZ	Not$End$Track

	MVI	M,First$Sector$On$Track	;Yes, reset to beginning
	LHLD	Track			;Update Track number
	INX	H
	SHLD	Track

Not$End$Track:
	LHLD	DMA$Address		;Update DMA Address
	LXI	D,Sector$Size * Sectors$Per$Write
	DAD	D
	SHLD	DMA$Address
	JMP	Write$Loop		;Write next block
;
Put$CPM$Write:			;At this point, the description of the
				;  operation required is in the variables
				;  contained in the Command Table, along
				;  with the Sector variable.

	PUSH	B			;Save sector count in C

;------ Change this routine to match the disk controller in use ------

	MVI	B,0			;Assume head 0
	LDA	Sector			;Get requested sector
	MOV	C,A			;Take a copy of it
	CPI	Last$Sector$on$Head$0+1	;Check if on head 1
	JC	Head$0			;No
	SUI	Last$Sector$on$Head$0	;Bias down for head 1
	MOV	C,A			;Save copy
	INR	B			;Set head 1
Head$0:
	MOV	A,B			;Get head
	STA	Head
	MOV	A,C			;Get sector
	STA	Sector$On$Head

	LXI	H,Disk$Control$5	;Activate controller
	MVI	M,80H

Wait$For$Boot$Complete:
	MOV	A,M			;Get status byte		
	ORA	A			;Check if complete
	JNZ	Wait$For$Boot$Complete	;No
					;Yes, check for errors
	LDA	Disk$Status
	CPI	80H		
	JC 	Put$CPM$Error		;Yes, an error occurred

;------ End of Physical Write routine ------

	POP	B			;Recover sector count in C
	RET
;
Put$CPM$Error:
	LXI	D,Put$CPM$Error$Message
	MVI	C,B$PRINTS		;Print string until $
	CALL	BDOS			;Output error message
	JMP	Main$Code		;Restart the loader
;

Put$CPM$Error$Message:
	DB	CR,LF,'Error in writing CP/M - retrying...',CR,LF,'$'
	END	Put$CPM
