;---------------------------------------------------------------
;
;  Stand-alone Intel Hex file loader for the Sol-20.
;
;  Execute by jumping to F000.
;
;  Displays address information as the hex file loads.
;  The video scroll time problem is avoided by first
;  clearing the screen and displaying address information
;  only every 16th hex file line. Intermediate pacifiers are
;  displayed every 4th hex line. With a typical hex file
;  format of 16 payload bytes per line, about 32K of memory
;  can be loaded before a screen scroll takes place. This
;  handles most any file that would be loaded this way.
;
;  Rev	   Date	     By		Desc
;  1.0	 04/05/15  M Douglas	Original
;
;---------------------------------------------------------------
	org	0f000h

; Equates

SOLOS	equ	0c000h		;base address of SOLOS/CUTER
CONOUT	equ	SOLOS+019h	;console out
SERIN	equ	SOLOS+022h	;input from psuedo port in A
RETRN	equ	SOLOS+004h	;return to SOLOS entry

CLRSCR	equ	11		;clear screen in SOLOS
CR	equ	13
LF	equ	10

;------------------------------------------------------------
; Display message to send the file, then loop receiving 
;    and storing the hex file.
;------------------------------------------------------------
init	lxi	h,mSend		;send file message
	call	dispMsg
	mvi	c,0		;clear echo character flag
	mvi	a,1		;init hex line counter
	push	a		;   maintained on the stack

; nxtLine - Look for new record

nxtLine	call	getChar		;read next character
	sui	':'		;record marker?
	jnz	nxtLine		;no, keep looking

; Have start of new record. Save the byte count and load address.
;   The load address is echoed to the screen every 16th hex file
;   line so the user can see the file load progress.

	mov	d,a		;init checksum in D to zero
	call	iByte		;input two hex digits (byte count)
	mov	a,e		;test for zero byte count
	ora	a
	jz	done		;count of 0 means end

; echo address every 16th line read, pacifier every 4th

	pop	a		;A = hex file line counter
	dcr	a
	jnz	not16		;skip if not a 16th line
	mvi	a,16		;restart 16 line count
	inr	c		;set echo flag
not16	push	a		;save line counter
	ani	03h		;4 line boundary?
	jnz	getAddr		;no	
	mvi	b,'.'		;yes, display pacifier
	call	CONOUT
	
; getAddr - read load address into HL

getAddr	mov	b,e		;B = byte count on line	
	call	iByte		;get MSB of address
	mov	h,e		;H = address MSB
	call	iByte		;get LSB of address
	mov	l,e		;L = address LSB
	mvi	c,0		;clear echo flag
	call	iByte		;ignore record type

; Receive the data bytes of the record and move to memory

data	call	iByte		;read a data byte (2 hex digits)
	mov	m,e		;store in memory
	inx	h
	dcr	b
	jnz	data

; Validate checksum

	call	iByte		;read and add checksum
	jz	nxtLine		;checksum good

; error - error in data or checksum. Display error message
;     and exit after flushing the rest of the file

error	lxi	h,mError	;display error message
	call	dispMsg
	jmp	flush		;flush remaining reception	

; done - hex file loaded without error

done	lxi	h,mDone		;display "complete" message
	call	dispMsg	

; flush - eat characters until none received  for 1/2 second 
;   so the rest of the file doesn't go to the monitor

flush	lxi	d,5800		;1/2 sec/(170*500ns)
flLoop	mvi	a,1		;read from serial port
	call	SERIN		;(136 cycles)
	jnz	flush		;(10) start over
	dcx	d		;(5) decrement timeout
	mov	a,d		;(5)
	ora	e		;(4)
	jnz	flLoop		;(10) loop until zero
	jmp	RETRN		;return to SOLOS

;-----------------------------------------------------------
; iByte	- read two ascii hex bytes and return binary
;    value in e. If a conversion error occurs, exit
;    directly to error label above.
;-----------------------------------------------------------
iByte	call	getChar		;get a character
	call	asc2Bin
	jc	error
	add	a		;put in msn, zero lsn
	add	a
	add	a
	add	a
	mov	e,a		;save byte with MSN in E

; 2nd byte (LSN)

	call	getChar		;get a character
	call	asc2Bin
	jc	error
	add	e		;combine msn and lsn
	mov	e,a		;save in E
	add	d		;add character to checksum
	mov	d,a
	ret

;-------------------------------------------------------------
; asc2Bin - ASCII hex digit to binary conversion. Digit
;    passed in a, returned in a. Carry returned clear
;    for valid conversion, carry set for error.
;-------------------------------------------------------------
asc2Bin	sui	30h		;'0' to 0
	rc			;error - below zero
	cpi	10		;0-9 ?
	jc	ascExit		;yes
	sui	11h		;A-F to 0-5
	rc			;error - below A
	adi	10		;convert back to A-F
	cpi	16		;too big? (carry clear if so)
ascExit	cmc			;complement carry
	ret

;-------------------------------------------------------------
; getChar - read a character from the the serial port
;    and return it in a. The character is echoed if the
;    echo flag in c is zero.
;-------------------------------------------------------------
getChar	mvi	a,1		;read from serial port
	call	SERIN
	jz	getChar		;loop until character there
	push	b		;save b
	mov	b,a		;save character in b
	mov	a,c		;echo flag asserted?
	ora	a
	jz	noEcho		;no
	call	CONOUT		;echo the character (in b)
noEcho	mov	a,b		;a=character read
	pop	b		;restore b
	ret

;------------------------------------------------------------
; dispMsg - display the null-terminated message passed in hl
;------------------------------------------------------------
dispMsg	mov	a,m		;get the next message byte
	ora	a		;null terminates
	rz
	mov	b,a		;b=character to display
	call	CONOUT		;display on console
	inx	h		;move to next byte
	jmp	dispMsg

;------------------------------------------------------------
;  Messages
;------------------------------------------------------------
mSend	db	CLRSCR,'Send Hex File Now: ',0
mError	db	CR,LF,LF,'Hex File Error',CR,LF,0
mDone	db	CR,LF,'Load Complete',CR,LF,0
	end
