;>>>The following is retyped from a MITS printout titled HDLOADER<<<
;>>>Adapted for assembly by Digital Research's ASM<<<
;>>>Comments with lower case are added by me<<<
;>>>I think this is a very early version of the loader PROM<<<<

;************************************************************
;*      THIS IS THE LOADER FOR THE HARD DIKS. IT READS      *
;*      THE OPSYS IMAGE FROM THE REMOVABLE PLATTER AND      *
;*      LOADS IT INTO MEMORY STATING AT 0.                  *
;************************************************************

HDLDR	EQU	0FC00H		;HDLDR PROM ADDRESS

DSKCHN	EQU	160		;FIRST DISK CHANNEL
DFLTDR	EQU	0		;DEFAULT DISK DRIVE
;				;NOTE THAT THE DRIVE NUMBER
;				;MUST BE MULTIPLIED BY 4
;				;IN THIS EQUATE, SINCE THE ASSEMBLER
;				;IS STUPID
DFLTBF	EQU	0		;CONTROLLER BUFFER
OSOFF	EQU	40		;OFFSET TO OPSYS PTRS
SEEK	EQU	0		;SEEK COMMAND
WRSEC	EQU	32		;WRITE SECTOR
RDSEC	EQU	48		;READ SECTOR
WRBUF	EQU	64		;WRITE BUFFER
RDBUF	EQU	80		;READ BUFFER
STAT	EQU	96		;STATUS

	ORG	HDLDR

;*
;**	INITIALIZE
;*
HDLDR:	XRA	A		;OPEN...
	OUT	DSKCHN		;...
	OUT	DSKCHN+2	;...
	OUT	DSKCHN+4	;...
	OUT	DSKCHN+6	;...DDRS
	OUT	DSKCHN+1	;SETUP...
	OUT	DSKCHN+5	;...
	CMA			;...
	OUT	DSKCHN+3	;...
	OUT	DSKCHN+7	;...DDRS
	MVI	A,44		;CLOSE... (2Ch)
	OUT	DSKCHN		;...
	OUT	DSKCHN+4	;...
	OUT	DSKCHN+6	;...
	MVI	A,36		;... (24h)
	OUT	DSKCHN+2	;...DDRS
;*
;**	FIND A STACK
;*
	LXI	H,0		;GET START ADDRESS
;	LXI	H,DUMSTK	;$$$$
;	JMP	ENDMEM		;$$$$

MEMLP:	MOV	A,M		;GET A BYTE
	MOV	B,A		;SAVE IT
	CMA			;INVERT IT
	MOV	M,A		;PUT IT BACK
	CMP	M		;WRITTEN RIGHT ?
	JNZ	ENDMEM		;NO = AT END
	MOV	M,B		;PUT BACK ORIGINAL
	INX	H		;MOVE POINTER
	JMP	MEMLP		;TRY NEXT ONE

ENDMEM:	SPHL			;WE HAVE A STACK

;*
;**	READ PACK DESCRIPTOR PAGE TO GET OPSYS ADDR
;*
	LXI	H,0		;DESCRIPTOR IS PAGE 0
	CALL	GETPAG		;GET IT TO CONTROLLER BUFFER
	MVI	B,OSOFF		;GET OFFSET TO OPSYS PTRS
				;(NOTE THAT THE OFFSET MUST BE >0)

OFFLP:	CALL	GETBYT		;SKIP BYTE
	DCR	B		;AT RIGHT PLACE?
	JNZ	OFFLP		;NO = CONTINUE
	CALL	GETBYT		;GET...
	MOV	L,A		;...
	CALL	GETBYT		;...
	MOV	H,A		;...OPSYS PAGE ADDR
	CALL	GETBYT		;GET...
	MOV	E,A		;...
	CALL	GETBYT		;...
	MOV	D,A		;...PAGE COUNT
	PUSH	D		;SAVE IT

	MVI	B,256-OSOFF-4
EATLP:	CALL	GETBYT
	DCR	B
	JNZ	EATLP
;*
;**	START LOADING IMAGE
;**	HL = PAGE ADDR, DE=MEM ADDR, TOP OF STACK=PAGE COUNT
;*
	LXI	D,0		;SET MEM ADDR

PAGELP:	CALL	GETPAG		;READ PAGE INTO BUFFER
	MVI	B,0		;SET BYTE COIUNTER

BYTELP:	CALL	GETBYT		;SAVE...
	STAX	D		;...NEXT BYTE
	INX	D		;MOVE MEM PTR
	DCR	B		;END OF PAGE ?
	JNZ	BYTELP		;NO = CONTINUE
	INX	H		;INCREMENT PAGE NUMBER
	XTHL			;SAVE IT, GET PAGE CNT
	DCX	H		;DEC PAGE CNT
	MOV	A,H		;AT...
	ORA	L		;...END ?
	JZ	LOADND		;YES = DONE
	XTHL			;PUT BACK CNT, GET PAGE PTR
	JMP	PAGELP		;GET NEXT PAGE

