;============================================================== 
;     BIDIRECTIONAL INTEL HEX I/O DEVICE DRIVER for PTDOS
;                  BY MARTIN EBERHARD
;
;============================================================== 
; REVISION HISTORY
;
; 0.01  27SEP2011 M.Eberhard
;   Created
; 0.02  29SEP2011 M.Eberhard
;   Bidirectional: support reading hex files too
; 0.03  03OCT2011 M.Eberhard
;   Print byte count at end; ESC aborts reads
; 0.04  05OCT2011 M.Eberhard
;   Record count for Tx and Rx (instead of byte Count)
; 0.05  18FEB2015 M.Eberhard
;   Offset incoming hex files. Warn about LOCAL key. Check
;   for overflow based on PTDOS GLLOW. Announce version.
; 1.00  28FEB2015 M.Eberhard
;   Run in SOLOS RAM. Better messages. Return correctly on
;   errors. Allow user abort when sending. Beautify comments.
;   Test for LOCAL key - don't just warn. MODE/SELECT key also
;   aborts. Print base address and (if provided) execution
;   address after receiving a hex file. Error on bogus hex.
;   Speed up inner data transfer loops. Rename variables.
;============================================================== 
; NOTES
;
; Written to be assembled by Digital Research's ASM
;
; This runs in SOLOS's RAM, starting at C863h, leaving SOLOS
; intact enough that its serial port routines may be used.
; (This only overwrites the tape file buffer for SOLOS and
; CUTER, as well as CUTER's console input line buffer.)
;
; Sending a file as Intel hex requires no other RAM. However
; receiving requires enough RAM to receive the entire hex file
; (since no serial port handshaking is supported). This receive
; buffer starts at 0A00h, which leaves enough room in RAM for
; PTDOS programs like COPY to use this device driver. (The
; received file is stored in this buffer in binary, which is
; far more compact than Intel hex.)
;============================================================== 
; INTEL HEX RECORD FORMAT
;
; (After initial colon, each character is a hex digit.)
;
; :CCAAAATT<data>SS
;
;    : = (ASCII colon) start-of-record indicator
;   CC = Total number of data bytes, in hex
; AAAa = Memory load address for the first data byte.
;        Subsequent data bytes are written to
;        sequential memory addresses
;   TT = Record type. The following types are supported:
;         00 = Normal data record
;              Note: A type 0 record with no data (CC = 0)
;              Also serves as an end-of-file record.
;         01 = End-of-file record (will not have data).
;              A non-zero address field will be reported as
;              the execution address.
;<data> =  CC data bytes, 2 hex digits each byte
;   SS = Record checksum. When all hex bytes of the
;        record (including the checksum) are added
;        together, the result will be 00.
;============================================================== 

;-------------
;SOLOS equates
;-------------
SOLOS	equ	0C000h		;SOLOS base address
AOUT	equ	SOLOS+1Ch	;write b to pseudoport in a
AINP	equ	SOLOS+22h	;read from pseudoport a into a

FBUF1	equ	SOLOS+0863h	;Tape header buffer. All RAM
CUTLB	equ	SOLOS+0A63h	;Cuter line buffer
CLBLN	eqh	81		;length of Cuter line buffer
				;past here is available
USARE	equ	SOLOS+0AB4h	;User area in SOLOS RAM

; pseudoportS

KBDPP	equ	0		;keyboard
VDMPP	equ	0		;VDM
SERPP	equ	1		;serial
PARPP	equ	2		;parallel
USRPP	equ	3		;user-written

;-------------
;PTDOS equates
;-------------
CONST	equ	0bc9Ch		;test console for waiting chr
CONIN	equ	0bcA2h		;get console chr into a
CONOUT	equ	0bc9Fh		;send a to console
SYSGLO	equ	0bcA5h		;pointer to system global area
GLLOW	equ	36h		;PTDOS lower bound offset PTR

;-------------
;ASCII equates
;-------------
CR	equ	0Dh		;Carriage Return
LF	equ	0Ah		;Line feed
ESC	equ	1Bh		;Escape

;---------------
;Program equates
;---------------
INTEL	equ	FBUF1		;Address for this driver, in
				;SOLOS RAM
HRMAX	equ	16		;max bytes per hex record
BLKSZ	equ	16		;max block size for PTDOS
RXBUF	equ	0A00h		;hex input buffer

;================================================== 
	ORG	INTEL		;Run in SOLOS's RAM
;================================================== 
;--------------------------
;PTDOS DEVICE DRIVER HEADER
;--------------------------
DTRB	dw	DORB		;read block
DTRNB	dw	DORB		;read next block
DTRLB	dw	0000h		;read prev block (No support)
DTWBR	dw	DOWB		;write block, read next block
DTWB	dw	DOWB		;write block
DTREW	dw	DOREW		;Rewind
DTEOF	dw	DOEOF		;EOF
DTCLO	dw	DOCLO		;Close driver
DTSEK	dw	0000h		;Seek (no support)
DTCTL	dw	0000h		;CTRL/STAT Request (No support)
DTBLK	dw	BLKSZ		;max PTDOS block size
DTITO	db	0		;no immediate transfer option
DTINI	dw	DOINI		;initialize

;***Driver Routine************
;INITIALIZATION
;On Entry:
;  No parameters
;On Exit:
;  RETURN1 = normal return
;*****************************
DOINI:	mvi	a,SERPP
	call	AINP		;flush the UART receiver

	call	CILPRT
	db	'Intel Hex Device Driver 1.0','0'+80h

;Test for LOCAL key-on by sending a null and seeing if
;we then receive a null within 1 chr time. b=0 here.
;Clear RAM variables while we are at it.

;This 35 mS delay should catch a LOCAL key that is on
;at 300 baud or above.

LRETRY:	lxi	d,SERPP*256+121	;d=serial pseudoport
				;e=delay loop counter
	mov	a,d		;d=SERPP
	mvi	b,0		;send null
	call	AOUT		;send b=0, null to serial

;Each pass through is 593 cycles, or 290 uS

STALUP:	lxi	h,RABEG		;(10)clear all RAM variables
	mvi	c,RAEND-RABEG	;(7)bytes to clear

;Clearing 15 bytes takes 405 cycles

INILP:	mov	m,b		;(7)b=0
	inx	h		;(5)
	dcr	c		;(5)
	jnz	INILP		;(10)

	dcr	e		;(5)timeout?
	rz			;(5)y: no LOCAL key

	mov	a,d		;(5)d=SERPP serial pseudoport
	call	AINP		;(146)Anything from the UART?
	jz	STALUP		;(10)n: keep waiting
	
	ora	a		;receive a null?
	rnz			;n: some other chr

	call	CILPRT
 db 'Turn off LOCAL key! Ready','?'+80h

WLOCAL:	call	CHKUSR
	jz	WLOCAL		;wait for chr from user

	jmp	LRETRY		;look at LOCAL again

;***Driver Routine*********
;CLOSE DRIVER
;On Entry:
;  no parameters
;On Exit:
;  RETURN1 = normal return
;**************************
DOCLO:	lda	TXOPEN		;Need to finish sending hex?
	ora	a		;in case of CLOSE w/o EOF
	rz			;n: RETURN1

	lxi	d,0		;tell DOEOF no data from PTDOS

;Fall into DOEOF to flush our buffer & send EOF

;***Driver Routine************
;EOF
;On Entry:
;  hl = source buffer address
;  de = buffer size
;On Exit:
;  RETURN1 = normal return
;*****************************
DOEOF:	call	DOWB		;flush any data in PTDOS buffer
	nop			;return will skip 3 bytes
	nop
	nop			;returns with hl=0 and
				;c=byte count

;Send a short hex record to flush TXBUF if necessary.
;c = byte count here.

	mov	a,c
	ora	a		;need to finish record?
	cnz	SNDHEX		;send short record, ret c=0

;Send EOF record (c = 0 and hl = 0 here)

	shld	RECADR		;address & byte count are 0
	call	SNDHEX		;send EOF record now

	xra	a		;clear transmit file open flag
	sta	TXOPEN

	jmp	PRNRC		;print record count on console
				;..and RETURN1

;***Driver Routine*********************************************
;READ BLOCK
;This assumes that the record with the lowest address is first
;and that the record with the highest address is last.
;First, read entire hex file into RAM at RXBUF, checking for
;buffer overflow or underflow. Then transfer data to PTDOS in
;the biggest chunks that PTDOS can handle. Print errors on the
;console, and tell PTDOS 'EOF' (RETURN1) when done.
;
;On Entry:
;  hl = PTDOS buffer address (destination for data)
;  de = max bytes PTDOS can handle
;  RXDONE = 0 if we need to receive the hex file
;         = 1 if the complete hex data is in RXBUF
;On Exit:
;  RXDONE = 1
;  RECADR = current address in input buffer
;  RXBC = remaining bytes in input buffer
;  RETURN1 if EOF, RETURN2 otherwise
;  hl = number of bytes read since call
;**************************************************************
DORB:	push	h		;PTDOS buffer address

	lxi	h,2		;account for above push h
	dad	sp		;remember return address
	shld	SAVESP		;..for error aborts

	lxi	h,RXDONE	;0 means get the hex file now
	mov	a,m
	ora	a		;already get the hex file?
	jnz	SNDPTD		;y:just keep sending to PTDOS

;-----------------------------------------------------
;Get the entire hex file from the serial port into the
;receive buffer (RXBUF), and report results on console
;-----------------------------------------------------
	inr	m		;remember that we got hex data

	push	d		;max PTDOS bytes

;Tell user to begin sending hex

	call	CILPRT
 db 'Send hex file now. ESCAPE aborts','.'+80h

	call	RCVHEX		;go get all the hex file

;Report results of receiving hex file, and set up pointers
;for SNDPTD

	push	h		;EOF record's address field

	lxi	h,RXBC+1	;just get the high byte, since
	mov	a,m		;..buffer is on page boundary
	sui	RXBUF/256	;compute bytes received
	mov	m,a		;RXBC is now the byte count

	call	PRNRC		;print record count on console

	call	CILPRT		;print base address on console
	db	'Bas','e'+80h
	lhld	BASADR
	call	AHEX16

	pop	h		;EOF record's address field

	lda	HRTYPE		;was the EOF record type 1?
	dcr	a
	jnz	HLDON1		;n: use 1st record's address

	mov	a,h		;was type 1 record's address 0?
	ora	d
	jz	HLDON1		;y: this is not an exec address

	call	CILPRT		;print exec address on console
	db	'Exe','c'+80h
	call	AHEX16
HLDON1:

	lxi	h,RXBUF		;point to beginning of data
	shld	RECADR		;..for SNDPTD

	pop	d		;max PTDOS bytes

;-----------------------------------------------------
; Send as much of the received data to PTDOS as PTDOS
; can handle.
; On Entry:
;    de = max number of bytes that PTDOS can take
;    top-of-stack = PTDOS buffer address
;    RECADR = the address of the next byte in RXBUF
;    RXBC = number of remaining bytes in RXBUF
; On Exit:
;    RECADR = next RXBUF address
;    RXBC = bytes remaining in RXBUF
;-----------------------------------------------------

;Calculate how many bytes we can send to PTDOS with this call
;to DORB, which will be the lesser of the available space in
;PTDOS's buffer (in de) and the available bytes in our receive
;buffer (in RXBC)

SNDPTD:	lhld	RXBC		;remaining bytes to send
	mov	a,h		;anything left?
	ora	l		;if not, we're done
	jz	ABORT		;this will do a RETURN1

	push	h

	mov	a,l		;16-bit compare
	sub	e
	mov	l,a		;hl:=hl-de
	mov	a,h		;=PTDOS space - available bytes
	sbb	d		;carry set if hl<de
	mov	h,a		;..if we'll transfer all that
	jnc	PPT1		;..we have

	pop	d		;de=this transfer's size
	push	d		;..which is all we have
	lxi	h,0		;..so nothing will be left

PPT1:	shld	RXBC		;remainder for next transfer
	pop	b		;fix stack

	pop	b		;bc=PTDOS buffer address
	lhld	RECADR		;hl=source data address

;bc = PTDOS buffer address
;de = number of bytes to transfer
;hl = source data address
;RXBC = remainder for next time

	push	d		;byte count for the return

;Loop to transfer all the data PTDOS can take, or all
;we have in our buffer.

PPTLUP:	mov	a,m		;get a source byte
	stax	b		;give it to PTDOS
	inx	h		;bump pointers
	inx	b
	dcx	d		;bump & test count
	mov	a,d
	ora	e
	jnz	PPTLUP

	shld	RECADR		;remember next source address

	pop	d		;de=number of bytes tranferred

;Fall into RWEND for RETURN2 - no EOF yet.

;***Exit****************************************
;Normal (non-EOF) return from DORB or DOWB
;On Entry:
;  de = number of bytes transfered to/from PTDOS
;***********************************************
RWEND:	pop	h		;fix stack for RETURN2
	inx	h
	inx	h
	inx	h
	push	h

	xchg			;hl=byte count for PTDOS
	ret

;***Driver Routine********************************************
;WRITE BLOCK
;On Entry:
;  hl = source buffer address
;  de = number of bytes to transfer
;On Exit:
;  RETURN1 = EOF encountered (Happens only if user aborts)
;  RETURN2 = normal return (via RWEND)
;  b = 0
;  c = RECUBC = unsent byte count (bytes still in TXBUF)
;  hl = 0 = number of blocks read
;
;Get bytes from PTDOS input buffer and put them in the Intel
;hex record buffer until the end of the input buffer. If
;there are HRMAX bytes in the record buffer then send an
;Intel hex record to the output port, and then keep going.
;*************************************************************
DOWB:	push	h		;source address

	lxi	h,2		;account for above push h
	dad	sp		;remember incoming SP
	shld	SAVESP		;..for error aborts

	lxi	h,0101h		;use serial port,
	shld	TXPORT		;set TXOPEN to 1 too

	pop	h		;source address

	lda	RECUBC		;record unsent byte count
	mov	c,a		;=number of bytes in TXBUF
	mvi	b,0		;bc=TXBUF byte count

WBLOOP:	call	CHKUSR		;Abort from user?

	mov	a,d		;de=byte count
	ora	e		;any file bytes available?
	jz	RWEND		;n: RETURN2 end

	mov	a,m		;get a source byte
	inx	h
	dcx	d

	push	h
	lxi	h,TXBUF		;find next buffer address
	dad	b		;hl points to next buffer loc
	mov	m,a		;put byte in buffer
	pop	h

	inr	c		;bump unsent byte count
	mov	a,c
	cpi	HRMAX		;full hex record?
	cz	SNDHEX		;y: send hex record via serial

	mov	a,c		;remember unsent byte count
	sta	RECUBC
	jmp	WBLOOP

;***Subroutine***************************************
;Read entire Intel hex file into RXBUF. Report errors
;on console; allow user to abort.
;
;==> Subroutine entry is at RCVHEX <==
;
;Use 1st record'S address as the address offset
;for all subsequent records
;
;On Entry:
;On Exit:
;  bc = total bytes in input buffer
;  hl = EOF record's address field
;  RECADR = RXBUF
;  BASADR = address field from first record
;  RXBC = the highest buffer address used + 1
;  RECCNT = total number of hex records received
; psw,de trashed
;
;Register usage during hex load:
;  b: scratch
;  c: record byte count
;  d: record checksum
;  e: loop count
; hl: memory address
;***************************************************
CHKSM:	ora	a		;Check record checksum in a
	jnz	CSERR		;report checksum error if <>0

;All done W/ record. check for 0-byte record, which is the EOF

	ora	c		;was last record 0 bytes?
	rz			;Y: done receiving hex

;==> Subroutine Entry <==

RCVHEX:

;Wait for colon to start record

GETCO:	call	GETSER		;get one serial port chr
	cpi	':'
	jnz	GETCO

;Loop to shift the following into registers:
; c: record byte count
; h: address high byte
; l: address low byte
; b: record type
; d: checksum of the above 4 bytes

	lxi	d,4		;d = reset cksm, e = loop 4 times

CATLP:	mov	c,h		;shift through regs so that
	mov	h,l		;...all is in place after
	mov	l,b		;...4 passes through loop
	call	GETBYT		;data is in b, cksm in a & d
	jnz	CATLP

;Remember the record type in case it's an exec address

	mov	a,b
	sta	HRTYPE

;If this is the first record then remember offset

	mov	b,d		;temp save checksum

	xchg			;de = record address
	lhld	RECCNT
	mov	a,h
	ora	l		;Z set if 1st record
	inx	h		;bump record count
	shld	RECCNT

	jnz	NOTR0		;first record?

	xchg			;hl = this record'S address
	shld	BASADR		;Y: its addr is the file offset
	xchg			;de = this record's address
NOTR0:

;Subtract file address offset from hl
;and add RXBUF offset to address in hl

	lhld	BASADR		;file address offset
	mov	a,e		;16-bit subtract
	sub	l
	mov	l,a
	mov	a,d
	sbb	h
	adi	RXBUF/256	;add RXBUF offset to address
	mov	h,a		;hl = fully offset address

	mov	d,b		;recover checksum
	
;Loop to get hex data into buffer at hl, checking for buffer
;under/overflow. Get checksum in a when loop ends. Speed is
;not particularly critical in this loop, as the transfer rate
;is limited by the serial port (1000 uS/byte at 9600 baud).

	mov	e,c		;loop count = record length

BYTLP:	call	GETBYT		;data in b, checksum in a&d
	jm	CHKSM		;done once we have checksum

;Error if address >= PTDOS'S GLLOW

	push	d
	push	h
	lhld	SYSGLO		;PTDOS system globals
	lxi	d,GLLOW
	dad	d		;hl = GLLOW address
	mov	e,m
	inx	h
	mov	d,m		;de = max address

	pop	h

	mov	a,l		;16-bit compare hl-de
	sub	e 
	mov	a,h
	sbb	d		;error if hl> = de
	pop	d
	jnc	ADERR		;report address error & quit

;Error if address < RXBUF

	mov	a,h
	cpi	RXBUF/256
	jc	ADERR		;report address error & quit

	mov	m,b		;park data in RXBUF

	inx	h
	shld	RXBC		;remember max address+1
	jmp	BYTLP

;***Subroutine***********************************
;Get 2 hex digits from the serial port, combine
;them into a byte, and add the result to checksum
;On Entry:
;  d = checksum so far
;  e = loop count
;On Exit:
;  b = data byte
;  a = d = updated checksum
;  e = e-1
;  Z flag set if e = 0, M flag set if e<0
;  all OtheR regs preserved
;************************************************
GETBYT:	call	GETHEX		;get high nibble
	rlc			;shift high nibble down
	rlc
	rlc
	rlc
	mov	b,a
	call	GETHEX		;get low nibble

	add	b		;combine nibbles, catch errors
	mov	b,a		;result in b for ret
	adc	d		;compute checksum, catch errors
	mov	d,a

	dcr	e		;bump & test loop count
	ret

;***Subroutine**************************************
;Get and validate one hex digit from the serial port
;On Exit:
;  new nibble is in a<3:0>
;  all Other regs preserved
;Rude exit on bogus hex character
;***************************************************
GETHEX:	call	GETSER		;get a serial port byte
	sui	'0'		;remove aSCII bias
	cpi	10		;0-9?
	rc			;Y: done, Z cleared

	sui	'A'-'0'		;test for gap chr
	cpi	6		;..or too large
	jnc	HEXERR

	adi	10		;make 'A'-'F' into 0Ah-0Fh
	ret

;***Subroutine***********************************
;Get one byte from the serial port, ignore nulls,
;allow the user to abort
;On Exit:
;  new byte is in a
;  Z cleared
;  all Other regs preserved
;************************************************
GETSER:	mvi	a,SERPP		;serial pseudoport
	call	AINP		;a = 0 and Z set if no chr
	ani	7Fh		;parity, test null, no chr
	rnz			;got a character

	call	CHKUSR		;Abort from user?
	jmp	GETSER		;wait for chr

;***Subroutine**************************
;Create and send one Intel hex record
;On Entry:
;  c = byte count
;  RECADR = address field for record
;  record data is at TXBUF
;  TXPORT = SERPP for serial port output
;On Exit:
;  c = 0
;  a,b trashed, all others preserved
;***************************************
SNDHEX:	push	h
	push	d

	mvi	b,':'		;initial colon
	call	OUTCH

	lhld	RECCNT		;bump record count
	inx	h
	shld	RECCNT

	mvi	d,0		;initialize checksum

	mov	b,c		;record length
	call	HEX8		;send byte as 2 hex digits

	lhld	RECADR		;record address
	call	HEX16		;send 4 hex digits

	mvi	b,0		;update record address
	dad	b
	shld	RECADR

	call	HEX8		;b=0: record type 0

	mov	a,c		;test for 0-byte record
	ora	a
	jz	RECCS

	lxi	h,TXBUF		;loop to send data, clear c

RECLP:	mov	b,m
	call	HEX8
	inx	h
	dcr	c		;endS with C=0=byte count
	jnz	RECLP

RECCS:	xra	a		;compute checksum
	sub	d
	mov	b,a		;send checksum byte
	call	HEX8

	pop	d
	pop	h

	mvi	b,CR		;ending CRLF
	call	OUTCH
	mvi	b,LF
	jmp	OUTCH

;***Subroutine*************
;Print record count message
;On Entry:
;  RECCNT = record count
;On Exit:
;  TXPORT = 0
;  psw,b,d,hl trashed
;**************************
PRNRC:	call	CILPRT
	db	'Records:',' '+80h

	lhld	RECCNT
	jmp	CHEX16

;***Subroutine***************************************
;Print ' address: ', followed by hl in hex on console
;On Entry:
;  hl = value to print
;On Exit:
;  TXPORT = 0
;  psw,b,d, trashed
;****************************************************
AHEX16:	call ILPRNT
	db	' address:',' '+80h

;Fall into CHEX16

;***Subroutine*************
;Print hl in hex on console
;On Entry:
;  hl = value to print
;On Exit:
;  TXPORT = 0
;  trashes psw,b
;**************************
CHEX16:	push	d
	xra	a		;hex to console
	sta	TXPORT
	call	HEX16
	pop	d
	ret

;***Subroutine******************************************
;Send hl as 4 hex digits
;Note: this is used to send addresses to the serial port
;and to send the record count to the console.
;On Entry:
;   d = checksum so far
;  hl = value to print
;  TXPORT = 0 for console, 1 for serial port
;On Exit:
;  Checksum in d has been updated
;  trashes psw,b
;*******************************************************
HEX16:	mov	b,h		;high byte 1st
	call	HEX8
	mov	b,l

;Fall into HEX8

;***Subroutine******************************
;Send b as 2 hex digits
;On Entry:
;   b = value to print
;   d = checksum so far
;  TXPORT = 0 for console, 1 for serial port
;On Exit:
;  Checksum in d has been updated
;  trashes psw,b
;*******************************************
HEX8:	mov	a,b		;get chr
	add	d		;add this byte to checksum
	mov	d,a		;D saveS checksum
	mov	a,b		;get chr again
	push	psw		;save a = chr low nibble
	rrc			;movE the high 4 down
	rrc
	rrc
	rrc
	call	HEX4		;send one hex digit
	pop	psw		;recover chr in a

;Fall into HEX4

;---Local Subroutine------------------------
;Send a<3:0> as a hex digit
;On Entry:
;   a<3:0> = value to print
;  TXPORT = 0 for console, 1 for serial port
;On Exit:
;  trashes psw,b
;-------------------------------------------
HEX4:	ani	0Fh		;Four on the floor
	adi	48		;we WORK with aSCII here
	cpi	58		;0-9?
	jc	OUTB		;yup
	adi	7		;make it a letter

OUTB:	mov	b,a		;output from reg b

;Fall into OUTCH

;***Subroutine*******************************
;Print character either to the console
;or to the serial port (pseudoport 1)
;On Entry:
;   b = value to print
;  TXPORT = 0 for console, 1 for serial port
;On Exit:
;  trashes psw,b
;********************************************
OUTCH:	lda	TXPORT
	ora	a		;console?
	jnz	AOUT		;pseudoport in a

	mov	a,b		;CONOUT wants chr in a
	jmp	CONOUT		;trashes b

;***Subroutine*************************************
;Print CR,LF, inline string
;The call to CILPRT is followed by an ASCII string.
;The last string byte has its msb set.
;trashes psw,b
;**************************************************
CILPRT:	call	ILPRNT
	db	CR+80h

;Fall into ILPRNT

;***Subroutine*************************************
;Print inline string
;The call to ILPRNT is followed by an aSCII string.
;The last string byte has its msb set.
;trashes psw,b
;**************************************************
ILPRNT:	xthl			;Save hl, get msg addr

IPLOOP:	mov	a,m
	ani	7Fh		;strip end marker
	call	CONOUT		;print byte (trashes b)
	mov	a,m		;end?
	inx	h		;Next byte
	ora	a		;msb set?
	jp	IPLOOP		;Do all bytes of msg

	xthl			;Restore hl,
				;..get return address
	ret

;***Subroutine******************************************
;Check for abort key from user and abort if so
;On Entry:
;  SAVESP = incoming stack pointer
;On Exit:
;  Z set: no character
;  Z clear: a = received character, with parity stripped
;*******************************************************
CHKUSR:	call	CONST		;user type anything?
	rz			;N: Done

	call	CONIN		;get the chr
	ani	7Fh		;strip parity
	jz	UABORT		;MODE/SELECT key aborts too

	cpi	ESC		;ESCAPE means quit
	rnz			;ignore all else

;User abort

UABORT:	call	CILPRT
	db	'ABOR','T'+80h
	jmp	ABORT		;fixes stack

;***Exit*************************
;Address error handler
; exit via RETURN1 to end load
;********************************
ADERR:	call	CILPRT
 db 'ADDRES','S'+80h

	jmp	ERRABT		;fixes stack

;***Exit*************************
;Checksum error handler
; exit via RETURN1 to end load
;********************************
CSERR:	call	CILPRT
 db 'CHECKSU','M'+80h

	jmp	ERRABT		;fixes stack

;***Exit*************************
;Hex character error handler
; exit via RETURN1 to end load
;********************************
HEXERR:	call	CILPRT
 db 'HE','X'+80h

;Fall into ERRABT

;***Exit*************************
;End of error handler
; exit via RETURN1 to end load
;********************************
ERRABT:	call	ILPRNT
	db	' ERRO','R'+80h

;Fall into ABORT

;***Exit***************************
;Repair stack, and return to PTDOS
;via the RETURN1 (EOF) return point
;**********************************
ABORT:	lhld	SAVESP		;initial stack pointer
	sphl

;Fall into DOREW

;***Driver Routine****************
;REWIND
;PTDOS doc says sequential device
;drivers should return with hl = 0
;On Exit:
;  RETURN1 = normal return
;  hl = 0
;*********************************
DOREW:	lxi	h,0
	ret

;********************************************
;RAM variables, initialized to 0 during DOINI
;********************************************

RABEG:	equ	$

SAVESP:	ds	2		;SP save for Rx abort

;Transmit flags and variables

TXPORT:	ds	1		;out to console or pseudoport
TXOPEN:	ds	1		;<>0: transmit file incomplete
				;..must follow TXPORT
RECUBC:	ds	1		;Tx record unsent byte count

;Receive flags and variables

RXDONE:	ds	1		;<>0: Rx hex data is in RXBUF
HRTYPE:	ds	1		;received hex record type
RECCNT:	ds	2		;received record count
BASADR:	ds	2		;incoming address offset
RECADR:	ds	2		;record address
RXBC:	ds	2		;highest addr of incoming hex

RAEND:	equ	$		;end of initialized variables

;Transmit buffer (holds one hex record's data)

TXBUF:	ds	HRMAX		;Intel hex transmit record buffer

	end

