;===============================================================
; MINHX:   MINIMAL INTEL HEX LOADER FOR SOLOS/CUTER
;
; BY MARTIN EBERHARD
;
;  VERS     DATE          NOTE
;  1.01   3JUN2011  CREATED
;  1.02   3JUN2011  SQUEEZE
;  1.03   3MAR2015  RELCOATE TO 4000h
;
;===============================================================

;***************************************************************
; 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) FOR EXAMPLE, 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' -
; LOAD AND EXECUTE AT CAB4.
;
; EXECUTION TERMINATES WITH A RETURN TO SOLOS FOR EITHER OF THE
; FOLLOWING CONDITIONS:
;   *  ANY RECORD WITH 0 DATA BYTES - TREATED AS END-OF-FILE,
;      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 (OR CUTER) IS INSTALLED AT ITS
; NORMAL LOCATION, C000.
;
; THE COMPLETE PROGRAM HAS BEEN CRAFTED TO BE AS SMALL AS
; POSSIBLE, WHILE STILL DOING A PASSABLE JOB OF CHECKING FOR
; ERRORS.
;
;***************************************************************
; 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 = NORMAL 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 (REGARDLESS OF RECORD TYPE) 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/CUTER BASE ADDRESS

; CUTER/SOLOS ENTRY POINTS, OFFSET FROM SOLOS BASE ADDRESS

SOUT	EQU	19H	;WRITE CHR IN B TO CURRENT PSEUDO PORT
AINP	EQU	22H	;READ FROM PSEUDO PORT IN A INTO A

; CUTER/SOLOS PSEUDO PORT DEFINITIONS. CHANGE CODE AT 'GETHX'
; TO LOAD FROM A DIFFERENT PORT.

KBDPP	EQU	0	;KEYBOARD 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                                           *
; E: LOOP COUNT                                                *
; HL: MEMORY ADDRESS                                           *
;***************************************************************
ORG	4000H

HEXLD:	JMP	GETCO		;SO THAT START ADDR=LOAD ADDR

; CHECK RECORD CHECKSUM IN A

CHKSM:	MVI	B,'?'		;IF WE NEED AN ERROR MESSAGE
	ORA	A		;TEST CHECKSUM
	JNZ	SOLOS+SOUT	;PRINT ?, RETURN VIA SOLOS

; ALL DONE WITH THIS RECORD. CHECK C=0 FOR 0-BYTE RECORD
; (WHICH IS TREATED AS EOF)

	ORA	C		;WAS LAST RECORD 0 BYTES?
	RZ			;YES: DONE

; WAIT FOR RECORD START: EAT ALL CHRS UNTIL WE GET A COLON

GETCO:	CALL	GETHX		;Z SET IF RESULT IS COLON
	JNZ	GETCO

; LOOP TO SHIFT THE FOLLOWING VALUES INTO REGISTERS:
;   C = RECORD BYTE COUNT
;   H = ADDRESS HIGH BYTE
;   L = ADDRESS LOW BYTE
;   B = RECORD TYPE (WILL GET CHUCKED)
;   D = CHECKSUM OF THE ABOVE 4 VALUES

	LXI	D,4		;D=RESET CKSUM, E=LOOP 4 TIMES

CATLP:	MOV	C,H		;SHIFT THROUGH REGS SO THAT
	MOV	H,L		;...EVERYTHING'S IN PLACE AFTER
	MOV	L,B		;...4 PASSES THROUGH THE LOOP
	CALL	GETBY		;DATA IN B, CHECKSUM IN A & D
	JNZ	CATLP

; LOOP TO GET DATA INTO MEMORY AT HL,
; AND GET CHECKSUM IN A WHEN THE LOOP ENDS

	MOV	E,C		;LOOP COUNT = RECORD LENGTH+1

BYTLP:	CALL	GETBY		;DATA IN B, CHECKSUM IN A & D
	JM	CHKSM		;DONE ONCE WE HAVE CHECKSUM

	MOV	M,B		;MOVE DATA INTO MEMORY
	INX	H
	JMP	BYTLP

;***************************************************************
; 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 A LITTLE MORE LIKELY TO CATCH AN ERROR WITH THE CHECKSUM.

; ON ENTRY:
;   D = CHCKSUM SO FAR
;   E = LOOP COUNT
; ON EXIT:
;   B=BYTE OF DATA
;   A=D=NEW CHECKSUM VALUE
;   E HAS BEEN DECREMENTED
;   Z FLAG SET IF E=0, M FLAG SET IF E<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

	DCR	E		;BUMP LOOP COUNT, TEST
	RET

;***************************************************************
; SUBROUTINE TO GET ONE CHR FROM SPECIFIED PSEUDOPORT AND
; CONVERT IT TO A HEX DIGIT, TESTING FOR COLON ALONG THE WAY.
;
; IGNORE NULLS, STRIP PARITY
; ON EXIT:
;    Z SET IF CHR WAS A COLON, A=0AH IF SO
;    NEW NIBBLE IS IN A <3:0>, A>0FH FOR MOST WRONG CHRS
;    ALL OTHER 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		;SPIN UNTIL CHARACTER

	SUI	'0'		;REMOVE ASCII BIAS
	CPI	10		;0-9?
	RC			;YES: WE'RE DONE, Z CLEARED
	RZ			;COLON IS ASCII '9' + 1, Z SET

	SUI	'A'-'9'-1	;SUBTRACT GAP TO ASCII A, CLR Z

	RET			;RETURN WITH CHR IN A, Z CLEAR
	
	END
