;===============================================================
; MINHX:   MINIMAL INTEL HEX LOADER FOR SOLOS/CUTER
;
; BY MARTIN EBERHARD
;
;  VERS     DATE          NOTE
;  1.01   3JUN2011  CREATED
;
;===============================================================

;***************************************************************
; OPERATION
;
; THIS PROGRAM IS A MINIMAL INTEL HEX LOADER THAT IS INTENDED
; TO BE ENTERED IN HEX ON THE SOLOS CONSOLE, AND USED JUST ONCE
; TO LOAD (AND SAVE TO TAPE OR DISK) THE SOMEWHAT LARGER AND
; MORE ROBUST 'HLOAD' INTEL HEX LOADER.
;
; THIS PROGRAM WILL LOAD AN INTEL HEX FILE FROM THE SERIAL PORT 
; INTO MEMORY AT THE ADDRESSES SPECIFIED IN THE INTEL HEX FILE.
;
; THIS PROGRAM LOADS AND RUNS IN SOLOS'S USER AREA, 'USARE'.
; EXECUTE AT CAB4.
;
; EXECUTION TERMINATES WITH A RETURN TO SOLOS ON EITHER OF THE
; FOLLOWING CONDITIONS:
;   *  ANY RECORD WITH 0 DATA BYTES - NO MESSAGE PRINTED
;   *  CHECKSUM ERROR - A '?' WILL BE PRINTED ON THE CONSOLE
;      BEFORE RETURNING TO SOLOS.
;
; TO ABORT A LOAD, RESET SOLOS WITH 'UPPERCASE'-'REPEAT'
;
;***************************************************************
; PROGRAM NOTES
;
; THIS PROGRAM IS WRITTEN TO BE ASSEMBLED BY CP/M'S ASM.COM.
;
; THIS PROGRAM ASSUMES SOLOS RUNS AT ITS NORMAL LOCATION, C000.
;
; THE COMPLETE PROGRAM HAS BEEN CRAFTED TO BE AS SMALL AS
; POSSIBLE, WHILE STILL DOING A DECENT JOB OF CHECKING FOR
; ERRORS.
;
; THE HEX LOADER COMMAND RUNS IN THE 'USER AREA' OF THE 1K RAM
; BUFFER ADJACENT TO SOLOS/CUTER (CALLED 'USARE' IN SOLOS
; SOURCE CODE). THIS AREA BEGINS AT 0CAB4H AND ENDS AT THE END
; OF RAM, CBFF. HOWEVER, SOLOS'S STACK BEGINS AT 0CBFF, AND
; GROWS TOWARD 0 -  SO IF THE LOADER IS TO LARGE, SOLOS'S STACK
; WILL OVERWRITE IT.  NOTE: SOLOS NEEDS ABOUT 22 BYTES OF STACK
; SPACE. SO THE LAST ADDRESS MUST BE BEFORE CBEA.
;
;***************************************************************
; INTEL HEX RECORD FORMAT
;
; AFTER THE INITIAL COLON, EACH CHARACTER IS A HEX DIGIT.
;
; :CCAAAATTdataSS
;
;    : = (ASCII COLON) START-OF-RECORD INDICATOR
;   CC = THE TOTAL NUMBER OF DATA BYTES, IN HEX
; AAAA = THE MEMORY LOAD ADDRESS FOR THE FIRST DATA BYTE
;        SUBSEQUENT BYTES ARE WRITTEN TO SEQUENTIAL ADRESSES
;   TT = RECORD TYPE. THE FOLLOWING TYPES ARE SUPPORTED:
;        00 = NORRMAL DATA RECORD
;        01 = END-OF-FILE. EOF RECORD MAY CONTAIN DATA.
; data = CC DATA BYTES (2 HEX DIGITS EACH)
;   SS = RECORD CHECKSUM. WHEN ALL HEX BYTES OF THE RECORD ARE
;        ADDED TOGETHER (INCLUDING THE SS CHECKSUM BYTE) THE
;        RESULT SHOULD BE 0. ANYTHING ELSE IS A CHECKSUM ERROR.
;
; NOTE: THE RECORD ':0000000000', WHICH IS A 0-BYTE NORMAL DATA
; RECORD, IS OFTEN USED AS AN END-OF-FILE RECORD. THIS PROGRAM
; TREATS ALL 0-BYTE RECORDS AS EOF RECORDS. ANY OTHER RECORD
; (WITH CORRECT CHECKSUM) WILL BE TREATED AS A DATA RECORD.
;
; NOTE: ANY CHARACTERS THAT OCCUR BEFORE THE INITIAL COLON (OF
; ANY RECORD) WILL BE IGNORED. THIS ALLOWS FOR CARRIAGE RETURNS
; AND LINE FEEDS TO MAKE THE HEX FILE PRETTY, AS WELL AS ANY
; COMMENTS, PROVIDED THE COMMENTS DO NOT INCLUDE ANY COLONS. 
;***************************************************************
SOLOS	EQU	0C000H	;SOLOS BASE ADDRESS

