; INTEL HEX LOADER FOR SOLOS/CUTER
; BY MARTIN EBERHARD
;
; VERS   DATE      NOTE
;  01   8MAY11  CREATED, ALLOWS CUTER RELOCATION
;
VERSH	EQU	"0"	;HIGH BYTE OF VERSION
VERSL	EQU	"1"	;LOW BYTE OF VERSION
	
; THIS PROGRAM WORKS REGARDLESS OF WHERE SOLOS/CUTER IS LOCATED
; IN MEMORY. THIS DEPENDS ON SOLOS/CUTER PASSING ITS BASE ADDRESS
; TO US IN HL, AS IS NORMAL. THIS PROGRAM ALSO ASSUMES THAT SOLOS/
; CUTER CALLED THIS PROGRAM, SO THAT A RET INSTRUCTION WILL RETURN
; TO "RETRN".

;***SHOULD INSTALL CUSTOM COMMAND AND END...*************

; CUTER/SOLOS ENTRY POINTS, OFFSET FROM SOLOS BASE ADDRESS,
; WHICH IS PASSED TO US IN HL BY SOLOS/CUTER

INIT	EQU	01H	;COLD-START SOLOS
RETRN	EQU	04H	;RETURN CONTROL TO SOLOS
FOPEN	EQU	07H	;OPEN TAPE FILE
FCLOS	EQU	0AH	;CLOSE TAPE FILE
RDBYT	EQU	0DH	;READ TAPE BYTE
WRBYT	EQU	10H	;WRITE TAPE BYTE
RDBLK	EQU	13H	;READ TAPE BLOCK
WRBLK	EQU	16H	;WRITE TAPE BLOCK
SOUT	EQU	19H	;WRITE CHR IN B TO CURRENT PSEUDO PORT
AOUT	EQU	1CH	;WRITE CHR IN B TO PSEUDO PORT IN A
SINP	EQU	1FH	;READ CURRENT PSEUDO PORT INTO A
AINP	EQU	22H	;READ FROM PSEUDO PORT IN A INTO A

; CUTER/SOLOS PSEUDO PORT DEFINITIONS

KBDPP	EQU	0	;KEYBOARD PSEUDO PORT
VDMPP	EQU	0	;VDM PSEUDO PORT
SERPP	EQU	1	;SERIAL PSEUDO PORT
PARPP	EQU	2	;PARALLEL PSEUDO PORT
USRPP	EQU	3	;USER-WRITTEN ROUTINE PSEUDO PORT


CR	EQU	0DH	;ASCII CARRIAGE RETURN
LF	EQU	0AH	;ASCII LINE FEED

ORG	7F00H		;FITS IN 32K SYSTEM

;
; REGISTER USAGE:
; B: SCRATCH
; C: RECORD BYTE COUNT
; D: RECORD CHECKSUM
; E: RECORD TYPE
; HL: MEMORY ADDRESS
;

; BEGINNING OF CODE

HEXLD:	SHLD	SOLBAS		;SOLOS BASE ADDR WAS PASSED IN HL

	MVI	C,RDYMSG-MSBASE	;READY MESSAGE
	CALL	PRINTF

; WAIT FOR RECORD START

GETCOL:	CALL	GETCHR
	CPI	':'
	JNZ	GETCOL

; GET BYTE COUNT INTO C AND RESTART CHECKSUM

	CALL	GETBYT
	MOV	C,B
	MOV	D,B		;RESET CHECKSUM IN D

; GET ADDRESS INTO HL

	CALL	GETBYT
	MOV	H,B		;HIGH BYTE OF ADDRESS
	CALL	GETBYT
	MOV	L,B		;LOW BYTE OF ADDRESS

; GET RECORD TYPE IN E

	CALL	GETBYT
	MOV	E,B

; TEST FOR 0-BYTE RECORD, WHICH IS AN ALTERNATE EOF

	MOV	A,C		;CHECK FOR 0-BYTE RECORD
	ORA	A
	JNZ	BYTLUP

	INR	A
	ORA	E
	MOV	E,A		;REMEMBER EOF

	JMP	GCHKSM

; LOOP TO GET DATA INTO MEMORY

BYTLUP:	CALL	GETBYT
	MOV	M,B
	INX	H
	DCR	C
	JNZ	BYTLUP	

; GET AND CHECK RECORD CHECKSUM

GCHKSM:	CALL	GETBYT
	MVI	C,CERMSG-MSBASE
	JNZ	EXIT		;TEST CHECKSUM, ERROR IF BAD

; ALL DONE WITH A RECORD

	MVI	C,DONMSG-MSBASE	;DONE, NOT ERROR
	DCR	E		;RECORD TYPE 1 IS EOF
	JZ	EXIT

	INR	E		;TEST FOR DATA RECORD
	JZ	GETCOL		;YES: GO GET ANOTHER RECORD

; BAD RECORD TYPE

	MVI	C,BRTMSG-MSBASE

;	FALL INTO EXIT

; PRINT EXIT MESSAGE, THEN RETURN TO SOLOS
; THE STACK ALREADY HAS SOLOS WARM ENTRY POINT

EXIT:
;	FALL INTO PRINTF, RETURN TO SOLOS

