.TITLE	Hxload -     Load ASCII hex files in KIM format
.SBTTL	Author -     Antonino N. J. Mione 
 
;
;		Edit #	Date		Description
;		------	----		-----------
;
 
.SBTTL	Macro definitions

	.macro nasc str opt
	  .irpc	chr <str>
	    .byte ''chr!$80
	  .endr
	  .ifnz <opt>
	    .byte $00
	  .endc
	.endm

.SBTTL	Definitions

;	Zero-page definitions
 
saddr	=	0		; Work area for addresses
count	=	2		; Character count of line
laddr	=	3		; Start-of-line address
ysav	=	5		; Save area for y-reg
lind	=	6		; Line index
hold	=	7		; Hold area for nibbles
cswl	=	$36		; Address of character out routine
cswh	=	$37		;		...
kswl	=	$38		; Address of keyin routine
kswh	=	$39		;		...

;
;	Monitor and DOS routine addresses
;

slot	=	$c200		; Slot where modem card resides (default=2)
home	=	$fc58		; Mon routine - clear screen
nxtchr	=	$fd75		; Mon routine - get line of input from keyboard
prbl3	=	$f94c		; Mon routine - print A reg and (X)-1 blanks
prbyte	=	$fdda		; Mon routine - print A-reg as hex
prerr	=	$ff2d		; Mon routine- print 'err'
rdkey	=	$fd0c		; Mon routine - read a key from input device
keyin	=	$fd1b		; Mon routine - read a key from keyboard
cout	=	$fded		; Mon routine - output char through output dev
cout1	=	$fdf0		; Mon routine - output char to screen
dos	=	$03d0		; Warmstart address of DOS
 
;
;	Definitions
;
 
inbuf	=	$0200		; Monitor input buffer address
cr	=	$8d		; <cr> with H.O. bit on
lf	=	$8a		; <lf> with H.O. bit on
semi	=	$bb		; Semicolon with H.O. bit on
space	=	$a0		; <sp> with H.O. bit on
 
;
;	Start of assembly address
;
 
	.=$2000
	.PHASE	$9000

st:	jmp	start		; Jump past data to start of code
 
;
;	Working storage definitions
;
 
chksum:	.byte	$00,$00		; Checksum for line
hxwrk1:	.byte	$00		; Hold area
mess1:	nasc	<ENTER FILENAME TO LOAD > 1	; Prompt message for filename
mess2:	nasc	<TYPE > 1		; 'TYPE ' command for most DECsystems
mess3:	nasc	<CHECKSUM ERROR> 1		; 'Checksum' error message
fnam:	.blkb	$1e		; Filename buffer
fnlen	=	.-fnam		; Length of filename buffer
 
;
;	Start of program
;
  
start:	jsr	home		; Clear the screen
	ldx	#mess1\		; Get the address of the prompt message
	ldy	#mess1^		;		...
	jsr	prstr		; Print the message
	ldx	#$00		; Make sure X is zero for the monitor call
	jsr	nxtchr		; Go get the filename
	stx	count		; Store the index as the character count
	ldx	#fnlen-1	; Get the buffer length
	lda	#space		; Use spaces to clear the buffer
fill1:	sta	fnam,x		; Stuff a space
	dex			; Decrement the count
	bmi	movfl1		; If we are done, go move the filename
	jmp	fill1		; Otherwise, continue clearing buffer
movfl1:	ldx	count		; Get the count
	lda	#$00		; Stuff a null just past the filename
	sta	fnam,x		;		...
	dex			; Decrement the count
movfl2:	lda	inbuf,x		; Get a char from input buf (last char first) 
	sta	fnam,x		; Stuff it in the filename buffer
	dex			; Decrement the count
	bmi	sendcm		; If we are done, send the message
	jmp	movfl2		; Otherwise, continue
sendcm:	lda	#slot\		; Change the output device to the prot
	sta	cswl		;		...
	lda	#slot^		;		...
	sta	cswh		;		...
	ldx	#$00		; Zero X
snloop:	lda	mess2,x		; Start with the command 'type'
	cmp	#$00		; Done?
	beq	snl2		; If so, go do the filename
	jsr	cout		; Stuff a character at the port
	inx			; Increment the buffer index
	jmp	snloop		; Loop for another character
snl2:	ldx	#$00		; Zero X
snl3:	lda	fnam,x		; Get a character from the filename
	cmp	#$00		; Is it a null?
	beq	snl3a		; If so, go try to receive the file
	jsr	cout		; Otherwise, stuff the character at the port
	inx			; Increment the buffer index
	jmp	snl3		; Loop for another character
snl3a:	lda	#cr		; Get a <cr>
	jsr	cout		; 	and print it
snl4:	lda	#cout1\		; Redirect output to be to the screen...
	sta	cswl		;		...
	lda	#cout1^		;		...
	sta	cswh		;		...
	lda	#slot\		;	and input to be from the port
	sta	kswl		;		...
	lda	#slot^		;		...
	sta	kswh		;		...
receve:	jsr	rdkey		; Get a character from the port
	jsr	cout		; Print the character
	cmp	#semi		; Is it the beginning of a line?
	bne	receve		; If not, try again
	jsr	doline		; Otherwise, read a line of ascii hex
	jmp	receve		; If that didn't skip, go back for next line
	lda	#keyin\		; Doline skipped, we are done
	sta	kswl		; Redirect input to be from keyboard
	lda	#keyin^		;		...
	sta	kswh		;		...
	jmp	dos 		; Jump to the DOS warmstart address

;
;	Doline routine
;
 
doline:	ldy	#$00		; Zero the
	sty	lind		;	load point index
	sty	chksum		;	and the checksum
	sty	chksum+1	;		...
	lda	#cr		; Start by printing a space
	jsr	cout		;		...
 
