;Install - Data Technology Corporation CP/M 2.2 Install.
;
;	+-----------------------+
;	|			|
;	|     I N S T A L L 	|
;	|			|
;	+-----------------------+
;
;
;	Version number:	2.2B
;	Version date:	December 3, 1980
;
;	Update date:	March 31, 1981
;		Source modified for assembly with ASM.
;
;	Update date:	April 27, 1981
;		Source modified for controller timeout.

;	Update date:	June 16, 1981
;		Source modified for new CONFIG parameters.
;
;	The following code is supplied to customers who
;	purchase a hard/floppy disk system from DTC.
;	The following code reads the user created CPMxx.com
;	file and writes out onto the Hard disk boot sectors.
;
;	The format of the Hard Disk Boot sectors are as follows:
;
;	Logical		Routine
;	Sector		Name
;
;	  0		Boot program
;
;	1 thru 8	CCP
;
;	9 thru 22	BDOS
;
;	23 thru 32	DTC CBIOS
;
;	The format of the CPMxx.com file is as follows:
;
;	CP/M		Routine
;	Sector		Name
;
;	0 thru 14	Do not care.
;
;	  15,16		Boot program
;
;	17 thru 32	CCP
;
;	33 thru 60	BDOS
;
;	61 thru 88	DTC CBIOS
	




VERS:	EQU	22

CR:	EQU	0Dh		;ASCII carrriage return
LF:	EQU	0Ah		;ASCII line feed
TAB:	EQU	9		;ASCII HORIZONTAL TAB
EOS:	EQU	'$'		;BDOS End of string
ERRCD:	EQU	0FFh		;BDOS error code



;	BDOS function equates.

PRTSTR:	EQU	09	;Print String    DE = buffer address.
RDCB:	EQU	10	;Read console buffer   DE = buffer address.
OPEN:	EQU	15	;Open file   DE = FCB address.
READS:	EQU	20	;Read sequential file    DE = FCB.
SETDMA:	EQU	26	;Set DMA address    DE = DMA address.

;	Page zero locations.

BDOSV:	EQU	5	;BDOS jump address.
DFCB:	EQU	005Ch	;Default FCB address.
DBUF:	EQU	0080h	;Default DMA buffer.



	




	ORG	100h

INSTAL:
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	LXI	H,0
	DAD	SP
	SHLD	SYSTK		;save system stack


	LXI	SP,STACK
	CALL	GFILEN		;Get file name
	CALL	SKIPF		;Skip do not care sectors
	CALL	RBOOT		;Read Boot
	CALL	RCPM		;Read CP/M
	CALL	RBIOS		;Read BIOS
	CALL	WOUT		;Write out onto hard disk

	LXI	D,ENDMSG
	MVI	C,PRTSTR
	CALL	BDOSV		;Output successful message

;	Return to system gracefully.

SYSRET:	LHLD	SYSTK
	SPHL
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET			;return to system

ENDMSG:	DB	CR,LF,'Disk Boot Successfully Updated.'
	DB	CR,LF,EOS




;	GFILEN - Get file name and opent the file.
;
;	ENTRY	loaction 5C = filename.

GFILEN:
	LXI	H,5Ch
	LXI	D,FCB
	LXI	B,12
	CALL	MOVDTA		;Move file name to FCB

	LXI	D,FCB
	MVI	C,OPEN
	CALL	BDOSV		;Open the file
	CPI	ERRCD
	RNZ			;If no error

	LXI	D,OPNERR
	MVI	C,PRTSTR
	CALL	BDOSV
	JMP	SYSRET

OPNERR:	DB	CR,LF,'Error on file open.',CR,LF,EOS



;	SKIPF - Skip meaningless sectors on file.

SKIPF:
	LXI	D,DBUF
	MVI	C,SETDMA
	CALL	BDOSV		;Set DMA address to default buffer

	MVI	A,15		;Skip 15 sectors
SKPF1:	PUSH	PSW
	LXI	D,FCB
	MVI	C,READS
	CALL	BDOSV		;Read a sector
	CPI	ERRCD
	JZ	SKPF2		;If error on read
	POP	PSW
	DCR	A
	JNZ	SKPF1		;If 16 sectors not read
	RET

SKPF2:	LXI	D,SKERR
	MVI	C,PRTSTR
	CALL	BDOSV		;Output error message
	JMP	SYSRET

SKERR:	DB	CR,LF,'File read error sectors 0 thru 15.'
	DB	CR,LF,EOS


;	RBOOT - Read the boot sector.

