	title "ME1702A Programmer Firmware Rev 2.03"
	subtitle "Copyright (C) 2020 Martin Eberhard"
	list b=4, c=132, n=80
	errorlevel 1		;eliminate all the 'not in bank 0' messages

;********************************************************************************
;* 1702A Programmer Firmware													*
;*																				*
;*Revision    Date        Author         Comments								*
;*	1.00    19 Dec 11  M. Eberhard	Created (never tested)						*
;*	1.01    08 Jan 12  M. Eberhard  Interrupt-driven UART, lots of features		*
;*	1.02    27 Jan 12  M. Eberhard  Improve smart algorithm, better ^C test.	*
;*                                  This is the 1st basically working vers.		*
;*	1.03    12 Feb 12  M. Eberhard  Support plain 'S9' as EOF record			*
;*	1.04    15 Feb 12  M. Eberhard  Check voltages with ADC. Checksum buffer.	*
;*									Add ES command.Fix bug in ER command		*
;*	1.05	20 Feb 12  M. Eberhard  Treat an EPROM full of FFs as backwards.	*
;*									Check programming range for blank.			*
;*	1.06   19 Mar 12  M. Eberhard  Support 1701/1702 also. Add DS command. Add	*
;*									Enhanced help screens. Note that R4 must be	*
;*									replaced with 100 ohms to support 1702s		*
;*	1.07	20 Mar 12  M. Eberhard	Better behavior when receiving records with	*
;*									mismatched page addresses. Test for ^C		*
;*									after each byte during programming. 		*
;*	1.08	27 NOV 12  M. Eberhard	Fix a few non-critical typoes, etc.			*
;*                                  Fix bug with UART queue pointer bumping!	*
;*									(never released)							*
;*	2.00	11 JAN 13  M. Eberhard	Rewritten to work with Loader Kernel 1.0. 	*
;*									This allows serial port firmware updates.	*
;*  2.01	17 Aug 15  M. Eberhard	Add automatic page address for loads.		*																				*
;*									Improve some message strings.				*
;*  2.02    03 Oct 20  M. Eberhard	fix bug when continuing to program after	*
;*									a voltage is reported to be out of bounds.	*
;*									(Thanks Thomas R.!)	Increase time for power	*
;*									supply to stabilize in epower_read from		*
;*									50 mS to 100 mS.							*
;* 2.03    21 Jan 2022  M. Eberhard Filter ADC results by looping until 2 ADC	*
;*									conversions in a row match (in get_analog).	*
;*									Prettier backspacing and Abort message.		*
;********************************************************************************
; 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	0x03	;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

;********************************************************************************
;*									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.				*
;*																				*
;* Kernel 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										*
;********************************************************************************
	page
;********************************************************************************
;* Include standard header, programmer headers, and macros						*
;********************************************************************************
TRUE	equ	0xff
FALSE	equ	0

	#include	ASCII.inc
	#include	MartinsMacros.inc
	#include	ME1702A.inc
	#include	KernelMemory.inc

;################################################################################
;# Initialization Routine														#
;#   Initialize 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			;still working on the I/O ports
				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	PAGE_ADDR		;page address is variables page
				clrf	PA_FLAGS		;default to automatic page address

				clrf	PROG_CYCLES		;default to smart algorithm
				movlw	(1 << ECHO_STATE)
				movwf	FLAGS			;clear lots of things			

;Clear the 256-byte EPROM buffer, which starts on an even page boundary
			movlw	high(EPROM_BUF)		;point to beginning of buffer
			movwf	FSR0H
			clrf	FSR0L

clrbuf_loop:	clrf	INDF0			;write value to buffer
				incfsz	FSR0L,F			;bump & test pointer
				bra		clrbuf_loop

;re-initialize the receive queue in case of trash from 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 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)
			movwf	INTCON			;peripheral ints, global enable only

;verify that this firmware matches the programmer
			call	K_PROG_ID
			xorlw	ME1702ID
			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.			#
;#   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 or print_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 for file downloads
				clrf	R3					;R3 counts bad hex records
				clrf	R4					;R4 counts hex records
				clrf	R5					;R5 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 error 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 8-bit hex parameter that came with the 2-character command.
; get_param will return default 0x00 and INP_FLAG=0 if no parameter, and
; will abort directly to main on bogus hex. The first parameter may be an
; address, an 8-bit value, or a 0/1 input. Save this parameter's value in
; FSRL and set up FSR1 as the EPROM buffer pointer. Set INP_FLAG if a value
; was provided, otherwise clear it.
;---------------------------------------------------------------------------
			call	get_param			;get a hex byte into R1
			movfw	R1
			movwf	FSR1L				;save first parameter in FSR1L

			movlw	high(EPROM_BUF)
			movwf	FSR1H

;-----------------------------------------------------------------------
; go execute a 2-character command, based on the first character
; R2 = 1st command chr
; R4 = 2nd command chr
; FSR1L = 1st parameter that came with the command
; FSRIH = high(EPROM_BUF)
; INP_FLAG = 1 if at least one parameter was provided, 0 otherwise
; All of these command-processing routines jump to main when done
;-----------------------------------------------------------------------
			movfw	R2

			xorlw	'?'					;help command?
			btfsc	STATUS,Z
			bra		cmd_help

			xorlw	'?' ^ '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'			;Page Address or Programming cycles cmd?
			btfsc	STATUS,Z
			goto	cmd_papc

			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 = first user parameter, if one was entered, 0 otherwise
;     INP_FLAG = 1 if at least one parameter was provided, 0 otherwise
;* All these routines return to main (or print_main), except the download
; commands, which return to no_prompt between records.
;********************************************************************************
;******=========================================================================*
;* Rx * Read Data, Read Voltages, or Reset command
;******
;*==============================================================================*
cmd_readres:	movfw	R4				;2nd command character

				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 unregulated (pre-regulated) voltage
				movlw	AN_VPRE | (1 << ADON)
				call	get_analog
				movlw	vpreequals_msg_index
				call	rep_analog

;get and report VCCS
				movlw	AN_VCCS | (1 << ADON)
				call	get_analog
				movlw	vccequals_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 RD bus and report result
				call	eread_data				;read data pins into W
				call	K_PRINTHEX2				;print it

				goto	main

;*****==========================================================================*
;* ? *  Help Commands
;*****
; 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 R4
				movfw	R4					;2nd command character
				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
;*****
; Incoming Motorola S-Record Command
; Valid Motorola S-record follows. ('S' already received)
; This routine validates a Motorola S-record, puts the payload into EPROM_BUF,
; and checks the checksum. The prompt is not printed after a data record or
; S5 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
;	R5 is a count of records loaded into the buffer so far
;   EPROM power is off
;   PA_MANUAL = 0 for automatic page address, 1 for manuually-set address
;   PA_INIT = 1 if automatic page address has been set
;   PAGE_ADDR = current page address
; 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
;	R5 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 (which tells us the record count)
; 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		report_recerr

hex_error:		movlw	hexerror_msg_index		;indicate hex error
				bra		report_recerr

s5_error:		movlw	rcnterror_msg_index		;indicate bad S5 record
				bra		report_recerr

cksum_error:	movlw	cksumerror_msg_index	;indicate checksum error

report_recerr:	incf	R3,F					;bump bad record count
				call	printf					;print error msg

				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 data-byte count				
				bra		download_rec			;go to common record download

;*****==========================================================================*
;* : * Incoming Intel Record Command
;*****
; 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  EPROM_BUF,
; 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
;	R5 is a count of records loaded into the buffer so far
;   B_STATE = 0
;   ERR_FLAG = 0
;   EPROM power is off
;   PA_MANUAL = 0 for automatic page address, 1 for manuually-set address
;   PA_INIT = 1 if automatic page address has been set
;   PAGE_ADDR = current page address
; 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
;	R5 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 this record
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	R5 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 completely in the buffer
;   PA_MANUAL = 0 for automatic page address, 1 for manuually-set address
;   PA_INIT = 1 if automatic page address has been set
;   PAGE_ADDR = current page address
; On Exit:
;   Record data payload is in the specified place in the EPROM buffer
;   W, R0, R1, R2, FSR1 trashed
;   R3 is a count of bad records so far
;   R4 is a count of records so far
;	R5 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	BYTE_COUNT			;save record byte count
				incf	R4,F				;bump hex record count

;Get the high address byte. If the page address 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 page address is now initialized.
				call	get_byte_cksum		;get address high byte into W & R1

				movlb	VAR_BANK			;ADR_OFFSET in in bank the variables page
				btfsc	PA_FLAGS,PA_MANUAL	;manually-set page address?
				bra		d_rec1				;y: page address is okay

				btfss	PA_FLAGS,PA_INIT	;automatic page address initialized?
				movwf	PAGE_ADDR			;n: set it to this record's address

				bsf		PA_FLAGS,PA_INIT	;page address is now initialized	
