	title "ME5204 Programmer Firmware Rev 2.01"
	subtitle "Copyright (C) 2015 Martin Eberhard"
	list b=4, c=132, n=80
	errorlevel 1		;eliminate all the 'not in bank 0' messages

;********************************************************************************
;* MM5204 Programmer Firmware													*
;*																				*
;*Revision    Date        Author         Comments								*
;*	1.00    27 NOV 12  M. Eberhard	Created from ME1702A rev 1.07				*
;*  2.00	09 JAN 13  M. Eberhard	Rewritten to work with Loader Kernel 1.0. 	*
;*									This allows serial port firmware updates.	*
;*  2.01	18 AUG 15  M. Eberhard  Add automatic address offset. Improve some	*
;*									messages.									*
;*																				*
;* To Do (Maybe):																*
;*   + Translate RV to actual voltages, in decimal								*
;*   + Record counts, programming cycles in decimal								*
;*   + Power-on Self Test														*
;********************************************************************************
; Major revision must change if the firmware is not fully compatable with		*
; previous revisions.															*
;********************************************************************************
REV_MAJOR			equ	0x02	;major revision number. Max = 9
REV_MINOR			equ	0x01	;minor revision number. Each nibble: max = 9

;********************************************************************************
; Specify the required Kernel Loader revision level. An error message will be	*
; printed if this does not match the kernel in FLASH.							*
;********************************************************************************
REQUIRED_KERN_REV	equ	0x10	;only the most significant nibble counts

;********************************************************************************
;*								GENERAL NOTES									*
;* This program assumes the PIC is clocked at 16 MHz, meaning a 4 MHz CPU       *
;* operating speed, or 0.25 uS instruction cycle time. The internal oscilator	*
;* has an accuracy of +/- 10%, which must be considered in all timing.			*
;*																				*
;* Page references in the comments refer to the appropriate pages in Microchip	*
;* document DS41452B "PIC16(L)F1516/7/8/9 Data Sheet"							*
;*																				*
;* RAM Usage: Most variables are kept in Common RAM, available in all banks.	*
;* Most of the banked RAM is used as queues and buffers, accessed as  Linear	*
;* RAM (address 0x2000 and up) via FSR0 and FSR1.								*
;*																				*
;* Because ^C and some hex errors abort rudely (without fixing the stack), the	*
;* stack overflow/underflow protection in config2 must be disabled.				*
;*																				*
;* Kernal calls are used for all UART communication, stalls, and some other		*
;* functions. See the file KernelMemory.inc for kernel subroutines.				*
;*																				*
;* See Help message at the end for commands and their operation.				*
;********************************************************************************
	page
;********************************************************************************
;* Include standard header and programmer header, and macros					*
;********************************************************************************
TRUE	equ	0xff
FALSE	equ	0

	#include	ASCII.inc
	#include	MartinsMacros.inc
	#include	ME5204.inc
	#include	KernelMemory.inc

;################################################################################
;# Initialization Routine														#
;#   just what the Kernel didn't initialize										#
;################################################################################
	org	LOADED_CODE

;disable all interrupts while we set things up
			clrf	INTCON					;disable interrupts while we work

;Set up the fixed voltage reference for the ADC (Page 137)
;fixed voltage reference enabled,  4.096V reference
			movlb	0x02					;Initialize Bank 2 registers
			movlw	(1 << FVREN) | (1 << ADFVR0) | (1 << ADFVR1)
			movwf	FVRCON

;Set up the A/D Converter (page 142)
			movlb	0x01

			clrf	ADCON0					;ADC disabled for now (Page 147)

			movlw	0x60					;left-justified result (page 144)
			movwf	ADCON1					;Fosc/64
											;Vref connected to Vdd(Page 148)

;initialize all reset source detectors (Page 73)
			movlw (1 << NOT_BOR) | (1 << NOT_POR) | (1 << NOT_RI) | (1 << NOT_RMCLR) | (1 << NOT_RWDT)
			movwf	PCON

		;Disable timers, weak pullups
			movlw	0x80					
			movwf	OPTION_REG				;disable weak pullups and timer control pin (page 157)

; initialize the I/O ports that were not initialized by the Kernel
; per the example on page 113.
			movlb	0x0
			clrf	PORTA					;EPROM read data
			clrf	PORTB					;EPROM address
			clrf	PORTD					;EPROM write data

			movlb	0x2
			movlf	LATA_INIT,LATA
			movlf	LATB_INIT,LATB
			movlf	LATD_INIT,LATD

		;disable brown-out circuitry
			clrf	BORCON					;disable brown-out circuitry (page 69)

			movlb	0x03
			clrf	ANSELA
			clrf	ANSELB
			clrf	ANSELD

			movlb	0x01
			movlf	TRISA_INIT,TRISA
			movlf	TRISB_INIT,TRISB
			movlf	TRISD_INIT,TRISD

;set variable defaults
			movlb	VAR_BANK				;some variables are in the last bank
			clrf	ADR_OFFSET				;default to no address offset
			clrf	AO_FLAGS				;default to automatic address offset

			clrf	PROG_CYCLES				;default to smart algorithm
			movlw	(1 << ECHO_STATE)		;default to echo on
			movwf	FLAGS

;Clear 512-byte EPROM buffer, which starts on an even page boundary
			movlw	high(EPROM_BUF)			;point FSR1 to beginning of buffer
			movwf	FSR1H
			clrf	FSR1L					;EPROM_BUF is page-aligned

			movlw	high(EPROMBUF_SIZE)		;Set COUNT up as loop counter
			movwf	COUNT_HIGH
			clrf	COUNT_LOW				;buffer size is a multiple of 256 bytes

init_ceb_loop:	clrf	INDF1				;clear buffer byte
				call	next_a_count
				btfss	STATUS,Z			;bump address and counter, test for 0
				bra		init_ceb_loop

;re-initialize the receive queue in case of trash from the loader
	if	low(RX_QUEUE)
			movlw	low(RX_QUEUE)
			movwf	RQ_IPTR
			movwf	RQ_OPTR
	else
			clrf	RQ_IPTR
			clrf	RQ_OPTR
	endif

;re-initialize the transmit queue in case of trash from the loader
	if	low(TX_QUEUE)
			movlw	low(TX_QUEUE)
			movwf	TQ_IPTR
			movwf	TQ_OPTR
	else
			clrf	TQ_IPTR
			clrf	TQ_OPTR
	endif

;initialize interrupt flags: queues are empty, echo is enabled
			movlw	(1 << TQ_EMPTY)
			movwf	INT_FLAGS

			movlb	0x03
			movfw	RCREG						;chuck any junk in the UART receiver
			movfw	RCREG						;it's a double-buffer

;Enable interrupts - we're ready to go (Page 80)
			movlw	(1 << PEIE) | (1 << GIE)	;peripheral ints, global enable only
			movwf	INTCON

;verify that this firmware matches the programmer
			call	K_PROG_ID
			xorlw	ME5204ID
			btfss	STATUS,Z
			goto	wrong_programmer

; verify that this firmware matches the kernel in FLASH. We only care about the
; major revision level - minor revision levels shouldn't affect compatibility.
;(This will work so long as the wrong kernel's K_PRINTF hasn't changed.) Continue
; running even with the wrong kernel revision.
			call	K_KERN_REV			;returns revision in W
			andlw	0xf0				;only care about major revision
			xorlw	REQUIRED_KERN_REV
			movlw	wrongkern_msg_index
			btfss	STATUS,Z
			call	cr_printf

; print sign-on message
			movlw	signon_msg_index

; fall into epwroff_print, to test powered-off voltages

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common Routine
;    Print message at W, shut off power to EPROM, check voltages, go to main
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
epwroff_print:	call	printf
				call	epower_off

				movlw	cr_msg_index

;fall into print_main

;################################################################################
;# Main Program Loop															#
;#   Wait for a complete (CR-terminated) line of input from the user, and stash	#
;#     the entire line in the Line Buffer										#
;#   Parse the line for commands and variables									#
;#   Branch to the appropriate command-processing routine based on the first	#
;#     character from the user. (Further decoding is done in the first-level	#
;#     command processing routines.)											#
;#   All command processing routines branch back to main except the two hex		#
;#     download routines, which branch back to no_prompt between hex records.	#
;################################################################################
print_main:		call	printf				;entry here prints string at W first

; Print prompt on a new line
main:			movlw	CR					;this will print CR LF
				call	K_CONOUT
				movlw	'>'					;prompt character
				call	K_CONOUT

;clear counters used for intel hex and motorola S-record file downloads
				clrf	R3					;R3 counts bad hex records
				clrf	R4					;R4 counts hex records
				clrf	COUNT_HIGH			;COUNT_HIGH counts records in selected page

;-----------------------------------------------------------------
; Get a complete, CR-terminated line into the line buffer
;
; Entry at no-prompt is used after receiving a hex data record, so
; that a prompt does not get printed between records, and so that
; the error and record counters continue to accumulate.
;-----------------------------------------------------------------
no_prompt:		bcf		FLAGS,ERR_FLAG		;no errors yet
				bcf		FLAGS,B_STATE		;not yet a blank-check, S5 record, or a checksum command
				call	get_line			;get a line from the user into the LINE_BUF
		
;------------------------------------------------------
; Get & save the 1st character from the command line.
; Test for hex download command immediately, for speed.
;------------------------------------------------------
				call	get_uchr			;result is in W and R0
				movwf	R2					;save 1st command chr in R2

				xorlw	':'					;beginning of Intel Hex record?
				btfsc	STATUS,Z
				bra		cmd_intel

				xorlw	':' ^ 'S'			;beginning of Motorola S-record record?
				btfsc	STATUS,Z
				bra		cmd_motorola

				btfsc	FLAGS,CR_FLAG		;is this line just a CR?
				bra		main				;yes: just ignore the line.

;------------------------------------------------------
; Get & save the 2nd character from the command line.
; The 2nd chr will be CR if that's what was typed. This
; will get caught as an error later.
;------------------------------------------------------
				call	get_uchr			;result is in W and R0
				movwf	R4					;save second chr of command in R4
			
;--------------------------------------------------------------------------
; Get the first two 16-bit hex parameters that came with the 2-character
; command. get_param will return default 0x0000  and INP_FLAG=0 if no
; parameter was provided, and will abort directly to main on bogus hex.
;--------------------------------------------------------------------------
;Get the first parameter and store it in FSR1. Set INP_FLAG if a parameter
;was provided, and clear it if not. The first parameter is usually the address.
;sometimes it is a 16-bit value, and sometimes it is a '0' or '1' input.
				bsf		FLAGS,DFLT_FLAG			;assume we will default
				call	get_param				;get the 1st parameter

				btfsc	FLAGS,INP_FLAG			;any input provided?
				bcf		FLAGS,DFLT_FLAG			;y: do not default

				movfw	R1
				movwf	FSR1L
				movfw	R3
				movwf	FSR1H

;test to see if the first parameter (the address) is greater than the max allowed
				sublw	(high(EPROMBUF_SIZE))-1	;carry cleared if FSR1H > high(EPROMBUF_SIZE)-1
				btfss	STATUS,C
				bra		cmd_error			;error if greater than max address

;translate address to be a buffer address
				movlw	high(EPROM_BUF)		;point to buffer instead of EPROM
				addwf	FSR1H,F

;get second parameter, which is a byte count, if it exists. Store it in COUNT_HIGH and COUNT_LOW
				call	get_param

				movfw	R1
				movwf	COUNT_LOW
				movfw	R3
				movwf	COUNT_HIGH

;if count is 000, set it to the EPROM size and remember that the range is an entire EPROM
				bcf		FLAGS,FULL_FLAG		;assume partial EPROM operation

				iorwf	COUNT_LOW,W			;set Z if both bytes were 00

				movlw	high(EPROMBUF_SIZE)
				btfsc	STATUS,Z
				movwf	COUNT_HIGH

				btfsc	STATUS,Z
				bsf		FLAGS,FULL_FLAG		;complete EPROM operation (no 'range' in messages)

;test to see if second parameter (the count) is greater than allowed
				subwf	COUNT_HIGH,W		;carry cleared if high(EPROMBUF_SIZE)>COUNT_HIGH
				btfss	STATUS,C
				bra		param2_ok			;below 200h

				iorwf	COUNT_LOW,W			;exactly equal to 200h?
				btfss	STATUS,Z
				bra		cmd_error			;no: count is too big

;copy the input parameters in save locations, so they can be re-used
param2_ok:		call	save_params			;make copies of FSR and COUNT

;-----------------------------------------------------------------------
; go execute a 2-character command, based on the first character.
; R2 = 1st command chr
; R4 = 2nd command chr
; FSR1H, FSR1L = 1st parameter that came with the command
; COUNT_HIGH, COUNT_LOW = 2nd  parameter that came with the command
; DFLT_FLAG = 1 if no parameters came with the command.
; FULL_FLAG = 1 if the byte count defaulted to the full EPROM size
; All of these command-processing routines jump to main when done
;-----------------------------------------------------------------------
			movfw	R2

			xorlw	'?'					;help command?
			btfsc	STATUS,Z
			bra		cmd_help

			xorlw	'?' ^ 'A'			;Address Offset command?
			btfsc	STATUS,Z
			goto	cmd_ao

			xorlw	'A' ^ 'D'			;Display command?
			btfsc	STATUS,Z
			goto	cmd_display

			xorlw	'D' ^ 'E'			;EPROM command?
			btfsc	STATUS,Z
			goto	cmd_eprom

			xorlw	'E' ^ 'F'			;Fill buffer command?
			btfsc	STATUS,Z
			goto	cmd_fill

			xorlw	'F' ^ 'M'			;Modify buffer command?
			btfsc	STATUS,Z
			goto	cmd_modify

			xorlw	'M' ^ 'P'			;Programming cycles cmd?
			btfsc	STATUS,Z
			goto	cmd_pc

			xorlw	'P' ^ 'R'			;Read or Reset command?
			btfsc	STATUS,Z
			goto	cmd_readres

			xorlw	'R' ^ 'T'			;Test command?
			btfsc	STATUS,Z
			goto	cmd_test

			xorlw	'T' ^ 'U'			;Upload command?
			btfsc	STATUS,Z
			goto	cmd_upload

			xorlw	'U' ^ 'W'			;write command?
			btfsc	STATUS,Z
			goto	cmd_write

;no match: fall into cmd_error

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common Routine
;   Bad command of some sort - print command error message and go to main
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cmd_error:		movlw	bad_input_msg_index			;indicate error
				bra		print_main

;********************************************************************************
;* Command Processing Routines
;* On Entry to all of these routines:
;*    R2 = first byte of command
;*    R4 = second byte of command, if there was one, CR otherwise
;*    FSIRL & FSR1H = first user parameter, if one was entered, 0 otherwise.
;*    COUNT_LOW & COUNT_HIGH = second user parameter, if one was entered,
;*    200 otherwise.
;* All these routines return to main, except the download commands, which return
;* to no_prompt between records.
;********************************************************************************
;******=========================================================================*
;* Rx * Read Data, Read Voltages, or Reset command
;******
;*==============================================================================*
cmd_readres:	movfw	R4

				xorlw	'D'				;RD is Read Data
				btfsc	STATUS,Z
				bra		rcmd_read

				xorlw	'D' ^ 'E'		;RE is Reset
				btfsc	STATUS,Z
				reset					;* RE * just reset right here

				xorlw	'E' ^ 'V'		;RV is Read Voltages
				btfss	STATUS,Z
				goto	cmd_error		;anything else is an error