RBOOT:
	LXI	H,BUFFER
	LXI	D,BUFFER+1
	LXI	B,255
	XRA	A
	MOV	M,A
	CALL	MOVDTA		;Clear 256 bytes of buffer

	LXI	D,BUFFER
	MVI	A,2
RBOOT1:
	PUSH	PSW
	PUSH	D
	MVI	C,SETDMA
	CALL	BDOSV		;Set DMA address

	LXI	D,FCB
	MVI	C,READS
	CALL	BDOSV		;Read the boot sector
	CPI	ERRCD
	JZ	RBOOT2
	POP	D
	LXI	H,128
	DAD	D
	XCHG
	POP	PSW
	DCR	A
	JNZ	RBOOT1
	RET

RBOOT2:
	LXI	D,BTERR
	MVI	C,PRTSTR
	CALL	BDOSV		;Output error message
	JMP	SYSRET

BTERR:	DB	CR,LF,'File read error on boot sector.'
	DB	CR,LF,EOS




;	RCPM - Read CPM sectors.

RCPM:
	MVI	A,22*2
	LXI	D,BUFFER+256
RCPM1:	PUSH	PSW
	PUSH	D		;Save buffer address
	MVI	C,SETDMA
	CALL	BDOSV		;Set DMA address to default buffer

	LXI	D,FCB
	MVI	C,READS
	CALL	BDOSV		;Read a sector
	CPI	ERRCD
	JZ	RCPM2		;If error on read
	POP	D
	LXI	H,128
	DAD	D
	XCHG
	POP	PSW
	DCR	A
	JNZ	RCPM1		;If 22*2 sectors not read
	RET

RCPM2:	LXI	D,RCERR
	MVI	C,PRTSTR
	CALL	BDOSV		;Output error message
	JMP	SYSRET

RCERR:	DB	CR,LF,'File read error in CPM sectors (17 thru 60).'
	DB	CR,LF,EOS
	RET




;	RBIOS - Read BIOS sectors.

RBIOS:
	LXI	H,BUFFER+22*256+256
	LXI	D,BUFFER+22*256+256+1
	LXI	B,255
	XRA	A
	MOV	M,A
	CALL	MOVDTA		;Clear the rest of the buffer

	LXI	D,BUFFER+22*256+256
RBIOS1:	PUSH	D		;Save buffer address
	MVI	C,SETDMA
	CALL	BDOSV		;Set DMA address to default buffer

	LXI	D,FCB
	MVI	C,READS
	CALL	BDOSV		;Read a sector
	POP	D
	LXI	H,128
	DAD	D
	XCHG
	ORA	A
	JZ	RBIOS1		;If no errors

	RET




;	WOUT - Write out boot sectors onto hard disk.

WOUT:
	LXI	D,DMESSG	;Output disk selection message
	CALL	INFCRT
	SUI	'A'
	JM	WOUT		;If invalid entry
	MOV	B,A		;Save CP/M drive #
	XRA	A		;Determine LUN
	LXI	H,CONTBL
	LXI	D,CONELN
	MVI	C,CONLEN
WOUT1:
	CMP	B
	JP	WOUT2
	ADD	M
	DAD	D
	DCR	C
	JP	WOUT1
	LXI	D,INVMSG	;Invalid drive select
	MVI	C,PRTSTR
	CALL	BDOSV
	JMP	WOUT
WOUT2:
	JZ	WOUT3		;This table?
	LXI	D,-CONELN	;No. Went too far.
	DAD	D		;HL = table entry address
	SUB	M
WOUT3:
	MOV	C,A		;Compute relative logical disk
	INX	H		;Get LUNiTYPE
	MOV	A,M
	STA	TYPE
	MOV	D,A
	ANI	TYPEDRV		;Floppy?
	JZ	WOUT4
	MOV	A,D
	ANI	TYPEN48+TYPEN96
	MVI	A,0
	JNZ	WOUT4
	MOV	A,B
	SUB	C
WOUT4:
	MOV	C,A
	INX	H		;Set drive type
	MOV	A,M
	STA	CIOADT+4
	INX	H		;Set track format code
	MOV	A,M
	STA	CIOFS+5
	INX	H		;Set LUN
	MOV	A,M
	STA	CIOESC+1
	STA	CIOPB+1
	STA	CIOADT+1
	STA	CIOFS+1
	INX	H		;Set # sectors
	MOV	A,M
	STA	LOGNSEC
	INX	H		;Compute logical address
	MOV	E,M
	INX	H
	MOV	D,M
	CALL	MUL
	XCHG
	LXI	H,CIOPB+1
	MOV	A,M
	ADD	B
	MOV	M,A
	INX	H
	MOV	M,D
	INX	H
	MOV	M,E
	LDA	CIOADT+4	;Does controller have Class 6,
	CPI	0FFh		;  op code?
	JZ	WOUTA
	LXI	H,CIOADT
	CALL	EXEC
	CZ	WAITF
	MOV	A,C
	ANI	FERR
	JZ	WOUT5
	LXI	D,ADTMSG
	MVI	C,PRTSTR
	CALL	BDOSV
	JMP	SYSRET