; LOCATIONS IN SOLOS/CUTER 1K RAM BLOCK, OFFSET FROM SOLOS BASE

USARE	EQU	0AB4H	;USER AREA IN RAM, OFFSET FROM SOLOS

; CUTER/SOLOS ENTRY POINTS, OFFSET FROM SOLOS BASE ADDRESS

INIT	EQU	01H	;COLD-START SOLOS
RETRN	EQU	04H	;RETURN CONTROL TO SOLOS
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

;***************************************************************
; BEGINNING OF CODE EXECUTION                                  *
;                                                              *
; REGISTER USAGE DURING HEX LOAD:                              *
; B: SCRATCH                                                   *
; C: RECORD BYTE COUNT                                         *
; D: RECORD CHECKSUM                                           *
; HL: MEMORY ADDRESS                                           *
;***************************************************************
ORG	SOLOS+USARE

HEXLD:

; EAT ALL CHRS UNTIL WE GET RECORD-START COLON

GETCO:	CALL	GETHX		;Z SET IF RESULT IS COLON
	JNZ	GETCO

; GET RECORD BYTE COUNT INTO C & RESTART CHECKSUM

	CALL	GETBY
	MOV	C,B		;BYTE COUNT
	MOV	D,B		;RESET CHECKSUM

; GET RECORD ADDRESS INTO HL

	CALL	GETBY
	MOV	H,B		;HIGH BYTE OF ADDRESS
	CALL	GETBY
	MOV	L,B		;LOW BYTE OF ADDRESS

; GET AND CHUCK RECORD TYPE

	CALL	GETBY

; TEST FOR 0-BYTE RECORD, WHICH IS AN A EOF
; DON'T LOOP TO GET DATA FOR 0-BYTE RECORDS

	MOV	A,C		;0-BYTE RECORD?
	ORA	A
	JZ	GCKSM		;NO: JUST CHECK CHECKSUM

; LOOP TO GET DATA INTO MEMORY AT HL

BYTLP:	CALL	GETBY
	MOV	M,B
	INX	H
	DCR	C
	JNZ	BYTLP

	INR	C		;REMEMBER NON-ZERO RECORD LENGTH

; GET AND CHECK RECORD CHECKSUM

GCKSM:	CALL	GETBY		;GET AND TEST CHECKSUM BYTE
	MVI	B,'?'		;IF WE NEED AN ERROR MESSAGE
	JNZ	SOLOS+SOUT	;PRINT ?, RETURN VIA SOLOS

; ALL DONE WITH THIS RECORD. CHECK C=0 FOR 0-BYTE RECORDS
; (WHICH ARE TREATED AS EOF) C=1 FOR NON-ZERO LENGTH RECORDS,
; (WHICH ARE TREATED AS DATA RECORDS)

	DCR	C		;NORMAL DATA RECORD?
	JZ	GETCO		;GO GET ANOTHER RECORD

	RET			;NORMAL EXIT TO SOLOS

;***************************************************************
; SUBROUTINE TO GET 2 HEX DIGITS FROM THE SPECIFIED PSEUDOPORT,
; COMBINE THEM INTO A BYTE, AND ADD RESULT TO CHECKSUM.
;
; TO KEEP THIS CODE SHORT, DON'T ACTUALLY TEST FOR ASCII
; CHARACTER ERRORS - LET THE CHECKSUM CATCH THESE ERRORS. WHEN
; WE COMBINE NIBBLES, WE USE 'ADD' INSTEAD OF 'ORA' SO THAT WE
; ARE MORE LIKELY TO CATCH AN ERROR.

; 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

	ADD	B		;COMBINE NIBBLES, CATCH ERRORS
	MOV	B,A		;SAVE RESULT FOR RETURN
	ADC	D		;COMPUTE CHECKSUM, CATCH ERRORS
	MOV	D,A
	RET

;***************************************************************
; SUBROUTINE TO GET ONE CHR FROM SPECIFIED PSEUDOPORT AND
; CONVERT IT TO A HEX DIGIT.
;
; IGNORE NULLS, STRIP PARITY
; ON EXIT:
;    Z SET IF CHR WAS A COLON, A IS NOT USEFUL IF SO
;    NIBBLE IS IN A
;    ALL REGISTERS PRESERVED
;***************************************************************
GETHX:	MVI	A,SERPP		;SERIAL PSEUDOPORT
	CALL	SOLOS+AINP	;A=0 AND Z SET IF NO CHR
	ANI	7FH		;STRIP PARITY, TEST NULL, NO CHR
	JZ	GETHX

	SUI	'0'		;REMOVE ASCII BIAS
	CPI	10
	RC			;IF 0-9 THEN WE'RE DONE, Z CLEARED
	RZ			;COLON IS ASCII '9' + 1, Z SET

	SUI	'A'-'9'-1	;SUBTRACT GAP TO ASCII A, CLEARS Z

	RET			;RETURN WITH CHR IN A
	
	END