d_rec1:

;Subtract off the user-supplied or automatically determined Page Address, add on the
;buffer address, and then 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	PAGE_ADDR
				subwf	R1,W				;W= high(record address) minus offset
				addlw	high(EPROM_BUF)
				movwf	FSR1H
			
;get address low byte and save it in FSR1, preparing to load data
				call	get_byte_cksum		;get address low byte into W
				movwf	FSR1L				;FSR1 will be used to move data

;if this is an Intel Hex record, get and validate the record type
				btfss	FLAGS,INTEL_STATE
				bra		dr_get_data			;Motorola record. we already got the record type

				call	get_byte_cksum		;get record-type byte into R1

				movf	R1,F				;test record type for 00
				btfsc	STATUS,Z			;zero is normal data record
				bra		dr_get_data

				decfsz	R1,F				;test for record type 01
				bra		rec_error			;anything higher than 01 is an error

;validate Intel type 01 (EOF) record
				movf	BYTE_COUNT,F		;byte count must be 0 for type 01 records
				btfss	STATUS,Z
				bra		rec_error			;error: no data bytes allowed for type 01 records

;---------------------------------------------------------------
; Loop to read all the record's data bytes. Write each data byte
; into EPROM_BUF only if it's address matches PAGE_ADDR.
;---------------------------------------------------------------
;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	BYTE_COUNT			;no bytes to get?
				btfsc	STATUS,Z
				bra		dr_cksum

				movwf	R2					;use R2 for 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 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	0					;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 address 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. (it is possible for a record to have just
; some of its data land in the buffer.)
				btfss	FLAGS,ERR_FLAG		;any data in this record not fit in the buffer?
				incf	R5,F				;bump count of successfully-loaded records

;get and validate the record's checksum
dr_cksum:		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	BYTE_COUNT,F
				btfss	STATUS,Z
				bra		dr_next_record		;not 0 bytes: go get another record

; if this is an S5 record, then 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
; from the S5 record
				btfss	FLAGS,B_STATE		;is this an S5 record?
				bra		dr_eof_rec			;no: this 0-byte record is the end of file

				decf	R4,W				;don't count this S5 record
				xorwf	FSR1L,W				;compare to the S5 record's record count
				btfss	STATUS,Z			;did we find the right number of records?
				call	s5_error			;no: 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 
; 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 record count and error count, and return to main
;---------------------------------------------------------
dr_eof_rec:		movlw	reccount_msg_index			;Records:
				call	cr_printf
				movfw	R4							;R4 has record count
				call	K_PRINTHEX2

				movlw	loadcount_msg_index			;records in page 
				call	printf

				movlb	VAR_BANK					;PAGE_ADDR is in the last page
				movfw	PAGE_ADDR					;get the page address we used

				btfss	PA_FLAGS,PA_MANUAL			;automatic page address mode?
				clrf	PAGE_ADDR					;y:prepare for next file load
	
				bcf		PA_FLAGS,PA_INIT			;de-initialize page address

				call	K_PRINTHEX2					;print w (the page address) in hex

				movlw	colonspace_msg_index		; :
				call	printf
				movfw	R5							;R5 has loaded record count
				call	K_PRINTHEX2

				movlw	errorcount_msg_index		;Bad Records:
				call	printf
				movfw	R3							;R3 has bad record count
				call	K_PRINTHEX2

				goto	main

;******=========================================================================*
;* Px *
;******
; Page Address or Programming Cycles Command
; On Entry:
;   R4 = second command character
;   Second command chr = 'A' for set Page Address
;   Second command chr = 'C' for set Programming Cycles
;   FSR1L  = 1st command parameter
;*==============================================================================*
cmd_papc:		movfw	R4

				xorlw	'A'				;Page Address cmd?
				btfsc	STATUS,Z
				bra		pcmd_pa

				xorlw	'A' ^ 'C'		;PC is Prog cycles, otherwise error
				btfss	STATUS,Z
				goto	cmd_error

;fall into pcmd_pc

;******-------------------------------------------------------------------------*
;* PC * Set Programming Cycles Command
;******
; On Entry:
;   FSR1L (command parameter) = programming cycles
;   (0 means use P+4P Smart Algorithm)
;*------------------------------------------------------------------------------*
pcmd_pc:		movfw	FSR1L				;get first parameter
				movwf	PROG_CYCLES			;save it as the programming cycles
				call	rep_pcycles			;report the new state
				goto	main

;******-------------------------------------------------------------------------*
;* PA * Set Page Address Command
;******
; Sets the upper 8 bits of address for uploads and downloads, or specifies
; automatic page address detection.
; On Entry:
;   FSR1L (command parameter) = high byte of addresses
;   INP_FLAG = 1 if there was a first command parameter, 0 if not
;*------------------------------------------------------------------------------*
pcmd_pa:		movlb	VAR_BANK			;PAGE_ADDR is in the last page
				movfw	FSR1L				;get first parameter
				movwf	PAGE_ADDR			;save it as the new page address

				bcf		PA_FLAGS,PA_MANUAL	;assume automatic page address
				bcf		PA_FLAGS,PA_INIT	;page address is no longer initialized	

				btfsc	FLAGS,INP_FLAG		;was a parameter provided?
				bsf		PA_FLAGS,PA_MANUAL	;y: page address is now manual	

				call	rep_page_addr		;print the page address
				goto	main

;******=========================================================================*
;* FB * Fill EPROM Buffer Command
;******
; The second character must be 'B', or it's bogus
; Fills all 256 bytes of the EPROM buffer with the given value
; On Entry:
;   The second command chr (in R4) must be 'B'
;   FSR1L = fill value for all of buffer
;   FSR1H = high(EPROM_BUF)
;*==============================================================================*
cmd_fill:		cjne	R4,'B',cmd_error		;second byte must be 'B'

				movlw	buffill_msg_index		;'Buffer filled with '
				call	cr_printf

				movfw	FSR1L					;get fill value from user input
				clrf	FSR1L					;start at the beginning

;loop to fill 256-byte EPROM buffer, which starts on an even page boundary
fill_buf_loop:		movwf	INDF1				;write value to buffer
					incfsz	FSR1L,F				;bump & test pointer
					bra		fill_buf_loop

				call	K_PRINTHEX2				;tell user fill value
				goto	main

;******=========================================================================*
;* Dx * Display Commands
;******
; On Entry:
;    R4 = DB for Display Buffer
;    R4 = DS for Display State
;    FSR1L = first user parameter
;*==============================================================================*
cmd_display:	movfw	R4

				xorlw	'S'					;display state?
				btfsc	STATUS,Z
				bra		dcmd_state

				xorlw	'S' ^ 'B'			;display buffer? otherwise error
				btfss	STATUS,Z
				goto	cmd_error

; fall into dcmd_buffer

;******-------------------------------------------------------------------------*
;* DB *  Display Buffer Command
;******
; Displays the specified portion of the EPROM buffer contents in human-readable
; form, followed by the checksum of that range
; On Entry:
;    FSR1 points to the buffer starting address
;*------------------------------------------------------------------------------*
dcmd_buffer:

;get second parameter, which is a byte count. get_param will return 0 (meaning
;256 bytes) if no value was entered.
				call	get_param				;get another hex byte into R1
				movfw	R1
				movwf	BYTE_COUNT				;remember byte count for results
				movwf	R3						;byte counter for some commands

				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
					incf	FSR1L,F				;-circular-increment 256-byte pointer
					decf	R3,F
					btfsc	STATUS,Z
				bra		dbuf_sum

; if this address ends with 0h, then 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
;  Page Address
;  EPROM Type
;  Programming Cycles/ Smart Programming 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_page_addr		;print page address
				call	rep_typecycles		;print EPROM type, programming cycles

				movlw	echo_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 256 bytes in the EPROM buffer as Intel hex or Motorola S-records
; 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
;    FSR1H = high(EPROM_BUF)
;*==============================================================================*
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 256 bytes of EPROM_BUF contents in a Hex format
; On entry:
;    PAGE_ADDR = high byte of all address fields   
;    INTEL_STATE = 1 for Intel Hex format, 0 for Motorola S-record format
;    FSR1H = high(EPROM_BUF)
;*------------------------------------------------------------------------------*
upload_buf:			clrf	FSR1L				;point to beginning of buffer

upload_loop:		movlw	0x10				;16 bytes per line
					movwf	R3					;R3 will be used 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. Byte count = number of data bytes Intel hex records,