WOUT5:
	LDA	CIOFS+5		;Floppy?
	CPI	0FFh
	JZ	WOUTA
	LXI	H,CIOFS
	CALL	EXEC		; diskettes
	CZ	WAITF
	MOV	A,C
	ANI	FERR
	JZ	WOUTA
	LXI	H,CIOFS
	CALL	ERROR
	JMP	SYSRET
WOUTA:

	LXI	H,CIOPB		;Set Command buffer address
	LXI	D,BUFFER	;Set data buffer address
	CALL	WDISK		;Write the data
	MOV	A,C
	ANI	FERR
	RZ
	LXI	H,CIOPB		;Report errors
	CALL	ERROR
	JMP	WOUT

TYPE:	DS	1
CIOESC:	DB	ESCMD,0,0,0,0,0
CIOADT:	DB	ADCMD,0,0,0,0,0
CIOFS:	DB	FSCMD,0,0,0,0,0
CIOPB:	DB	WTCMD
	DB	0
LOGSEC:	DB	0,0			;Write logical sector 0
LOGNSEC:
	DB	0		;Write two or three tracks
	DB	0			;Perform no retries

DMESSG:	DB	CR,LF
	DB	'Select Drive : ',EOS
INVMSG:	DB	CR,LF,'Invalid drive selection.',EOS
ADTMSG:	DB	CR,LF,'Drive Assignment error.',EOS

;	Configuration table
;
CONTBL:
	IF	LUN0
	DB	LUN0NLD
	DB	LUN0TYPE+TYPEN48*N48M0+TYPEN96*N96M0
	DB	LUN0DAT,B0
	DB	0 SHL 5
	DB	2*26*(NH0+NF0)+3*16*(N48M0+N96M0)
	DW	LUN0SEC
	ENDIF
	IF	LUN1
	DB	LUN1NLD
	DB	LUN1TYPE+TYPEN48*N48M1+TYPEN96+N96M1
	DB	LUN1DAT,B1
	DB	1 SHL 5
	DB	2*26*(NH1+NF1)+3*16*(N48M1+N96M1)
	DW	LUN1SEC
	ENDIF
	IF	LUN2
	DB	LUN2NLD
	DB	LUN2TYPE+TYPEN48*N48M2+TYPEN96*N96M2
	DB	LUN2DAT,B2
	DB	2 SHL 5
	DB	2*26*(NH2+NF2)+3*16*(N48M2+N96M2)
	DW	LUN2SEC
	ENDIF
	IF	LUN3
	DB	LUN3NLD
	DB	LUN3TYPE+TYPEN48*N48M3+TYPEN96*N96M3
	DB	LUN3DAT,B3
	DB	3 SHL 5
	DB	2*26*(NH3+NF3)+3*16*(N48M3+N96M3)
	DW	LUN3SEC
	ENDIF
CONELN:	EQU	8		;Entry length
CONLEN:	EQU	($-CONTBL)/CONELN



;	INFCRT - Output message and input from console.
;
;	ENTRY	DE = message address.
;
;	EXIT	A = First character entered (upper case).

INFCRT:
	MVI	C,PRTSTR
	CALL	BDOSV		;Output message
	LXI	D,INBUFX
	MVI	C,RDCB
	CALL	BDOSV
	LDA	INBUFX+1
	ANA	A
	MVI	A,CR
	RZ
	LDA	INBUF
	CPI	'A'+20h
	RC			;If upper case
	CPI	'Z'+20h+1
	RNC			;If upper case
	SUI	20h		;Fold to uppercase
	RET

INBUFX	DB	10,0
INBUF	DB	0,0,0,0,0,0,0,0,0,0






;	MOVDTA - Move data utility program.
;
;	ENTRY	HL = Source field.
;		DE = Destination field.
;		BC = number of bytes.

MOVDTA:
	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCX	B
	MOV	A,B
	ANA	C
	CPI	0FFh		;-1
	JNZ	MOVDTA
	RET

;	Multiply single X double
;
;	Entry:	DE = Multiplicand
;		 C = Multiplier
;	Exit:	HL = Product (least significant)
;		 B = Product (most significant)
;
MUL:
	LXI	H,0
	MVI	B,0
	MOV	A,C
	ORA	A
	RZ