dolin1:	jsr	getbyt		; Get a hex byte
	pha			; Save it on the stack
	jsr	prbyte		; Display it on the screen
	pla			; Fetch it back
	cmp	#$00		; Is it zero?
	bne	norskp		; If not, continue gobbling line
	jmp	rskp		; Otherwise, return with a skip
norskp:	pha			; Save the character on the stack
	lda	#space		; Get a space and
	jsr	cout		;	print it
	pla			; Fetch the character back
	jsr	chksad		; Add it to the checksum
	sta	count		; The first byte is a count, store it
	jsr	getbyt		; Get the next hex byte
	jsr	chksad		; Add it to the checksum
	sta	laddr+1		; That is the H.O. byte of the load address
	jsr	prbyte		; Print the hex on the screen 
	jsr	getbyt		; Get the next byte
	jsr	chksad		; Add that into the checksum
	sta	laddr		; That is the L.O. byte of the load address
	jsr	prbyte		; Print that byte on the screen
dolin2:	ldx	count		; Fetch the character count
dolin3:	dex			; Decrement the count
	bmi	dolchk		; If we are done, do the checksum stuff
	jsr	getbyt		; Get a byte
	ldy	lind		; Get the load point index
	jsr	chksad		; Add the data into the checksum
	sta	(laddr),y	; Stuff the data where it belongs
	jsr	prbyte		; Print the byte out as hex
	inc	lind		; Up the load point index once
	jmp	dolin3		; Loop for more data
dolchk:	jsr	getbyt		; Get another byte from the port
	pha			; Stuff it on the stack
	jsr	prbyte		; Print it out on the creen
	pla			; Fetch back the character
	cmp	chksum+1	; Compare that with the checksum
	bne	chkerr		; If they don't match, we have problems
	lda	chksum+1	; Get the H.O. byte of the checksum
	jsr	prbyte		; Print it as hex
	lda	#space		; Get a space
	jsr	cout		; Print that out next
	jsr	getbyt		; Get another byte from the port
	pha			; Hold it on the stack
	jsr	prbyte		; Print the byte on the screen
	pla			; Fetch the data back
	cmp	chksum		; Compare that to the L.O. byte of the checksum
	bne	chkerr		; If they don't match, tell the user
	lda	chksum		; Fetch the L.O. byte of the checksum
	jsr	prbyte		; Print that as hex
	rts			; Return, this line worked good
 

chksad:	pha			; Save the accumulator accross this call
	clc			; Clear the carry flag for addition
	adc	chksum		; Add in the checksum
	sta	chksum		; Stuff that back
	lda	chksum+1	; Adjust the H.O. byte of the checksum
	adc	#$00		;	by whatever is in the carry
	sta	chksum+1	;		...
	pla			; Restore the AC
	rts			; Return

chkerr:	ldx	#mess3\		; Get the address of the 'checksum error' mess.	ldy	#mess3^		;		...
	jsr	prstr		; Print that error text
	rts			; Return

;
;	Getbyt routine
;
 
getbyt:	jsr	getnib		; Get the first nibble of the two
	sta	hold		; Stuff that here
	jsr	getnib		; Get the second nibble
	sta	hold+1		; Hold that here
	lda	hold		; Get the first of the two
	asl	a		; Shift it to the left side
	asl	a		;		...
	asl	a		;		...
	asl	a		;		...
	ora	hold+1		; Combine that with the second nibble
	rts			;	and return
 
;
;	Getnib routine
;
 
getnib:	jsr	rdkey		; Read a character from the port
	cmp	#$b0		; Is it in the proper range?
	bmi	badchr		;	0<=char<=f
	cmp	#$ba		;		...
	bmi	getnb3		; This is good, no special adjustment
	cmp	#$c1		;		...
	bmi	badchr		;		...
	cmp	#$c7		;		...
	bmi	getnb2		; This is good, but adjust it down a bit
	jmp	badchr		; Bad character in input
 
getnb2:	sec			; Set the carry flag
	sbc	#$07		; Adjust the letters into the proper range
 
getnb3:	sec			; Set the carry flag
	sbc	#$b0		; Make the character numeric
	rts			;	and return
 
badchr:	pla			; Pull 3 return addresses off the stack
	pla			;		...
	pla			;		...
	pla			;		...
	pla			;		...
	pla			;		...
	jmp	receve		; Try to recover from next line

 
;
;	Prstr subroutine
;
 
prstr:	stx	saddr		; Store the address in a work area
	sty	saddr+1		;		...
	ldy	#$00		; Zero Y
 
prst1:	lda	(saddr),y	; Get a byte to print
	beq	prsdon		; If it is a null, we are done
	jsr	cout		; Output the character
	iny			; Increment the index
	beq	prsdon		; If this wraps around, we are done
	jmp	prst1		; Loop for another character
 
prsdon:	rts			; Return
 
;
;	Rskp - do a skip return
;

rskp:	sta	hxwrk1		; Hold the accumulator
	pla			; Fetch the L.O. byte of the return address
	tax			; Put that in X
	pla			; Get the H.O. byte of the return address
	tay			; Stuff that in Y
	txa			; Get the L.O. byte back again
	clc			; Clear the carry flag
	adc	#$04		; Add in 4 (3 byte inst. + 1 for not doing rts)
	bcc	rskp2		; If there was no carry, continue
	iny			; Increment the H.O. byte
rskp2:	sta	saddr		; Store the L.O. byte
	sty	saddr+1		;	and the H.O. byte
	lda	hxwrk1		; Get back the old value in the AC
	jmp	(saddr)		; Jump to the new return address

;
;	End of HEXLD source
;

	.DEPHASE

	.end