; SUBROUTINE TO PRINT NULL-TERMINATED MESSAGE AT MSBASE + C TO VDM
; TRASHES BC	

PRINTF:	LXI	H,MSBASE
	MVI	B,0
	DAD	B		;HL POINTS TO MESSAGE STRING

MSGLUP:	MOV	A,M		;CHR TO PRINT GOES IN A
	ORA	A		;TEST FOR NULL TERMINATION
	RZ

	PUSH	H
	CALL	PRINTV
	POP	H
	INX	H
	JMP	MSGLUP	

; SUBROUTINE TO GET 2 HEX DIGITS AND COMBINE THEM INTO A BYTE
; AND ADD RESULT TO CHECKSUM
; ON ENTRY:
;   D = CHCKSUM SO FAR
; ON EXIT:
;   B=BYTE OF DATA
;   A=D=NEW CHECKSUM VALUE
;   Z FLAG SET IF CHECKSUM IS 0
;   ALL OTHER REGISTERS PRESERVED

GETBYT:	CALL	GETHEX
	RLC			;SHIFT HIGH NIBBLE IN PLACE
	RLC
	RLC
	RLC
	MOV	B,A
	CALL	GETHEX

	ORA	B		;COMBINE NIBBLES
	MOV	B,A		;SAVE RESULT FOR RETURN
	ADD	D		;COMPUTE CHECKSUM
	MOV	D,A
	RET

; SUBROUTINE TO GET A HEX DIGIT IN A

GETHEX:	CALL	GETCHR
	CPI	'9'+1		;IS CHR BELOW ASCII 9
	JC	GH1		;YES: GO DEAL WITH DIGIT

	CPI	'A'		;IS CHR BETWEEN 9 AND A?
	JC	AERROR		;YES: ERROR
	SUI	'A'-'9'-1	;NO: SUBTRACT OFFSET	

GH1:	SUI	'0'
	CPI	10H
	RC

AERROR:	POP	B		;REPAIR STACK: CALL GETHEX
	POP	B		;REPAIR: CALL GETBYT
	MVI	C,AERMSG-MSBASE
	JMP	EXIT		;PRINT ERROR MESSAGE & EXIT

; SUBROUTINE TO GET ONE CHR IN A, IGNORING NULLS. ECHO CHR TO VDM SCREEN
; ON ENTRY:
; ON EXIT:
;    CHR IS IN A
;    ALL REGISTERS PRESERVED
;    

GETCHR:	PUSH	H
	PUSH	B

GETCH2:	CALL	GETSER		;GO GET A CHR
GCRET:	JZ	GETCH2

	ANI	7FH		;STRIP PARITY
	JZ	GETCH2		;SKIP OVER NULLS

	CALL	PRINTV		;ECHO TO VDM SCREEN, PUT CHR INTO B
	MOV	A,B		;CHR WAS SAVED IN B

	POP	B
	POP	H
	RET

; SUBROUTINE TO PRINT CHR ON VDM SCREEN VIA SOLOS/CUTER
; THE BASE ADDRESS OF SOLOS/CUTER IS STORED AT SOLBAS
; ON ENTRY:
;    A=CHR TO PRINT
;    SOLBAS HAS BASE ADDRESS FOR SOLOS/CUTER
;    TOP OF STACK HAS RETURN ADDRESS
; ON EXIT:
;    B=PRINTED CHARACTER
;    A,HL TRASHED

PRINTV:	MOV	B,A		;VDM WANTS CHR IN B
	MVI	A,VDMPP		;SELECT VDM PSEUDO PORT
	PUSH	B
	LXI	B,AOUT		;COMPUTE AOUT BASE ADDRESS
	JMP	GETSR1		;RECYCLE SOME CODE

; SUBROUTINE TO GET A CHARACTER FROM THE SERIAL PORT VIA SOLOS/CUTER
; THE BASE ADDRESS OF SOLOS/CUTER IS STORED AT SOLBAS
; ON ENTRY:
;    SOLBAS HAS BASE ADDRESS FOR SOLOS/CUTER
;    TOP OF STACK HAS RETURN ADDRESS
; ON EXIT IF CHR READY:
;    A=CHR FROM SERIAL PORT
;    Z FLAG CLEARED
;    HL TRASHED
; ON EXIT IF CHR NOT READY:
;    Z FLAG SET
;    A,HL TRASHED

GETSER:	MVI	A,SERPP		;SELECT SERIAL PSEUDO PORT
	PUSH	B
	LXI	B,AINP		;COMPUTE AOUT BASE ADDRESS

GETSR1:	LHLD	SOLBAS
	DAD	B
	POP	B
	PCHL

; NULL-TERMINATED STRINGS
MSBASE:
RDYMSG:	DB	CR,LF,'HEXLD VERS ',VERSH,VERSL,CR,LF,0
BRTMSG:	DB	'? RTYPE',0
AERMSG:	DB	'? ASCII',0
CERMSG:	DB	'? CKSUM',0
DONMSG:	DB	CR,LF,'OK',0

; RAM STORAGE
SOLBAS	DW	0		;SOLOS BASE ADDRESS GOES HERE
	
	END