;This writes 0 to port 0FDh, and then executes the loaded
;code as quickly as possible immediately after the port write.
;(why?)

LOADND:	POP	H		;CLEAR STACK
	LXI	H,0		;PUT START ADDR...
	PUSH	H		;...ONTO STACK
	DAD	SP		;GET ADDR OF WHERE TO PUT...
	DCX	H		;...TURNOFF PROGRAM
	MVI	M,0C9H		;PUT IN RETURN
	DCX	H		;PUT IN...
	MVI	M,0FDH		;...DEV ADDR
	DCX	H		;PUT IN...
	MVI	M,0D3H		;...OUTPUT

	PCHL			;TURN IT OFF

;*
;**	GETPAG	-- GET PAGE FROM DISK
;*
GETPAG:	PUSH	H		;SAVE...
	PUSH	D		;..
	PUSH	B		;...REGS

;Compute CYL, Sector, Head from page number

	MVI	B,48		;GET SECTORS/CYL
	LXI	D,16		;

DIV1:	DAD	H
	MOV	A,D
	RAL
	CMP	B
	JC	DIV2
	SUB	B
	INR	L

DIV2:	MOV	D,A
	DCR	E
	JNZ	DIV1

;*	HL=QUOTIENT (CYL), A=REMAINDER (SECTOR & HEAD)


	MOV	B,A		;SAVE SECTOR

;Seek Cyl in HL

	CALL	DSKWT		;WAIT FOR CONTROLLER
	MOV	A,L		;SEND...
	OUT	DSKCHN+7	;...LOW ORDER PART OF CYL
	MOV	A,H		;GET HIGH ORDER PART OF TRACK
	ORI	DFLTDR+SEEK	;ADD ON DRIVE AND SEEEK COMMAND
	OUT	DSKCHN+3	;START SEEK
	CALL	DSKWT		;WAIT FOR IT

;check seek status (but don't do anything with an error)

	MVI	A,21		;SELECT...<<<<<<<this is not in HD-TBL<<<<<<<
	OUT	DSKCHN+7	;...
	MVI	A,STAT+DFLTDR	;...
	OUT	DSKCHN+3	;...SEEK STATUS
	CALL	DSKIWT		;WAIT FOR IT
	IN	DSKCHN+5	;GET ERROR CODE
	ANI	16		;MASK OFF ALL BUT ERROR BIT
	ORA	A		;ERROR ?<<<<<<<<<<<<<<to here<<<<<<<<<<<<<<<<
;*
;**	CHECK FOR SEEK ERROR WOULD GO HERE
;*

;Compute head and sector from B

	MVI	C,0		;GET HEAD
	MOV	A,B		;OUR SECTOR...
	CPI	24		;...ON HEAD 1
	JC	DOREAD		;NO = READY
	SBI	24		;ADJUST SECTOR
	MVI	C,32		;GET HEAD BITS
;*	A=SECTOR, C=HEAD*32

;Read sector into controller buffer

DOREAD:	ADD	C		;GET...
	MOV	B,A		;...SECTOR COMMAND
	CALL	DSKWT		;WAIT ON CONTROLLER
	MOV	A,B		;SEND...
	OUT	DSKCHN+7	;...COMMAND
	MVI	A,RDSEC+DFLTBF+DFLTDR	;GET SECTOR READ
	OUT	DSKCHN+3	;;SEND COMMAND
	CALL	DSKWT

;Issue read buffer command

	XRA 	A		;SEND...
	OUT	DSKCHN+7	;...READ COUNT
	MVI	A,RDBUF		;SEND...
	OUT	DSKCHN+3	;...READ BUFFER COMMAND
	CALL	DSKIWT
	POP	B		;RESTORE...
	POP	D		;...
	POP	H		;...REGS
	RET

;*
;**	GET BYTE FROM CONTROLLER BUFFER
;*
GETBYT:	IN	DSKCHN+5	;GET BYTE
	RET

;*
;**	WAIT FOR CONTROLLER ACK
;*
DSKWT:	IN	DSKCHN		;GET STATUS
	RLC			;READY ?
	JNC	DSKWT		;NO = KEEP WAITING
	IN	DSKCHN+1
	IN	DSKCHN+3
	IN	DSKCHN+5
	RET

;*
;**	WAIT FOR CONTROLLER INPUT
;*
DSKIWT:	IN	DSKCHN+4
	RLC
	JNC	DSKIWT
	RET

;	DS	20		;$$$$
;DUMSTK	EQU	*		;$$$$

	END
	