; Byte count = 3 + number of data bytes for Motorola S-records.				
					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		;print & compute checksum along the way

;get page address, & send as high byte of this record's address
					movlb	VAR_BANK
					movfw	PAGE_ADDR
					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. Skip for S-record.
					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 and send record 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

;see if we have more data to send
					movf	FSR1L,F				;test for 256 bytes sent already
					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
;******
; 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
;   Second command chr = 'T' for set EPROM type
;   ERR_FLAG is cleared
;   FSR1 = user-input start address for all these commands 
;*==============================================================================*
cmd_eprom:

;get second parameter, which may be a byte count. get_param will return 0 if no
;value was entered.
			call	get_param				;get another hex byte into R1
			movfw	R1
			movwf	BYTE_COUNT				;remember byte count for prange
			movwf	R3						;set up actual byte counter

			cje		R4,'T',ecmd_type		;set EPROM type? (don't power up 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
;   R3 = BYTE_COUNT = byte count of range to checksum or program
;   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

					incf	FSR1L,F			;next address
					decfsz	R3,F
					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 = buffer start address
;   FSR1L = EPROM address
;   BYTE_COUNT = user-input byte count
;   PLAIN_1702 is 0 for 1702A, 1 for 1701 or 1702
;   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
;*------------------------------------------------------------------------------*
ecmd_prog:
;Remember start address in R3
				movfw	FSR1L
				movwf	R3					;save start address for verify

;Tell the user which type of EPROM is selected and which algorithm will be used
				call	rep_typecycles

;if a plain 1702 is selected, then tell the user that programming will be slow
				movlw	slow1702_msg_index
				btfsc	FLAGS,PLAIN_1702
				call	cr_printf

;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
											;(also sets FSR1H=high(EPROM_BUF)

;-------------------------------------------------------------------------------
; Check for backwards EPROM
;
; Also set up R5 to be the erased value for the selected EPROM type
;
; An erased 1702A reads as 00, whereas an erased 1701/2 reads as FF.
; If this is a 1702A, then check for an EPROM full of FFs - this is possibly
; a backwards EPROM, possibly a plain 1702. It is and certainly one that can't
; be programmed as a 1702A.
;
; Unfortunately, a backwards plain 1702 cannot be detected by this hardware.
;-------------------------------------------------------------------------------
				clrf	R5					;erased value for 1702A is 00
				btfsc	FLAGS,PLAIN_1702
				decf	R5,F				;erased value for 1702 is FF

				btfsc	FLAGS,PLAIN_1702
				bra		not_bkwds			;can't test a 1701/2 for backwards

				clrf	FSR1L				;temp use FSR1L as counter

bkwds_loop:			call	eread_byte		;returns result in R2 & W
					incf	R2,F			;test for R2=FF
					btfss	STATUS,Z		;is data zero?
				bra		not_bkwds

					incfsz	FSR1L,F			;bump address pointer
					bra		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 matches R5
;-------------------------------------------------------------------------------
				movfw	R3					;put starting address back
				movwf	FSR1L
				movfw	BYTE_COUNT			;local copy of byte count
				movwf	R1					;R1 counts bytes

				clrf	R4					;accumulate result in R4

epbc_loop:			call	eread_byte			;Z set if zero
					xorwf	R5,W				;is this byte erased?
					iorwf	R4,F			;accumulate non-zero-ness

				incf	FSR1L,F				;bump EPROM address pointer
				decfsz	R1,F				;decrement and test counter
				bra		epbc_loop

; Non-blank programming range if Z not set. Ask user if we should continue
				movlw	notblank_msg_index	;Not blank. Program anyway?
				movf	R4,F				;test for 0
				btfss	STATUS,Z
				call	ask_yn				;abort if no
											;(also sets FSR1H=high(EPROM_BUF)

;-------------------------------------------------------------------------------
; Get ready to program EPROM
;-------------------------------------------------------------------------------
;point FSR1 to starting address in the buffer again
				movfw	R3				;put starting address back
				movwf	FSR1L

;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
;   R3 = start address
;   BYTE_COUNT = number of bytes to program
;   FSR1H = EPROM_BUF high address byte
;   PROG_CYCLES = number of times to program the EPROM
;   PLAIN_1702 is 0 for 1702A, 1 for 1701 or 1702
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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; B_STATE is cleared
				bra		prog_verify			;go verify	

;-----------------------------------------------------------------------
; Smart Programming Failed
; We tried to smart-program 255 times: write failure - report and quit
;-----------------------------------------------------------------------
smart_fail:		movlw	writefail_msg_index
				call	cr_printf
				goto	ecmd_done

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; P+4P 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 X 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
;   R3 = start address
;   FSR1H = EPROM_BUF high address byte
;   BYTE_COUNT = number of bytes to program
;   PLAIN_1702 is 0 for 1702A, 1 for 1701 or 1702
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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?
					goto	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
;
; 1702As erase to 00. For 1702As - 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'.
;
; Plain 1701/2s erase to FF. For 1701/2s - so we quit programming when all
; bits that need  to be '0' are '0'. Again, if other bits that should be
; '1' are now '0' since no additional programming will turn them back to '1'.
;
; Such errors will be caught by the verify step.
;----------------------------------------------------------------------------
					call	epower_read			;set up power supply for reading
					movfw	R3					;starting address
					movwf	FSR1L
					movfw	BYTE_COUNT			;bytes to do
					movwf	R5

scmp_loop:				call	eread_byte			;returns with value in W and R2

						xorwf	INDF1,W				;compare buffer data to EPROM data (in W)
						movwf	R0					;R0 has all the different bits

						movfw	INDF1				;get buffer data again
						btfsc	FLAGS,PLAIN_1702	;which kind of EPROM?
						comf	INDF1,W				;1701/2 is FF when cleared

						andwf	R0,W				;Just look at bits going to 1 (0 for 1701/2)
						btfss	STATUS,Z			;any '1' bits are bits that haven't yet programmed
					bra		smart_loop			;mismatch: go write another pass

						incf	FSR1L,F				;next address
						decfsz	R5,F				;more bytes to do?
						bra		scmp_loop

;----------------------------------------------------------------------
; Smart Programming 'P' programming complete.
; Report what P equals, and announce '+4P' over-programming step
;----------------------------------------------------------------------
				movlw	pequals_msg_index
				call	cr_printf			;trashes R0-R2
				movfw	R4
				call	K_PRINTHEX2			;trashes R0-R1
				movlw	overprog_msg_index
				call	printf

;----------------------------------------------------------------------
; over-write 4X the count in R4		 
;----------------------------------------------------------------------
				call	epower_prog		;turn on high voltage supply

loop_4P:			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		loop_4P

; fall into prog_verify, with B_STATE cleared

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Verify step for both programming algorithms
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
prog_verify:	call	epower_read		;prog voltage off, set up for reading

;announce verify step
				movlw	verifying_msg_index
				call	cr_printf

;restore FSR1L (start address) for verify
				movfw	R3				;we saved starting address in R3
				movwf	FSR1L
					
;fall into ecmd_compare to verify

;******-------------------------------------------------------------------------*
;* EC * EPROM Compare Command
;******
; Compares EPROM data to buffer and reports differences. This routine is aslo
; used at the end of the EP command to verify the programmed EPROM.
; On Entry:
;   FSR1 = buffer start address
;   FSR1L = EPROM address
;   BYTE_COUNT = number of bytes to verify
;   B_STATE is cleared, telling rep_mismatch this is not an EPROM verify
;   ERR_FLAG is cleared (unless we are verifying, and we had an error during
;   the write stage)
;   EPROM is already powered up for reading
;*==============================================================================*
ecmd_compare:		movfw	BYTE_COUNT			;create counter
					movwf	R3

;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

					incf	FSR1L,F				;next address
					decfsz	R3,F				;more bytes to do?
					bra		ecmp_loop

				movlw	eprom_msg_index			;'EPROM '
				call	prange					;print string, then 'range ' if needed

				movlw	cmpgood_msg_index		;'matches buffer.'

; fall into ecmd_done

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Common Routine: Finish one of the Ex commands
; Print success or failure 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		;y: print 'Fail'

				goto	epwroff_print		;print message, power down, goto main

;******-------------------------------------------------------------------------*
;* ET *  Set EPROM Type Command
;******
; 00 = 1702A, 01 = plain 1701/1702, all others illegal
; On Entry:
;   FSR1L = EPROM type: 00 = 1702A, 1 = 1702
;   EPROM is *not* powered up for reading
; On Exit:
;   EPROM is powered off
;   PLAIN_1702 is cleared for 1702A, set for 1701/1702
;*------------------------------------------------------------------------------*
ecmd_type:		movlw	MAX_EPROM_TYPE+1		;do we support this type?
				subwf	FSR1L,W
				btfsc	STATUS,C
				goto	cmd_error

;save the new EPROM type
				bcf		FLAGS,PLAIN_1702
				btfsc	FSR1L,0					;0 or 1 allowed
				bsf		FLAGS,PLAIN_1702

;set programming algorithm based on EPROM type
				movlw	DPC_1702				;default programming cycles for 1702A
				btfss	FLAGS,PLAIN_1702
				movlw	DPC_1702A				;default programming cycles for 1702
				movwf	PROG_CYCLES				

;report settings
reptyp:			call	rep_typecycles
				goto	main

;******-------------------------------------------------------------------------*
;* EB * EPROM Blank Check Command
;******
; Verify that the specified region of the EPROM is blank
; (Blank means all bytes=00 for 1702A, all bytes=FF for 1702)
; On Entry:
;   FSR1L = starting EPROM address
;   R3 = BYTE_COUNT = number of EPROM bytes to check
;   PLAIN_1702 = 0 for 1702A, 1 for 1702
;   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

					btfsc	FLAGS,PLAIN_1702		;skip if 1702A, which erases to 00
					comf	R2,W					;now a clear 1702 will be 00 too

; R1 = actual EPROM data; W = EPROM data for 1702A, inverse of EPROM data for 1702
					addlw	0						;Z set means erased
					btfss	STATUS,Z				;is data zero?
					call	rep_mismatch

					incf	FSR1L,F					;bump address pointer
					decfsz	R3,F					;decrement and test counter
					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
;******
; Modify Buffer Command
; Allows user to interactively modify data in the EPROM buffer.
; Second command chr (in R4) must be 'B'
; On Entry:
;   FSR1L, FSR1H = starting buffer address
;   INDF1 = starting buffer data
; On Exit:
;    Normal exit is via ^C, detected by get_line
;*==============================================================================*
cmd_modify:		cjne	R4,'B',cmd_error	;second chr must be 'B'

;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
					movfw	FSR1L			;save address for later
					movwf	R3

					call	get_line		;get user input into buffer, trash FSR1

					movfw	R3				;fix address pointer
					movwf	FSR1L
					movlw	high(EPROM_BUF)
					movwf	FSR1H

					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 R
					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 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.
					incf	FSR1L,F			;next buffer address
					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 bus
; Second command chr = 'D' to write to the EPROM Data bus
;*==============================================================================*
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 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_WD		;write data now

				goto	main

;******--------------------------------------------------------------------------*
;* WA * Test EPROM Address 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	;write FSR1L to address pins
				goto	main

;******=========================================================================*
;* Tx * Test Command
;******
; Sets or clears various things. Also includes Terminal Echo
; R4 = 2nd command chr (see tests below)
; FSR1L = ASCII 0 or 1 for off or on (user input)
;*==============================================================================*
cmd_test:

;test first parameter (in FSR1L) for 0 or 1 - anything else is an error.
			movlw	0xfe				;mask lsb
			andwf	FSR1L,W				;only 0,1 allowed
			btfss	STATUS,Z
			goto 	cmd_error

;hunt for command, and execute it.
			movfw	R4					;2nd character of command

			xorlw	'C'					;Control Chip Enable?
			btfsc	STATUS,Z
			bra		tcmd_ce

			xorlw	'C'	 ^ 'D'			;Control Vdd & Vgg?
			btfsc	STATUS,Z
			bra		tcmd_vdd

			xorlw	'D'	 ^ 'E'			;Control echo?
			btfsc	STATUS,Z
			bra		tcmd_echo

			xorlw	'E'	 ^ 'P'			;Control PROG?
			btfsc	STATUS,Z
			bra		tcmd_prog

			xorlw	'P'	 ^ 'V'			;Control Vcc?
			btfsc	STATUS,Z
			bra		tcmd_vcc

			xorlw	'V'	 ^ '9'			;Control Minus 9v?
			btfsc	STATUS,Z
			bra		tcmd_m9

			xorlw	'9'	 ^ 'M'			;Control Master power? Error 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
; Turning master power off also turns -9V off
;*------------------------------------------------------------------------------*
tcmd_master:	movlw	masterpwr_msg_index		;'Master power'
				call	cr_printf

				movlb	0x02					;LATC & LATE are in bank 2

				movf	FSR1L,F					;test param1 for 0
				btfsc	STATUS,Z
				bra		power_off
;power on
				bcf		LATE,NEN_ALLPOWER
				goto	tcmd_on					;' on' then done with command

power_off:		bsf		LATE,NEN_ALLPOWER

				movlw	off_msg_index			;' off'
				call	printf

				call	sub_m9_off				;Common -9V off subroutine

				goto	main					;done with command	

;******-------------------------------------------------------------------------*
;* TV * Test Vcc Command
;******
; Turns Vcc (programming voltages) off/on
; FSR1L = 0 or 1 for off/on
; Turning on programming voltage also turns off -9V
;*------------------------------------------------------------------------------*
tcmd_vcc:		movf	FSR1L,F					;test param1 for 0
				btfsc	STATUS,Z
				bra		vcc_off

;Vcc on
				call	sub_m9_off				;Common -9V off subroutine

				movlw	vcc48_msg_index			;'Vcc high voltage'
				call	cr_printf

				movlb	0x02					;LATE is in bank 2
				bcf		LATE,NEN_PVOLTAGE

				goto	tcmd_on					;' on' then done

vcc_off:		movlb	0x02					;LATE is in bank 2
				bsf		LATE,NEN_PVOLTAGE

				movlw	vcc48_msg_index			;'Vcc high voltage'
				call	cr_printf

				goto	tcmd_off				;' off' then done with command

;******-------------------------------------------------------------------------*
;* TC * Test Chip Enable Command
;******
; Turns EPROM Chip Enable off/on. Chip Enable 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 & LATE are 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

;******-------------------------------------------------------------------------*
;* TD * Test Vdd and Vgg Command
;******
; Turns Vdd and Vgg off/on
; FSR1L = 0 or 1 for off/on
; Turning these on also turns minus 9V off
;*------------------------------------------------------------------------------*
tcmd_vdd:		movf	FSR1L,F					;test param1 for 0
				btfsc	STATUS,Z
				bra		vdd_off

;Vdd & Vgg on, -9V off
				call	sub_m9_off				;Common -9V off subroutine

				movlw	vddvgg_msg_index		;'Vdd & Vgg'
				call	cr_printf

				movlb	0x02					;LATC is in bank 2
				bsf		LATC,EN_VDDVGG
				goto	tcmd_on					;' on' then done

vdd_off:		movlw	vddvgg_msg_index		;'Vdd & Vgg'
				call	cr_printf

				movlb	0x02					;LATC is in bank 2
				bcf		LATC,EN_VDDVGG
				goto	tcmd_off				;' off' then done with command

;******-------------------------------------------------------------------------*
;* T9 * Test Minus 9 Volts Command
;******
; Turns minus 9 volts off/on
; FSR1L = 0 or 1 for off/on
; turning on minus 9 also turns off programming voltages 
;*------------------------------------------------------------------------------*
tcmd_m9:		movf	FSR1L,F					;test param1 for 0
				btfsc	STATUS,Z
				bra		m9_off

;-9v on, Vgg, programming voltages off first
				movlb	0x02					;LATC & LATE are in bank 2
				bcf		LATC,EN_VDDVGG
				bsf		LATE,NEN_PVOLTAGE

				movlw	vddvgg_msg_index		;'Vdd & Vgg'
				call	cr_printf
				movlw	off_msg_index			;' off'
				call	printf

				movlw	vcc48_msg_index			;'Vcc high voltage'
				call	cr_printf
				movlw	off_msg_index			;' off'
				call	printf

				movlw	minus9_msg_index		;'-9V'
				call	cr_printf
				
				movlb	0x02					;LATE is in bank 2
				bsf		LATE,EN_MINUS9
				goto	tcmd_on					;' on' then done with command

m9_off:			call	sub_m9_off				;Common -9V off subroutine	

				goto	main					;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:		movf	FSR1L,F						;test param 1 for 0
				btfsc	STATUS,Z
				bra		prog_off

;program on, meaning  low voltage
				movlw	progsignal_msg_index		;'program signal'
				call	cr_printf

				movlb	0x02						;LATC is in bank 2
				bsf		LATC,EN_PROGRAM

				goto	tcmd_on						;' on' then done with command

;program off, meaning at Vcc
prog_off:		movlb	0x02						;LATC is in bank 2
				bcf		LATC,EN_PROGRAM

				movlw	progsignal_msg_index		;'-PROG signal'
				call	cr_printf
				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	echo_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 print 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
;    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, return from there

;*==============================================================================*
; Subroutine to print CR, then message, followed by 'range ', if needed
; On Entry:
;   W = index to string to print ('EPROM ' or 'Buffer ')
;   BYTE_COUNT <> 0 if 'range ' is needed
;*==============================================================================*
prange:			call	cr_printf				;print provided string with initial CR

				movf	BYTE_COUNT,F			;set Z flag if 0
				movlw	range_msg_index			;'range '
				btfss	STATUS,Z
				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			;convert to hex & print, return from there

;*==============================================================================*
; Subroutine to print the row's address as 2 hex digits, followed by a ': '
;
; 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

				movfw	FSR1L					;low byte of address
				call	K_PRINTHEX2				;don't mess up HEX_CKSUM

				movlw	colonspace_msg_index	; ': '
				goto	printf					;print colon, return from there

;*==============================================================================*
;Subroutine to print the page address, which may be
;either a value or 'automatic'
;On Entry:
;  PA_MANUAL = 1 if manual page address mode selected
;            = 0 if automatic page address mode
;  PA_ADDR   = page address if manual mode
;*==============================================================================*
rep_page_addr:	movlw	pageaddr_msg_index	;'Page Address: '
				call	cr_printf

				movlb	VAR_BANK			;PAGE_ADDR is in the last page
				movfw	PAGE_ADDR			;manually-set page address

				btfsc	PA_FLAGS,PA_MANUAL	;manual page address mode?
				goto	K_PRINTHEX2			;y: print page address and return

;Page address is automatic
				movlw	auto_pa_msg_index	;'automatic'
				bra		printf				;return from there

;*==============================================================================*
; Subroutine to report an EPROM/Buffer mismatch
; Prints address, buffer data, EPROM data, unless B_STATE (verify blank 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:
;   FSR1L = EPROM address
;   INDF1 = buffer data
;   B_STATE valid
;   R2 = EPROM data
; On Exit:
;    W, R0, R1, BSR, HEX_CKSUM 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:
				call	printf					;note: cr_printf would trash R2
				movfw	FSR1L
				call	K_PRINTHEX2

				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 transmission
; is not blocked by XOFF
; 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 print the selected EPROM type and programming cycles
;* Entry at rep_typecycles reports both EPROM type and programming cycles
;* Entry at rep_pcycles reports only programming cycles
;*==============================================================================*
rep_typecycles:	movlw	slct1702A_msg_index		;EPROM type?
				btfsc	FLAGS,PLAIN_1702		;bit cleared for 1702A
				movlw	slct1702_msg_index
				call	printf

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 1- or 2-character hex parameter value from the Line
; Buffer, skipping spaces. Returns default 00 if there is no parameter.
; This routine is not case-sensitive - lowercase is treated as uppercase.
; A bad hex character will cause a rude abort to cmd_error
; 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
;    Result in R1, R1=0 (default) 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 00
;*==============================================================================*
get_param:	bcf		FLAGS,INP_FLAG		;no characters yet
			clrf	R1					;value is 0 so far
			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			;qet line fuffer chr 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 2)
gp_get_digits:	btfsc	FLAGS,CR_FLAG		;CR ends us
			return

				bsf		FLAGS,INP_FLAG		;we found some user input
			
				call	K_HEX2BIN			;convert chr in R0 int low nibble of R1
				btfsc	STATUS,C			;C set means hex character error
			goto	cmd_error

				call	get_uchr			;get another chr - result in W & R0

				xorlw	' '					;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:
;    result is uppercase in both R0 and W
;    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:	movfw	R0					;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:
;    The next line buffer chr is in W & R0
;    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
;    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:
;    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 W & R0 (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(LINE_BUF_LAST),gl_loop

;bump pointer
					incf	FSR1L,F

;echo if allowed
gl_echo:			movfw	R0						;put chr into W for echo
					btfsc	FLAGS,ECHO_STATE		;is echo enabled?
gl_ebs:				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 back up on screen too.
;------------------------------------------------------------------------
gl_backspace:		cje		FSR1L,low(LINE_BUF),gl_loop	;empty line buffer?

					btfss	FLAGS,ECHO_STATE		;is echo enabled?
					bra		gl_loop

					decf	FSR1L,F				;back up in line buffer

					movlw	BS					;erase previous typed chr
					call	K_CONOUT
					movlw	' '
					call	K_CONOUT
					movlw	BS
					bra		gl_ebs				;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 -9V off and brag about it
;*==============================================================================*
sub_m9_off:		movlb	0x02					;LATE is in bank 2
				bcf		LATE,EN_MINUS9					

				movlw	minus9_msg_index		;'-9V'
				call	cr_printf
				movlw	off_msg_index			;' off'
				goto	printf					;return via printf

;*==============================================================================*
; Subroutine to turn off the power to the EPROM and test power supply voltages
; for reasonable voltage values
; On Exit:
;    If any voltages are out of spec, then FSR1H = high(EPROM_BUF)
;    BSR, R0, W trashed
;    main EPROM power is off
;    programming voltage is off
;    -9V is off
;    WD is 0xFF
;*==============================================================================*
epower_off:		movlb	0x02					;LATC & LATE are in bank 2
				movlw	EPROM_OFF_C				;remove PROG, CS, Vdd, Vgg
				movwf	LATC
				movlw	EPROM_OFF_E				;Power off
				movwf	LATE
				clrf	PORTD					;EPROM write data off too

;give the power supply a chance to stabilize
				movlw	PS_DELAY
				call	K_STALL25M

; check to see that the power supply voltages are reasonable - preregulator first
				movlw	AN_VPRE | (1 << ADON)	;pre-regulator ADC
				call	get_analog

				movlw	VOFF_MAX				;report if too high
				subwf	ADRESH,W
				movlw	rvprestuck_msg_index	;in case Vpre is not shutting off
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

;Next, test Vcc
				movlw	AN_VCCS | (1 << ADON)	;Vccs ADC
				call	get_analog

				movlw	VOFF_MAX
				subwf	ADRESH,W
				movlw	rvccstuck_msg_index		;in case Vcc is not off
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

;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 off
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error
												;(also sets FSR1H=high(EPROM_BUF)

				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:
;    If any voltages are out of spec, then FSR1H = high(EPROM_BUF)
;    BSR, R0, W trashed
;    main EPROM power is on
;    programming voltage is off
;    -9V is on
;    WD is 0xFF
;*==============================================================================*
epower_read:

;turn off high voltage, set up for reading
				movlb	0x02
				bsf		LATE,NEN_PVOLTAGE		;turn high voltage off

				bcf		LATE,NEN_ALLPOWER		;turn on main, in case it was off				

				movlw	0xff					;write data must be high to read
				movwf	EPROM_WD
				movlw	0x02					;2 uS stall
				call	K_STALL1U

				bsf		LATE,EN_MINUS9			;turn on the -9V
				bsf		LATC,EPROM_CS			;turn on chip select

;give the power supply a chance to stabilize
				movlw	PS_DELAY
				call	K_STALL25M

; check to see that the power supply voltages are reasonable - preregulator first
				movlw	AN_VPRE | (1 << ADON)	;pre-regulator ADC
				call	get_analog

				movlw	VPRE_LO_MAX				;panic if too high
				subwf	ADRESH,W
				movlw	pvpre70high_msg_index	;in case Vpre is too high
				btfsc	STATUS,C				;C set means ADRESH>W
				goto	panic					;Vpre is too high

				movlw	VPRE_LO_MIN-1
				subwf	ADRESH,W
				movlw	rvpre12low_msg_index	;in case Low-voltage Vpre is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

;Next, test Vcc
				movlw	AN_VCCS | (1 << ADON)	;Vccs ADC
				call	get_analog

				movlw	V5_MAX
				subwf	ADRESH,W
				movlw	rvcc5high_msg_index		;in case Low-voltage Vcc is too high
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

				movlw	V5_MIN-1
				subwf	ADRESH,W
				movlw	rvcc5low_msg_index		;in case Low-voltage Vcc is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

;Now, test Vbb
				movlw	AN_VBB | (1 << ADON)	;Vbb ADC
				call	get_analog

				movlw	V5_MAX
				subwf	ADRESH,W
				movlw	rvbb5high_msg_index		;in case Low-voltage Vbb is too high
				btfsc	STATUS,C				;C set means ADRESH>W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

				movlw	V5_MIN-1
				subwf	ADRESH,W
				movlw	rvbb5low_msg_index		;in case Low-voltage Vbb is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error
												;(also sets FSR1H=high(EPROM_BUF)

				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:
;    If any voltages are out of spec, then FSR1H = high(EPROM_BUF)
;    BSR, R0, W trashed
;    -9V is off
;    programming voltage is on
;*==============================================================================*
epower_prog:

;turn off CS and -9V, and power up high voltage for programming
				movlb	0x02					;LATC, LATE are in bank 2
				bcf		LATC,EPROM_CS			;chip select off during programming

				bcf		LATE,EN_MINUS9			;turn off the -9V
				movlw	2						;2.5 uS stall
				call	K_STALL1U
				bcf		LATE,NEN_PVOLTAGE		;power up the device for programming

;stall while the power supply comes up to voltage
				movlw	PS_DELAY
				call	K_STALL25M

; check to see that the power supply voltages are reasonable - pre-regulator first
				movlw	AN_VPRE | (1 << ADON)	;pre-regulator ADC
				call	get_analog

				movlw	VPRE_HI_MAX				;panic if too high
				subwf	ADRESH,W
				movlw	pvpre70high_msg_index	;in case Vpre is too high
				btfsc	STATUS,C				;C set means ADRESH>W
				goto	panic					;Vpre is too high

				movlw	VPRE_HI_MIN-1
				subwf	ADRESH,W
				movlw	rvpre70low_msg_index	;in case High-voltage Vpre is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

;Next, test Vcc
				movlw	AN_VCCS | (1 << ADON)	;Vccs ADC
				call	get_analog

				movlw	VCC_HI_MAX
				subwf	ADRESH,W
				movlw	pvcc48high_msg_index	;In case high-voltage Vcc is too high
				btfsc	STATUS,C				;C set means ADRESH>W
				goto	panic					;high-voltage Vcc is too high

				movlw	VCC_HI_MIN-1
				subwf	ADRESH,W
				movlw	rvcc48low_msg_index		;In case High-voltage Vcc is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error and keep going
												;(also sets FSR1H=high(EPROM_BUF)

;Now, test Vbb
				movlw	AN_VBB | (1 << ADON)	;Vbb ADC
				call	get_analog

				movlw	VBB_HI_MAX
				subwf	ADRESH,W
				movlw	pvbb48high_msg_index	;in case high-voltage Vbb is too high
				btfsc	STATUS,C				;C set means ADRESH>W
				goto	panic					;high-voltage Vbb is too high

				movlw	VBB_HI_MIN-1
				subwf	ADRESH,W
				movlw	rvbb48low_msg_index		;in case High-voltage Vbb is too low
				btfss	STATUS,C				;C cleared means ADRESH<=W
				call	report_yn				;report error
												;(also sets FSR1H=high(EPROM_BUF)
				return

;*==============================================================================*
; Subroutine to read one EPROM byte
; Note that the hardware is slow - a good delay is required.
; On Entry:
;   FSR1L = EPROM address = buffer address low byte
;   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	;(2.25) Write FSR1L to EPROM address pins

			movlw	.49				;(0.25) delay for EPROM and esp. our hardware
			call	K_STALL1U		;(50.25 uS)

;fall into eread_data

;*==============================================================================*
; Subroutine to read the EPROM data pins
;   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
			movwf	R2				;(0.25)result in r2 too for reporting
			return					;(0.50)

;*==============================================================================*
; Subroutine to put address in FSR1 to the EPROM
;   address bits A0-A7 go to EPROM_AN (Port B latch) Note that the address
;   ports invert.
; On Entry:
;    FSR1L = EPROM address
; On Exit:
;    EPROM address is set
;    trashes W, BSR
; takes 1.75 uS
;*==============================================================================*
ewrite_address:	movlb	0x02		;(0.25)EPROM_AN is in bank 2
				comf	FSR1L,W		;(0.25)buffer low addr is EPROM addr, inverted
				movwf	EPROM_AN	;(0.25)address to EPROM

				return				;(0.5)

;*==============================================================================*
; Subroutine to write one pass through the EPROM
; Check for control-C after each byte, asbort if control-C found
; Print a pacifier dot when done with range
; On Entry:
;    R3 = start address
;    BYTE_COUNT = byte count
;    FSR1H = EPROM buffer address high byte
;    EPROM is powered up for programming
;    EPROM_CS is already active
;
; On Exit:
;   EPROM is still powered up for programming
;   trashes R0,R1,R2,W,BSR,FSRIL
;
; Numbers in parenthesis are time in uS
; Vdd and Vgg are for 173.25 uSec
; Vdd and Vgg are off for ~694 uS, including call and return
; Note: Tphi tw max duty cycle = 20%
;(Vdd and Vgg on / (Vdd and Vgg on + Vdd and Vgg off <= 20%)
;*==============================================================================*
ewrite_range:	call	wait_txempty		;wait for the transmitter to empty

				movfw	R3					;R3 has start address
				movwf	FSR1L				;set up EPROM buffer pointer

				movfw	BYTE_COUNT			;R2 counts bytes
				movwf	R2

				bcf		INTCON,GIE			;mask interrupts while we write

; loop once through the entire EPROM range. Select the correct subroutine based
; on the PLAIN_1702 bit. Check for control-C after each byte.
ewrite_loop:		btfss	FLAGS,PLAIN_1702	;bit cleared means 1702A
					call	write_1702A

					btfsc	FLAGS,PLAIN_1702	;bit set means 1701/2
					call	write_1702

;see if the user is trying to abort with a control-C
					bsf		INTCON,GIE			;(.25)let Rx interrupt in
					call	chk_ctrlc			;(2.5 if nothing typed)
					bcf		INTCON,GIE			;(.25)global interrupt mask

;move on to next location and test for done
					incf	FSR1L,F				;(0.25)bump address (circular)
					decfsz	R2,F				;(0.25)dec & test byte count
					bra		ewrite_loop			;(0.50)

;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 a 1701/1702
; o  The 1702 reads as FF when erased.
; o  The maximum PROG pulse width is 20 mS. (we do 10 mS here.)
; o  The maximum PROG duty cycle is 2%. (We do 1.9% here.)
;
; On Entry:
;   FSR1L = EPROM address = buffer address low byte
;   INDF1 = data byte to write
; On Exit:
;    R0, R1 trashed
;*==============================================================================*
write_1702:

;put true address to the address pins. Note that the hardware inverts,
;so we actually write inverted to compensate
				call	ewrite_address	;(2.25) Write FSR1L to EPROM address pins

;put the data to the EPROM (note the port inverts, but low on the EPROM = 1)
;If the data to write is FF (erased state), then don't write this byte
				incf	INDF1,W			;(0.25) test for FF
				btfsc	STATUS,Z		;(0.5) Don't program FF's
				return

				movfw	INDF1				;(0.25) Get data from buffer
				movwf	EPROM_WD		;(0.25) and write it to the pins

;stall for 500 uS (data stable to Vdd and Vgg on)
;We go a lot longer than required 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	0x02			;(.25) stall for 500 uS
				call	K_STALL250U		;(~500)

;Apply Vdd and Vgg
				bsf		LATC,EN_VDDVGG	;[0.25]

;stall for 10 us (Tdw= 1 uS min from data (and Vdd and Vgg) to PROG pulse)
				movlw	.9				;[0.25]
				call	K_STALL1U		;(10.25)

;set PROGRAM low (active), to -1.8V for 1702s
				bsf		LATC,EN_PROGRAM	;[0.25]

;stall for 10 mSec (Tphipw = 20 mSec max) (This is our pulse width)
				movlw	.40				;[0.25]
				call	K_STALL250U		;[10000]

;set PROGRAM high (inactive)
				bcf		LATC,EN_PROGRAM	;[0.25]

;stall for 10 us (Tvd= 1 uS min from PROG pulse to Vdd and Vgg)
;(also meets tdh=1 uS  data hold time after PROG pulse)
				movlw	.9				;[0.25]
				call	K_STALL1U		;(10.25)

;remove Vdd, Vgg
				bcf		LATC,EN_VDDVGG	;(0.25)

;stall to meet duty cycle requirement with a little margin.
;Max duty cycle for PROG is 2%. PROG was active for 10 mS, so we must stall
;for at least 500 mSec. We actually stall here for 525 mS. We also have a 500 uS
;stall at the beginning that counts here.
				movlw	.21
				goto	K_STALL25M		;[525 mS]

;*==============================================================================*
; Subroutine to write one byte to a 1702A
; o  The 1702A reads as 00 when erased.
; o  The maximum PROG pulse width is 3 mS. (we do 2.5 mS here.)
; o  The maximum PROG duty cycle is 20%. (We do 19% here.)
;
; On Entry:
;   FSR1L = EPROM address = buffer address low byte
;   INDF1 = data byte to write
; On Exit:
;    R0, R1 trashed
;*==============================================================================*
write_1702A:

;Put inverted address to EPROM (note the port inverts)
				call	ewrite_address	;(2.25) Write FSR1L to EPROM 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_WD		;(0.25)

;stall for 500 uS (Tacw=25 uS min from inverted address to Vdd and Vgg 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	0x02				;(.25) stall for 500 uS
				call	K_STALL250U		;(~500)

;Apply Vdd and Vgg
				bsf		LATC,EN_VDDVGG	;[0.25]

;stall for 30 us (Tach=25 uS min from Vdd and Vgg to true address)
				movlw	.29				;[0.25]
				call	K_STALL1U		;(30.25)

;put  address to EPROM pins
				call	ewrite_address	;(2.25) Write FSR1L to EPROM address pins

;stall for 100 uS (Tvw=100 uS min from Vdd & Vgg power to PROGRAM)
;                (Tatw = 10 uS min from true address to PROGRAM)
				movlw	.99				;[0.25]
				call	K_STALL1U		;[100.25]

;set PROGRAM low (active)
				bsf		LATC,EN_PROGRAM	;[0.25]

;stall for 2.5 mS (Tphi tw=3 mS max. Min is unspecified.)
				movlw	.10
				call	K_STALL250U

;set PROGRAM high (inactive)
				bcf		LATC,EN_PROGRAM	;[0.25]

;stall for 20 uSec (Tath=10 uS min, Tvd = 10 uS min, 100 uS max)
				movlw	.19				;[0.25]
				call	K_STALL1U		;[50.25]

;remove Vdd, Vgg
				bcf		LATC,EN_VDDVGG	;(0.25)

;stall to meet duty cycle requirement with a little extra.
; Vdd and Vgg were on for 2146.25 uS. The code takes 533 uS while Vdd and Vgg
; are off. We need to stall for a total of  (4 X 2.654 ms) - 533 uS = 10.08 mS uS
; We'll add extra for good measure
				movlw	.40				;(.25) stall for 10 mS

				goto	K_STALL250U		;return from there

;*==============================================================================*
; Subroutine to report one analog measurement
; On Entry:
;    W = initial string
;    ADRESH = high bytre of result
;    ADRESL = low byte of result
;    Bank Select Register = 1 (for ADSH & ADSL)
;*==============================================================================*
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 (filtered) 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
;trashes W,R0
;*==============================================================================*
get_analog:
;select ADC input channel and enable ADC
				movlb	0x01			;bank select for ADC
				movwf	ADCON0			;select analog input per reg W

;delay for ADC acquisition 
				movlw	0x05			;5 uS is enough time for settling
				call	K_STALL1U

;Use R0 to filter the analog input
				movlw	0xFF			;won't match 1st time

ga_loop:		movwf	R0

;initiate conversion
				bsf		ADCON0,ADGO

;wait for conversion to complete
ga_wait:		btfsc	ADCON0,ADGO
				bra		ga_wait

;Filter the result: Wait until 2 reads in a row match on the high word
				movfw	ADRESH
				xorwf	R0,F			;same?
				btfss	STATUS,Z
				bra		ga_loop			;n:read again

;result is ready in ADRESH and ADRESL
				return

;********************************************************************************
;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, Vgg
				movwf	LATC
				movlw	EPROM_OFF_E			;Power off
				movwf	LATE

				movlw	panic_msg_index		;general panic message
				call	cr_printf			;local printf for these messages
				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'
; On Exit:
;    FSR1H = high(EPROM_BUF)
; trashes W, R0, FSR0, FSR1
;********************************************************************************
report_yn:		call	cr_printf			;print input message
				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'
; On Exit:
;    FSR1H = high(EPROM_BUF)
; trashes W, R0, FSR0, FSR1L
;*==============================================================================*
ask_yn:			call	printf				;print page 1 string
				call	get_line			;get CR-terminated string
				call	get_uchr			;look at first character
				xorlw	'Y'					;anything that starts with Y is okay

				movlw	high(EPROM_BUF)		;because get_line trashes FSR1
				movwf	FSR1H				;(these do not afect Z.)

				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
slct1702A_msg_index	equ	$ - string_table
			dw	slct1702A_msg	
slct1702_msg_index	equ	$ - string_table
			dw	slct1702_msg	
panic_msg_index	equ	$ - string_table
			dw	panic_msg
pvpre70high_msg_index	equ	$ - string_table
			dw	pvpre70high_msg
pvcc48high_msg_index	equ	$ - string_table
			dw	pvcc48high_msg
pvbb48high_msg_index	equ	$ - string_table
			dw	pvbb48high_msg
rvpre12low_msg_index	equ	$ - string_table
			dw	rvpre12low_msg
rvcc5high_msg_index	equ	$ - string_table
			dw	rvcc5high_msg
rvcc5low_msg_index	equ	$ - string_table
			dw	rvcc5low_msg
rvbb5high_msg_index	equ	$ - string_table
			dw	rvbb5high_msg
rvbb5low_msg_index	equ	$ - string_table
			dw	rvbb5low_msg
rvpre70low_msg_index	equ	$ - string_table
			dw	rvpre70low_msg
rvcc48low_msg_index	equ	$ - string_table
			dw	rvcc48low_msg
rvbb48low_msg_index	equ	$ - string_table
			dw	rvbb48low_msg
rvprestuck_msg_index	equ	$ - string_table
			dw	rvprestuck_msg
rvccstuck_msg_index	equ	$ - string_table
			dw	rvccstuck_msg
rvbbstuck_msg_index	equ	$ - string_table
			dw	rvbbstuck_msg
vpreequals_msg_index	equ	$ - string_table
			dw	vpreequals_msg
vccequals_msg_index	equ	$ - string_table
			dw	vccequals_msg
vbbequals_msg_index	equ	$ - string_table
			dw	vbbequals_msg
slow1702_msg_index	equ	$ - string_table
			dw	slow1702_msg
pageaddr_msg_index	equ	$ - string_table
			dw	pageaddr_msg
auto_pa_msg_index	equ	$ - string_table
			dw	auto_pa_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
vddvgg_msg_index	equ	$ - string_table
			dw	vddvgg_msg
vcc48_msg_index	equ	$ - string_table
			dw	vcc48_msg
progsignal_msg_index	equ	$ - string_table
			dw	progsignal_msg
minus9_msg_index	equ	$ - string_table
			dw	minus9_msg
echo_msg_index	equ	$ - string_table
			dw	echo_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         TC <0/1>  Test Chip select\r"
			da	"  EP <adr> <cnt>  Program from buffer       TD <0/1>  Test vDd & vgg\r"
			da	"  ER <adr> <cnt>  Read into buffer          TP <0/1>  Test PROG\r"
            da  "  ES <adr> <cnt>  checkSum EPROM            TV <0/1>  Test Vcc 48V\r""
			da	"  ET <0/1> set EPROM Type: 0=1702A, 1=1702  T9 <0/1>  Test -9V\r"
			da	"  PC <cnt> set programming Pass Count         (0=off, 1=on)\r"
			da	"    (00 = use smart algorithm)              WA <val>  Write Address pins\r"                              
			da	"FILE TRANSFER COMMANDS (?F for help)        WD <val>  Write Data pins\r"
			da	"  PA        automatic Page Address mode     RD        Read Data pins\r"                                  
			da	"  PA <val>  set Page Address to <val>         (do WD 0 and TM 1 first)\r"
			da	"  UI        Upload buffer as Intel hex      RV        Read Voltages\r"
			da	"  US        Upload buffer as S-records\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 and Data are 8 bits, from 00 to FF. You can use ^S\r"
			da	"during DB buffer display to pause/restart printing of the buffer contents. MB\r"
			da	"allows you to edit the buffer contents, one byte at a time. The address and\r"
			da	"data for the specified address are displayed. If you type a hexidecimal value\r"
			da	"followed by <return>, then that value will replace the buffer data. If you just\r"
			da	"type <return>, then the buffer data will remain unchanged. After you type\r"
			da	"<return>, the data from the next address will be displayed, allowing you to\r"
			da	"modify it next. You can continue to view/edit sequential buffer data until you\r"
			da	"type ^C. If you continue past address FF, the address will wrap around to 00.\r",0

;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

copyright:	da  "\r\r"
			da	"                     NEW TECHNOLOGY FOR OLD TECHNOLOGY\r\r"
			da	"The Intel 1702 was the first  erasable PROM,  invented in 1971 by Dov Frohman,\r"
			da	"who was awarded US patent  3660819 for the idea in 1972.  It was soon replaced\r"
			da	"by the 1702A,  which could be programmed much more quickly.  These PMOS EPROMs\r"
			da	"provided 256 bytes of permanent storage, and were erasable with UV light. They\r"
			da	"were first  marketed for development, far too expensive to ship with products.\r"
			da	"Falling prices eventually led to their use in production too. The 1702A became\r"
			da	"obsolete in 1975 with the introduction of much better NMOS EPROMS, such as the\r"
			da	"2708.  The 1702A was produced for several more years by several manufacturers,\r"
			da	"used in legacy designs, including several versions of the Altair computer.\r\r"
			da	"Fast  forward to 2012.  The 1702 and 1702A are so obsolete  that even the most\r"
			da	"\"universal\"  programmers on the market can't program them. Vintage programmers\r"
			da	"capable of programming  PMOS EPROMS are quite rare and are getting  expensive.\r"
			da	"Those who restore vintage computers,  pinball machines, video games, etc. find\r"
			da	"1702As socketed in various pieces of eWaste, but struggle to program them.\r\r"
			da	"I designed the ME1702A as a low-cost,  fully functional 1702/1702A  programmer\r"
			da	"for you, the vintage technology enthusiast. Enjoy!\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	"                   ME1702/A PROGRAMMER DIAGNOSTIC 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, also performs T9 0, turning off all.\r"
			da	"   power to the EPROM. With TM 1 and TV 0, Vcc is set to 5 volts.\r"
			da	"T9 1 puts -9V on Vdd and Vgg, and also performs TV 0 and TD 0\r"
			da	"TV 1 turns on the high-voltage, putting 48 volts on Vcc, and 60 volts on Vbb.\r"
			da	"   TV 1 also performs T9 0, turning off -9 volts.\r"
			da	"TD controls both Vdd and Vgg. TD 0 sets both inactive (at Vcc). TD 1 sets Vgg\r"
			da	"   active (0 volts), and Vgg active (about 9 volts when TV 1). TD 1 also\r"
			da	"   performs T9 0, turning off the -9 supply.\r"
			da	"TC 1 sets Chip Select active (0 volts). TC 0 sets Chip Select to Vcc.\r"
			da	"TP 1 sets PROG active (0 volts). TP 0 sets PROG to Vcc.\r"
			da	"WA <val> writes <val> to the EPROM address pins. Note that these are pulled up\r"
			da	"   to Vcc, so do TM 1 and maybe TV 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 Vcc.\r"
			da	"RD reads the EPROM data pins and prints the result on the console. You must\r"
			da	"   first do WD 0 and TM 1 if you want to test these signals. If you are\r"
			da	"   reading an EPROM, then you must also do TP 0, T9 1, and CS 1.\r"
			da	"RV reads several analog voltages and prints the results on the console in an\r"
			da	"   obscure hexidecimal format.\r"
			da	"NOTE: the 1702/A specs reference voltages from Vcc, not ground as is done here.\r"
			da	"Reference your meter to Vcc, measure voltages, and compare to the 1702/A specs.",0
;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

ehelp_msg:	da	"\r\r"
			da	"                           EPROM COMMANDS HELP\r\r"
			da	"<adr> is the (2-digit hex) beginning address and <cnt> is the (2-digit hex)\r"
			da	"byte count for the for the operation. <beg> and <cnt> both default to 00.\r"
			da	"<cnt> = 00 is interpreted as 100 hex, i.e. the entire EPROM.\r\r"
			da	"ET <type> selects an EPROM, setting the PROG pulse width and duty cycle, and\r"
			da	"selecting the recommended algorithm. You can then change the algorithm with the\r"
			da	"PC command. Pulse width and duty cycle can only be changed with the ET command.\r\r"
			da	"<type>     EPROM            PROG PULSE WIDTH  DUTY CYCLE  RECOMMENDED ALGORITHM\r"
			da	"  00   Generic 1702A         2.5 mSec +/-10%    19.0%     P+4P Smart Algorithm\r"
			da	"  01   1601/1602/1701/1702   10 mSec +/-10%     1.9%      0Ah passes\r"
			da	"  02-FF  Reserved\r\r"
			da	"Note that each programming pass through a 1702 will take about 2.2 minutes.\r\r"
			da	"The EPROM is always verified (compared to the buffer) when done. PC <cnt> sets\r"
			da	"the number of programming passes through the specified range. Setting\r"
			da	"<cnt> = 00 selects the P+4P Smart Algorithm. This will program the EPROM\r"
			da	"repeatedly until it matches the buffer. Then it will over-program the EPROM\r"
			da	"4 times as many passes as it took to get the EPROM to match the buffer.\r",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\r"
			da	"Hex or simple Motorola S-record format. No special command is needed to\r"
			da	"download a file to the buffer memory - the hex records themselves are directly\r"
			da	"recognized. The UI and US commands upload in Intel and S-record formats.\r\r"
			da	"The Page Address is the upper byte of the 2-byte address in each hex record,\r"
			da	"and can be set with the PA command. PA with no parameter resets the Page\r"
			da	"Address default. For uploads, the Page Address defaults to 00, and is used as\r"
			da	"the upper address byte in each transmitted record. For downloads, the Page\r"
			da	"Address defaults to the high address byte from the first received record.\r"
			da	"Received data will not be written to the buffer if the upper address byte in\r"
			da	"the record does not match the Page Address.\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

lhelp_msg	da	"\r\r"
			da	"                         NOTES ON THE FIRMWARE LOADER\r\r"
			da	"This firware can be loaded via the ME1702/A 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	"ME1702/A 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 ME\r"
			da	"Loader prompt, send the Intel hex firmware file to the ME1702/A. 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 ME1702/A firmware, but 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	"ME1702/A, or by typing the ESC key several times.\r" 
			da	0

;				 12345678901234567890123456789012345678901234567890123456789012345678901234567890

signon_msg:	da	"\r\r*=====================================*\r"
			da	"*              ME1702/A               *\r"
			da	"*=====================================*\r"
			da	"*    1702A & 1702 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	"\rPlease be 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+4P Smart programming algorithm enabled",0

backwards_msg:	da	"\rAll bytes of this EPROM are FF and cannot be programmed.\r"
				da	"Is it inserted backwards? Is it really a 1702A?",0

slct1702A_msg:	da	"\r1702A selected\r"
				da	"PROG pulse = 2.5 mS, 19% duty cycle",0
slct1702_msg:	da	"\r1601/1602/1701/1702 selected\r"
				da	"PROG pulse = 10 mS, 1.9% duty cycle",0

panic_msg:		da	"PANIC! ",0
pvpre70high_msg:	da	"Vpre is too high!",0
pvcc48high_msg:	da	"High-voltage Vcc is too high!",0
pvbb48high_msg:	da	"High-voltage Vbb is too high!",0
rvpre12low_msg:	da	"Low-voltage Vpre is too low",0
rvcc5high_msg:	da	"Low-voltage Vcc is too high",0
rvcc5low_msg:	da	"Low-voltage Vcc is too low",0
rvbb5high_msg:	da	"Low-voltage Vbb is too high",0
rvbb5low_msg:	da	"Low-voltage Vbb is too low",0
rvpre70low_msg:	da	"High-voltage Vpre is too low",0
rvcc48low_msg:	da	"High-voltage Vcc is too low",0
rvbb48low_msg:	da	"High-voltage Vbb is too low",0
rvprestuck_msg:	da	"Vpre is not shutting off",0
rvccstuck_msg:	da	"Vcc is not shutting off",0
rvbbstuck_msg:	da	"Vbb is not shutting off",0

vpreequals_msg:	da	"Vpre: ",0
vccequals_msg:	da	" Vcc: ",0
vbbequals_msg:	da	" Vbb: ",0

;				     12345678901234567890123456789012345678901234567890123456789012345678901234567890
slow1702_msg:	da	"Note that each programming pass through an entire 1702 takes about 2.2 minutes.",0

pageaddr_msg:	da	"Page Address: ",0
auto_pa_msg:	da	"automatic",0
progcycles_msg:	da	"Programming Cycles: ",0
buffill_msg:	da	"Buffer filled with ",0

smartprog_msg:	da	"P+4P 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
loadcount_msg:	da	", Records loaded in Page ",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 Address: ",0
mismatchb_msg:	da	"   Buffer: ",0
mismatche_msg:	da	"   EPROM"			;falls into colonspace_msg
colonspace_msg:	da	": ",0

isblank_msg:	da	"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	"  -CE Signal",0
vddvgg_msg:		da	"   Vdd & Vgg",0
vcc48_msg:		da	"Vcc 48 Volts",0
progsignal_msg:	da	"-PROG Signal",0
minus9_msg:		da	"   -9V Power",0
echo_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	"matches buffer",0
overprog_msg:	da	". 4P over-programming",0
loaderrev_msg:	da	"ME Loader Kernel version ",0
fwrev_msg:		da	"ME1702/A 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!",0

	end