;***************************************************************
; INTEL HEX LOADER FOR SOLOS/CUTER
; BY MARTIN EBERHARD
;
; VERS   DATE      NOTE
;  01   8MAY2011  CREATED, ALLOWS CUTER RELOCATION
;  02   8MAY2011  FIXED LOCATION FOR SOL20, RUNS IN USER RAM
;                 INSTALLS CUSTOM COMMAND 'HL'
;  03  12MAY2011  CHECK FOR CTRL-C WHILE WAITING FOR CHR
;
; TO DO:
; + CHECK FOR HL COMMAND ALREADY IN TABLE
;***************************************************************

VERSH	EQU	'0'	;HIGH BYTE OF VERSION
VERSL	EQU	'3'	;LOW BYTE OF VERSION
	
;***************************************************************
; THIS PROGRAM ASSUMES SOLOS/CUTER RUNS AT ITS NORMAL LOCATION,
; C000H. THIS PROGRAM ALSO ASSUMES THAT SOLOS/CUTER CALLED THIS
; PROGRAM, SO THAT A RET INSTRUCTION WILL RETURN TO "RETRN".

; THE 'EXECUTE' ADDRESS FOR THIS PROGRAM SHOULD BE THE SAME AS
; THE LOAD ADDRESS. THIS WILL INSTALL CUSTOM COMMAND 'HL' INTO
; SOLOS/CUTER. IF SOLOS/CUTER'S CUSTOM COMMAND TABLE IS FULL,
; THE LAST ENTRY WILL BE REPLACED WITH 'HL'.
;
; TYPING CONTROL-C WILL ABORT THE HEX LOAD.
;***************************************************************

SOLOS	EQU	0C000H	;SOLOS BASE ADDRESS

; CUTER/SOLOS ENTRY POINTS, OFFSET FROM SOLOS BASE ADDRESS

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

; LOCATIONS IN SOLOS/CUTER 1K RAM BLOCK

CUTAB	EQU	083CH	;CUSTOM CMD TABLE, OFFSET FROM SOLOS
MAXCU	EQU	6	;MAX # OF CUSTOM COMMAND ENTRIES
USARE	EQU	0AB4H	;USER AREA IN RAM, OFFSET FROM SOLOS

; 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

;ASCII EQUATES

CTRLC	EQU	03H	;CONTROL-C
CR	EQU	0DH	;ASCII CARRIAGE RETURN
LF	EQU	0AH	;ASCII LINE FEED

;***************************************************************
ORG	SOLOS+USARE	;RUNS IN SOLOS/CUTER RAM USER AREA

  JMP INSTL		;SO THAT GO ADDRESS = LOAD ADDRESS
;***************************************************************
;ENTRY POINT FOR ACTUAL HEX LOAD
;
; REGISTER USAGE:
; B: SCRATCH
; C: RECORD BYTE COUNT
; D: RECORD CHECKSUM
; E: RECORD TYPE
; HL: MEMORY ADDRESS
;***************************************************************
HEXLD:	MVI	C,RDYMS-MSBAS	;READY MESSAGE
	CALL	PRINT

; EAT ALL CHRS UNTIL WE GET RECORD START COLON

GETCO:	CALL	GETCH
	CPI	':'
	JNZ	GETCO

; GET BYTE COUNT INTO C AND RESTART CHECKSUM

	CALL	GETBY
	MOV	C,B
	MOV	D,B		;RESET CHECKSUM IN D

; GET ADDRESS INTO HL

	CALL	GETBY
	MOV	H,B		;HIGH BYTE OF ADDRESS
	CALL	GETBY
	MOV	L,B		;LOW BYTE OF ADDRESS

; GET RECORD TYPE IN E

	CALL	GETBY
	MOV	E,B