;fall into rcmd_volts

;******-------------------------------------------------------------------------*
;* RV * Read Voltages Command
;******
; Use A/D converters to read and display some voltages
; Display the raw hex for now - convert to voltage in a future version
;*------------------------------------------------------------------------------*
rcmd_volts:	

;get and report negative supply voltage
				movlw	AN_VNEG | (1 << ADON)
				call	get_analog
				movlw	vnegequals_msg_index
				call	rep_analog

;get and report VSS
				movlw	AN_VSS | (1 << ADON)
				call	get_analog
				movlw	vssequals_msg_index
				call	rep_analog

;get and report VBB
				movlw	AN_VBB | (1 << ADON)
				call	get_analog
				movlw	vbbequals_msg_index
				call	rep_analog

				goto	main

;******-------------------------------------------------------------------------*
;* RD * Read EPROM Data Pins Command
;******
; simply reads the eprom data bus and reports the results - 
; this is a test command, not meant for reading the EPROM in general
; EPROM power, address, etc. are not set up prior to a read.
;*------------------------------------------------------------------------------*
rcmd_read:		movlw	dataread_msg_index		;print banner
				call	cr_printf

;read the EPROM RDN data and report result
				call	eread_data		;result in R2 & W
				call	K_PRINTHEX2		;print it

				goto	main

;*****==========================================================================*
;* ? *  Help Command
;*****
; Displays one of several messages, based in the next character
; No parameters
;*==============================================================================*
cmd_help:		movlb	0x03				;PMADRL and PMADRH are in bank 3

;Decode the various help commands, based on the chr in R0
				movfw	R4
				xorlw	CR
				movlw	help_msg_index		;'?' with no 2nd chr means plain help
				btfsc	STATUS,Z
				goto	print_main
				
				movfw	R4
				xorlw	'B'					;help with the buffer commands?
				movlw	bhelp_msg_index
				btfsc	STATUS,Z
				goto	print_main

				movfw	R4
				xorlw	'C'					;secret copyright command?
				movlw	copyright_index
				btfsc	STATUS,Z
				goto	print_main

				movfw	R4
				xorlw	'D'					;help with the diagnostic commands?
				movlw	dhelp_msg_index
				btfsc	STATUS,Z
				goto	print_main

				movfw	R4
				xorlw	'E'					;help with eprom commands?
				movlw	ehelp_msg_index
				btfsc	STATUS,Z
				goto	print_main

				movfw	R4
				xorlw	'F'					;help with download commands?
				movlw	fhelp_msg_index
				btfsc	STATUS,Z
				goto	print_main

				movfw	R4
				xorlw	'L'					;help ME Loader?
				movlw	lhelp_msg_index
				btfsc	STATUS,Z
				goto	print_main

				goto	cmd_error			;unrecognized command

;*****==========================================================================*
;* S *  Incoming Motorola S-Record Command
;*****
; Valid Motorola S-record follows. ('S' already received)
; This routine validates a Motorola S-record, puts the payload into the buffer,
; and checks the checksum. The prompt is not printed after a data record, so
; that we can keep up with reception.
; Supports S1 (data), S5 (record count), and S9 (end-of-file) record types.
; S9 records can be either a complete S9 record, or just 'S9', as you find
; in some S-record files for (for example) the Altair 680
; B_STATE flag is used to remember that we are doing an S5 record
; On Entry:
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	COUNT_HIGH is a count of records loaded into the buffer so far
;   B_STATE = 0: not yet a blank-check, S5 record,  or a checksum command
;   ERR_FLAG = 0
;   AO_MANUAL = 1 if the address offset is manual, 0 if automatic
;   AO_INIT = 1 if the automatic address offset has been set
;   ADDR_OFFSET = the address offset
;   EPROM power is off
; On Exit:
;   W, R0, R1, R2, FSR1 trashed
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	COUNT_HIGH is a count of records loaded into the buffer so far
;*==============================================================================*
cmd_motorola:	bcf		FLAGS,INTEL_STATE	;not Intel, but Motorola
;start Motorola checksum at 1 so that 00 is a good result at the end
				movlf	0x01,HEX_CKSUM

;Get & validate single-character Motorola record type
				call	get_chr				;get W = record type from line buffer

				xorlw	'1'					;S1 data record?
				btfsc	STATUS,Z
				bra		mot_s1

				xorlw	'1' ^ '9'			;S9 EOF record?
				btfsc	STATUS,Z
				goto	mot_s9				;none of the above: error

				xorlw	'9' ^ '5'			;S5 record-count record?
				btfss	STATUS,Z
				bra		rec_error			;no: unsupported record type

;*------------------------------------------------------------------------------*
; This is a Motorola S5 Record-Count record
; Get the rest of the record - B_STATE will tell us to check the record-count
; when done.
;*------------------------------------------------------------------------------*
mot_s5:			bsf		FLAGS,B_STATE		;remember that this is an S5 record

;get byte count and subtract 3 to account for address and checksum bytes
;The actual number of data bytes for S5 and S9 records must be 0
mot_nodatarec:	call	get_byte_cksum		;get a hex byte into W & R1
				movlw	0x03
				subwf	R1,W				;W has record byte count
				btfsc	STATUS,Z			;byte count must be 0
				bra		download_rec		;0 data bytes: good S5 or S9 record so far

;fall into rec_error if not 0 data bytes

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common Routines: Report bad hex record of some sort, and go get another record
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
rec_error:		movlw	recerror_msg_index		;indicate record error
				bra		ha_error

hex_error:		movlw	hexerror_msg_index		;indicate hex error
				bra		ha_error

s5_error:		movlw	rcnterror_msg_index		;indicate bad S5 record
				bra		ha_error

cksum_error:	movlw	cksumerror_msg_index	;indicate checksum error

ha_error:		incf	R3,F					;bump bad record count
				call	printf					;print error msg without leading CR

				bra		dr_next_record

;*------------------------------------------------------------------------------*
; This is a Motorola S1 (data) record 
; Get the byte count and subtract 3 to account for address and checksum bytes
;*------------------------------------------------------------------------------*
mot_s1:			call	get_byte_cksum			;get a hex byte into W & R1
				movlw	0x03
				subwf	R1,W					;W has record byte count				
				bra		download_rec

;*****==========================================================================*
;* : * Incoming Intel Record Command
;*****
; Valid Intel Hex record follows. (Colon already received)
; Supports 00 (data) and 01 (end-of-file) record types. 0-byte data records
; are also treated as end-of-file records.
; This routine validates an Intel Hex record, puts the payload into the buffer,
; and checks the checksum. The prompt is not printed after a data record, so
; that we can keep up with reception. 
; On Entry:
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	COUNT_HIGH is a count of records loaded into the buffer so far
;   B_STATE = 0
;   ERR_FLAG = 0
;   AO_MANUAL = 1 if the address offset is manual, 0 if automatic
;   AO_INIT = 1 if the automatic address offset has been set
;   ADDR_OFFSET = the address offset
;   EPROM power is off
; On Exit:
;   W, R0, R1, R2, FSR1 trashed
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	COUNT_HIGH is a count of records loaded into the buffer so far
;*==============================================================================*
cmd_intel:		bsf		FLAGS,INTEL_STATE	;Intel, not Motorola
				clrf	HEX_CKSUM			;start Intel checksum at 0

;get record data byte count into W
				call	get_byte_cksum

;fall into download_rec

;*********----------------------------------------------------------------------*
;* S & : * Common Hex Record Download Routine
;*********
; On Entry:
;   W= number of data bytes expected in the record
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	COUNT_HIGH is a count of records loaded into the buffer so far
;   HEX_CKSUM is initialized for the particular record type
;   EPROM power is off
;   INTEL_STATE = 1 for Intel Hex record, 0 for S-Record
;   B_STATE = 1 for Motorola S5 Record, 0 otherwise
;   ERR_FLAG = 0, meaning that so far, this record fits in the buffer
;   AO_MANUAL = 1 if the address offset is manual, 0 if automatic
;   AO_INIT = 1 if the automatic address offset has been set
;   ADDR_OFFSET = the address offset
; On Exit:
;   Record data payload is in the specified place in the buffer
;   W, R0, R1, R2, FSR1 trashed
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	COUNT_HIGH is a count of records loaded into the buffer so far
; (Around 320 cycles = 80 uS for a worst-case 1-byte record. 9600 baud = 1000
; chr/sec, or about 1mS per character. We can parse an entire record between 2
; received characters, no problem.)
;*------------------------------------------------------------------------------*
download_rec:	movwf	COUNT_LOW			;save record byte count
				incf	R4,F				;bump hex record count

;get the high address byte. If the address offset is set for automatic and is
;uninitialized, then set it to be the same as this record's high address byte,
;and remember that the address offset is now initialized.
				call	get_byte_cksum		;get address high byte into W & R1

				movlb	VAR_BANK			;ADR_OFFSET & AO_FLAGS are in the variables page
				btfsc	AO_FLAGS,AO_MANUAL	;manually-set address offset?
				bra		d_rec1				;y: address offset is okay

				btfss	AO_FLAGS,AO_INIT	;automatic address offset initialized?
				movwf	ADR_OFFSET			;n: set it to this record's address

				bsf		AO_FLAGS,AO_INIT	;address offset is now initialized	
d_rec1:

;Subtract off the address offset, then add on the buffer address, and save the
;result in FSR1H. We will test the address of each received byte later, so that
;we can tolerate records that have some data within range, and some not.
				movfw	ADR_OFFSET
				subwf	R1,W				;W= high(record address) minus offset
				addlw	high(EPROM_BUF)
				movwf	FSR1H

;get address low byte and save it in FSR1L, preparing to load data
				call	get_byte_cksum		;get address low byte into W & R1
				movwf	FSR1L				;FSR1 will be used to move data

;if this is an Intel Hex record, get and validate record-type byte
				btfss	FLAGS,INTEL_STATE
				bra		dr_get_data			;no record-type byte for S-records

				call	get_byte_cksum		;get Intel Hex record type byte into R1

				movf	R1,F				;test record type for type 0
				btfsc	STATUS,Z			;zero is normal data record
				bra		dr_get_data

				decfsz	R1,F
				bra		rec_error			;any type higher than 1 is an error

;validate Intel type 1 record
				movf	COUNT_LOW,F			;byte count must be 0 for type 1 records
				btfss	STATUS,Z
				bra		rec_error

;if this is a 0-byte record, then don't load any data. Just check its checksum and call it done.
dr_get_data:	movfw	COUNT_LOW
				btfsc	STATUS,Z
				bra		dr_ckcum

				movwf	R2						;R2 will be our loop counter

;Loop to get R2 bytes of data in this record. If any of this record's data bytes
;do not fit in the buffer, then set ERR_FLAG.
dr_data_loop:		call	get_byte_cksum		;get a hex data byte into W & R1

					;test to see if this byte will miss the buffer. Clear C and set ERR_FLAG if so
					movlw	high(EPROM_BUF)		;FSR1H should be >= high(EPROM_BUF)
					subwf	FSR1H,W				;subtract buffer address offset

					sublw	high(EPROMBUF_SIZE)	;C cleared if high(EPROM address) > high(EPROMBUF_SIZE)
					btfss	STATUS,C
					bsf		FLAGS,ERR_FLAG		;remember not to count this page as sucessful

					movfw	R1					;recover received byte
					btfsc	STATUS,C			;don't write if wrong page for this byte
					movwf	INDF1				;write byte to buffer

					addfsr	1,1					;bump 16-bit buffer address pointer

					decfsz	R2,F				;bump & test loop pointer
					bra		dr_data_loop

;if all bytes in this record landed in the buffer, then count it as a successfully-loaded record.
				btfss	FLAGS,ERR_FLAG		;any data in this record not fit in the buffer?
				incf	COUNT_HIGH,F		;bump count of successfully-loaded records

;get and validate the record checksum
dr_ckcum:		call	get_byte_cksum		;get checksum into Z
				btfss	STATUS,Z			;checksum should be zero
				bra		cksum_error

;if this is a 0-byte record, then treat it as the eof. Otherwise, go get another record
				movf	COUNT_LOW,F
				btfss	STATUS,Z
				bra		dr_next_record		;not 0 bytes: go get another record

				btfss	FLAGS,B_STATE		;is this an S5 record?
				bra		dr_eof_rec			;no: this 0-byte record is the end of file

;S5 record is done - compare the number of records we have received (in R4)
; to the value in FSR1L - the low byte of the address field, which is the record count
				decf	R4,W				;don't count this S5 record
				xorwf	FSR1L,W				;compare to the S5 record's record count
				btfss	STATUS,Z
				call	s5_error			;report S5 record count mismatch

;------------------------------------------------------------
; This record is done, and we expect another one. Print
; Cr-LF, and go get another record, without printing a prompt
;------------------------------------------------------------
dr_next_record:	movlw	CR					;K_CONOUT will add the LF
				call	K_CONOUT
				goto	no_prompt			;done with command, no prompt

;*------------------------------------------------------------------------------*
; This is a Motorola  S9 record (which specifies the record count) 
; It may be just 'S9', or may be complete S9 record.
;*------------------------------------------------------------------------------*
mot_s9:			call	get_chr				;test for CR
				decf	LB_OPTR,F			;put pointer back
				btfss	FLAGS,CR_FLAG		;was it the CR?
				bra		mot_nodatarec		;no: go get the rest of the rest of the record

;fall into dr_eof_rec to finish download
;-----------------------------------------------------
; End of File record encountered.
; Report results
;-----------------------------------------------------
dr_eof_rec:		movlw	reccount_msg_index		;'Records: '
				call	cr_printf
				movfw	R4						;R4 has record count
				call	K_PRINTHEX2

				movlw	errorcount_msg_index	;', Bad Records: '
				call	printf
				movfw	R3						;R3 has bad record count
				call	K_PRINTHEX2

				movlw	cr_msg_index			;new line
				call	printf

				movfw	COUNT_HIGH				;COUNT_HIGH has successfully-loaded record count
				call	K_PRINTHEX2

				movlw	loadcount_msg_index		;'records loaded in buffer with Address Offset: '
				call	printf

				movlb	VAR_BANK				;ADR_OFFSET is in the last page
				movfw	ADR_OFFSET				;current address offset
				call	K_PRINTHEX2				;print address offset in hex

;-------------------------------------------------------------------------
;Reset automatic address offset settings for next file, and return to main
;-------------------------------------------------------------------------
				movlb	VAR_BANK				;AO_FLAGS is in the last page
				btfss	AO_FLAGS,AO_MANUAL		;automatic address offset mode?
				clrf	ADR_OFFSET				;y:prepare for next file load
				bcf		AO_FLAGS,AO_INIT		;de-initialize address offset

				goto	main

;******=========================================================================*
;* PC * Set Programming Cycles Command
;******
; On Entry:
;   R4 = second command character, must be 'C'
;   FSR1 = programming cycles.
;   (0 means use P+5P Smart Algorithm)
;*==============================================================================*
cmd_pc:			cjne	R4,'C',cmd_error	;C is Prog cycles, otherwise error

				movlw	high(EPROM_BUF)		;no high byte allowed
				subwf	FSR1H,W				;note that high(EPROM_BUF) was added
				btfss	STATUS,Z
				goto	cmd_error

				movfw	FSR1L				;get first parameter
				movwf	PROG_CYCLES			;save it as the programming cycles
				call	rep_pcycles			;report the new state
				goto	main