MUL1:
	DAD	D
	JNC	MUL2
	INR	B
MUL2:
	DCR	A
	JNZ	MUL1
	RET




SYSTK:	DW	0		;Hold stack

FCB:	DS	12		;file name
	DB	0,0,0,0,0
	DB	0,0,0,0,0
	DB	0,0,0,0,0
	DB	0,0,0,0,0
	DB	0,0,0,0



	DS	50
STACK:	DS	1




;	Disk I/O Routines
;
;
	IF	I696
;	E X E C

EXEC:	MVI	B,BUSY		;Wait for not busy.
	MVI	C,BUSY and (not BUSY)
	CALL	WAITM
	RNZ
	

	MVI	A,SLCT		;Alert controller
	OUT	DIO+1
EXEC1:
	MOV	C,B		;Wait for controller busy
	CALL	WAITM
	RNZ

	MVI	A,DODTA		;Enable data in
	OUT	DIO+1

EXEC2:	IN	DIO+2		;Get status
	XRI	0FFh
	JM	EXEC2		;If not requesting next byte
	ANI	CMND+DIROUT
	JNZ	EXEC3		;If CMND or DIROUT false
	MOV	A,M
	INX	H
	OUT	DIO		;Send byte from command buffer
	JMP	EXEC2

EXEC3:	CMP	A		;Z:=1
	RET
;
;
;
;
;	WDISK - Output from memory buffer.
;	ENTRY:	HL = COMMAND BUFFER ADDRESS
;		DE = DATA BUFFER ADDRESS
;

WDISK:	CALL	EXEC		;Output command
	RNZ			;Return if timeout
WDISK1:	IN	DIO+2		;Read status
	ORA	A
	JP	WDISK1		;If request is present
	ANI	CMND
	JNZ	GCMPS		;If done with transfer
	LDAX	D		;Get the data byte
	OUT	DIO
	INX	D		;Advance buffer address
	JMP	WDISK1
;
;
;
;
;	RDISK - Input to memory buffer.
;
;	Entry:	HL = command buffer address
;		DE = data buffer address

RDISK:	CALL	EXEC
	RNZ			;Return if timeout
RDISK1:	IN	DIO+2		;Read status
	ORA	A
	JP	RDISK1		;If request is present
	ANI	CMND
	JNZ	GCMPS
	IN	DIO
	STAX	D
	INX	D
	JMP	RDISK1
;
;
;
;
;	WAITF - Wait for function to complete.

WAITF:	MVI	B,REQ+CMND	;Wait for both REQ and CMND
	MOV	C,B
	CALL	WAITM
	RNZ
;
;	Get completion status.

GCMPS:	IN	DIO		;Get completion status
	MOV	C,A

GCMP1:	IN	DIO+2
	ORA	A
	JP	GCMP1		;If REQ not set

	MOV	B,A
	IN	DIO		;Get message byte
	RET
	ENDIF
;

;
;
;
	IF	I796
;	EXEC - Output the command
;
;	Enter:	HL is the command buffer address
;		DE - data transfer address.

EXEC:
	MOV	A,E		;Output DMA address
	OUT	DIO+2
	MOV	A,D
	OUT	DIO+3
	MOV	A,L
	OUT	DIO+4
	MOV	A,H
	OUT	DIO+5
	MVI	A,0
	OUT	DIO+6
	OUT	DIO+7
	OUT	DIO
	CMP	A		;Z:=1
	RET


;	Disk read/write
;
;	Entry:	same as EXEC
;
RDISK:
WDISK:	CALL	EXEC
	RNZ			;Return if timeout

;	WAITF - Wait until transfer done
;
;	Enter:	none
;	Exit:	when transfer completed

WAITF:	MVI	B,CMDDON	;Wait for CMDDON
	MOV	C,B
	CALL	WAITM
	RNZ			;Return if timeout
;

;	GCMPS - Get completion status
;
;	Enter:	none
;	Exit:	Status in C
GCMPS:	IN	DIO+1
	MOV	C,A
	RET
	ENDIF

;	WAITM - Wait for controller with timeout
;
;	Entry:	B=Status mask
;		C=Status value
;	Exit:	Z=1 if OK, else timeout with A=C=TERR
;
WAITM:
	PUSH	D		;Save D
	PUSH	H
	LXI	H,138		;Two minute timeout
	LXI	D,0		;Max wait @4MHZ is 868 ms