; TEST FOR 0-BYTE RECORD, WHICH IS AN ALTERNATE EOF

	MOV	A,C		;CHECK FOR 0-BYTE RECORD
	ORA	A
	JNZ	BYTLP

	INR	A
	ORA	E		;ANY TYPE BUT 0 OR 1 IS ERROR
	MOV	E,A		;REMEMBER EOF

	JMP	GCKSM

; LOOP TO GET DATA INTO MEMORY

BYTLP:	CALL	GETBY
	MOV	M,B
	INX	H
	DCR	C
	JNZ	BYTLP	

; GET AND CHECK RECORD CHECKSUM. C=0 HERE, WHICH POINTS TO
; CHECKSUM ERROR MESSAGE

GCKSM:	CALL	GETBY
	JNZ	PRINT		;NZ MEANS CHECKSUM ERROR

; ALL DONE WITH A RECORD - CHECK RECORD TYPE
	MVI	C,DONMS-MSBAS	;DONE MESSAGE, NOT ERROR
	DCR	E		;RECORD TYPE 1 IS EOF
	JZ	PRINT		;DONE MSG THEN RETURN TO SOLOS

	INR	E		;TEST FOR DATA RECORD
	JZ	GETCO		;YES: GO GET ANOTHER RECORD

; BAD RECORD TYPE

	MVI	C,BRTMS-MSBAS	;RECORD TYPE ERROR

;	FALL INTO PRINT, RETURN TO SOLOS

;***************************************************************
; SUBROUTINE TO PRINT STRING AT MSBAS + C TO CONSOLE. STRING IS
; TERMINATED WITH MSB SET.
; TRASHES B	
;***************************************************************
PRINT:	LXI	H,MSBAS
	MVI	B,0
	DAD	B		;HL POINTS TO MESSAGE STRING

MSGLP:	MOV	A,M		;GET STRING CHR

	CALL	PRNTC		;CHR RETURNS IN A WITH MSB=0
	CMP	M		;WAS MSB SET FOR THIS CHR?
	RNZ			;YES: DONE

	INX	H
	JMP	MSGLP	

;***************************************************************
; 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
;***************************************************************
GETBY:	CALL	GETHX		;GET HIGH NIBBLE
	RLC			;SHIFT HIGH NIBBLE IN PLACE
	RLC
	RLC
	RLC
	MOV	B,A
	CALL	GETHX		;GET LOW NIBBLE

	ORA	B		;COMBINE NIBBLES
	MOV	B,A		;SAVE RESULT FOR RETURN
	ADD	D		;COMPUTE CHECKSUM
	MOV	D,A
	RET

; LOCAL SUBROUTINE TO GET A HEX DIGIT IN A<3:0>

GETHX:	CALL	GETCH
	SUI	'0'		;REMOVE ASCII BIAS
	CPI	10
	RC			;IF 0-9 THEN WE'RE DONE

	SUI	'A'-'9'-1	;SUBTRACT GAP TO ASCII A
	CPI	10		;BETWEEN ASCII 9 AND A?
	JC	AERR		;YES: ERROR
	CPI	10H		;ABOVE ASCII F?
	RC			;NO: DONE

;ASCII CHARACTER ERROR - NOT A VALID HEX DIGIT

AERR:	POP	B		;REPAIR STACK: CALL GETHX
	POP	B		;REPAIR: CALL GETBY
	MVI	C,AERMS-MSBAS	;ASCII ERROR
	JMP	PRINT		;PRINT ERROR MESSAGE & EXIT

;***************************************************************
; SUBROUTINE TO GET ONE CHR FROM SERIAL PORT, IGNORING NULLS,
; STRIPPING PARITY, AND CHECKING FOR CONTROL-C FROM USER
; ECHO CHR TO VDM SCREEN
; ON ENTRY:
; ON EXIT:
;    CHR IS IN A
;    ALL REGISTERS PRESERVED
;***************************************************************
GETCH:	MVI	A,SERPP		;SELECT SERIAL PSEUDO PORT
	CALL	SOLOS+AINP
	JZ	GETC2		;NO AVAILABLE CHR?

	CALL	PRNTC		;ECHO TO CONSOLE, STRIP PARITY
	ORA	A		;TEST FOR NULL
	RNZ			;DONE UNLESS NULL