;******=========================================================================*
;* AO * Set Address Offset Command
;******
; Sets an offset for the upper 8 bits of address for uploads and downloads
; On Entry:
;	DFLT_FLAG = 1 if the user did not provide an address offset value.
;   R4 = second command character, must be 'O'
;   FSIRL = address offset
;*==============================================================================*
cmd_ao:			cjne	R4,'O',cmd_error	;O is address offset, otherwise error

				movlw	high(EPROM_BUF)		;no high byte allowed
				subwf	FSR1H,W				;note that high(EPROM_BUF) was added
				btfss	STATUS,Z
				goto	cmd_error

				movfw	FSR1L				;get first parameter
				movlb	VAR_BANK			;ADR_OFFSET is in the variables bank
				movwf	ADR_OFFSET			;save it as the new address offset

				bcf		AO_FLAGS,AO_MANUAL	;assume automatic address offset
				bcf		AO_FLAGS,AO_INIT	;address offset is no longer initialized	

				btfss	FLAGS,DFLT_FLAG		;was a parameter provided?
				bsf		AO_FLAGS,AO_MANUAL	;y: address offset is now manual	

				call	rep_adr_offset		;print the address offset

				goto	main

;******=========================================================================*
;* FB * Fill EPROM Buffer Command
;******
; Fills all 512 bytes (2 pages) of the EPROM buffer with the given value
; On Entry:
;   The second command chr (in R4) must be 'B'
;   FSIRL = fill value, defaulting to 0.
;   Undocumented: COUNT is the number of bytes to fill, starting at buffer
;   address 0
;*==============================================================================*
cmd_fill:		cjne	R4,'B',cmd_error	;second byte must be 'B'

				movlw	high(EPROM_BUF)		;no high byte allowed
				subwf	FSR1H,W				;note that high(EPROM_BUF) was added
				btfss	STATUS,Z
				goto	cmd_error

				movlw	buffill_msg_index	;'Buffer filled with '
				call	cr_printf

				movfw	FSR1L				;get fill value from user input
				movwf	R0

				movlw	high(EPROM_BUF)		;point to beginning of buffer
				movwf	FSR1H
				movlw	low(EPROM_BUF)
				movwf	FSR1L

;loop to fill EPROM buffer
fill_buf_loop:		movfw	R0				;get fill value
					movwf	INDF1			;write value to buffer

					call	next_a_count	;bump address pointer, dec COUNT

					btfss	STATUS,Z		;Z set when COUNT is 0000
					bra		fill_buf_loop

				movfw	R0					;get fill value again
				call	K_PRINTHEX2			;tell user fill value
				goto	main

;******=========================================================================*
;* Dx * Display Commands
;******
; On Entry:
;    R4 = DB for Display Buffer
;    R4 =DS for Display State
;    FSR1 = first user parameter < 0x200
;    COUNT_HIGH, COUNT_LOW = second user parameter <= 0x200
;*==============================================================================*
cmd_display:	movfw	R4

				xorlw	'S'					;display state?
				btfsc	STATUS,Z
				bra		dcmd_state

				xorlw	'S' ^ 'B'			;display buffer? otherwise error
				btfss	STATUS,Z
				bra		cmd_error

; fall into dcmd_buffer

;******-------------------------------------------------------------------------*
;* DB *  Display Buffer Command
;******
; Displays the specified range of buffer contents in human-readable form,
; followed by the checksum of the displayed range
; On Entry:
;   FSR1 = starting buffer address
;   COUNT_LOW & COUNT_HIGH= byte count
;*------------------------------------------------------------------------------*
dcmd_buffer:		clrf	HEX_CKSUM			;start buffer checksum, set Z

;print the address, (The first one may not be on a 16-byte boundary)
dbuff_loop:			btfsc	STATUS,Z
					call	print_addr

					movfw	INDF1				;get a byte
					call	print_hex_csum		;send data byte, accumulate checksum

					movlw	' '					;print a space between bytes
					call	K_CONOUT

;bump & test pointers
					call	next_a_count		;bump FSR1, decrement COUNT, test
					btfsc	STATUS,Z
				bra		dbuf_sum

; if this address ends with 0h, set Z so that the address gets printed
					movlw	0fh					;test for least-sig nibble = 0
					andwf	FSR1L,W				;Z set if we need to print the address
					bra		dbuff_loop

;print buffer sum and quit. 
dbuf_sum:		movlw	buffer_msg_index		;'Buffer '
				call	prange					;print string, the 'range ' as needed

				movlw	csum_msg_index			;'checksum: '
				call	printf					;print message

				movfw	HEX_CKSUM				;get buffer checksum
				call	K_PRINTHEX2
				goto	main


;******-------------------------------------------------------------------------*
;* DS *  Display State Command
;******
; Displays:
;  Kernel Loader revision
;  Address Offset
;  Programming Cycles/ Smart Mode
;  Echo State
;*------------------------------------------------------------------------------*
dcmd_state:		movlw	loaderrev_msg_index	;loader rev
				call	cr_printf
				call	K_KERN_REV			;return loader revision in W
				movwf	R0
				swapf	R0,W				;print the major revision first
				call	K_PRINTHEX1
				movlw	'.'					;decimal point seperator
				call	K_CONOUT
				call	K_KERN_REV			;return loader revision in W
				call	K_PRINTHEX1			;now print the minor revision

				movlw	fwrev_msg_index		;firmware rev message
				call	cr_printf

				call	rep_adr_offset		;address offset
				call	rep_pcycles			;print eprom type, programming cycles

				movlw	echostate_msg_index	;echo state
				call	cr_printf
				movlw	off_msg_index
				btfsc	FLAGS,ECHO_STATE
				movlw	on_msg_index

				goto	print_main

;******=========================================================================*
;* Ux *  Upload Commands
;******
; Transmits all 512 bytes in the EPROM buffer in one of two formats:
; On Entry:
;    Second command chr is in R4
;    Second command chr = 'I' for Intel Hex upload
;    Second command chr = 'S' for Motorola S-Record upload
;*==============================================================================*
cmd_upload:		movfw	R4
				bcf		FLAGS,INTEL_STATE	;Assume Motorola for a moment

				xorlw	'S'					;Motorola S-Record?
				btfsc	STATUS,Z
				bra		upload_buf

				bsf		FLAGS,INTEL_STATE	;assume we are doing intel

				xorlw	'S' ^ 'I'			;Intel Hex? if not, it's an error
				btfss	STATUS,Z
				goto	cmd_error

; fall into upload_buf

;***********--------------------------------------------------------------------*
;* UI & US * Common Upload Routine
;***********
; Transmit all 512 bytes of EPROM buffer contents in a Hex format
; On entry:   
;    INTEL_STATE = 1 for Intel Hex, 0 for S-record
;*------------------------------------------------------------------------------*
upload_buf:		movlw	high(EPROM_BUF)			;set up FSR1 as EPROM buffer pointer
				movwf	FSR1H
				clrf	FSR1L

upload_loop:		movlw	0x10				;16 bytes per line
					movwf	R3					;for loop count

					clrf	HEX_CKSUM			;start row checksum at 00

;print CR LF, and either ':', or 'S1'
					movlw	mot_start_rec_index
					btfsc	FLAGS,INTEL_STATE
					movlw	int_start_rec_index
					call	cr_printf

;send byte count for this record				
					movlw	0x00				;adjust byte count for S-records
					btfss	FLAGS,INTEL_STATE
					movlw	0x03				;S-record: account for address, checksum bytes
					addwf	R3,W				;bytes per record

					call	print_hex_csum		;compute checksum along the way

;compute & send high byte of this record's address
					movlw	HIGH(EPROM_BUF)		;remove buffer address offset
					subwf	FSR1H,W				;W:=FSR1H-high(EPROM_BUF)
					movlb	VAR_BANK			;ADR_OFFSET is in bank the variables page
					addwf	ADR_OFFSET,W		;add on address offset
					call	print_hex_csum		;send & accumulate checksum along the way

;send low byte of this record's address
					movfw	FSR1L				;low byte of address
					call	print_hex_csum		;accumulate checksum along the way

;if this is an Intel Hex record, send 00 record type
					movlw	0x00
					btfsc	FLAGS,INTEL_STATE
					call	print_hex_csum

;loop to send R3 bytes of data from FSR1
up_byte_loop:			moviw	FSR1++			;get a byte, bump pointer
						call	print_hex_csum	;send data byte, accumulate checksum
						decfsz	R3,F
						bra		up_byte_loop

;compute checksum - 
;   for Intel hex, checksum = 2's compliment of the sum
;   for Motorola s-records, checksum = 1's compliment of the sum
					comf	HEX_CKSUM,F			;compliment sum of bytes
					btfsc	FLAGS,INTEL_STATE	;add one if Intel hex
					incf	HEX_CKSUM,F

					movfw	HEX_CKSUM			;get the final checksum
					call	print_hex_csum		;set HEX_CKSUM up for next record too

;see if we have more data to send
					movf	FSR1L,F				;test for 256 bytes sent already
					btfss	STATUS,Z
					bra		upload_loop	

;see if we have sent both pages
					movlw	HIGH(EPROM_BUF)+high(EPROMBUF_SIZE)
					subwf	FSR1H,W
					btfss	STATUS,Z
					bra		upload_loop	

;all done sending data. Send appropriate eof record and quit
				movlw	int_end_rec_index
				btfss	FLAGS,INTEL_STATE
				movlw	mot_end_rec_index
				goto	print_main

;******=========================================================================*
;* Ex * EPROM Commands
;******
; These commands allow the user to manipulate the EPROM
; On Entry:
;   R4 = second command character
;   Second command chr = 'B' for blank check
;   Second command chr = 'C' for compare to buffer
;   Second command chr = 'P' for program EPROM
;   Second command chr = 'R' for read EPROM into to buffer
;   Second command chr = 'S' for checksum
;   ERR_FLAG is cleared
;   B_STATE = 0: not yet a blank-check command, an S5 record,  or a cksum command
;    FSIRL & FSR1H = first user parameter, if one was entered, 0 otherwise.
;    COUNT_LOW, COUNT_HIGH= 2nd user parameter if one was entered, 200 otherwise.
;*==============================================================================*
cmd_eprom:	call	epower_read				;power the EPROM up for reading

			movfw	R4

			xorlw	'P'						;EPROM Program?
			btfsc	STATUS,Z
			bra		ecmd_prog

			xorlw	'P' ^ 'C'				;EPROM Compare?
			btfsc	STATUS,Z
			bra		ecmd_compare

			xorlw	'C' ^ 'R'				;EPROM Read?
			btfsc	STATUS,Z
			bra		ecmd_read

			bsf		FLAGS,B_STATE			;set for blank check and checksum

			xorlw	'R' ^ 'B'				;EPROM Blank Check?
			btfsc	STATUS,Z
			bra		ecmd_blankchk

			xorlw	'B' ^ 'S'				;Error if not EPROM checksum
			btfss	STATUS,Z
			goto	cmd_error


;fall into ecmd_cksum

;***********--------------------------------------------------------------------*
;* ES & ER * EPROM Checksum & EPROM Read Commands
;***********
; EPROM Checksum just computes and reports the low byte of the sum of the entire
;  EPROM's data. EPROM Read also writes the EPROM data into the buffer.
; On Entry:
;   B_STATE cleared for read command, set for checksum command
;   FSR1 = starting buffer address
;   COUNT_LOW, COUNT_HIGH= byte count of region to checksum
;   ERR_FLAG is cleared
;   EPROM is already powered up for reading
;*------------------------------------------------------------------------------*
ecmd_cksum:
ecmd_read:		clrf	HEX_CKSUM			;accumulate checksum here

; read EPROM and add up the data
esum_rd_loop:		call	eread_byte
					addwf	HEX_CKSUM,F

					btfss	FLAGS,B_STATE	;read or checksum?
					movwf	INDF1			;write it to the buffer for read only

					call	next_a_count	;bump address, decrement & test count
					btfss	STATUS,Z
					bra		esum_rd_loop

				call	epower_off			;power down

;Print results
				movlw	eprom_msg_index		;'EPROM '
				call	prange				;print string, then 'range ' if needed

				movlw	read_msg_index		;'read into buffer. '
				btfss	FLAGS,B_STATE		;EPROM read?
				call	printf				;print message

				movlw	csum_msg_index		;'checksum: '
				call	printf				;print message

				movfw	HEX_CKSUM			;print checksum
				call	K_PRINTHEX2					

				goto	main

;******-------------------------------------------------------------------------*
;* EP * EPROM Program Command
;******
; Programs EPROM from buffer, with verify
; On Entry:
;   PROG_CYCLES has the required number of programming cycles (0=smart)
;   FSR1 = FSR1H_SAVE,FSR1L_SAVE=starting buffer address
;   COUNT_HIGH,COUNT_LOW = COUNTH_SAVE,COUNTL_SAVE = number of bytes to program
;   B_STATE is cleared, so rep_mismatch will know this is not a blank-check
;   ERR_FLAG is cleared
;   EPROM is already powered up for reading
;*------------------------------------------------------------------------------*

;Tell the user which which algorithm will be used
ecmd_prog:		call	rep_pcycles

;Ask the user if s/he is ready, warning him/her about the danger of a backwards EPROM
				movlw	ready2prog_msg_index
				call	ask_yn				;ask if the user is ready

;-------------------------------------------------------------------------------
; Check for backwards EPROM
;
;An erased MM5204 reads as 00. Check for an EPROM full of FFs - this is possibly
;a backwards EPROM It is and certainly one that can't be programmed.
;-------------------------------------------------------------------------------
				clrf	FSR1L
				movlw	high(EPROM_BUF)
				movwf	FSR1H

bkwds_loop:			call	eread_byte		;returns result in R2 & W
					incf	R2,F			;test for read value=FF
					btfss	STATUS,Z		;is data zero?
				bra		not_bkwds

					addfsr	1,1				;16-bit increment FSR1
					cjne	FSR1H,high(EPROMBUF_SIZE),bkwds_loop

;The EPROM is completely full of FF - it is likely backwards, and is surely unprogrammable
				movlw	backwards_msg_index
				call	printf
				goto	abort
not_bkwds:

;-------------------------------------------------------------------------------
; Blank-check the selected region of the EPROM
; A blank EPROM byte reads as 0x00
;-------------------------------------------------------------------------------
				call	restore_params		;recover original starting address
				clrf	R4					;accumulate result in R4

epbc_loop:			call	eread_byte		;Z will be set if 0
					iorwf	R4,F			;accumulate non-zero-ness
					call	next_a_count	;bump pointer, test for done
					btfss	STATUS,Z
					bra		epbc_loop

;if non-blank programming range, ask Should we continue?
				movlw	notblank_msg_index	;Not blank. Program anyway?
				movf	R4,F				;test for 0
				btfss	STATUS,Z
				call	ask_yn				;abort if no

;-------------------------------------------------------------------------------
; Get ready to program EPROM
;-------------------------------------------------------------------------------
;see if this is a smart program or a fixed number of cycles to program
				movf	PROG_CYCLES,F
				btfsc	STATUS,Z
				bra		smart_prog