WAITML:
	IF	I696
	IN	DIO+2
	ENDIF
	IF	I796
	IN	DIO
	ENDIF
	ANA	B		;Mask wait bits
	CMP	C		;Check value
	JZ	WAITM1
	DCX	D		;Not ready.  Decrement time
	MOV	A,D
	ORA	E
	JNZ	WAITML
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	WAITML
	MVI	B,0		;Timeout
	MVI	A,TERR
	ORA	A
WAITM1:
	POP	H
	POP	D		;Restore D
	MOV	C,A		;Return status in C
	RET
;	DTC Error Print Routine
;
;Called at completion of disk command when error status is returned.
;
;	Entry:	HL = Address of Command Descriptor Block
;		C = Status byte
;
ERROR:
	PUSH	B		;Save status
	PUSH	H		;SAVE ADDRESS OF CDB
	MOV	A,M		;GET CLASS CODE
	ANI	0E0H
	RAL			;MAKE CDB LENGTH INDEX
	RAL
	RAL
	MOV	E,A		;GET CDB LENGTH
	MVI	D,0
	LXI	H,CDBLEN
	DAD	D
	MOV	C,M
	POP	H		;RESTORE CDB ADDRESS
	PUSH	H
	LXI	D,ERRCDB
	CALL	PUTHEX		;BUILD CDB FOR PRINT
	LXI	D,EHEAD		;Print header
	MVI	C,PRTSTR
	CALL	BDOSV
	POP	H		;Get status
	POP	B
	PUSH	H
	MOV	A,C		;Timeout?
	ANI	TERR
	LXI	D,TOMSG
	JNZ	ERROR1
	LXI	H,CIOESC	;No. READ ERROR SENSE
	LXI	D,SENSE
	CALL	RDISK
	LXI	D,ESENSE
	MVI	C,PRTSTR
	CALL	BDOSV
	LXI	D,ESENS1
	LXI	H,SENSE
	MOV	A,M
	MVI	C,PRTSTR
	ORA	A
	CM	BDOSV
	LXI	D,ETYPE
	LXI	H,SENSE		;BUILD ERROR SENSE MESSAGE
	MOV	A,M
	RAR
	RAR
	RAR
	RAR
	ANI	3
	CALL	HEXASC
	LXI	D,ECODE
	MOV	A,M
	CALL	HEXASC
	LXI	D,ELUN
	INX	H
	MOV	A,M
	RLC
	RLC
	RLC
	ANI	7
	CALL	HEXASC
	MOV	A,M
	ANI	01FH
	MOV	M,A
	LXI	D,ELAD
	MVI	C,3
	CALL	PUTHEX
	LXI	D,ESENS2	;PRINT MESSAGE
ERROR1:
	MVI	C,PRTSTR
	CALL	BDOSV
	POP	H		;RESTORE CDB ADDRESS
	RET
;
;

;	PUT HEXADECIMAL STRING
;
;	ENTRY:	HL = ADDRESS OF HEX NUMBER STRING
;		DE = ADDRESS OF HEX ASCII STRING
;		 C = NUMBER OF BYTES TO CONVERT
;
PUTHEX:	CALL	HEXBYT
	MVI	A,' '
	STAX	D
	INX	D
	DCR	C
	JNZ	PUTHEX
	MVI	A,EOS
	STAX	D
	RET
;
;
HEXBYT:	MOV	A,M
	RAR
	RAR
	RAR
	RAR
	CALL	HEXASC
	MOV	A,M
	INX	H
HEXASC:	ANI	0FH
	ADI	090H
	DAA
	ACI	040H
	DAA
	STAX	D
	INX	D
	RET
;
;
;	CDB length table (indexed by class)
CDBLEN:	DB	6,10,0,0,0,0,6,6
;
EHEAD:	DB	CR,LF,LF,'Disk error:'
	DB	CR,LF,'Command Descriptor:',TAB,TAB
ERRCDB:	DS	31
;
TOMSG:	DB	CR,LF,TAB,TAB,TAB,TAB,'Timeout',EOS
ESENSE:	DB	CR,LF,'Error Sense:',EOS
ESENS1:	DB	CR,LF,TAB,TAB,TAB,TAB,'Block address valid.',EOS
ESENS2:	DB	CR,LF,TAB,'Error type:',TAB,TAB
ETYPE:	DS	1
	DB	CR,LF,TAB,'Error code:',TAB,TAB
ECODE:	DS	1
	DB	CR,LF,TAB,'Logical unit:',TAB,TAB
ELUN:	DS	1
	DB	CR,LF,TAB,'Logical address:',TAB
ELAD:	DS	4
SENSE:	DS	4
;
BUFFER:	DS	9*1024+256
;
	END