GETC2:	CALL	SOLOS+SINP	;CHECK FOR KBD CHR
	JZ	GETCH		;NO KBD CHR: TRY SERIAL AGAIN

	CPI	CTRLC		;CONTROL-C TYPED?
	JNZ	GETCH		;NO: KEEP WAITING
	JMP	SOLOS+RETRN	;YES: SOLOS WILL FIX STACK
	

;***************************************************************
; SUBROUTINE TO PRINT CHR ON CONSOLE VIA SOLOS/CUTER
; THE BASE ADDRESS OF SOLOS/CUTER IS STORED AT SOLOS
; NULLS GET IGNORED
; ON ENTRY:
;    A=CHR TO PRINT
;    SOLOS HAS BASE ADDRESS FOR SOLOS/CUTER
;    TOP OF STACK HAS RETURN ADDRESS
; ON EXIT:
;    A=PRINTED CHARACTER WITH MSB=0
;***************************************************************
PRNTC:	PUSH	B
	ANI	7FH		;STRIP PARITY; TEST FOR NULL
	MOV	B,A		;VDM WANTS CHR IN B
	CNZ	SOLOS+SOUT	;DON'T PRINT NULLS
	MOV	A,B		;RECOVER CHR W/ PARITY STRIPPED
	POP	B
	RET

;***************************************************************
; MSB-TERMINATED STRINGS
;***************************************************************
MSBAS:	DB	'? CKSU','M'+80H	;MUST BE FIRST
BRTMS:	DB	'? RTYP','E'+80H
AERMS:	DB	'? ASCI','I'+80H
DONMS:	DB	CR,LF,'O','K'+80H
RDYMS:	DB	' ',VERSH,VERSL,CR,LF+80H

;***************************************************************
; INSTALL HEXLOAD COMMAND IN CUSTOM COMMAND TABLE AND RETURN TO
; SOLOS. THIS CODE IS AT THE END SO THAT A LARGE SOLOS STACK
; CAN WIPE IT OUT AFTER THE INSTALL COMPLETES.
;***************************************************************

; SEARCH FOR NULL ENTRY IN TABLE. IF THE FIRST 5 ENTRIES ARE
; FULL, THEN INSTALL 'HL' IN THE 6TH (LAST) ENTRY, OVERWRITING
; ANY COMMAND THAT MAY HAVE BEEN THERE. NOTE THAT THIS DOES NOT
; CHECK FOR 'HL' COMMAND ALREADY IN THE TABLE.

INSTL:	LXI	H,CUTAB+SOLOS-4	;CUST CMD TABLE ADDR
	LXI	B,MAXCU*256+(DONMS-MSBAS)
				;MAX CUST CMD ENTRIES, DONE MSG
	XRA	A		;HANDY 0
	
INST0:	INX	H		;NEXT ENTRY
	INX	H
	INX	H
	INX	H

	DCR	B		;ANY MORE ENTRIES?
	JZ	INST1		;FULL? OVERWRITE THE LAST ENTRY

	CMP	M		;A IS ZERO
	JNZ	INST0		;FOUND AN EMPTY ENTRY?

; INSTALL HEX LOAD COMMAND INTO CUSTOM COMMAND TABLE AT HL

INST1:	MVI	M,'H'
	INX	H
	MVI	M,'L'
	INX	H
	MVI	M,HEXLD AND 0FFH	;LOW BYTE OF ADDRESS
	INX	H
	MVI	M,(HEXLD/256)		;HIGH BYTE OF ADDRESS
	INX	H
	MOV	M,A			;A=0: NEW TABLE END


; INSTALL IS COMPLETE. PRINT MESSAGE AT C AND RETURN TO SOLOS
	
	JMP	PRINT		;PRINT MSG & RETURN TO SOLOS
	
	END