;fall into dumb_prog
				
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Fixed-Cycle Programming Routine
; Write sequentially through the EPROM PROG_CYCLES times
; On Entry:
;   ERR_FLAG is cleared
;   B_STATE is cleared, so rep_mismatch will know this is not a blank-check
;   main power supply is on
;   FSR1 = FSR1H_SAVE,FSR1L_SAVE=starting buffer address
;   COUNT_HIGH,COUNT_LOW = COUNTH_SAVE,COUNTL_SAVE = number of bytes to program
;   PROG_CYCLES = number of times to program the EPROM
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
dumb_prog:		call	epower_prog			;turn on high voltage supply

;tell the user that programming is starting
				movlw	programing_msg_index
				call	cr_printf

				movfw	PROG_CYCLES
				movwf	R4					;R4 counts cycles

; loop R1 (the requested number of) times through the EPROM range
dumb_loop:			call	ewrite_range	;write the range once
					decfsz	R4,F			;repeat PROG_CYCLES times
					bra		dumb_loop

; go verify what we wrote
				bra		prog_verify			;report if we ever saw Vbb too low first

;-----------------------------------------------------------------------
; Smart Programming Failed
; We tried to smart-program 255 times: write failure - report and quit.
; if ERR_FLAG is set, then also tell the user that VBB was low.
;-----------------------------------------------------------------------
smart_fail:		movlw	vbbwaslow_msg_index
				btfsc	FLAGS,ERR_FLAG		;this bit set if Vbb was low
				call	cr_printf

				movlw	writefail_msg_index
				call	cr_printf
				goto	ecmd_done

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; P+5P Smart Programming Routine
; Write the entire EPROM range, read it back, and compare.
; Repeat until every '1' bit in the buffer is also '1' in the EPROM.
; The number of passes required for this is 'P'. Then, write the
; entire range another 4 * P times. 
;
; On Entry:
;   ERR_FLAG is cleared
;   B_STATE is cleared, so rep_mismatch will know this is not a blank-check
;   main power supply is on
;   FSR1 = FSR1H_SAVE,FSR1L_SAVE=starting buffer address
;   COUNT_HIGH,COUNT_LOW = COUNTH_SAVE,COUNTL_SAVE = number of bytes to program
;   COUNT_LOW, COUNT_HIGH = number of bytes to program
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
smart_prog:		movlw	smartprog_msg_index	;announce smart programming
				call	cr_printf

				clrf	R4					;R4 remembers how many writes it took

smart_loop:			incf	R4,F
					btfsc	STATUS,Z		;have we tried 255 times already?
					bra		smart_fail		;yes: failure message & quit	

					call	epower_prog		;turn on high voltage supply
					call	ewrite_range	;write the EPROM range once

;------------------------------------------------------------------------
; Special Compare -
; Check to see if all programmable bits have been programmed
;
; MM5204s erase to 00 - so we quit programming when all bits that need
; to be '1' are '1'. If other bits that should be '0' are now '1', since
; no additional programming will turn them back to '0'. Such errors will
; be caught by the verify step.
;------------------------------------------------------------------------
					call	epower_read			;set up power supply for reading

					call	restore_params		;recover original starting address & byte count

scmp_loop:				call	eread_byte		;returns with value in W and R2

						xorwf	INDF1,W			;compare buffer data to EPROM data (in W)
						andwf	INDF1,W			;find just the bits that will become 1
						btfss	STATUS,Z		;any '1' bits are bits that haven't yet programmed
					bra		smart_loop			;mismatch: go write another pass

						call	next_a_count	;circular inc FSR1, decrement COUNT & test for 0
						btfss	STATUS,Z

						bra		scmp_loop

;----------------------------------------------------------------------
; write success - report what P equals, announce over-programming step
;----------------------------------------------------------------------
				movlw	pequals_msg_index
				call	cr_printf			;trashes R0-R2
				movfw	R4					;how many passes did it take?
				call	K_PRINTHEX2			;trashes R0-R1
				movlw	overprog_msg_index
				call	printf

;----------------------------------------------------------------------
; over-write 5X the count in R4		 
;----------------------------------------------------------------------
				call	epower_prog		;turn on high voltage supply

overp_loop:			call	ewrite_range	;write the whole range		
					call	ewrite_range	;write the whole range		
					call	ewrite_range	;write the whole range		
					call	ewrite_range	;write the whole range		
					call	ewrite_range	;write the whole range		
					decfsz	R4,F
					bra		overp_loop

;fall into prog_verify

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common Routine: Report if Vbb was low during programming, then do verify
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
prog_verify:	movlw	vbbwaslow_msg_index
				btfsc	FLAGS,ERR_FLAG		;this bit set if Vbb was low
				call	cr_printf

				bcf		FLAGS,ERR_FLAG		;clear flag for verify step

				call	epower_read			;prog voltage off, set up for reading

;announce verify step
				movlw	verifying_msg_index
				call	cr_printf

;restore FSR1, COUNT_HIGH & COUNT_LOW for verify
				call	restore_params	;recover original starting address & byte count
					
;fall into ecmd_compare to verify

;******-------------------------------------------------------------------------*
;* EC * EPROM Compare Command
;******
; Compares EPROM data to buffer and reports differences
; On Entry:
;   FSR1 = starting buffer address
;   COUNT_HIGH, COUNT_LOW = number of bytes to verify
;   ERR_FLAG is cleared
;   B_STATE is cleared, telling rep_mismatch this is not an EPROM blank-check
;   EPROM is already powered up for reading
;*------------------------------------------------------------------------------*
ecmd_compare:

;loop to compare eprom to buffer
ecmp_loop:			call	eread_byte		;returns with value in W and R2

					xorwf	INDF1,W			;compare to buffer
					btfss	STATUS,Z
					call	rep_mismatch

					call	next_a_count
					btfss	STATUS,Z
					bra		ecmp_loop

				movlw	eprom_msg_index		;'EPROM '
				call	prange				;print string, then 'range ' if needed

				movlw	cmpgood_msg_index	;good compare message

; fall into ecmd_done

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common Routine: Finish one of the Ex commands
; Print success or failiure message, and Power the EPROM Off, then go to main
; On Entry:
;    W=address of the right success message
;    ERR_FLAG = 0 for success, 1 for some sort of failure
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ecmd_done:		btfsc	FLAGS,ERR_FLAG	;any errors?
				movlw	fail_msg_index	;then don't print success message

				goto	epwroff_print	;print message, power down, goto main

;******-------------------------------------------------------------------------*
;* EB * EPROM Blank Check Command
;******
; Verify that the specified region of the EPROM is clear (all bytes=00)
; On Entry:
;   FSR1 = starting EPROM address + EPROM_BUF
;   COUNT_LOW, COUNT_HIGH = number of bytes to check
;   B_STATE is set, telling rep_mismatch this is an EPROM blank-check
;   ERR_FLAG is cleared
;   EPROM is already powered up for reading
;*------------------------------------------------------------------------------*
ecmd_blankchk:
;loop to compare eprom to buffer
ebc_loop:			call	eread_byte		;result in W & R2

					movf	R2,F			;Z set means erased
					btfss	STATUS,Z		;is data zero?
					call	rep_mismatch

					call	next_a_count
					btfss	STATUS,Z
					bra		ebc_loop

				movlw	eprom_msg_index		;'EPROM '
				call	prange				;print string, then 'range ' if needed

				movlw	isblank_msg_index	;message if blank
				bra		ecmd_done			;done with command

;******=========================================================================*
;* MB * Modify Buffer Command
;******
; Allows user to interactively modify data in the buffer.
; Second command chr (in R4) must be 'B'
; On Entry:
;   FSR1 = buffer address, known to be within the buffer
; On Exit:
;    Normal exit is via ^C, detected by get_line
;*==============================================================================*
cmd_modify:		cjne	R4,'B',cmd_error	;second chr must be 'B' sets Z too

;if Z set, print buffer address, followed by ': '
mb_loop:			btfsc	STATUS,Z
					call	print_addr

;print buffer data followed by a space
					movfw	INDF1			;get, print the data
					call	K_PRINTHEX2
					movlw	' '				;space seperator
					call	K_CONOUT

;get user input - either CR to skip or new hex value for this location
					call	save_params		;save FSR1 particularly
					call	get_line		;get user input into buffer (trashes FSR0 & FSR1)
					call	restore_params	;recover FSR1

					call	get_param		;decode ASCII hex into R1	

					btfss	FLAGS,INP_FLAG	;clear if no byte found
					bra		mb_nxtbyte

					movfw	R1				;temp save new value in R4
					movwf	R4

					call	get_param		;there shouldn't be anything more 	
					btfsc	FLAGS,INP_FLAG	;clear if no byte found
				goto	cmd_error			;huh?

					movfw	R4				;recover user input
					movwf	INDF1			;overwrite buffer data with new user data

					movlw	' '				;space separator
					call	K_CONOUT			
mb_nxtbyte:
;every 8 bytes, print CR and address again
;this will wrap to the beginning of the buffer if you keep going.
					call	next_a			;next address, with wrap
					movlw	0x07			;test for every 8th byte
					andwf	FSR1L,W			;set Z flag if we need to print the address
					bra		mb_loop

;******=========================================================================*
;* Wx * Write Commands
;******
; (These are intended for programmer testing)
; R4 = 2nd command chr
; Second command chr = 'A' to write to the EPROM Address pins
; Second command chr = 'D' to write to the EPROM Data pins
;*==============================================================================*
cmd_write:	movlb	0x02			;for EPROM_WD (LATD) and EPROM_AN (LATB)

			movfw	R4

			xorlw	'A'				;Write Address?
			btfsc	STATUS,Z
			bra		wcmd_addr

			xorlw	'A' ^ 'D'		;Write Data? Otherwise it's an error
			btfss	STATUS,Z
			goto	cmd_error

;fall into wcmd_data

;******-------------------------------------------------------------------------*
;* WD * Test EPROM Write Data Pins Command
;******
; Writes first parameter (in FSR1L) to the EPROM write-data register
; On Entry:
;    BSR = 2
;*------------------------------------------------------------------------------*
wcmd_data:		comf	FSR1L,W			;data drivers invert
				movwf	EPROM_WDN		;write data now

				goto	main

;******--------------------------------------------------------------------------*
;* WA * Write EPROM Address Pins Command
;******
; Writes first parameter (in FSR1L and FSRH) to the EPROM address register
; On Entry:
;    EPROM address = FSR1- EPROM_BUF
;    BSR = 2
;*------------------------------------------------------------------------------*
wcmd_addr:		call	ewrite_address		;FSR1 - EPROM_BUF to address pins
				goto	main

;******=========================================================================*
;* Tx * Test Command
;******
; Sets or clears various things. Also includes Terminal Echo
; R4 = 2nd command chr (see testes below)
; FSR1L = ascii 0 or 1 for off or on (user input)
;*==============================================================================*
cmd_test:

;test first parameter (in FSR1) for 0 or 1
				movlw	high(EPROM_BUF)		;no high byte allowed
				subwf	FSR1H,W				;note that high(EPROM_BUF) was added
				btfss	STATUS,Z
				goto	cmd_error

				movlw	0xfe				;mask lsb
				andwf	FSR1L,W				;only 0,1 allowed
				btfss	STATUS,Z
				goto 	cmd_error

				movfw	R4

				xorlw	'B'					;Control Vbb?
				btfsc	STATUS,Z
				bra		tcmd_vbb

				xorlw	'B' ^ 'C'			;Control Chip Enable & Power Saver?
				btfsc	STATUS,Z
				bra		tcmd_ce

				xorlw	'C' ^ 'D'			;Control Vdd?
				btfsc	STATUS,Z
				bra		tcmd_vdd

				xorlw	'D' ^ 'E'			;Control echo?
				btfsc	STATUS,Z
				bra		tcmd_echo

				xorlw	'E' ^ 'H'			;Control high-voltage enable?
				btfsc	STATUS,Z
				bra		tcmd_highv

				xorlw	'H' ^ 'P'			;Control PROG?
				btfsc	STATUS,Z
				bra		tcmd_prog

				xorlw	'P' ^ 'M'			;Control Master power? erroro if not
				btfss	STATUS,Z
				goto	cmd_error

;fall into tcmd_master

;******-------------------------------------------------------------------------*
;* TM * Test Master Voltage Control Command
;******
; Turns master voltage control off/on
; On Entry:
;   FSR1L = 0 or 1 for off/on
; Note that the hardware is active-low
;*------------------------------------------------------------------------------*
tcmd_master:	movlw	masterpwr_msg_index	;'Master power'
				call	cr_printf

				movlb	0x02				;LATE is in bank 2

				movf	FSR1L,F				;test param1 for 0
				btfsc	STATUS,Z
				bra		power_off
;power on
				bsf		LATE,EN_ALLPOWER
				goto	tcmd_on				;' on' then done with command

power_off:		bcf		LATE,EN_ALLPOWER
				goto	tcmd_off			;' off' then done with command

;******-------------------------------------------------------------------------*
;* TH * Test Programming high voltages Command
;******
; Turns programming voltages off/on
; FSR1L = 0 or 1 for off/on
;*------------------------------------------------------------------------------*
tcmd_highv:		movlw	highv_msg_index		;'High voltages'
				call	cr_printf

				movlb	0x02				;LATE is in bank 2
				movf	FSR1L,F				;test param1 for 0
				btfsc	STATUS,Z
				bra		highv_off
;High V on
				bcf		LATE,NEN_PVOLTAGE
				goto	tcmd_on				;' on' then done

highv_off:		bsf		LATE,NEN_PVOLTAGE
				goto	tcmd_off			;' off' then done with command

;******-------------------------------------------------------------------------*
;* TB * Test Vbb Command
;******
; Turns Vbb off/on
; FSR1L = 0 or 1 for off/on
;*------------------------------------------------------------------------------*
tcmd_vbb:		movlw	vbb_msg_index		;'Vbb'
				call	cr_printf

				movlb	0x02				;LATE is in bank 2
				movf	FSR1L,F				;test param1 for 0
				btfsc	STATUS,Z
				bra		vbb_off
;Vbb on
				bcf		LATE,NEN_VBB		;it's active low
				goto	tcmd_on				;' on' then done

vbb_off:		bsf		LATE,NEN_VBB
				goto	tcmd_off			;' off' then done with command

;******-------------------------------------------------------------------------*
;* TD * Test Vdd Command
;******
; Turns Vdd off/on
; FSR1L = 0 or 1 for off/on
;*------------------------------------------------------------------------------*
tcmd_vdd:		movlw	vdd_msg_index		;'Vdd'
				call	cr_printf

				movlb	0x02				;LATC is in bank 2
				movf	FSR1L,F				;test param1 for 0
				btfsc	STATUS,Z
				bra		vdd_off
;Vdd on
				bsf		LATC,EN_VDD
				goto	tcmd_on				;' on' then done

vdd_off:		bcf		LATC,EN_VDD
				goto	tcmd_off			;' off' then done with command

;******-------------------------------------------------------------------------*
;* TC * Test Chip Select (and Power Saver) Command
;******
; Turns EPROM Chip Select off/on. Chip Select is an active-low signal.
; FSR1L = 0 or 1 for off/on
; "on" means low voltage, "off" means high voltage
;*------------------------------------------------------------------------------*
tcmd_ce:		movlw	cesignal_msg_index	;'CE Signal '
				call	cr_printf

				movlb	0x02				;LATC is in bank 2

				movf	FSR1L,F				;test param1 for 0
				btfsc	STATUS,Z
				bra		ce_off
;CE on
				bsf		LATC,EPROM_CS
				goto	tcmd_on				;' on' then done


ce_off:			bcf		LATC,EPROM_CS
				goto	tcmd_off			;' off' then done with command

;******-------------------------------------------------------------------------*
;* TP * Test PROGRAM Signal Command
;******
; Turns PROGRAM off/on. PROGRAM is an active-low signal
; FSR1L = 0 or 1 for off/on
; "on" means low voltage, "off" means high voltage
;*------------------------------------------------------------------------------*
tcmd_prog:		movlw	progsignal_msg_index	;'-PROG signal'
				call	cr_printf

				movlb	0x02					;LATC is in bank 2

				movf	FSR1L,F					;test param1 for 0
				btfsc	STATUS,Z
				bra		prog_off

;program on, meaning  low voltage
				bsf		LATC,EN_PROGRAM
				goto	tcmd_on					;' on' then done with command

;program off, meaning at Vss
prog_off:		bcf		LATC,EN_PROGRAM
				goto	tcmd_off				;' off' then done with command

;******-------------------------------------------------------------------------*
;* TE * Set Terminal Echo State Command
;******
; Turns Echo off/on
; FSR1L = 0 or 1 for off/on
;*------------------------------------------------------------------------------*
tcmd_echo:		movlw	echostate_msg_index		;'echo'
				call	cr_printf

				bcf		FLAGS,ECHO_STATE		;assume echo off for now
				movf	FSR1L,F					;test param1 for 0
				btfsc	STATUS,Z
				bra		tcmd_off

				bsf		FLAGS,ECHO_STATE		;echo on

;fall into tcmd_on

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common on/off routines for Tx commands
; These print either ' on' or 'off', then go to main
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
tcmd_on:		movlw	on_msg_index		;' on'
				goto	print_main			;done with command

tcmd_off:		movlw	off_msg_index		;' off'
				goto	print_main			;done with command

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Hang forever if this firmware does not match the Loader Kernel
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
wrong_programmer:	movlw	wrongprog_msg_index
					call	cr_printf
					movlw	.100			;stall for 25 chr times, to print message
					call	K_STALL250U		;25 mS
					reset					;let user type L to go to loader

;################################################################################
;#								Subroutines										#
;################################################################################

;*==============================================================================*
; Subroutines to print null-terminated strings
;
; String chrs are packed 2 to a 14-bit word, as the MPASM assembler likes to do.
; One of the bytes might be 0 because the assembler pads that way. These 0's
; are ignored. The terminating null will have both bytes of the 14-bit word 0.
; All CR's get translated to CR-LF by K_CONOUT. 
; The receive interrupt is masked while the string is enqueued, so it goes in
; without echos interspersed. This makes error messages during hex loading
; look pretty. Note that long strings may cause us to miss rx characters.
; On Entry at printf
;    W = index into string_table for desired string
; Entry at cr_printf prints a CR-LF first.
; On Exit:
;    W, R0-R1, BSR trashed
;    Receive interrupt (RCIE) is enabled
;*==============================================================================*
cr_printf:	movwf	R1					;temp save string pointer
			movlw	CR

			call	K_CONOUT			;print CRLF
			movfw	R1					;recover string pointer

;fall into printf

printf:		movlb	0x03				;memory control registers are in bank 3
			bcf		PMCON1,CFGS			;select program memory

;table look-up string address based on W input to this routine
			addlw	low(string_table)	;compute table entry address
			movwf	PMADRL
			movlw	high(string_table)
			movwf	PMADRH
			btfsc	STATUS,C			;carry as needed
			incf	PMADRH,F			;PMADR now has the address of the string pointer

			bsf		PMCON1,RD			;initiate 14-bit word read
			nop							;(page 98) mandatory 2 nops
			nop

;move string address into PMADR for string access
			movfw	PMDATL
			movwf	PMADRL
			movfw	PMDATH
			movwf	PMADRH				;PMADR now has the address of the string itself

			goto	K_PRINTF			;print string at PMADRH, PMADRL
				
;*==============================================================================*
; Subroutine to print CR, then message, followed by 'range ', if needed
; On Entry:
;   W = index to string to print ('EPROM ' or 'Buffer ')
; FULL_FLAG = 1 if the byte count defaulted to the full EPROM size
;*==============================================================================*
prange:			call	cr_printf				;print provided string with initial CR

				movlw	range_msg_index			;'range '
				btfss	FLAGS,FULL_FLAG
				call	printf					;return from there
				return

;*==============================================================================*
; Subroutine to print W as a 2-character hex value, and add value to HEX_CKSUM
; On Entry:
;    byte to send is in W
;    HEX_CKSUM is valid for all prior bytes
; On Exit:
;    W, R0, R1, FSR0, BSR trashed
;    incoming byte has been added to HEX_CKSUM
;*==============================================================================*
print_hex_csum:	addwf	HEX_CKSUM,F			;accumulate checksum
				goto	K_PRINTHEX2

;*==============================================================================*
; Subroutine to print the row's address as 3 hex digits, followed by ': '
;
; On Entry:
;   FSR1 has the buffer address of the  byte to be printed
; On Exit:
;   This row's address has been printed, followed by a colon
;   HEX_CKSUM unchanged
;   W, R0, R1, FSR0 trashed
;*==============================================================================*
print_addr:		movlw	CR						;print CR-LF
				call	K_CONOUT

				movlw	HIGH(EPROM_BUF)
				subwf	FSR1H,W					;W:=FSR1H-high(EPROM_BUF)
				call	K_PRINTHEX1				;print just 1 nibble

				movfw	FSR1L					;low byte of address
				call	K_PRINTHEX2				;don't mess up HEX_CKSUM

				movlw	colonspace_msg_index	; ': '
				goto	printf

;*==============================================================================*
;Subroutine to print the address offset, which may either be
;a value or 'automatic'
;On Entry:
;  AO_MANUAL  = 1 if manual address offset mode selected
;             = 0 if automatic address offset mode
;  ADR_OFFSET = address offset if manual mode
;*==============================================================================*
rep_adr_offset:	movlw	aoffset_msg_index	;'Address Offset: '
				call	cr_printf

				movlb	VAR_BANK			;ADR_OFFSET is in the last page
				movfw	ADR_OFFSET			;manually-set address offset

				btfsc	AO_FLAGS,AO_MANUAL	;manual address offset mode?
				goto	K_PRINTHEX2			;y: print address offset and return

;Address offset is automatic
				movlw	auto_msg_index		;'automatic'
				bra		printf				;return from there

;*==============================================================================*
; Subroutine to report an EPROM/Buffer mismatch
; Prints address, buffer data, EPROM data, unless B_STATE (blank check cmd),
; in which case, doesn't print the buffer data.
; Check to see if the user typed ^C to abort, and aborts if so
; On Entry:
;   FSR1 = address
;   INDF1 = buffer data
;   B_STATE set if this is a blank-check, and buffer data shouldn't be reported
;   R2 = EPROM data
; On Exit:
;    W, R0, R1, BSR, HEX_CKSUM, FSR0 trashed
;    ERR_FLAG set
;*==============================================================================*
rep_mismatch:	call	chk_ctrlc			;see if the user is trying to abort

				bsf		FLAGS,ERR_FLAG		;remember that we had an error

				movlw	mismatcha_msg_index	;'Error at address '
				call	printf				;note: cr_printf would trash R2

				call	print_addr			;3-digit address, followed by ': '

				btfsc	FLAGS,B_STATE		;Skip buffer data if EB blank check cmd
				bra		rep_mm_skipb

				movlw	mismatchb_msg_index	;Buffer:
				call	printf
				movfw	INDF1
				call	K_PRINTHEX2

rep_mm_skipb:	movlw	mismatche_msg_index	;EPROM:
				call	printf
				movfw	R2
				call	K_PRINTHEX2

;fall into wait_txempty to flush transmit queue

;*==============================================================================*
; Subroutine to wait until the Tx Queue is empty
; The queue is empty when the transmit interrupt is shut off
; and not XOFF'd
; trashes BSR
;*==============================================================================*
wait_txempty:		btfsc	INT_FLAGS,XOFF_STATE	;XOFF'd?
					bra		wait_txempty
	
					movlb	0x01					;for PIE1
					btfss	PIE1,TXIE				;is transmitter enabled?
				return
					bra		wait_txempty

;*==============================================================================*
;* Subroutine to report the selected programming mode, and cycles for dumb mode
;*==============================================================================*
rep_pcycles:	movlw	smarten_msg_index		;in case of smart programming algorithm
				movf	PROG_CYCLES,F			;Smart Algorithm?
				btfsc	STATUS,Z
				goto	printf					;return from there

				movlw	progcycles_msg_index	;dumb programming: how many cycles?
				call	cr_printf
				movfw	PROG_CYCLES
				goto	K_PRINTHEX2				;return from there

;*==============================================================================*
; Subroutine to get a multi-character hex parameter value from the Line
; Buffer, skipping spaces and ignoring leading zeros. Returns default 0000 if
; there is no parameter. digits before the last 4 are ignored.
; This routine is not case-sensitive - lowercase is treated as uppercase.
; On Entry:
;    LB_OPTR points to the next chr in the line buffer
;    CR_FLAG set if the CR for this line has already been found
; On Exit:
;    R0 trashed
;    R1 = low byte of input value
;    R3 = high byte of input value
;    R1 & R3 = 0 if CR for this input has already been reached 
;    Abort rudely to cmd_error if non-hex character found
;    CR_FLAG set if CR found
;    INP_FLAG set if a value is found - cleared if defaulting to 0
;*==============================================================================*
get_param:		bcf		FLAGS,INP_FLAG			;no characters yet
				clrf	R1						;value is 0 so far
				clrf	R3
				btfsc	FLAGS,CR_FLAG			;already found end of line?
				return							;return with R1=0 if so

;skip over any leading spaces
gp_skip_spaces:		call	get_uchr			;result in W & R0
					xorlw	' '					;ignore leading spaces
					btfsc	STATUS,Z
					bra		gp_skip_spaces

;loop to get digits until space or CR. Throw away high digits (more than 3)
gp_get_digits:		btfsc	FLAGS,CR_FLAG		;CR ends us
				return

					bsf		FLAGS,INP_FLAG		;remember that we found some user input

					movlw	0x0f
					andwf	R3,F				;clear high nibble of high byte
					movlw	0xf0
					andwf	R1,W				;peel off the high nibble of the low byte

					iorwf	R3,F				;combine the new high byte nibbles
					swapf	R3,F				;put the 2 high nibbles in the right order

					call	K_HEX2BIN			;convert new chr in R0 into low nibble of R1 & W
					btfsc	STATUS,C			;C set means hex character error
				goto	cmd_error

					call	get_uchr			;get another chr - result in W & R0

					xorlw	' '					;trailing space ends us
					btfss	STATUS,Z
					bra		gp_get_digits

				return
			
;*==============================================================================*
; Subroutine to get one character from the Line Buffer, and convert it to
; uppercase, if necessary.
; On Entry:
;    LB_OPTR points to the next chr in the line buffer
; On Exit:
;    The next line buffer chr is in W & R0
;    FSR0 trashed
;    LB_OPTR incremented
;    CR_FLAG set if this chr is a CR
;*==============================================================================*
get_uchr:	call	get_chr				;get a chr form the line buffer into W & R0

;Convert any lowercase to uppercase
			sublw	'z'					;above lowercase z?
			btfss	STATUS,C
			bra		case_ok				;yes: don't try to change case

			btfsc	R0,0x06				;if bit 6 is set, then clear bit 5
			bcf		R0,0x05				;that makes it uppercase
case_ok:	movf	R0,W				;get chr into W too
			return

;*==============================================================================*
; Subroutine to get one character from the Line Buffer
; Note that the Line Buffer always terminates with a CR
; On Entry:
;    LB_OPTR points to the next chr in the line buffer
; On Exit:
;    result in R0
;    W trashed
;    FSR0 trashed
;    LB_OPTR incremented
;    CR_FLAG set if this chr is a CR
;*==============================================================================*
get_chr:	movlw	high(LINE_BUF)		;point FSR0 to line buffer
			movwf	FSR0H
			movfw	LB_OPTR				;current position in line buffer
			movwf	FSR0L

;set CR_FLAG if this is a CR
			movlw	CR
			xorwf	INDF0,W
			btfsc	STATUS,Z
			bsf		FLAGS,CR_FLAG		;it's a CR

;get buffer character into W & R0, and bump line buffer pointer
			movfw	INDF0
			movwf	R0
			incf	LB_OPTR,F

			return
	
;*==============================================================================*
; Subroutine to get an exactly 2-character hex value from the Line Buffer, and
; also add the result to HEX_CKSUM
; This is as fast as possible so we can keep up with hex record loading
; This routine *IS* cases sensitive - lowercase will cause an error.
; This routine also does not skip over white space.
; On Entry:
;    LB_OPTR points to the next chr in the line buffer
; On Exit:
;    R0 trashed
;    Result in W and R1
;    Result added to HEX_CKSUM
;    Z set if checksum = 0
;    Abort rudely to hex_error if non-hex character (or CR) found
; (45 cycles, worst case)
;*==============================================================================*
get_byte_cksum:		call	get_nibble		;high nibble
					btfss	STATUS,C		;C set means error
					call	get_nibble		;low nibble (result will be in R1 and W)
					btfsc	STATUS,C		;C cleared means success
					goto	hex_error

					addwf	HEX_CKSUM,F		;add to checksum, set Z appropriately

					return

;*==============================================================================*
; Subroutine to get one character from the Line Buffer, convert it to a binary
; nibble and test for good hex.
; This routine *IS* case-sensitive - lowercase will cause an error.
; On Entry:
;    R1 = previous nibble
; On Exit:
;    R0 = binary value of nibble
;    W = R1 = combined nibbles (upper nibble of R1 entry value is lost.)
;    C cleared if no error, set if bad hex character
; (16 cycles, worst case)
;*==============================================================================*
get_nibble:		call	get_chr			;get line buffer chr into R0, case sensitive
				goto	K_HEX2BIN

;*==============================================================================*
; Subroutine to get a CR-terminated line from the Receive Queue and store it in
; the Line Buffer, echoing along the way as needed. We will wait here until we
; get a CR. 
;   *  Nulls and LF's have been stripped by the interrupt handler.
;   *  BS and DEL backup one in the buffer.
;   *  control-C aborts rudely to main
;   *  Input is echoed to the transmit queue if ECHO_FLAG is set
; On Entry:
;    RQ_OPTR = low address of next chr in rx queue
;    ECHO_STATE causes chr's to echo
; On Exit:
;    RQ_FULL cleared
;    CR_FLAG is cleared for new line
;    LB_OPTR reset to low(LINE_BUF) for new line
;    one complete line of input, including CR, is in LINE_BUF
;    the line has been echoed, except for the CR at the end
;    W, R0, FSR0, FSR1 trashed
;*==============================================================================*
get_line:		movlw	high(LINE_BUF)		;point FSR1 to start of line buffer 
				movwf	FSR1H
				movlw	low(LINE_BUF)
				movwf	FSR1L

				movwf	LB_OPTR				;point to buffer start
				bcf		FLAGS,CR_FLAG		;clear CR flag for new line

;-------------------------------------------------------------
;transfer queue bytes to the input buffer until a CR is found.
;-------------------------------------------------------------
;spin here until we get a character from the queue
gl_loop:			call	K_CONIN		;chr returned in R0 & W (trashes FSR0)
					btfsc	STATUS,Z		;sets Z flag if quueue is empty
					bra		gl_loop

;if ctrl-C, then abort command
					xorlw	CTRLC
					btfsc	STATUS,Z
					goto	main

;check for backspace or delete, which are treated the same
					xorlw	CTRLC ^ BS			;backspace?
					btfss	STATUS,Z
					xorlw	BS ^ DEL			;delete?
					btfsc	STATUS,Z
					bra		gl_backspace		;bs or del: go deal with it

;put queue chr into the line buffer
					movfw	R0					;get a queue chr
					movwf	INDF1				;put chr in line buffer

;done if we found the CR (which does not get echoed yet)
					xorlw	CR
					btfsc	STATUS,Z
				return

;If line buffer is full, don't increment pointer or echo
					cje		FSR1L,low(LINEBUF_LAST),gl_loop

;bump pointer
					incf	FSR1L,F

;echo if allowed
gl_echo:			movfw	R0						;put new chr into W for echo
					btfsc	FLAGS,ECHO_STATE		;is echo enabled?
					call	K_CONOUT				;yes: put chr in W in tx queue

					bra		gl_loop			;loop for more characters

;------------------------------------------------------------------------
; We just found a backspace or delete chr. Back up buffer pointer, unless
; we are at the beginning of the buffer. Then echo the character
;------------------------------------------------------------------------
gl_backspace:	cje		FSR1L,low(LINE_BUF),gl_loop	;empty line buffer?

				decf	FSR1L,F				;back up in line buffer
				bra		gl_echo				;echo the backspace

;*==============================================================================*
; Subroutine to flush any user input in the Rx queue, and check for control-C.
; Abort if Control-C found
; Trashes R0, W, FSR0
;*==============================================================================*
;spin in this routine until the Rx queue is empty, or control-C is detected
chk_ctrlc:			call	K_CONIN			;returns chr in R0
					btfsc	STATUS,Z			;(.50)sets Z flag if quueue is empty
				return						;(.50)done- no input

;if ctrl-C, then abort. Otherwise, see if there are more queue chrs
					cjne	R0,CTRLC,chk_ctrlc

;fall into abort

;*==============================================================================*
; Abort operation: print 'abort', power down, and start over at main
;*==============================================================================*
abort:			movlw	abort_msg_index
				goto	epwroff_print

;*==============================================================================*
; Subroutine to turn off the power to the EPROM
; and test power supply voltages for reasonable voltage values
; On Exit:
;    BSR, R0, W trashed
;    main EPROM power is off
;    programming voltage is off
;    WDN and AN are 0xFF, driving the EPROM pins low
;*==============================================================================*
epower_off:		movlb	0x02				;LATC & LATE are in bank 2
				movlw	EPROM_OFF_C			;remove PROG, CS, Vss
				movwf	LATC
				movlw	EPROM_OFF_E			;Power off
				movwf	LATE

				movlw	0xff				;drive data lines to 0 volts
				movwf	EPROM_WDN
				movwf	EPROM_AN			;and the address lines


;give the power supply a chance to stabilize
				movlw	0x04				;100 mS stall
				call	K_STALL25M

; check to see that the power supply voltages are reasonable - VNEG first
				movlw	AN_VNEG | (1 << ADON)
				call	get_analog

				movlw	VNEG_OFF_MIN+1		;report if too low
				subwf	ADRESH,W
				movlw	rvneggt0_msg_index		;in case Vneg off is too high (above 0V)
				btfss	STATUS,C				;C set means ADRESH>=VNEG_OFF_MIN
				call	report_yn				;report error and keep going

				movfw	ADRESH
				sublw	VNEG_OFF_MAX
				movlw	rvnegstuck_msg_index	;in case Vneg is not shutting off (too low)
				btfss	STATUS,C				;C set if ADRESH<=VNEG_OFF_MAX
				call	report_yn				;report error and keep going

;Next, test Vss
				movlw	AN_VSS | (1 << ADON)	;Vsss ADC
				call	get_analog

				movlw	VOFF_MAX
				subwf	ADRESH,W
				movlw	rvssstuck_msg_index		;in case Vss is not shutting off
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error and keep going

;Now, test Vbb
				movlw	AN_VBB | (1 << ADON)	;Vbb ADC
				call	get_analog

				movlw	VOFF_MAX
				subwf	ADRESH,W
				movlw	rvbbstuck_msg_index		;in case Vbb is not shutting off
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error and keep going

				return

;*==============================================================================*
; Subroutine to turn off the high voltage supply and power EPROM up for reading
; and test power supply voltages for reasonable read voltage values
; On Exit:
;    BSR, R0, W trashed
;    main EPROM power is on
;    programming voltage is off
;    WDN is 0x00
;*==============================================================================*
;turn off high voltage, set up for reading
epower_read:	movlb	0x02

				movlw	EPROM_READ_E		;power on, VBB on, program voltages off
				movwf	LATE
				movlw	EPROM_READ_C		;program off off, VDD on
				movwf	LATC

				clrf	EPROM_WDN			;inverted write data must be low to read

;give the power supply a chance to stabilize
				movlw	0x02				;50 mS stall
				call	K_STALL25M

; check to see that the power supply voltages are reasonable - VNEG first
				movlw	AN_VNEG | (1 << ADON)	;pre-regulator ADC
				call	get_analog

				movlw	VNEG_12_MIN-1
				subwf	ADRESH,W
				movlw	rvneg12low_msg_index	;in case Low-voltage Vneg -12V is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going

				movfw	ADRESH
				sublw	VNEG_12_MAX
				movlw	rvneg12hi_msg_index		;in case Vneg -12V is too high
				btfss	STATUS,C				;C set means ADRESH<=VNEG_12_MAX
				call	report_yn				;report error and keep going

;Next, test Vss
				movlw	AN_VSS | (1 << ADON)	;Vsss ADC
				call	get_analog

				movlw	V5_MIN-1
				subwf	ADRESH,W
				movlw	rvss5low_msg_index		;in case Low-voltage Vss +5V is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going

				movfw	ADRESH
				sublw	V5_MAX
				movlw	rvss5high_msg_index		;in case Low-voltage Vss +5V is too high
				btfss	STATUS,C				;C set means ADRESH<=V5_MAX
				call	report_yn				;report error and keep going

;Now, test Vbb
				movlw	AN_VBB | (1 << ADON)	;Vbb ADC
				call	get_analog

				movlw	V5_MIN-1
				subwf	ADRESH,W
				movlw	rvbb5low_msg_index		;in case Low-voltage Vbb +5V is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going

				movfw	ADRESH
				sublw	V5_MAX
				movlw	rvbb5high_msg_index		;in case Low-voltage Vbb +5V is too high
				btfss	STATUS,C				;C set means ADRESH<=V5_MAX
				call	report_yn				;report error and keep going

				return

;*==============================================================================*
; Subroutine to power the EPROM up for programming
; and test power supply voltages for reasonable values
; On Entry:
;    main EPROM power is already on
; On Exit:
;    BSR, R0, W trashed
;    programming voltage is on
;*==============================================================================*
;turn off CS and power up high voltage for programming
epower_prog:	movlb	0x02				;LATC, LATE are in bank 2
				bcf		LATC,EPROM_CS		;chip select off during programming
				movlw	0x01				;stall for 1 microsecond mol
				call	K_STALL1U

				bcf		LATE,NEN_PVOLTAGE	;power up the device for programming

;stall while the power supply comes up to voltage
				movlw	0x02				;50 mS stall
				call	K_STALL25M

; check to see that the power supply voltages are reasonable - Vneg first
				movlw	AN_VNEG | (1 << ADON)
				call	get_analog

				movlw	VNEG_36_MIN-1
				subwf	ADRESH,W
				movlw	pvneg36low_msg_index	;in case Vneg is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				goto	panic					;panic if too low

				movfw	ADRESH
				sublw	VNEG_36_MAX
				movlw	rvneg36hi_msg_index		;in case Vneg programming voltage is too high
				btfss	STATUS,C				;C set means ADRESH<=VNEG_36_MAX
				call	report_yn				;report error and keep going

;Next, test Vss
				movlw	AN_VSS | (1 << ADON)
				call	get_analog

				movlw	V13_MIN-1
				subwf	ADRESH,W
				movlw	rvss13low_msg_index		;in case High-voltage Vss (+13V) is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going

				movfw	ADRESH
				sublw	V13_MAX
				movlw	pvss13high_msg_index	;in case +13V Vss is too high
				btfss	STATUS,C				;C set means ADRESH<=V13_MAX
				goto	panic					;panic if too high

;Now, test Vbb
				movlw	AN_VBB | (1 << ADON)
				call	get_analog

				movlw	V13_MIN-1
				subwf	ADRESH,W
				movlw	rvbb13low_msg_index		;in case Mid-voltage Vbb (+13V) is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going

				movfw	ADRESH
				sublw	V13_MAX
				movlw	pvbb13high_msg_index	;in case +13V Vbb is too high
				btfss	STATUS,C				;C set means ADRESH<=V13_MAX
				goto	panic					;panic if too high

				return

;*==============================================================================*
; Subroutine to read one MM5204 EPROM byte
; Note that the hardware is slow - a good delay is required.
; On Entry:
;   FSR1L = EPROM address = buffer address low byte
;   FSR1H - high(EPROM_BUF) = EPROM address high byte (just one bit)
;   EPROM is set up for reading, including write data=FF
; On Exit:
;   Value is in W and R2
;   Z set if value is zero
;   trashes R0, BSR
;*==============================================================================*
eread_byte:		call	ewrite_address		;(3.0) FSR1 - EPROM_BUF to address pins

				movlw	.9					;(0.25) delay for EPROM and esp. our hardware
				call	K_STALL1U			;(10.25 uS)

;fall into eread_data

;*==============================================================================*
; Subroutine to read the EPROM data pins
; On Entry:
;   EPROM is set up for reading, including write data=FF
; On Exit:
;   Value is in W and R2
;   Z set if value is zero
;*==============================================================================*
eread_data:		movlb	0x00				;(0.25 uS) EPROM_RDN and PORTE are in bank 0
				comf	EPROM_RDN,W			;(0.25 uS)get actual data, inverted
				andlw	0x7f				;(0.25 uS)bits 0-6 so far - assume high bit is 0
				btfss	PORTE,EPROM_RDN7	;(0.5 uS)get bit 7 (which is inverted)
				iorlw	0x80				;set high bit in result, clear Z flag
				movwf	R2					;(0.25 uS)corrected result in R2
				return						;(0.5 uS)

;*==============================================================================*
; Subroutine to put the address in FSR1 to the EPROM
;   address bits A0-A7 go to EPROM_AN (Port B latch) Address bit 8 goes to
;   bit EPROM_AN8 in port A latch. Note that the address ports invert
; On Entry:
;    FSR1L = low 8 bits of address
;    FSR1H - high(EPROM_BUF) = A8
; On Exit:
;    EPROM address is set
;    trashes W, BSR
; takes 2.5 uS
;*==============================================================================*
ewrite_address:	movlb	0x02			;(.025) LAT registers are in bank 2
				comf	FSR1L,W			;(0.25) invert the low 8 bits of the address
				movwf	EPROM_AN		;(0.25)

				bsf		LATA,EPROM_AN8	;(0.25) assume high bit is 0
				movlw	high(EPROM_BUF)	;(0.25) subtract off buffer base address
				subwf	FSR1H,W			;(0.25)

				btfss	STATUS,Z		;(0.5) test A8
				bcf		LATA,EPROM_AN8	;A8 was high

				return					;(0.5)

;*==============================================================================*
; Subroutine to write one pass through the MM5204 EPROM
; Check for control-C after each byte, asbort if control-C found
; Print a pacifier dot when done with range
; On Entry:
;    COUNTH_SAVE and COUNTL_SAVE = byte count
;    FSR1H_SAVE and FSR1L_SAVE = EPROM buffer start address
;    EPROM is powered up for programming
;    EPROM_CS is already active
;
; On Exit:
;   EPROM is still powered up for programming
;   trashes R0,R1,W,BSR,FSR0L,FSR0H,FSR1L,FSR1H
;   ERR_FLAG set if Vbb was too low
;   Pacifier dot has been printed
;
; Numbers in parenthesis are time in uS
;*==============================================================================*
ewrite_range:	call	wait_txempty		;wait for the transmitter to empty (pacifier dot)

				call	restore_params		;recover original starting address & byte count

; loop once through the entire EPROM range. Check for control-C before each byte.
; leave interrupts masked when done
ewrite_loop:		bsf		INTCON,GIE		;(.25)let Rx interrupt in
					call	chk_ctrlc		;(2.5 if nothing typed) abort on ^C (trashes FSR0)

					bcf		INTCON,GIE		;(.25)global interrupt mask while we write

					call	ewrite_byte		;write a byte to the EPROM (int's are masked)

;move on to next location and test for done
					call	next_a_count	;(4.50)bump address, dec and test count
					btfss	STATUS,Z		;(0.25)
					bra		ewrite_loop		;(0.50)

;done: unmask interrupts 
				bsf		INTCON,GIE			;global interrupt mask

;once per pass through the EPROM range, print a pacifier dot
				movlw	'.'					;Print '.' as pacifier
				goto	K_CONOUT			;return from there

;*==============================================================================*
; Subroutine to write one byte to the MM5204
; o  The MM5204 reads as 00 when erased, so we won't program 00 to bytes.
; o  The minimum program pulse width is 0.5 mS, and the maximum PROG pulse
;    width is 5 mS. (we do 1 mS here.)
; o  The maximum Vdd duty cycle is 25%. (We do 21.6% here.)
;
; On Entry:
;   FSR1L = EPROM address = buffer address low byte
;   FSR1H = buffer address high byte
;   FSR1H - high(EPROM_BUF) = EPROM address high byte (0 or 1)
;   INDF1 = data byte to write
; On Exit:
;    ERR_FLAG set if Vbb was too low. (Panic if it was too high!)
;    R0, R1 trashed
;*==============================================================================*
ewrite_byte:	call	ewrite_address	;(3.0) FSR1 - EPROM_BUF to address pins


;put the data to the EPROM (note the port inverts, but low on the EPROM = 1)
;If the data to write is 00 (erased state), then don't write this byte
				movfw	INDF1			;(0.25) Get data from buffer
				btfsc	STATUS,Z		;(0.5) Don't program 00's
				return

				movwf	EPROM_WDN		;(0.25) Note that the EPROM's data inputs are
										;inverted while programming, but our data outputs
										;also invert.
	
;stall for 1000 uS (Tds=40 uS min from address and data to to PROGRAM on)
;We go a lot longer here, because it does not cost us any overall programming
;time - Vdd is off, so we take it out of the stall at the end.
				movlw	.04				;(.25) stall for 1000 uS
				call	K_STALL250U		;(~1000)

;Apply Vbb (active low!)
				movlb	0x02			;(0.25) LATE, LATC are in bank 2
				bcf		LATE,NEN_VBB	;(0.25)

;stall for 2.5 uS (tbh=1 uS min from Vbb to Vdd)

				movlw	.01				;(0.25)
				call	K_STALL1U			;(2.25)

;Apply Vdd
				bsf		LATC,EN_VDD		;[0.25]

;stall for 60 uS (Tsh=40 uS min, 100 uS max from Vdd to PROGRAM)
;include the 58 uS time for the A/D conversion below
				movlw	.2						;[0.25]
				call	K_STALL1U				;[2.25]

;test VBB high voltage (+25V)

				movlw	AN_VBB | (1 << ADON)	;[0.25]
				call	get_analog				;[56] this sets BSR to 0x01

				movlw	VBB_25_MIN-1			;[0.25]
				subwf	ADRESH,W				;[0.25]
				btfss	STATUS,C				;[0.50]C cleared means ADRESH<=W
				bsf		FLAGS,ERR_FLAG			;remember error and keep going

				sublw	VBB_25_MAX-VBB_25_MIN	;[0.25]
				movlw	pvbb25high_msg_index	;[0.25]in case +25V Vbb is too high
				btfss	STATUS,C				;[0.50]C set means ADRESH<=VBB_25_MAX
				goto	panic					;panic if too high


;set PROGRAM low (active)
				movlb	0x02					;[0.25] LATC & LATE are in bank 2
				bsf		LATC,EN_PROGRAM			;[0.25]

;stall for 1 mS (Tpw=0.5 mS min, 5 mS max)
				movlw	0x04					;[0.25]
				call	K_STALL250U				;[1000.25]

;set PROGRAM high (inactive)
				bcf		LATC,EN_PROGRAM			;[0.25]

;stall for 2.5 uSec (Tsh = 1.0 uS min)
				movlw	.01						;[0.25]
				call	K_STALL1U				;[2.25]

;remove Vdd
				bcf		LATC,EN_VDD				;(0.25)

;stall for 2.5 uSec (Tbh = 1.0 uS min)
				movlw	.01						;(0.25)
				call	K_STALL1U				;(2.25)

;Remove Vbb (active low!)
				bsf		LATE,NEN_VBB			;(0.25)

;stall to meet duty cycle requirement with a little extra.
; Vdd was on for 1104.25 uS. The code takes 1014 uS while Vdd are off. For 25%
; duty cycle, we need to stall for a total of  (3 X 1104.25s) - 1013 uS = 2.30 mS
; We'll add extra for good measure
				movlw	.12						;(.25) stall for 3.0 mS
				goto	K_STALL250U				;return from there

;*==============================================================================*
;* Subroutine to save FSR1 and COUNTH,COUNTL in special save location
;* trashes W, BSR
;*==============================================================================*
save_params:	movlb	VAR_BANK		;variables page

				movfw	FSR1L			;save the starting address
				movwf	FSR1L_SAVE
				movfw	FSR1H
				movwf	FSR1H_SAVE

				movfw	COUNT_LOW
				movwf	COUNTL_SAVE
				movfw	COUNT_HIGH
				movwf	COUNTH_SAVE

;*==============================================================================*
;* Subroutine to restore FSR1 and COUNTH,COUNTL from special save location
;* trashes W, BSR
;*==============================================================================*
restore_params:	movlb	VAR_BANK		;variables page

				movfw	FSR1L_SAVE
				movwf	FSR1L
				movfw	FSR1H_SAVE
				movwf	FSR1H

				movfw	COUNTL_SAVE
				movwf	COUNT_LOW
				movfw	COUNTH_SAVE
				movwf	COUNT_HIGH
				return

;*==============================================================================*
; Subroutine to circular-increment FSR1, decrement COUNT_HIGH &COUNT_LOW, and
; test for done. Z set if COUNT has been decremented to 0
;*==============================================================================*
next_a_count:	call	next_a			; circular-increment memory pointer FSR1
			
;COUNT is our counter. Decrement it and check for 0
				movlw	1				;painful 16-bit decrement
				subwf	COUNT_LOW,F
				btfss	STATUS,C
				decf	COUNT_HIGH,F

				movfw	COUNT_HIGH		;test for 0
				iorwf	COUNT_LOW,W
				return					;with Z flag set if COUNT=0

;*==============================================================================*
; Subroutine to circular-increment FSR1
; (2.25 uS total)
;*==============================================================================*
next_a:			addfsr	FSR1,1			;(0.25)16-bit inc FSR1

;wrap buffer pointer, assuming buffer starts at 0x2000.
	if	high(EPROMBUF_SIZE) & 0x01		;256-byte buffer?
				bcf		FSR1H,0x1
	endif
	if	high(EPROMBUF_SIZE) & 0x02		;512-byte buffer?
				bcf		FSR1H,0x2
	endif
				return					;(0.50)

;*==============================================================================*
; Subroutine to report one analog measurement
; On Entry:
;    W = initial string
;    ADRESH = high bytre of result
;    ADRESL = low byte of result
;*==============================================================================*
rep_analog:		call	cr_printf			;print string

				movlb	0x01				;bank select for ADC
				movfw	ADRESH				;result high byte
				call	K_PRINTHEX2

				movlb	0x01				;bank select for ADC
				movfw	ADRESL				;result low byte
				goto	K_PRINTHEX2			;return from there

;*==============================================================================*
; Subroutine to get one ADC measurement (see page 146)
; On Entry:
;    W=ADC channel with ADON bit set
; on Exit:
;    ADRESH = high byte of result
;    ADRESL = low byte of result
;    Bank Select Register = 1
; This routine takes about 53 uS
;*==============================================================================*
;select ADC input channel and enable ADC
get_analog:		movlb	0x01			;[0.25]bank select for ADC
				movwf	ADCON0			;[0.25]select analog input per reg W

;delay for ADC acquisition 
				movlw	0x05			;[0.25] 5 uS is enough time for settling
				call	K_STALL1U		;[5.25]

;initiate conversion
				bsf		ADCON0,ADGO		;[0.25]

;wait for conversion to complete (note: fosc/64 so Tad=4 uS, and a full conversion takes 46 uS)
get_analog_wait:	btfsc	ADCON0,ADGO	;[46.5]
					bra		get_analog_wait

;result is ready in ADRESH and ADRESL
				return					;[0.25]

;********************************************************************************
;General panic routine
; Shut power off immediately, go back to Main
; On Entry, W=message index
;********************************************************************************
panic:			movwf	R4					;temp save message
				movlb	0x02				;LATC & LATE are in bank 2
				movlw	EPROM_OFF_C			;remove PROG, CS, Vdd, Vbb
				movwf	LATC
				movlw	EPROM_OFF_E			;Power off
				movwf	LATE

				movlw	panic_msg_index
				call	cr_printf
				movfw	R4					;this particular panic
				goto	print_main

;********************************************************************************
; Voltage-Error Reporting Routine
;   Report an out-of-spec voltage, and ask the user whether or not to continue
; On Entry:  W=message index
; Aborts if not 'Y'
; trashes W, R0, FSR0, FSR1
;********************************************************************************
report_yn:		call	cr_printf
				movlw	anyway_msg_index	;proceed anyway?

;fall into ask_yn

;*==============================================================================*
; Ask the user for y (yes) or n (no)
; On Entry:
;    W = page 1 ask string
; Aborts if not 'Y'
; trashes W, R0, FSR0, FSR1
;*==============================================================================*
ask_yn:			call	printf				;print page 1 ask string
				call	get_line			;get CR-terminated string
				call	get_uchr			;look at first character
				xorlw	'Y'					;anything that starts with Y is okay
				btfsc	STATUS,Z
				return

				goto	abort			;anything else aborts

;################################################################################
;# Null-Terminated Strings														#
;#   These are packed into 14-bit words by the PIC assembler. Some words will	#
;#   have 0 as one of their bytes, which are not the terminating null.			#
;#   Note that the print routines convert CR's int CR-LF's.						#
;#																				#
;#   Each string must have an entry in string_table. printf expects an index	#
;#   into this table to find the 14-bit starting adress of the string.			#
;################################################################################
string_table:
help_msg_index	equ	$ - string_table
			dw	help_msg
bhelp_msg_index	equ	$ - string_table
			dw	bhelp_msg
copyright_index	equ	$ - string_table
			dw	copyright
dhelp_msg_index	equ	$ - string_table
			dw	dhelp_msg
ehelp_msg_index	equ	$ - string_table
			dw	ehelp_msg
fhelp_msg_index	equ	$ - string_table
			dw	fhelp_msg
lhelp_msg_index	equ	$ - string_table
			dw	lhelp_msg
signon_msg_index	equ	$ - string_table
			dw	signon_msg
ready2prog_msg_index	equ	$ - string_table
			dw	ready2prog_msg
notblank_msg_index	equ	$ - string_table
			dw	notblank_msg
anyway_msg_index	equ	$ - string_table
			dw	anyway_msg
smarten_msg_index	equ	$ - string_table
			dw	smarten_msg
backwards_msg_index	equ	$ - string_table
			dw	backwards_msg
panic_msg_index	equ	$ - string_table
			dw	panic_msg
pvneg36low_msg_index	equ	$ - string_table
			dw	pvneg36low_msg
pvss13high_msg_index	equ	$ - string_table
			dw	pvss13high_msg
pvbb13high_msg_index	equ	$ - string_table
			dw	pvbb13high_msg
pvbb25high_msg_index	equ	$ - string_table
			dw	pvbb25high_msg
rvneg36hi_msg_index	equ	$ - string_table
			dw	rvneg36hi_msg
rvneg12low_msg_index	equ	$ - string_table
			dw	rvneg12low_msg
rvss5high_msg_index	equ	$ - string_table
			dw	rvss5high_msg
rvss5low_msg_index	equ	$ - string_table
			dw	rvss5low_msg
rvbb5high_msg_index	equ	$ - string_table
			dw	rvbb5high_msg
rvbb5low_msg_index	equ	$ - string_table
			dw	rvbb5low_msg
rvneg12hi_msg_index	equ	$ - string_table
			dw	rvneg12hi_msg
rvss13low_msg_index	equ	$ - string_table
			dw	rvss13low_msg
rvbb13low_msg_index	equ	$ - string_table
			dw	rvbb13low_msg
rvnegstuck_msg_index	equ	$ - string_table
			dw	rvnegstuck_msg
rvssstuck_msg_index	equ	$ - string_table
			dw	rvssstuck_msg
rvbbstuck_msg_index	equ	$ - string_table
			dw	rvbbstuck_msg
rvneggt0_msg_index	equ	$ - string_table
			dw	rvneggt0_msg
vnegequals_msg_index	equ	$ - string_table
			dw	vnegequals_msg
vssequals_msg_index	equ	$ - string_table
			dw	vssequals_msg
vbbequals_msg_index	equ	$ - string_table
			dw	vbbequals_msg
vbbwaslow_msg_index	equ	$ - string_table
			dw	vbbwaslow_msg
aoffset_msg_index	equ	$ - string_table
			dw	aoffset_msg
auto_msg_index	equ	$ - string_table
			dw	auto_msg
progcycles_msg_index	equ	$ - string_table
			dw	progcycles_msg
buffill_msg_index	equ	$ - string_table
			dw	buffill_msg
smartprog_msg_index	equ	$ - string_table
			dw	smartprog_msg
programing_msg_index	equ	$ - string_table
			dw	programing_msg
pequals_msg_index	equ	$ - string_table
			dw	pequals_msg
verifying_msg_index	equ	$ - string_table
			dw	verifying_msg
abort_msg_index	equ	$ - string_table
			dw	abort_msg
dataread_msg_index	equ	$ - string_table
			dw	dataread_msg
bad_input_msg_index	equ	$ - string_table
			dw	bad_input_msg
recerror_msg_index	equ	$ - string_table
			dw	recerror_msg
hexerror_msg_index	equ	$ - string_table
			dw	hexerror_msg
cksumerror_msg_index	equ	$ - string_table
			dw	cksumerror_msg
rcnterror_msg_index	equ	$ - string_table
			dw	rcnterror_msg
errorcount_msg_index	equ	$ - string_table
			dw	errorcount_msg
reccount_msg_index	equ	$ - string_table
			dw	reccount_msg
loadcount_msg_index	equ	$ - string_table
			dw	loadcount_msg
int_start_rec_index	equ	$ - string_table
			dw	int_start_rec
int_end_rec_index	equ	$ - string_table
			dw	int_end_rec
mot_start_rec_index	equ	$ - string_table
			dw	mot_start_rec
mot_end_rec_index	equ	$ - string_table
			dw	mot_end_rec
cr_msg_index	equ	$ - string_table
			dw	cr_msg
mismatcha_msg_index	equ	$ - string_table
			dw	mismatcha_msg
mismatchb_msg_index	equ	$ - string_table
			dw	mismatchb_msg
mismatche_msg_index	equ	$ - string_table
			dw	mismatche_msg
colonspace_msg_index	equ	$ - string_table
			dw	colonspace_msg
isblank_msg_index	equ	$ - string_table
			dw	isblank_msg
success_msg_index	equ	$ - string_table
			dw	success_msg
writefail_msg_index	equ	$ - string_table
			dw	writefail_msg
fail_msg_index	equ	$ - string_table
			dw	fail_msg
on_msg_index	equ	$ - string_table
			dw	on_msg
off_msg_index	equ	$ - string_table
			dw	off_msg
masterpwr_msg_index	equ	$ - string_table
			dw	masterpwr_msg
cesignal_msg_index	equ	$ - string_table
			dw	cesignal_msg
vdd_msg_index	equ	$ - string_table
			dw	vdd_msg
vbb_msg_index	equ	$ - string_table
			dw	vbb_msg
highv_msg_index	equ	$ - string_table
			dw	highv_msg
progsignal_msg_index	equ	$ - string_table
			dw	progsignal_msg
echostate_msg_index	equ	$ - string_table
			dw	echostate_msg
eprom_msg_index	equ	$ - string_table
			dw	eprom_msg
range_msg_index	equ	$ - string_table
			dw	range_msg
read_msg_index	equ	$ - string_table
			dw	read_msg
csum_msg_index	equ	$ - string_table
			dw	csum_msg
buffer_msg_index	equ	$ - string_table
			dw	buffer_msg
cmpgood_msg_index	equ	$ - string_table
			dw	cmpgood_msg
overprog_msg_index	equ	$ - string_table
			dw	overprog_msg
loaderrev_msg_index	equ	$ - string_table
			dw	loaderrev_msg
fwrev_msg_index	equ	$ - string_table
			dw	fwrev_msg
wrongkern_msg_index:	equ	$ - string_table
			dw	wrongkern_msg
wrongprog_msg_index	equ	$ - string_table
			dw	wrongprog_msg

	
;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890
help_msg:	da	"\r\r"
			da	"EPROM COMMANDS (?E for help)               DIAGNOSTIC COMMANDS (?D for help)\r"
			da	"  EB <adr> <cnt> Blank check                 TM <0/1> Test Master power\r"
			da	"  EC <adr> <cnt> Compare to buffer           TH <0/1> Test High voltages\r"
			da	"  EP <adr> <cnt> Program from buffer         TB <0/1> Test vBb\r"
			da	"  ER <adr> <cnt> Read into buffer            TD <0/1> Test vDd\r"
            da  "  ES <adr> <cnt> checkSum EPROM              TC <0/1> Test Chip select\r"
			da	"  PC <cnt>       set programming Pass Count  TP <0/1> Test Prog signal\r"
			da	"     (00 = use smart algorithm)                 (0=off, 1=on)\r"
			da  "FILE TRANSFER COMMANDS (?F for help)         WA <adr> Write Address pins\r"   
			da	"  AO       automatic Address Offset mode     WD <val> Write Data pins\r"
			da	"  AO <val> set Address Offset to <val>       RD       Read Data pins\r"                               
			da	"  UI       Upload buffer as Intel hex          (do WD FF, TD 1, and TM 1 first)\r"
			da	"  US       Upload buffer as S-records        RV       Read Voltages\r"
			da	"  :        begin Intel hex record          OTHER COMMANDS\r"
			da	"  S        begin Motorola S-record           DS       Display all Settings\r"
			da	"BUFFER COMMANDS (?B for help)                TE <0/1> Terminal Echo (default 1)\r"
			da  "  DB <adr> <cnt> Display Buffer contents     RE       REset programmer\r"
			da	"  FB <val>       Fill Buffer with <val>      ^C       Abort command\r"
			da	"  MB <adr>       Modify Buffer (^C to quit)  ^S, ^Q   Pause, restart Tx data\r"
			da	"All values are hex and default to 0.         ?L       Notes on firmware loader\r"
			da	0
;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

bhelp_msg:	da	"\r\r"
			da	"                             BUFFER COMMANDS HELP\r\r"
			da	"These commands let you view and modify the buffer contents. DB and FB are self-\r"
			da	"explanatory. Addresses are 9 bits, from 000 to 1FF. Data are 8 bits, from 00 to\r"
			da	"FF. You can use ^S during DB buffer display to pause/restart printing of the\r"
			da	"buffer contents. MB allows you to edit the buffer contents, one byte at a time.\r"
			da	"The address and data for the specified address are displayed. If you type a\r"
			da	"hexidecimal value followed by <return>, then that value will replace the buffer\r"
			da	"data. If you just type <return>, then the buffer data will remain unchanged.\r"
			da	"After you type <return>, the data from the next address will be displayed,\r"
			da	"allowing you to modify it next. You can continue to view/edit sequential buffer\r"
			da	"data until you type ^C. If you continue past address 1FF, the address will wrap\r"
			da	"around to 000.\r",0
;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

copyright:	da  "\r\r"
			da	"                             An Obscure EPROM\r"
			da	"The National Semiconductor MM5204 EPROM, and the MM4204 mil-spec version, were\r"
			da	"among the last PMOS EPROMs, and were intended to be used with National's\r"
			da	"SC/MP II microprocessor and its ilk. The MM5204 was designed into a few other\r"
			da	"systems in the mid-70s, such as early versions of Processor Technology's SOL-20\r"
			da	"computer, with 'Solos' stored in four MM5204s on its Personality Module.\r\r"
			da	"Programmers for these obscure EPROMs are rare - PMOS EPROMs can't be programmed\r"
			da	"by most so-called universal programmers. Not widely used, an incompatible pin-\r"
			da	"out, and completely different programming voltages than other PMOS EPROMS (such\r"
			da	"as the more popular 1702A from Intel): few programmers could ever program the\r"
			da	"MM5204. When they were introduced, National recommended using either their\r"
			da	"IMP16-P system with the IMP16-P/805 option, or their IPC16P system with the\r"
			da	"IPC16P/805 option - or you could send a file to them (via Telex or on paper\r"
			da	"tape), and they'd program the EPROMs for you. They also mention the Data I/O\r"
			da	"Model V programmer as capable of programming these EPROMs.\r\r"
			da	"Now, decades after the MM4204 & MM5204 have become hopelessly obsolete, you can\r"
			da	"program them easily with this ME5204 EPROM programmer. Enjoy!\r\r"
			da	"                                                      -Martin Eberhard\r\r"
			da	"This design Copyright (c) 2015 Martin Eberhard - like anyone would steal it.",0
;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

dhelp_msg:	da	"\r\r"
			da	"                 ME5204 PROGRAMMER DIAGNOSTIC TEST COMMANDS\r\r"
			da	"All T commands take 0 or 1 as an argument: 0 means inactive, 1 means active.\r"
			da	"TM 1 turns on the main power. TM 0 turns off all power to the EPROM.\r"
			da	"TH controls the high-voltage(programming) supply. TH 0 turns off the high\r"
			da	"   voltage: Vss & Vbb = +5V, and Vneg = -12V. TH 1 turns on high voltage:\r"
			da	"   Vss = +13V, Vbb = +13V or +25V, and Vneg = -36V.\r"
			da	"TB controls Vbb only when the programming supply is on (TH 1). TB 0 sets Vbb\r"
			da	"   inactive (+13V). TB 1 sets Vbb active (+25V).\r"
			da	"TD controls Vdd. TD 0 sets Vdd inactive (at Vss, either +5V or +13V, depending\r"
			da	"   on TH). TD 1 sets Vdd active (either -12V or -36V, depending on TH).\r"
			da	"TC 1 sets Chip Select active (0 volts). TC 0 sets Chip Select inactive (Vss).\r"
			da	"TP 1 sets PROG active (0 volts). TP 0 sets PROG to Vss (either +5V or +13V).\r"
			da	"WA <val> writes <val> to the EPROM address pins. Note that these are pulled up\r"
			da	"   to Vss, so do TM 1 and maybe TH 1 if you expect to measure these signals.\r"
			da	"WD <val> writes <val> to the EPROM data pins. These are also pulled up to Vss.\r"
			da	"RD reads the EPROM data pins and prints the result on the console. You must\r"
			da	"   first do WD FF and TM 1 if you want to test these signals. If you are\r"
			da	"   reading an EPROM, then you must also do TH 0, TM 1, TD 1, and CS 1.\r"
			da	"RV reads several analog voltages and prints the unvarnished results in hex.\r"
			da	"NOTE: the MM5204 spec references voltages from Vss, not ground as is done here.\r"
			da	"Reference your meter to Vss, measure voltages, and compare to the MM5204 specs.",0
;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

ehelp_msg:	da	"\r\r"
			da	"                           EPROM COMMANDS HELP\r\r"
			da	"<adr> is the (3-digit hex) beginning address and <cnt> is the (3-digit hex)\r"
			da	"byte count for the for the operation. <beg> defaults to 000, and <cnt> defaults\r"
			da	"to 200 (i.e. the entire EPROM). The maximum value for <adr> is 1FF, and the\r"
			da	"maximum value for <cnt> is 200. <cnt>=000 is treated as <cnt>=200. The EPROM is\r"
			da	"always verified (compared to the buffer) when done programming.\r\r"
			da	"PC <cnt> sets the number of programming passes through the specified range.\r"
			da	"<cnt> = 00 selects the P+5P Smart Algorithm. This will repeatedly program the\r"
			da	"EPROM until it matches the buffer, and then over-program the EPROM 5 times as\r"
			da	"many passes as it took to get the EPROM to match the buffer. (This is the\r"
			da	"algorithm recommended in National Semiconductor's MM5204 data sheet.)\r\r"
			da	"Vss and Vdd voltages are checked before programming begins, and during reads.\r"
			da	"Under-voltages are reported, giving you the option to continue anyway. Over-\r"
			da	"voltages will abort programming with a panic. Vbb is only raised to 25V briefly\r"
			da	"while programming each data byte, and is only measured then. If Vbb is too high\r"
			da	"then programming will abort with a panic. Vbb too low will be reported when the\r"
			da	"programming is finished. Voltage measurements are not very accurate, and will\r"
			da	"catch only gross errors. Use a voltmeter for accurate adjustment.",0

;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890
fhelp_msg:	da	"\r\r"
			da	"                         FILE TRANSFER COMMANDS HELP\r\r"
			da	"These commands transfer files to and from the buffer in either simple Intel Hex\r"
			da	"or simple Motorola S-record format. No special command is needed to download a\r"
			da	"file to the buffer memory - the hex records themselves are directly recognized.\r"
			da	"The UI and US commands upload in Intel and S-record formats.\r\r"
			da	"The Address Offset affects the upper byte of the 2-byte address in each hex\r"
			da	"record, and can be set with the AO command. AO with no parameter resets the\r"
			da	"Address Offset to default. For uploads, the Address Offset defaults to 00, and\r"
			da	"is added to the upper address byte in each transmitted record. For downloads,\r"
			da	"the Address Offset defaults to the high address byte from the first received\r"
			da	"record, and is is subtracted from the high address byte of each received\r"
			da	"record. Received data will not be written to the buffer if the resulting\r"
			da	"address high byte is greater than 01.\r\r"
			da	"Downloaded records will generate an error for the following reasons:\r"
			da	" ERROR CODE         ERROR\r"
			da	"   ? Cnt       S5 record-count mismatch (S-records only)\r"
			da	"   ? Csm       Checksum error\r"
			da	"   ? Hex       Illegal hexadecimal character\r"
			da	"   ? Rec       Unsupported record type\r",0

;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890
lhelp_msg	da	"\r\r"
			da	"                         NOTES THE ON FIRMWARE LOADER\r\r"
			da	"This firware can be loaded via the ME5204 serial port. To invoke the Loader,\r"
			da	"type capital 'L' a few times immediately after power on, or immediately after\r"
			da	"issuing a REset command. You should see the ME Loader prompt instead of the\r"
			da	"ME5204 banner.\r\r"
			da	"IMPORTANT: The Loader requires your terminal program to accept XON/XOFF\r"
			da	"handshaking. The loader expects an Intel hex file exactly as produced by the\r"
			da	"Microchip MAPSM assembler. (The firmware must be written to be compatible with\r"
			da	"the ME Loader Kernel version 1.x, not documented here.)  Once you see the\r"
			da	"ME Loader prompt, send the Intel hex firmware file to the ME5204. It will take\r"
			da	"a while to load. When it is done, the error count must be 0000 - otherwise, the\r"
			da	"firmware is probably corrupted.\r\r"
			da	"Note that a bad firmware load will trash the ME5204 firmware, but it will not\r"
			da	"trash the Loader. Even with trashed firmware, you can type  several 'L's\r"
			da	"immediately after powering on, to invoke the Loader and try again.\r\r"
			da	"You can exit the Loader and run the loaded firmware either by power-cycling the\r"
			da	"ME5204, or by typing the ESC key several times.\r" 
			da	0

;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

signon_msg:	da	"\r\r*====================================*\r"
			da	"*               ME5204               *\r"
			da	"*====================================*\r"
			da	"*   MM4204/MM5204 EPROM Programmer   *\r"
			da	"*         By Martin Eberhard         *\r"
			da	"*       Firmware Version  "
			da	((REV_MAJOR + '0') << 7) + '.'
			da	((((REV_MINOR & 0xf0) >> 4) + '0' ) << 7) + ((REV_MINOR & 0x0F) + '0')
			da	"       *\r"
			da	"*====================================*\r\r"
			da	"Type ? for command list\r",0

ready2prog_msg:	da	"\rMake sure the EPROM is inserted correctly, with pin 1\r"
				da	"closest to the socket handle. Ready to program (Y/N)? ",0

notblank_msg:	da	"\rThe EPROM is not blank in the programming range"			;fall into anyway_msg
anyway_msg:		da	". Proceed anyway (Y/N)? ",0

smarten_msg:	da	"\rP+5P Smart programming algorithm enabled",0

;					   12345678901234567890123456789012345678901234567890123456789012345678901234567890
backwards_msg:	da	"\rAll bytes of this EPROM are FF and cannot be programmed.\r"
				da	"Is it inserted backwards? Is it really a MM4204 or MM5204?",0

panic_msg:	da	"PANIC! ",0
pvneg36low_msg:	da	"-36V Vneg is too low!",0
pvss13high_msg:	da	"+13V Vss is too high!",0
pvbb13high_msg:	da	"+13V Vbb is too high!",0
pvbb25high_msg:	da	"+25V Vbb is too high!",0
rvneg12hi_msg:	da	"-12V Vneg is too high",0
rvneg12low_msg:	da	"-12V Vneg is too low",0
rvss5high_msg:	da	"+5V Vss is too high",0
rvss5low_msg:	da	"+5V Vss is too low",0
rvbb5high_msg:	da	"+5V Vbb is too high",0
rvbb5low_msg:	da	"+5V Vbb is too low",0
rvneg36hi_msg:	da	"-36V Vneg is too high",0
rvss13low_msg:	da	"+13V Vss is too low",0
rvbb13low_msg:	da	"+13V Vbb is too low",0
rvnegstuck_msg:	da	"Vneg is not shutting off",0
rvssstuck_msg:	da	"Vss is not shutting off",0
rvbbstuck_msg:	da	"Vbb is not shutting off",0
rvneggt0_msg:	da	"Vneg is above 0V",0
vbbwaslow_msg:	da	"+25V Vbb was low during programming",0

vnegequals_msg:	da	"Vneg: ",0
vssequals_msg:	da	" Vss: ",0
vbbequals_msg:	da	" Vbb: ",0

loadcount_msg:	da	" records loaded in buffer with "	;Fall into aoffset_msg
aoffset_msg:	da	"Address Offset: ",0
auto_msg:		da	"automatic",0
progcycles_msg:	da	"Programming Cycles: ",0
buffill_msg:	da	"Buffer filled with ",0

smartprog_msg:	da	"P+5P Smart "			;fall into programing_msg
programing_msg:	da	"Programming",0
pequals_msg:	da	"P=",0
verifying_msg:	da	"Verifying",0
abort_msg:		da	"Abort",0
dataread_msg:	da	"Data Read: ",0

bad_input_msg:	da	"\rHuh?",0

recerror_msg:	da	" ? Rec",0
hexerror_msg:	da	" ? Hex",0
cksumerror_msg:	da	" ? Csm",0
rcnterror_msg:	da	" ? Cnt",0

errorcount_msg:	da	", Bad "				;falls into reccount_msg
reccount_msg:	da	"Records: ",0

int_start_rec:	da	":",0				;Intel record start
int_end_rec:	da	"\r:00000001FF\r",0	;Intel EOF record
mot_start_rec:	da	"S1",0				;Motorola record start
mot_end_rec:	da	"\rS9030000FC"		;Motorola EOF record (falls int cr_msg)
cr_msg:			da	"\r",0				;just a simple CR

mismatcha_msg:	da	"\rError at address ",0
mismatchb_msg:	da	"   Buffer: ",0
mismatche_msg:	da	"   EPROM"			;falls into colonspace_msg
colonspace_msg:	da	": ",0

isblank_msg:	da	"EPROM is blank",0
success_msg:	da	"Success",0
writefail_msg:	da	"Write "			;fall into fail_msg
fail_msg:		da	"Fail",0

on_msg:			da	" on",0
off_msg:		da	" off",0
masterpwr_msg:	da	"Master Power",0
cesignal_msg:	da	"-CS Signal",0
vdd_msg:		da	"Vdd",0
vbb_msg:		da	"Vbb",0
highv_msg:		da	"High Voltage",0
progsignal_msg:	da	"-Prog Signal",0
echostate_msg:	da	"Echo",0

eprom_msg:		da	"EPROM ",0
range_msg:		da	"range ",0
read_msg:		da	"read into buffer. ",0	
csum_msg:		da	"checksum: ",0
buffer_msg:		da	"Buffer ",0
cmpgood_msg:	da	"EPROM matches buffer",0
overprog_msg:	da	". 5P over-programming",0	;longest message last
loaderrev_msg:	da	"ME Loader Kernel version ",0
fwrev_msg:		da	"ME5204 firmware version "
		da	((REV_MAJOR + '0') << 7) + '.'
		da	((((REV_MINOR & 0xf0) >> 4) + '0' ) << 7) + ((REV_MINOR & 0x0F) + '0'),0
wrongkern_msg:	da	"Wrong ME Loader Kernel revision for this firmware!",0
wrongprog_msg:	da	"Wrong firmware for this programmer!\r",0

	end
