 	TITLE "PICTIC II Interpolating Time Interval Counter - Richard H McCorkle, 03/07/2010"

; Copyright © 2010, Richard H McCorkle. All rights reserved. 

; Redistribution and use in source and binary forms, with or without
; modification, are permitted for non-commercial purposes only 
; provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright
;    notice, this list of conditions and the following disclaimer.
;
; 2. Redistributions in binary form must reproduce the above copyright
;    notice, this list of conditions and the following disclaimer in the
;    documentation and/or other materials provided with the distribution.
;
; This software is provided by the author and contributors "as is" and
; any express or implied warranties, including, but not limited to, the
; implied warranties of merchantability and fitness for a particular 
; purpose are disclaimed. In no event shall the author or contributors 
; be liable for any direct, indirect, incidental, special, exemplary, 
; or consequential damages (including, but not limited to, procurement
; of substitute goods or services; loss of use, data, or profits; or 
; business interruption) however caused and on any theory of liability, 
; whether in contract, strict liability, or tort (including negligence 
; or otherwise) arising in any way out of the use of this software, 
; even if advised of the possibility of such damage. 

;*********************************************************************
;			 CPU Type & Fuses:
;*********************************************************************

	LIST n=58, p=PIC16F688 
	errorlevel	1
	include P16F688.INC
	__CONFIG _INTOSCIO & _PWRTE_ON & _WDT_OFF & _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF  

;*********************************************************************
;		       Function Description:
;*********************************************************************
;  The PICTIC II is a high resolution time interval counter with
;interpolation that sends 10-digit BCD data to a PC serial port for 
;logging and analysis. No input or clock conditioning is provided
;and it is up to the user to insure TTL conditioned inputs are 
;used. When there is insufficient time between the stop and the next 
;start input the counter reset may not occur in time to sample the 
;next start, resulting in skipped samples. 
;  Clock rates from 10M to 80M can be used setting the basic counter
;resolution from 100ns to 12.5ns. Time to voltage interpolation is
;then used to increase the clock resolution by as much as 800 times, 
;increasing the maximum resolution from 62.5ps to 15.7ps depending 
;on the clock rate used. A user set interpolation gain factor  
;reduces the interpolation gain for greater accuracy with a zero 
;setting providing no interpolation. Typically a 50 MHz XO timebase 
;is combined with an interpolation gain of 400 to provide a sample 
;resolution of 50ps.  
;  Dual software peak detectors are included to determine the Min/Max 
;count limits to simplify initial calibration of the interpolators. 
;An automatic calibration mode can be enabled that monitors variations 
;in the offset and span of the interpolators with temperature and age 
;and corrects the data in software for improved long-term accuracy. 
;  Time to voltage interpolation is used to drastically reduce the 
;hardware required for low cost and ease of assembly. Modern PIC 
;microcontrollers include multiple 10-bit A/D inputs and have a 
;fast enough conversion time to read the start and stop capacitor  
;voltages directly between samples if they are buffered using a 
;CMOS op-amp. The PICTIC II interpolators use just enough circuitry 
;to charge two sample capacitors with a constant current during 
;the start and stop intervals, buffer and offset correct the
;cap voltages, read the voltages using the PIC ADC, and discharge 
;the capacitors after the data is read. A two IC gating circuit 
;provides outputs to the interpolators and synchronized gating of 
;the prescaler feeding the main counter in the PIC. By using minimal 
;hardware and adapting a traditional interpolator for maximum use of 
;features already present in a PIC the cost and complexity of an 
;interpolating time interval counter is drastically reduced.
;  Interpolation accuracy is reduced due to variations in supply
;voltage, capacitance, charge current, DAC accuracy, and offset 
;voltage with changing temperature and component age. To maintain 
;accuracy a means of calibrating the interpolator is required. By 
;only using a portion of the A/D range for the data and a portion 
;for offset and scaling variations, sufficient A/D range is 
;available to compensate for variations in temperature and aging 
;dynamically in software so common low-cost components can be   
;used. Using a software calibration routine to compensate for 
;variations dynamically relaxes the temperature and stability 
;requirements of the components used, resulting in a low cost, 
;high accuracy interpolator. 
;  The key to understanding the PICTIC II is each interpolator
;sample is a minimum of one clock period and a maximum of two 
;clock periods in duration due to the synchronizers used. The 
;initial clock period provides sufficient time for the sample 
;switch to turn on fully before the measurement interval begins. 
;The difference between the switch turn on time and the beginning 
;of the measurement interval sets the minimum ADC count returned. 
;Whenever the input and timebase leading edges pass through phase 
;coincidence the ADC count passes from maximum to minimum or
;minimum to maximum allowing determination of the interpolator 
;minimum and maximum ADC count limits from the sample data. 
;  Dual software peak detectors are used over many sample periods 
;to determine the minimum and maximum ADC values returned over 
;a calibration period. If sufficient samples are taken the zero 
;offset and span of the interpolator hardware can be determined. 
;A low stability XO timebase varies sufficiently to insure there 
;are frequent phase coincidences between the timebase and the 
;inputs and simplifies calibration. When the timebase drifts off  
;frequency the inputs pass coincidence at a recurring rate. If 
;the inputs maintain phase coincidence with the timebase for an
;extended period the calibration routine stops functioning 
;correctly as no zero and span limits are reached during the 
;calibration period. This is important to note as the calibration 
;routine may need to use longer calibration periods or be 
;disabled completely when using a high stability external timebase. 
;  During initial setup the interpolator sample capacitor value 
;is selected for the timebase frequency and charge current used.
;The charge current is then adjusted by resistor selection to 
;return a peak to peak span of approximately 800 ADC counts with
;offset trimmers provided to center the data in the ADC span. 
;To simplify initial calibration the peak values can be displayed    
;continuously and the peak detectors can be reset manually after 
;an adjustment to speed initial setup. Once calibration has been 
;performed allow a full calibration cycle (2 hrs min) to complete 
;to place the zero and span values in EEPROM memory before   
;disabling the calibration routine. The zero and span values for  
;each interpolator can also be entered directly into memory in 
;HEX and saved manually to EEPROM providing fixed calibration  
;values for use at restart. In the manual calibration mode the 
;last zero and span values entered by the user or determined by 
;the autocal routine and stored are used as fixed constants in 
;the correction routine. 
;  During operation in automatic calibration mode the peak 
;values are read and the peak detectors are reset after a user 
;set number of samples have occurred. The peak values establish 
;the hardware zero and span and these values are stored to 
;EEPROM and used during the next calibration period to correct  
;the data mathmatically for an exact 0 - span even when the 
;hardware span or offset is slightly different due to 
;temperature variations and component aging. 
;  Testing has shown the maximum peak values to be highly 
;stable but the minimum peak values can have samples with 
;relatively large variances from the norm. To provide beter 
;stability in the cal values an offset limiter was added. 
;When the limiter is enabled samples greater than +/- 3 
;counts from the average of the previous two samples are 
;ignored. The limiter can be enabled or disabled and the  
;limit can be changed by serial command. Once enabled four 
;updates will occur before the offset limiter takes effect.   
;  After correction processing the interpolator data has a 
;fixed range from 0 to the interpolation gain factor 
;representing one clock cycle. If the interpolator data 
;is outside the zero or span limits during correction 
;processing the data is limited and a status bit is set to 
;provide an indication of the interpolator limit condition. 
;During startup a zero or full scale ADC output can occur, 
;so a 5-minute delay is added before the peak detectors 
;are reset and the first calibration cycle begins. The 
;correction takes the form expressed in the following 
;equations: 

;Imin = Data offset from zero 
;Data - Imin = offset corrected data 
;Imax - Imin = actual hardware span 
;Igain - Desired Data Span

;Corrected Interpolator Value = offset corrected data *  
;desired data span / actual hardware span
;					OR
;((Data - Imin) * Igain) / (Imax - Imin) = Corrected Data

;Delay = ((Counter Value * Igain) + Start Delay) - Stop Delay 

;  To determine the time delay the counter value is multiplied
;by the interpolator gain, the corrected start delay is added,  
;and the corrected stop delay is subtracted from the result 
;before display. Offset and gain drift with temperature 
;occurs similarly in both interpolators, so the temperature 
;variations in the interpolators tend to cancel out when the
;stop value is subtracted from the start value in calculating
;the total delay. The 36-bit data is converted to 10-digit BCD 
;and displayed immediately after each sample. If the data 
;exceeds 36 bits or the displayed value exceeds 10 digits an 
;overflow flag is set in the status message to indicate the  
;condition and the low 10 digits are displayed. 
;  Various data types can be toggled on or off for display during 
;the update providing a view of the interpolators and autocal
;routines in action. A 1-bit HEX status message can be enabled 
;to display the counter configuration and overflow status. In 
;the digit 8 = Inverted TXD, 4 = Autocal enabled, 2 = the counter 
;overflowed the displayed value, and 1 = an interpolator value 
;exceeded the min/max limits.
;  The serial baud rate and amount of data displayed determines 
;the maximum update rate, but the sampling routine can operate 
;at much faster rates (up to approximately 10 KHz) if averaging 
;routines are added. Displaying only the 10-digit counter data 
;the maximum update rate is about 80 Hz to allow sufficient   
;time to transmit the data at 9600 baud between samples. When 
;additional data types are displayed the maximum update rate 
;must be reduced accordingly. An RS232 converter is assumed 
;but an inverted TXD can be enabled with the @@Ci command and 
;disabled with the @@Cn command if TTL levels are used. A 
;transistor inverter for RXD and inverted TXD from the PIC  
;can be used with a short connecting cable to provide full 
;duplex communication with a PC at TTL levels. Tie RTS and 
;CTS (pins 7 & 8) together at the PC or through the cable or 
;serial commands won't function correctly. 

;*********************************************************************
;			Default EEPROM Values
;*********************************************************************
;The EEPROM should be pre-loaded as shown below during the 
;PIC programming process.  

;Zero = 112, Mid = 512, Span = 800, Igain = 400, Time = 7200
;Limit Offset at 4 count variance, at print final value only, 
;normal TXD polarity, AutoCal enabled. 

;00-07	00 70 02 00 03 20 00 70   
;08-0F	02 00 03 20 01 90 1C 20 
;10-17	04 20 01 FF FF FF FF FF 
 
;*********************************************************************
;			I/O Pin Assignments:
;*********************************************************************

;     Register Bit 	Pin	Function
;	PORTA 	0 	(13)	A/D Ch 0 In
;		1 	(12)	A/D Ch 1 In
;		2 	(11)	Stop In 
;		3 	( 4)	Start In  
;		4 	( 3)	Reset Out
;		5 	( 2)	Inv PS D to TMR1 In  
;	PORTC	0	(10)	PS A In
;		1 	( 9)	PS B In
;		2 	( 8)	PS C In
;		3 	( 7)	PS D In	
;		4 	( 6)	USART Async TXD Out
;		5 	( 5)	USART Async RXD In

;*********************************************************************
;		     Serial Command Functions:
;*********************************************************************
; The Expanded Command set allows process variables to 
; be printed or modified by serial port command.

; Basic Command Set:

;     Command			Function
;	 #		Aborts current command or data entry

;	@@A		Calibrate A Interpolator Commands
;	@@B		Calibrate B Interpolator Commands
;	@@C		Calibration Commands
;	@@D		Select display parameters  
; 	@@P		Print Commands - once to serial TX  
; 	@@R		Run Command - Resets and starts counter  
;	@@S		Stop Command - Stops counter 
; 	@@U		Update EEPROM Calibration Values

; Expanded Command Set:

;	@@A		Calibrate A Interpolator Commands 
; 	@@Ac		Set Ch A Center Value (xxx HEX)
; 	@@As		Set Ch A Span Value (xxx HEX)
; 	@@Az		Set Ch A Zero Value	(xxx HEX)

;	@@B		Calibrate B Interpolator Commands 
; 	@@Bc		Set Ch B Center Value (xxx HEX)
; 	@@Bs		Set Ch B Span Value (xxx HEX)
; 	@@Bz		Set Ch B Zero Value	(xxx HEX)	

;	@@C		Calibration Commands
; 	@@Cd		Disable Autocal Mode, Manual Cal Only  	
; 	@@Ce		Enable Autocal Mode
;	@@Cg		Set Interpolation Gain Value xxx HEX
; 	@@Ci		Set Inverted TXD polarity for TTL comm
; 	@@Cl		Enable Cal Offset Limiter
; 	@@Cn		Set Normal TXD polarity for RS-232 comm
; 	@@Cr		Reset Autocal Peak Setectors
; 	@@Ct		Set Calibration Time in Samples (xxxx HEX)
;	@@Cp		Print Calibration Values, Gain, and Time in HEX		
; 	@@Cu		Disable Cal Offset Limiter		
; 	@@Cv		Set Cal Offset Limiter Variance  (x HEX)		

; 	@@D		Display parameters
; 	@@Dc		Toggle Display Calibration Values in BCD
; 	@@Dd		Toggle Display 10-Digit Time Delay in BCD
; 	@@Di		Toggle Display Corrected Start and Stop in BCD
; 	@@Dp		Toggle Display Peak Detector Values
; 	@@Dr		Toggle Display Raw Interpolator Data in BCD
; 	@@Ds		Toggle Display Status Digit
; 	@@Dt		Toggle Display TIC Counter Data in BCD
; 	@@Dv		Toggle Display Interpolated Value in BCD

; 	@@P		Print parameters:
;	@@Pc		Print Calibration Data in BCD
;	@@Pg		Print Interpolation Gain in BCD 
;	@@Pi		Print Interpolator Data in BCD
;	@@Ps		Print Status Digit
; 	@@Pt		Print Calibration Time in BCD

;*********************************************************************
;			   Change History:
;*********************************************************************

; 02/20/2010 PICTIC II Initial Code	 		    (Ver 0.10)
; 02/24/2010 Add 40-bit BCD for full 9,999,999,999 count    (Ver 0.20)
; 02/25/2010 Correct Stop assignment			    (Ver 0.30)
; 02/26/2010 Add cal offset limiter			    (Ver 0.40)
; 03/05/2010 Add inverter, change to TMR1		    (Ver 0.50)
; 03/05/2010 PICTIC II Initial Release Code		    (Ver 1.00)

;*********************************************************************
;		      Define Storage Locations
;*********************************************************************
; Maintain registers in sequence H->L for indexed ops!  

     CBLOCK 0x20 		;Bank 0 Registers - 96 max, 92 used		
	TX_BUF			;the next char to be xmitted
	RX_BUF			;the last char received
	flg0,flg1		;flag bit storage
	cHdr0			;command header flags
	Ha,Ma,La,Sa,SSa		;40 bit math reg A 
	Hb,Mb,Lb,Sb,SSb 	;40 bit math reg B 
	bCnt,bTst		;Binary-to-BCD regs
	BD6,BD5,BD4,BD3		;must stay in sequence BD6 -> BD0
	BD2,BD1,BD0
	Hbyte,Mbyte,Lbyte	;print register storage
	Sbyte,SSbyte
	Hdata,Mdata		;phase data from counter
	Ldata,Sdata,SSdata
	HPS,MPS,LPS		;print data storage
	SPS,SSPS
	HOFct,LOFct		;overflow counter
	btmp,btmp2		;background temp regs
	BytCt,Dadr		;serial data entry regs
	Htmp,Ltmp		;16-bit temp storage
	HcalT,LcalT		;autocal timers
	temp			;interrupt temporary storage
	Padr			;EEPROM address register
	TSS			;temp serial storage
	HriA,LriA		;interpolator A reg 
	HriB,LriB		;interpolator B reg
	HiaM,LiaM		;Ch A Max count
	HibM,LibM		;Ch B Max count
	HiaO,LiaO		;Ch A Min count
	HibO,LibO		;Ch B Min count
	Hstart,Lstart		;Start delay
	Hstop,Lstop		;Stop delay
	HIval,LIval		;Interpolator value

;Registers saved and restored to EEPROM - Keep in order!

	HiaZ,LiaZ		;Ch A Zero
	HiaC,LiaC		;Ch A Center
	HiaS,LiaS		;Ch A Span
	HibZ,LibZ		;Ch B Zero
	HibC,LibC		;Ch B Center
	HibS,LibS		;Ch B Span
	HIgain,LIgain		;Interpolation Gain
	Hcal,Lcal		;AutoCal Time
	CalOL			;Offset Limiter Variance 
	DMB			;Display Mode Bits
	CMB			;Command bits
     ENDC

;Registers 70-7F common in all pages in 16F688 so stack is stored here
;NOTE: If not common in PIC used, don't use page 1 variables in the
;background without disabling interrupts as background routine could 
;be interrupted on either page and W may not get saved properly in 
;W_TEMP on interrupt of a page 1 routine. 

     CBLOCK 0x7d
	W_TEMP			;temp storage for W on interrupt
	STAT_TEMP		;ditto for status reg
	FSR_TEMP		;ditto for FSR
     ENDC

     CBLOCK 0xa0  		;Bank 1 Registers - 80 max, 5 used	
	Hiapo,Liapo		;previous offset storage
	Hibpo,Libpo
	CalLT			;cal limit timer
     ENDC

;*********************************************************************
;			 Hardware Bit Assignments:
;*********************************************************************
;Hardware bit flags used to simplify reassignment of pins
 
#define t1e	T1CON,TMR1ON	;Timer 1 Enable
#define t1f	PIR1,TMR1IF	;Timer 1 Overflow Flag
#define RACF 	INTCON,RAIF	;PORTA IOC Flag
#define	adF	ADCON0,1	;A/D Converter start/done flag
#define	adCH	ADCON0,2	;A/D Converter Channel  
#define Zflag 	STATUS,Z	;Zero Flag
#define Cflag 	STATUS,C	;Carry Flag
#define CRST 	PORTA,4		;Reset Out

;*********************************************************************
;			  Flag Bit Assignments:
;*********************************************************************
;Flag bits used to optimize memory usage

; CMB:
; B0=ACM, B1=ITX, B2=COLE, B3=N/U
; B4=N/U, B5=N/U, B6=N/U, B7=N/U

; DMB:
; B0=TDE, B1=IPE, B2=CVE, B3=DCV
; B4=DID, B5=DTD, B6=PSE, B7=DPE

; flg0:
; B0=BrdyF, B1=LoByt, B2=Nrdy, B3=DUF
; B4=FpF, B5=Drdy, B6=negFlag, B7=Ilmt

; flg1:
; B0=OFflg, B1=HoldF, B2=A1rd, B3=OLEF
; B4=N/U, B5=N/U, B6=N/U, B7=N/U

;*********************************************************************

#define	ACM	CMB,0		;Autocal Mode Flag
#define	ITX	CMB,1		;Inverted TXD Flag 
#define	COLE	CMB,2		;Cal Offset Limit Enable
#define	TDE	DMB,0		;TIC Direct Print Enable
#define IPE	DMB,1		;Raw Interpolator Print Enable
#define CVE	DMB,2		;Calibration Values Print Enable
#define DCV	DMB,3		;Corrected Start and Stop Print Enable
#define DID	DMB,4		;Interpolated Value Print Enable
#define DTD	DMB,5		;10-Digit Time Delay Print Enable
#define	PSE	DMB,6		;Status Print Enable
#define	DPE	DMB,7		;Peak Data Print Enable 
#define BrdyF	flg0,0		;Byte Ready Flag
#define	LoByt	flg0,1		;Low Byte Flag
#define	Nrdy	flg0,2		;Not Ready Flag
#define DUF	flg0,3		;Data Update Flag
#define FpF	flg0,4		;First Cal Flag
#define Drdy	flg0,5		;Print Data Ready Flag
#define negFlag	flg0,6		;Neg Flag
#define Ilmt	flg0,7		;I Limit Flag
#define OFflg	flg1,0		;Counter Overflow Flag
#define HoldF	flg1,1		;Hold flag
#define A1rd	flg1,2		;ADC Channel 1 Read Flag
#define OLEF	flg1,3		;Offset Limit Enabled Flag

;*********************************************************************
;			Command Header Flags:
;*********************************************************************

#define Hdr0 	cHdr0,0		;Header 0 Flag	(@   )
#define Hdr1 	cHdr0,1		;Header 1 Flag	(@@  )
#define Hdr2 	cHdr0,2		;Header 2 Flag	(@@A )
#define Hdr3 	cHdr0,3		;Header 3 Flag	(@@B )
#define Hdr4 	cHdr0,4		;Header 4 Flag	(@@C )
#define Hdr5 	cHdr0,5		;Header 5 Flag	(@@D )
#define Hdr6 	cHdr0,6		;Header 6 Flag	(@@P )
#define Hdr7 	cHdr0,7		;Header 7 Flag	(Nrtn)

;*********************************************************************
;			   Initialization
;*********************************************************************
;set interrupt vector and start of code

     org 0			;initialize code
	nop			;required for the ICD 
	clrf	STATUS		;ensure we are at page 0	
	clrf    PCLATH     	;ensure bank bits are cleared
	goto	start
     org 4			;interrupt routine
	movwf	W_TEMP		;"push" instructions
	swapf	STATUS,W	;swapf affects NO status bits
	bcf	STATUS,RP0	;select page 0
	movwf	STAT_TEMP	;save STATUS
	movf	FSR,W
	movwf	FSR_TEMP	;save FSR
	goto	int_srv

;InitCon initializes controller ports and registers

start	call	InitCon		;init regs, get constants from prom
	bsf	CRST		;set reset to enable
	bsf	INTCON,GIE	;enable interrupts

;*********************************************************************
;			   Background Routine							
;*********************************************************************
;Handles communication and print tasks between interrupts

Bkgnd	call	Rx232		;check for character received
	btfsc	BrdyF		;if byte ready
	call	CmdProc		;Process command 	
	btfss	Drdy		;if data ready flag set		
	goto	Bkgnd
	bcf	Drdy		;clear data ready flag 			
	btfsc	HoldF		;in hold mode
	goto	Bkgnd		;just loop
	btfsc	TDE		;if TIC display enabled
	call	prtData		;print counter data
	btfsc	IPE		;if enabled
	call	prtID		;print interpolator data
	btfsc	DPE		;if enabled
	call	prtPD		;print peak detector data 
	btfsc	CVE		;if enabled
	call	prtCD		;print calibration data
	bcf	INTCON,GIE	;disable interrupts
	call	Res2A		;read Igain, if zero 
	btfss	negFlag		;skip interpolation print
	goto	$ + 6
	bsf	INTCON,GIE	;enable interrupts
	btfsc	DCV		;if enabled
	call	prtCV		;print corrected start and stop
	btfsc	DID		;if enabled
	call	prtIV		;print interpolated value
	bsf	INTCON,GIE	;enable interrupts
	btfsc	DTD		;if enabled
	call	prtPS		;print time delay
	btfsc	PSE		;if status display enabled	
	call	prtSt		;print status 
	call	TxCrLf		;print EOL		
	bcf	Ilmt		;clear I limit flag
	bcf 	OFflg		;clear Counter Overflow Flag
	goto	Bkgnd

;*********************************************************************
;		     Interrupt Service Routine
;*********************************************************************
;Calculate counter correction from interpolator and add to data
;Full clock cycles in TMR1, partial clock cycle determined from 
;interpolators by (((start count - start zero) * Igain) / start span) 
; - (((stop count - stop zero) * Igain) / stop span) for corrected
;interpolator value. 
	
int_srv	btfsc	t1f		;if TMR1 OF
	call	incOF		;increment OF counters
	btfsc 	INTCON,INTF	;if stop interrupt
	goto	ISRJ0		;process data	
	btfss 	RACF		;PORTA IOC set?
	goto	pop
RdA0	bsf	adF		;set convert flag to start conversion
	btfsc	adF		;wait till conversion done
	goto	$ - 1
	bsf	adCH		;select channel 1
	bsf	A1rd		;aet Ch 1 read
	bsf	STATUS,RP0	;select bank 1
	movf	ADRESL,W	;get A/D result in W
	bcf	STATUS,RP0	;select bank 0
	movwf	LriA		;save raw interpolator A 
	movf	ADRESH,W
	movwf	HriA
	movf	PORTA,W		;clear IOC flag
	bcf	RACF
	btfss 	INTCON,INTF	;if stop interrupt clear
	goto	pop		;exit
ISRJ0	btfss	A1rd		;Ch 1 read not set?
	goto	RdA0 		;go back and read Ch0 first
	bsf	adF		;set convert flag to start conversion
	btfsc	adF		;wait till conversion done
	goto	$ - 1
	bcf	adCH		;select channel 0
	bcf	A1rd		;clear Ch 1 read
	bsf	STATUS,RP0	;select bank 1
	movf	ADRESL,W	;get A/D result in W
	bcf	STATUS,RP0	;select bank 0
	movwf	LriB		;save raw interpolator B 
	movf	ADRESH,W
	movwf	HriB
	call	readctr		;get data values, reset counter
	call	Acal		;read cal values
	call	data2B		;Counter to B
	call	Res2A		;read Igain, if zero 
	btfss	negFlag		;skip interpolation
	goto	ISRJ1
	call	A2strt		;A to start
	call	B2stop		;B to stop
	call	data2B		;Counter to B
	call	Res2A		;Resolution to A
	call	MB40X16		;multiply B * A
 	call	Aclr
	movf	Hstart,W	;start to A
	movwf	Sa
	movf	Lstart,W
	movwf	SSa
	call	addAL		;add start delay to counter
	call	Aclr
	movf	Hstop,W		;stop to A
	movwf	Sa
	movf	Lstop,W
	movwf	SSa
	call	subAL		;subtract stop delay
ISRJ1	call	B2PS		;save time delay in print
	bsf	Drdy		;set data ready flag		
	btfsc	DUF		;data update flag set, 
	call	D2prom		;update EPROM with new data
	btfsc	t1f		;if TMR1 OF set
	call	incOF		;increment OF counters
	call	Res2A		;read Igain, if zero 
	btfss	negFlag		;skip display interpolated test
	goto	pop
	btfss	DID		;if display interpolated value set
	goto	pop
	call	Aclr		;put stop in top bits of A 
	movf	Hstop,W
	movwf	Ha
	movf	Lstop,W
	movwf	Ma
	call	Bclr		;put start in top bits of B 
	movf	Hstart,W
	movwf	Hb
	movf	Lstart,W
	movwf	Mb
	call	subA		;Start - Stop = interpolated value in B
	movf	Hb,W		;save value for print
	movwf	HIval
	movf	Mb,W
	movwf	LIval
pop	bcf	STATUS,RP0	;bank 0
	movf	FSR_TEMP,W	;restore FSR
	movwf	FSR
	swapf	STAT_TEMP,W	;restore STATUS
	movwf	STATUS
	swapf	W_TEMP		;set status bits
	swapf	W_TEMP,W	;restore W
	retfie			;return from interrupt

;*********************************************************************
;				Subroutines
;*********************************************************************
;clear time counter 

clrTC	bcf	CRST		;clear the prescaler & interpolator
	clrf	TMR1L		;clear TMR1
	clrf	TMR1H
	movf	PORTA,W		;clear IOC flag
	bcf	RACF
	bcf	INTCON,INTF	;clear start interrupt
	bsf	CRST		;release the counter
	bcf	t1f		;clear TMR1 overflow flag
	clrf	LOFct		;clear overflow counter
	clrf	HOFct
	bcf	adCH		;select channel 0
	bcf	A1rd		;clear Ch 1 read
	bcf	Ilmt		;clear I limit flag
	bcf 	OFflg		;clear Counter Overflow Flag
	goto	clrSC		;init peak detectors

;*********************************************************************
;			Read PICTIC Counter	
;*********************************************************************
;Full clock cycles in prescaler, TMR1, and OFctr, partial 
;clock cycles determined from time to voltage interpolators.

readctr	swapf	PORTC,W		;read 4 PS bits 
	andlw	0xf0		;mask unused bits
	movwf	SSdata
	movf	TMR1L,W		;move TMR1 to Data
	movwf	Sdata
	movf	TMR1H,W
	movwf	Ldata
	btfsc	t1f		;if TMR1 OF set
	call	incOF		;increment OF counters
	movf	LOFct,W		;move OFctr to Data
	movwf	Mdata
	movf	HOFct,W
	movwf	Hdata
	bcf	CRST		;clear the prescaler & interpolator
	clrf	TMR1L		;clear TMR1
	clrf	TMR1H
	movf	PORTA,W		;clear IOC flag
	bcf	RACF
	bcf 	INTCON,INTF	;clear stop interrupt
	bsf	CRST		;release the counter
	bcf	t1f		;clear TMR1 overflow flag
	clrf	LOFct		;clear overflow counter
	clrf	HOFct
	movlw	0x04		;shift data 4 bits right
	bcf	Cflag		;to 36-bit counter value
	rrf	Hdata
	rrf	Mdata
	rrf	Ldata
	rrf	Sdata
	rrf	SSdata
	addlw	0xff
	btfss	Zflag
	goto	$ - 8
	return

;*********************************************************************
;			  AutoCal Routine
;*********************************************************************
;peak detector routines determine min and max interpolator values
;for calculating actual interpolator zero and span. Min count is
;lowest resolution or zero, max count is span as no sample can be 
;< 1 or > 2 clock cycles with this design.

Acal	movf	LriA,W		;put raw A value in temp	
	movwf	Ltmp
	movf	HriA,W
	movwf	Htmp
	movf	LiaC,W		;subtract center value
	subwf	Ltmp
	btfss	Cflag
	decf	Htmp
	movf	HiaC,W
	subwf	Htmp
	btfsc	Htmp,7		;if sample < center 
	goto	ACJ1		;do min test
	movf	LriA,W		;put raw A value in temp	
	movwf	Ltmp
	movf	HriA,W
	movwf	Htmp
	movf	LiaM,W		;subtract Ch A Max from temp
	subwf	Ltmp
	btfss	Cflag
	decf	Htmp
	movf	HiaM,W
	subwf	Htmp
	btfsc	Htmp,7		;if positive, value is > Max
	goto	ACJ2
	movf	HriA,W		;save raw A value as new Ch A Max
	movwf	HiaM
	movf	LriA,W
	movwf	LiaM
	goto	ACJ2
ACJ1	movf	HiaO,W		;put Ch A offset in temp	
	movwf	Htmp
	movf	LiaO,W
	movwf	Ltmp
	movf	LriA,W		;subtract raw A value from offset
	subwf	Ltmp
	btfss	Cflag
	decf	Htmp
	movf	HriA,W
	subwf	Htmp
	btfsc	Htmp,7		;if positive, value is < offset
	goto	ACJ2
	movf	HriA,W		;save value as new Ch A offset
	movwf	HiaO
	movf	LriA,W
	movwf	LiaO
ACJ2	movf	LriB,W		;put raw B value in temp
	movwf	Ltmp	
	movf	HriB,W	
	movwf	Htmp
	movf	LibC,W		;subtract center value
	subwf	Ltmp
	btfss	Cflag
	decf	Htmp
	movf	HibC,W
	subwf	Htmp
	btfsc	Htmp,7		;if sample < center 
	goto	ACJ3		;do min test
	movf	LriB,W		;put raw B value in temp
	movwf	Ltmp	
	movf	HriB,W	
	movwf	Htmp
	movf	LibM,W		;subtract Ch B Max from temp
	subwf	Ltmp
	btfss	Cflag
	decf	Htmp
	movf	HibM,W
	subwf	Htmp
	btfsc	Htmp,7		;if positive, value is > Max
	goto	ACJ4
	movf	HriB,W		;save raw B value as new Ch B Max
	movwf	HibM
	movf	LriB,W
	movwf	LibM
	goto	ACJ4
ACJ3	movf	HibO,W		;put Ch B offset in temp	
	movwf	Htmp
	movf	LibO,W
	movwf	Ltmp
	movf	LriB,W		;subtract raw B value from temp
	subwf	Ltmp
	btfss	Cflag
	decf	Htmp
	movf	HriB,W
	subwf	Htmp
	btfsc	Htmp,7		;if positive, value is < offset
	goto	ACJ4
	movf	HriB,W		;save raw B value as new Ch B offset
	movwf	HibO
	movf	LriB,W
	movwf	LibO
ACJ4	movlw	0xff		;dec cal timer
	addwf	LcalT
	btfss	Cflag	
	addwf	HcalT
	movf	HcalT,W		;if not zero return
	btfss	Zflag
	return
	movf	LcalT,W
	btfss	Zflag
	return
	btfss	FpF		;don't update first pass
	goto	$ + 3
	bcf	FpF
	goto	clrSC
	btfss	ACM		;if autocal flag set 
	goto	clrSC
	btfss	COLE		;if offset limiter enabled 
	goto	ACJ5
	btfsc	OLEF		;and if > 4 samples
	goto	$ + 7		;skip cal limit enable routine
	bsf	STATUS,RP0
	decf	CalLT		;decrement cal limit timer
	bcf	STATUS,RP0
	btfss	Zflag		;if zero
	goto	ACJ5
	bsf	OLEF		;set limit enable flag
	bsf	STATUS,RP0
	movf	Hiapo,W		;put previous offset in temp
	bcf	STATUS,RP0
	movwf	Htmp
	bsf	STATUS,RP0
	movf	Liapo,W
	bcf	STATUS,RP0
	movwf	Ltmp
	movf	LiaZ,W		;add current offset
	addwf	Ltmp
	btfsc	Cflag
	incf	Htmp
	movf	HiaZ,W
	addwf	Htmp
	bcf	Cflag		;divide by 2 to average
	rrf	Htmp
	rrf	Ltmp
	movf	LiaO,W		;subtract IA offset from 
	subwf	Ltmp		;2 sample average
	btfss	Cflag
	decf	Htmp
	movf	HiaO,W
	subwf	Htmp
	btfss	Htmp,7		;if result neg
	goto	$ + 6
	comf	Htmp		;make pos
	comf	Ltmp
	incf	Ltmp
	btfsc	Zflag
	incf	Htmp
	movf	Htmp,W		;if Htmp not 0
	btfss	Zflag
	goto	ACJ6		;skip this sample
	movf	CalOL,W		;subtract limit
	subwf	Ltmp		;if result is pos
	btfsc	Cflag		;sample is = > limit
	goto	ACJ6		;so skip this sample
ACJ5	movf	HiaZ,W		;save current offset 
	bsf	STATUS,RP0
	movwf	Hiapo		;as previous offset
	bcf	STATUS,RP0
	movf	LiaZ,W
	bsf	STATUS,RP0
	movwf	Liapo
	bcf	STATUS,RP0
	movf	HiaO,W		;save new offset 
	movwf	HiaZ		;as new zero value
	movf	LiaO,W
	movwf	LiaZ
ACJ6	btfss	COLE		;if offset limiter enabled 
	goto	ACJ7
	btfss	OLEF		;and if > 4 samples
	goto	ACJ7
	bsf	STATUS,RP0
	movf	Hibpo,W		;put previous offset in temp
	bcf	STATUS,RP0
	movwf	Htmp
	bsf	STATUS,RP0
	movf	Libpo,W
	bcf	STATUS,RP0
	movwf	Ltmp
	movf	LibZ,W		;add current offset
	addwf	Ltmp
	btfsc	Cflag
	incf	Htmp
	movf	HibZ,W
	addwf	Htmp
	bcf	Cflag		;divide by 2 to average
	rrf	Htmp
	rrf	Ltmp
	movf	LibO,W		;subtract IB offset from 
	subwf	Ltmp		;2 sample average
	btfss	Cflag
	decf	Htmp
	movf	HibO,W
	subwf	Htmp
	btfss	Htmp,7		;if result neg
	goto	$ + 6
	comf	Htmp		;make pos
	comf	Ltmp
	incf	Ltmp
	btfsc	Zflag
	incf	Htmp
	movf	Htmp,W		;if Htmp not 0
	btfss	Zflag
	goto	ACJ8		;skip this sample
	movf	CalOL,W		;subtract limit
	subwf	Ltmp		;if result is pos
	btfsc	Cflag		;sample is > limit
	goto	ACJ8		;so skip this sample
ACJ7	movf	HibZ,W		;save current offset 
	bsf	STATUS,RP0
	movwf	Hibpo		;as previous offset
	bcf	STATUS,RP0
	movf	LibZ,W
	bsf	STATUS,RP0
	movwf	Libpo
	bcf	STATUS,RP0
	movf	HibO,W		;save new offset 
	movwf	HibZ		;as new zero value
	movf	LibO,W
	movwf	LibZ
ACJ8	movf	HiaM,W		;save Max values in span
	movwf	HiaS
	movf	LiaM,W
	movwf	LiaS
	movf	HibM,W
	movwf	HibS
	movf	LibM,W
	movwf	LibS
	movf	LiaZ,W		;max - offset = new span values
	subwf	LiaS
	btfss	Cflag
	decf	HiaS
	movf	HiaZ,W
	subwf	HiaS
	movf	LibZ,W
	subwf	LibS
	btfss	Cflag
	decf	HibS
	movf	HibZ,W
	subwf	HibS
	movf	HiaS,W		;A span to tmp
	movwf	Htmp
	movf	LiaS,W
	movwf	Ltmp
	bcf	Cflag		;/2
	rrf	Htmp
	rrf	Ltmp
	movf	LiaZ,W		;add offset
	addwf	Ltmp
	btfsc	Cflag
	incf	Htmp
	movf	HiaZ,W
	addwf	Htmp
	movf	Htmp,W		;save A center
	movwf	HiaC
	movf	Ltmp,W
	movwf	LiaC
	movf	HibS,W		;B span to tmp
	movwf	Htmp
	movf	LibS,W
	movwf	Ltmp
	bcf	Cflag		;/2
	rrf	Htmp
	rrf	Ltmp
	movf	LibZ,W		;add offset
	addwf	Ltmp
	btfsc	Cflag
	incf	Htmp
	movf	HibZ,W
	addwf	Htmp
	movf	Htmp,W		;save B center
	movwf	HibC
	movf	Ltmp,W
	movwf	LibC
	bsf	DUF		;set flag to update values in EEPROM		
clrSC	movf	HiaC,W		;init peak detectors to center
	movwf	HiaM
	movwf	HiaO
	movf	LiaC,W
	movwf	LiaM
	movwf	LiaO
	movf	HibC,W
	movwf	HibM
	movwf	HibO
	movf	LibC,W
	movwf	LibM
	movwf	LibO
	movf	Hcal,W		;reload cal timer
	movwf	HcalT
	movf	Lcal,W
	movwf	LcalT
	return

;*********************************************************************
;		    Interpolator Correction Routines
;*********************************************************************
;Interpolator A to start delay

A2strt	movf	LriA,W		;save Ch A as start count
	movwf	Lstart		
	movf	HriA,W
	movwf	Hstart		
	movf	LiaZ,W		;remove zero offset 
	subwf	Lstart		;from start count
	btfss	Cflag
	decf	Hstart
	movf	HiaZ,W
	subwf	Hstart
	btfss	Hstart,7	;if neg, data < zero
	goto	$ + 5
	clrf	Lstart		;make data zero
	clrf	Hstart
	bsf	Ilmt		;set I limit flag
	return
	call	Bclr		;move start to B
	movf	Lstart,W
	movwf	Mb
	movf	Hstart,W
	movwf	Hb
	call	Res2A		;Resolution to A
	call	MB16X16		;multiply B * A
	call	Aclr
	movf	LiaS,W		;actual span in A
	movwf	Ma
	movf	HiaS,W
	movwf	Ha
	call	DB24X16		;divide B / A
	movf	Lb,W		;Save corrected start
	movwf	Lstart		
	movf	Mb,W
	movwf	Hstart
	movwf	Hb		;move start high in B
	movf	Lb,W
	movwf	Mb		
	clrf	Lb
	call	Res2A		;Resolution to A
	call	subA		;sub max from count 
	btfsc	Hb,7		;if pos data > limit
	return		
	call	Res2A		;Resolution limit to A
	movf	Ma,W		;save limit as start
	movwf	Lstart	
	movf	Ha,W
	movwf	Hstart
	bsf	Ilmt		;set I limit flag
	return		

;*********************************************************************
;Interpolator B to stop delay

B2stop	movf	LriB,W		;save Ch B as stop count
	movwf	Lstop		
	movf	HriB,W
	movwf	Hstop		
	movf	LibZ,W		;remove zero offset 
	subwf	Lstop		;from stop count
	btfss	Cflag
	decf	Hstop
	movf	HibZ,W
	subwf	Hstop
	btfss	Hstop,7		;if neg, data < zero
	goto	$ + 5
	clrf	Lstop		;make data zero
	clrf	Hstop
	bsf	Ilmt		;set I limit flag
	return
	call	Bclr		;move stop to B
	movf	Lstop,W
	movwf	Mb
	movf	Hstop,W
	movwf	Hb
	call	Res2A		;Resolution to A
	call	MB16X16		;multiply B * A
	call	Aclr
	movf	LibS,W		;actual span in A
	movwf	Ma
	movf	HibS,W
	movwf	Ha
	call	DB24X16		;divide B * A
	movf	Lb,W		;Save corrected stop
	movwf	Lstop		
	movf	Mb,W
	movwf	Hstop
	movwf	Hb		;move stop high in B
	movf	Lb,W
	movwf	Mb		
	clrf	Lb
	call	Res2A		;Resolution to A
	call	subA		;sub max from count 
	btfsc	Hb,7		;if pos data > limit
	return		
	call	Res2A		;Resolution limit to A
	movf	Ma,W		;save limit as stop
	movwf	Lstop	
	movf	Ha,W
	movwf	Hstop
	bsf	Ilmt		;set I limit flag
	return		

;*********************************************************************
;						Data Movement Routines 
;*********************************************************************
;Interpolator resolution to A, clear neg flag if 0

Res2A	call	Aclr
	bcf	negFlag
	movf	HIgain,W
	movwf	Ha
	btfss	Zflag
	bsf	negFlag
	movf	LIgain,W
	movwf	Ma
	btfss	Zflag
	bsf	negFlag
	return	

;*********************************************************************
;40-bit B to A

B2A	movf	Hb,W		;B to A 40-bit data
	movwf	Ha
	movf	Mb,W
	movwf	Ma
	movf	Lb,W
	movwf	La
	movf	Sb,W
	movwf	Sa
	movf	SSb,W
	movwf	SSa
	return

;*********************************************************************
;40-bit data to B

data2B	movf	SSdata,W	;40-bit data to 40-bit B	
	movwf	SSb	
	movf	Sdata,W
	movwf	Sb
	movf	Ldata,W
	movwf	Lb
	movf	Mdata,W
	movwf	Mb
	movf	Hdata,W
	movwf	Hb
	return

;*********************************************************************
;B to 40-bit print storage

B2PS	movf	SSb,W		;40-bit B to 40-bit print storage
	movwf	SSPS
	movf	Sb,W
	movwf	SPS
	movf	Lb,W
	movwf	LPS
	movf	Mb,W
	movwf	MPS
	movf	Hb,W
	movwf	HPS
	return

;*********************************************************************
;			     Math Routines 
;*********************************************************************
;24-bit & 40-bit math routines (adapted from AN611) 

Aclr	clrf	Ha		;clear A
	clrf	Ma
	clrf	La
	clrf	Sa
	clrf	SSa
	return

;*********************************************************************

addAL	movf	SSa,W		;A + B -> B  (40 bits)
	addwf	SSb		;add low byte
	btfsc	Cflag		;add in carry if necessary
	call	add3
	movf	Sa,W
	addwf	Sb		;add mid bytes
	btfsc	Cflag
	call	add2
addA	movf	La,W		;A + B -> B  (24 bits)
	addwf	Lb		;add mid bytes
	btfsc	Cflag
	call	add1
	movf	Ma,W
	addwf	Mb		;add mid bytes
	btfsc	Cflag
	incf	Hb
	movf	Ha,W
	addwf	Hb		;add hi byte
	return
add3	incf	Sb		;skip chain
	btfsc	Zflag
add2	incf	Lb
	btfsc	Zflag
add1 	incf	Mb
	btfsc	Zflag
	incf	Hb
	return

;*********************************************************************
cmpA 	comf	La		;24-bit 2s complement of A -> A
	comf	Ma
	comf	Ha
	goto	add5

;*********************************************************************

cmpAL	comf	SSa		;40-bit 2s complement of A -> A
	comf	Sa		;invert all the bits in A
	comf	La
	comf	Ma
	comf	Ha
	incf	SSa		;add one to A
	btfsc	Zflag		;skip chain
add6	incf	Sa
	btfsc	Zflag
add5	incf	La
	btfsc	Zflag
add4 	incf	Ma
	btfsc	Zflag
	incf	Ha
	return

;*********************************************************************

Bclr	clrf	Hb		;clear B
	clrf	Mb
	clrf	Lb
	clrf	Sb
	clrf	SSb
	return

;*********************************************************************
cmpB	comf	Lb		;24-bit 2s complement of B -> B
	comf	Mb
	comf	Hb
	goto	add2

;*********************************************************************
cmpBL	comf	SSb		;40-bit 2s complement of B -> B 
	comf	Sb
	comf	Lb
	comf	Mb
	comf	Hb
	incf	SSb
	btfsc	Zflag
	goto	add3
	return

;*********************************************************************
;Divide unsigned 24 bit number in B by unsigned 16 bit divisor in 
;A on call with result as 24 bit unsigned number in B 

;Adaption of code 8-July-2000 by Nikolai Golovchenko, see
;http://www.piclist.com/techref/microchip/math/div/16by8lzf-ng.htm

DB24X16	movlw	D'24'		;24-bit data
	movwf	temp		;loop counter
	clrf 	Ltmp		;use tmp register as temporary
	clrf 	Htmp
	bcf	Cflag
DB3    	rlf	Lb		;shift next msb into temporary
    	rlf 	Mb
       	rlf	Hb
    	rlf 	Ltmp
       	rlf	Htmp
	movf	Ltmp,W		;save temp in low A for restore
	movwf	SSa
	movf	Htmp,W
	movwf	Sa
	bcf	negFlag		;clear borrow flag
	movf	Ma,W     	;subtract divisor from temporary
      	subwf	Ltmp 	
	btfsc	Cflag		;if borrow
	goto	$ + 5
	movlw	0xff		;dec Htmp
	addwf	Htmp
	btfss	Cflag		;if borrow
	bsf	negFlag		;set negFlag
	movf	Ha,W
       	subwf	Htmp 	
	btfss	Cflag		;if borrow
	bsf	negFlag		;set negFlag
	bcf	Cflag
	btfss	negFlag   	;if no borrow, set Cflag
	bsf	Cflag		;carry is the next bit of result
	btfss	negFlag     	;if borrow, 
	goto	$ + 5
	movf	SSa,W		;restore temp 
	movwf	Ltmp
	movf	Sa,W
	movwf	Htmp
   	decfsz 	temp		;repeat 24 times to find result
       	goto 	DB3
   	rlf	Lb		;shift last bit into B
    	rlf 	Mb
       	rlf	Hb
 	return

;*********************************************************************
;clear OF flag, increment the overflow counter

incOF	bcf	t1f		;clear TMR1 OF flag
	incf	LOFct		;increment OF counters
	btfsc	Zflag
	incf	HOFct
	btfsc	Zflag	
	bsf 	OFflg		;set flag on overflow 
	return

;*********************************************************************
;multiply unsigned 16-bit number in B by unsigned 
;16-bit number in A into 24-bit result in B.  

MB16X16	movf	Ha,W		;move A to tmp
	movwf	Htmp
	movf	Ma,W
	movwf	Ltmp
	call	Aclr		;move B to low A
	movf	Hb,W
	movwf	Ma
	movf	Mb,W
	movwf	La
	call	Bclr		;clear B (result)
	movlw	D'16'		;multiply tmp x A -> B		
	movwf	temp		;bit counter (16 bit multiplier)
mlp0	bcf	Cflag		;left-shift 24 bit B reg 
	rlf	Lb		;rotate result left (*2)
	rlf	Mb
	rlf	Hb
	rlf 	Ltmp
	rlf 	Htmp
	btfsc	Cflag		;if bit is clear then no add	
	call	addA		;add A to B
	decfsz	temp		;loop til done
	goto	mlp0
	return			;return with correction in B

;*********************************************************************
;multiply unsigned 40-bit number in B by unsigned 
;16-bit number in A into 40-bit result in B.  

MB40X16	movf	Ha,W		;move A to tmp
	movwf	Htmp
	movf	Ma,W
	movwf	Ltmp
	call	B2A		;move B to A
	call	Bclr		;clear B (result)
	movlw	D'16'		;multiply tmp x A -> B		
	movwf	temp		;bit counter (16 bit multiplier)
mlp1	bcf	Cflag		;left-shift 40 bit B reg 
	rlf	SSb		;rotate result left (*2)
	rlf	Sb
	rlf	Lb
	rlf	Mb
	rlf	Hb
	rlf 	Ltmp
	rlf 	Htmp
	btfsc	Cflag		;if bit is clear then no add	
	call	addAL		;add A to B
	decfsz	temp		;loop til done
	goto	mlp1
	return			;return with correction in B

;*********************************************************************

subA	call	cmpA		;B - A -> B (24 bits)
	goto	addA		;addA returns to caller

;*********************************************************************

subAL	call	cmpAL		;B - A -> B (40 bits)
	goto	addAL		;addAL returns to caller

;*********************************************************************
;			Hex Print Routines 
;*********************************************************************
;Provides display of constant data in the same Hex format used 
;during data entry. 

;print the zero, center, and span cal data in HEX (12 bits)

hpCD	movf	HiaZ,W		;IA Zero
	movwf	Mbyte
	movf	LiaZ,W
	movwf	Lbyte
	call	hp12
	movf	HiaC,W		;IA Center
	movwf	Mbyte
	movf	LiaC,W
	movwf	Lbyte
	call	hp12
	movf	HiaS,W		;IA Span
	movwf	Mbyte
	movf	LiaS,W
	movwf	Lbyte
	call	hp12
	movf	HibZ,W		;IB Zero
	movwf	Mbyte
	movf	LibZ,W
	movwf	Lbyte
	call	hp12
	movf	HibC,W		;IB Center
	movwf	Mbyte
	movf	LibC,W
	movwf	Lbyte
	call	hp12
	movf	HibS,W		;IB Span
	movwf	Mbyte
	movf	LibS,W
	movwf	Lbyte
	goto	hp12

;*********************************************************************
;print the Interpolation Gain in HEX (12 bits)

hpIG	movf	HIgain,W	;print Interpolation Gain
	movwf	Mbyte
	movf	LIgain,W	
	movwf	Lbyte
	goto	hp12

;*********************************************************************
;print the Cal Offset Limiter Data 

hpCL	movlw	"D"		;print limiter status
	btfsc	COLE
	movlw	"E"
	call 	Tx
	movf	CalOL,W
	movwf	Lbyte
	goto	hp4

;*********************************************************************
;print the Cal Time in HEX (16 bits)

hpCT	movf	Hcal,W		;print Calibration time
	movwf	Mbyte
	movf	Lcal,W	
	movwf	Lbyte
	goto	hp16

;*********************************************************************
;Print one HEX digit for the hardware configuration and overflow
;status. In the digit an 8 = Inverted TXD, 4 = Autocal enabled, 
;a 2 = the counter overflowed the displayed value, and 1 = an 
;interpolator value exceeded the limits.

prtSt	clrf	Lbyte
	movlw	0x08
	btfsc	ITX		;inverted TXD
	addwf	Lbyte
	movlw	0x04
	btfsc	ACM		;autocal mode
	addwf	Lbyte
	movlw	0x02
	btfsc	OFflg		;Counter OF flag
	addwf	Lbyte
	movlw	0x01
	btfsc	Ilmt		;I limit flag
	addwf	Lbyte
	goto	hp4

;*********************************************************************
;output 16 bit chars in Mbyte, Lbyte as HEX ASCII to TX DATA

hp16	swapf	Mbyte,W
	movwf	TX_BUF
	call	TxHex		;print Mbyte hi nibble in hex	
hp12	movf	Mbyte,W
	movwf	TX_BUF
	call	TxHex		;print Mbyte low nibble in hex	
hp8	swapf	Lbyte,W
	movwf	TX_BUF
	call	TxHex		;print Lbyte hi nibble in hex	
hp4	movf	Lbyte,W
	movwf	TX_BUF
	call	TxHex		;print Lbyte low nibble in hex	
	goto	TxSp		;Tx space returns to caller

;*********************************************************************
;convert lo nibble in TX_BUF to HEX ASCII and send 

TxHex	movf	TX_BUF,W	;get transmit data
	andlw	0x0f		;mask hi bits
	sublw	0x09		;9 - W if >9, Cflag = 0
	movf	TX_BUF,W	;get data
	andlw	0x0f		;mask hi bits
	btfss	Cflag		;is input >9
	addlw	0x07		;if >9 add 7 for A-F
	addlw	"0"	
	goto	Tx

;*********************************************************************
;			BCD Print Routines
;*********************************************************************
;print interpolated gain

prtIG	movf	HIgain,W	;interpolated gain
	movwf	Hbyte
	movf	LIgain,W
	movwf	Mbyte
	goto	prt3D

;*********************************************************************
;print interpolated value as +/- 3-digit BCD range

prtIV	movf	HIval,W		;print interpolated value
	movwf	Hbyte
	movf	LIval,W
	movwf	Mbyte
	movlw	" "
	btfss	Hbyte,7		;if neg
	goto	$ + 7
	comf	Hbyte		;invert neg value	
	comf	Mbyte	
	incf	Mbyte
	btfsc	Zflag	
	incf	Hbyte
	movlw	"-"		;print neg sign
	call	Tx
	goto	prt3D

;*********************************************************************
;print corrected start and stop data

prtCV	movf	Hstart,W	;start
	movwf	Hbyte
	movf	Lstart,W
	movwf	Mbyte
	call	prt3D
	movf	Hstop,W		;stop
	movwf	Hbyte
	movf	Lstop,W
	movwf	Mbyte
	goto	prt3D

;*********************************************************************
;print interpolator values start data, stop data, 
;Ch A zero, Ch A span, Ch B zero, Ch B span  

prtIC	call	prtID
prtCD	movf	HiaZ,W		;IA Zero
	movwf	Hbyte
	movf	LiaZ,W
	movwf	Mbyte
	call	prt3D
	movf	HiaS,W		;IA Span
	movwf	Hbyte
	movf	LiaS,W
	movwf	Mbyte
	call	prt3D
	movf	HibZ,W		;IB Zero
	movwf	Hbyte
	movf	LibZ,W
	movwf	Mbyte
	call	prt3D
	movf	HibS,W		;IB Span
	movwf	Hbyte
	movf	LibS,W
	movwf	Mbyte
	goto	prt3D

;*********************************************************************
;print interpolator zero, center, span values 

prtCC	movf	HiaZ,W		;IA Zero
	movwf	Hbyte
	movf	LiaZ,W
	movwf	Mbyte
	call	prt3D
	movf	HiaC,W		;IA Center
	movwf	Hbyte
	movf	LiaC,W
	movwf	Mbyte
	call	prt3D
	movf	HiaS,W		;IA Span
	movwf	Hbyte
	movf	LiaS,W
	movwf	Mbyte
	call	prt3D
	movf	HibZ,W		;IB Zero
	movwf	Hbyte
	movf	LibZ,W
	movwf	Mbyte
	call	prt3D
	movf	HibC,W		;IB Center
	movwf	Hbyte
	movf	LibC,W
	movwf	Mbyte
	call	prt3D
	movf	HibS,W		;IB Span
	movwf	Hbyte
	movf	LibS,W
	movwf	Mbyte
	goto	prt3D

;*********************************************************************
;print interpolator peak values 

prtPD	movf	HiaO,W		;IA Min
	movwf	Hbyte
	movf	LiaO,W
	movwf	Mbyte
	call	prt3D
	movf	HiaM,W		;IA Max
	movwf	Hbyte
	movf	LiaM,W
	movwf	Mbyte
	call	prt3D
	movf	HibO,W		;IB Min
	movwf	Hbyte
	movf	LibO,W
	movwf	Mbyte
	call	prt3D
	movf	HibM,W		;IB Max
	movwf	Hbyte
	movf	LibM,W
	movwf	Mbyte
	goto	prt3D

;*********************************************************************
;output 16 bit BCD chars in BD0,BD1,BD2 as ASCII to TX DATA
;print as 0-999 3-digit BCD interpolator values

prtID	movf	HriA,W		;IA Raw
	movwf	Hbyte
	movf	LriA,W
	movwf	Mbyte
	call	prt3D
	movf	HriB,W		;IB Raw
	movwf	Hbyte
	movf	LriB,W
	movwf	Mbyte
prt3D 	call	B16_BCD		;convert bytes to BCD	
	movf	BD1,W		;send BD1 low nibble
	movwf	TX_BUF
	call 	TxChar
	call	TXBD2		;send BD2
	goto	TxSp

;*********************************************************************
;output 16 bit BCD chars in BD0,BD1,BD2 as ASCII to TX DATA
;max count = 65535 or 5-digit BCD

prtCT	movf	Hcal,W		;print Calibration time
	movwf	Hbyte
	movf	Lcal,W
	movwf	Mbyte
prt16	call	B16_BCD		;convert bytes to BCD
	call	TXBD1
	call	TXBD2
	goto	TxSp		;Tx returns to caller

;*********************************************************************
;output 37 bit BCD chars in BD2,BD3,BD4,BD5,BD6 as ASCII to TX DATA
;max = 9,999,999,999 or full 10-digit BCD, set OFflg if > 10 digits 

prtData	movf	Hdata,W		;print 37 bits of data
	movwf	Hbyte
	movf	Mdata,W
	movwf	Mbyte
	movf	Ldata,W
	movwf	Lbyte
	movf	Sdata,W
	movwf	Sbyte
	movf	SSdata,W
	movwf	SSbyte
	goto	prt40s
prtPS	movf	SSPS,W		;print low 37 bits of 40-bit value
	movwf	SSbyte
	movf	SPS,W
	movwf	Sbyte
	movf	LPS,W
	movwf	Lbyte
	movf	MPS,W
	movwf	Mbyte
	movf	HPS,W
	movwf	Hbyte
prt40s	call	B40_BCD		;convert bytes to BCD
	movf	BD0,W		;if > 10 digits set OFflg
	btfss	Zflag		
	bsf	OFflg
	movf	BD1,W
	btfss	Zflag		
	bsf	OFflg
	call	TXBD2		;send 10-digit BCD
	call	TXBD3
	call	TXBD4
	call	TXBD5
	call	TXBD6
	goto	TxSp		;Tx space returns to caller

;*********************************************************************
;16 bit binary to BCD conversion (adapted from AN544)
;input in Hbyte, Mbyte and output in BD0, BD1, BD2

B16_BCD	bcf	Cflag		;clear carry bit
	movlw	D'16'	
	movwf	bCnt		;set bCnt = 16 bits
	call	clrBCD		;clear work registers
RL16	rlf	Mbyte		;rotate 1 bit 
	rlf	Hbyte
	rlf	BD2
	rlf	BD1
	rlf	BD0
	decfsz	bCnt		;16 bits done?
	goto	$ + 2		;no, process more BCD
	return			;yes, return
	movlw	BD2		;load addr of BD2 as indir addr
	movwf	FSR
	movlw	0x03		;process 3 registers
	call	CnvBCD		;convert to BCD
	goto	RL16		;get next bit

;*********************************************************************
;40 bit binary to BCD conversion (adapted from AN544)
;input in Hbyte, Mbyte, Lbyte, Sbyte, SSbyte and 
;output in BD0, BD1, BD2, BD3, BD4, BD5, BD6

B40_BCD	bcf	Cflag		;clear carry bit
	movlw	D'40'	
	movwf	bCnt		;set bCnt = 40 bits
	call	clrBCD		;clear work registers
RL40	rlf	SSbyte		;rotate 1 bit 
	rlf	Sbyte
	rlf	Lbyte
	rlf	Mbyte
	rlf	Hbyte
	rlf	BD6
	rlf	BD5
	rlf	BD4
	rlf	BD3
	rlf	BD2
	rlf	BD1
	rlf	BD0
	decfsz	bCnt		;40 bits done?
	goto	$ + 2		;no, process more BCD
	return			;yes, return
	movlw	BD6		;load addr of BD6 as indir addr
	movwf	FSR
	movlw	0x07		;process 7 registers
	call	CnvBCD		;convert to BCD
	goto	RL40

;*********************************************************************
;clear BCD work registers

clrBCD	clrf	BD0			
	clrf	BD1
	clrf	BD2
	clrf	BD3
	clrf	BD4
	clrf	BD5
	clrf	BD6
	return

;*********************************************************************
;convert to BCD

CnvBCD	movwf	btmp		;W has # regs on call
Adj	movf	INDF,W		;get reg via indirect addr
	addlw	0x03
	movwf	bTst		;sum to bTst for test
	btfsc	bTst,3		;test if >0x07
	movwf	INDF		;yes - store sum
	movf	INDF,W		;get original or sum
	addlw	0x30		;test hi byte
	movwf	bTst		;sum to bTst for test
	btfsc	bTst,7		;test result >0x70
	movwf	INDF		;save as BCD
	incf	FSR		;next reg
	decfsz	btmp		;Done?
	goto	Adj		;no, do next adj
	return			;yes, return

;*********************************************************************
;send nibbles of BD0,BD1,BD2,BD3,BD4 as BCD ASCII to TX DATA

TXBD1	movf	BD0,W		;send MSD first
	movwf	TX_BUF		;send BD0 low nibble
	call	TxChar
	movf	BD1,W		;send BD1
TXBD	movwf	TX_BUF
	swapf	TX_BUF		;BD1 hi nibble
	call 	TxChar
	swapf	TX_BUF		;BD1 low nibble
	goto	TxChar
TXBD2	movf	BD2,W		;send BD2
	goto	TXBD
TXBD3	movf	BD3,W		;send BD3
	goto	TXBD
TXBD4	movf	BD4,W		;send BD4
	goto	TXBD
TXBD5	movf	BD5,W		;send BD5
	goto	TXBD
TXBD6	movf	BD6,W		;send BD6
	goto	TXBD

;*********************************************************************
;convert lo nibble in TX_BUF to BCD ASCII and send 

TxChar	movf	TX_BUF,W	;get the buffer
	andlw	0x0f		;mask high nibble
	addlw	"0"		;make into ASCII
Tx	btfss	TXSTA,TRMT	;test for Tx buffer empty 	
	goto	$ - 1		;wait till buffer empty
	movwf	TXREG		;send it 
	return

;*********************************************************************
;transmit carriage return, line feed 

TxCrLf	movlw	"\r"		;send CR direct to Tx
	call	Tx
	movlw	"\n"		;send LF direct to Tx
	goto	Tx		;Tx returns to caller

;*********************************************************************
;transmit space to separate values

TxSp	movlw	" "		;send space
	goto	Tx		;Tx returns to caller

;*********************************************************************
; Get data via USART rcv mode

Rx232	btfss	PIR1,RCIF	;have we received a char?
	return			;no - nothing to do
	movf	RCSTA,W		;check for rcv status for error
	andlw	0x06		;select only error bits
	btfss	Zflag		;if any set, jump to
	goto	RxErr		;error service routine
	movf	RCREG,W		;get char from input buffer
	movwf	RX_BUF		;store in RX_BUF
	bsf	BrdyF		;set byte available flag
	return
RxErr	bcf	RCSTA,CREN	;clear CREN to clear overrun error
	movf	RCREG,W		;read RCREG to clear framing error
	bsf	RCSTA,CREN	;set CREN to rcv
	bcf	BrdyF		;clear byte ready flag
	clrf	cHdr0		;clear header flags
	return

;*********************************************************************
;	 	       Serial Command Decoder
;	  Based on Simple Commands by Richard H McCorkle
;    http://www.piclist.com/techref/member/RHM-SSS-SC4/Scmds.htm
;*********************************************************************
;Check command received via USART rcv mode. # aborts current command

CmdProc	bcf	BrdyF		;clear byte ready flag
	movlw	"#"		;is char a "#" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 5		;no, continue
	bcf	Nrdy		;clear transfer flags
	bcf	LoByt
	clrf	TSS		;clear temp reg
	goto	Chf		;clear header flags and exit
	btfss	Hdr7		;is header 7 flag set? (Number Return)
	goto	H6		;no, test next header
	call	GetByt		;get data bytes
	btfsc	Nrdy		;check not ready
	return			;if set, get next data
	goto	Chf		;clear header flags and exit

H6	btfss	Hdr6		;header 6 flag set (@@P received)? 
	goto	H5		;no, test for next header

; @@Pc	Print Calibration Data in BCD
	movlw	"c"		;is char a "c" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	call	prtCC		;print Calibration Data
	call	prtCT
	goto	Prtn		;clear header flags and exit

; @@Pg	Print Interpolated Gain in BCD
	movlw	"g"		;is char a "g" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	call	prtIG		;print Interpolator Gain
	goto	Prtn		;clear header flags and exit

; @@Pi	Print Interpolator & Cal Data in BCD
	movlw	"i"		;is char an "i" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	call	prtIC		;print Interpolator & Cal Data
	goto	Prtn		;clear header flags and exit

; @@Ps	Print Status
	movlw	"s"		;is char an "s" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	call	prtSt
	goto	Prtn		;clear header flags and exit

; @@Pt	Print Calibration Time in BCD
	movlw	"t"		;is char a "t" ?
	call	RxChk
	btfss	Zflag
	goto	Chf		;no valid @@P command, exit
	call	prtCT		;print Calibration time
Prtn	call	TxCrLf		;send new line
	goto	Chf		;clear header flags and exit

H5	btfss	Hdr5		;header 5 flag set (@@D received)? 
	goto	H4		;no, test for next header

; @@Dc	Toggle Display Cal Values (Z,S) 
	movlw	"c"		;is char a "c" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x04		;toggle bit 2 
	goto	tdb

; @@Dd	Toggle Display 10-Digit Time Delay in BCD 
	movlw	"d"		;is char a "d" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x20		;toggle bit 5 
	goto	tdb

; @@Di	Toggle Display Corrected Start and Stop in BCD 
	movlw	"i"		;is char an "i" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x08		;toggle bit 3
	goto	tdb

; @@Dp	Toggle Display Peak Detector Values 
	movlw	"p"		;is char a "p" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x80		;toggle bit 7 
	goto	tdb

; @@Dr	Toggle Display Raw Interpolator Data in BCD 
	movlw	"r"		;is char an "r" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x02		;toggle bit 1
	goto	tdb

; @@Ds	Toggle Display Status Bits
	movlw	"s"		;is char an "s" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x40		;toggle bit 6
	goto	tdb

; @@Dt	Toggle Display TIC direct
	movlw	"t"		;is char a "t" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	movf	DMB,W
	xorlw	0x01		;toggle bit 0
	goto	tdb

; @@Dv	Toggle Display Interpolated Value
	movlw	"v"		;is char a "v" ?
	call	RxChk
	btfss	Zflag
	goto	Chf		;no valid @@D command, exit
	movf	DMB,W
	xorlw	0x10		;toggle bit 4 
tdb	movwf	DMB
	goto	Chf		;clear header flags and exit

H4	btfss	Hdr4		;is header 4 flag set? (@@C rcvd)
	goto	H3		;no, test for next header

; @@Cd	Disable Autocal Mode, Manual Cal  	
	movlw	"d"		;is char a "d" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bcf	ACM		;clear autocal flag
	goto	Chf		;clear header flags and exit

; @@Ce	Enable Autocal Mode
	movlw	"e"		;is char an "e" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bsf	ACM		;set autocal flag
	call	clrSC		;init peak detectors
	goto	Chf		;clear header flags and exit

; @@Cg	Set Interpolation Gain Value xxx HEX
	movlw	"g"		;is char a "g" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HIgain
	goto	G2B

; @@Ci	Set Inverted TXD polarity for TTL Comm
	movlw	"i"		;is char an "i" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 5		;no, next header
	bsf	ITX		;set ITX flag
	bsf	BAUDCTL,SCKP 	;set inverted transmit 
	bsf	DUF		;set DUF to store in EPROM		
	goto	Chf		;clear header flags and exit

; @@Cl	Enable Cal Offset Limiter
	movlw	"l"		;is char an "l" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	COLE
	goto	Chf		;clear header flags and exit

; @@Cn	Set Normal TXD polarity for RS-232 comm
	movlw	"n"		;is char an "n" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 5		;no, next header
	bcf	ITX		;clear ITX flag
	bcf	BAUDCTL,SCKP 	;set normal transmit 
	bsf	DUF		;set DUF to store in EPROM		
	goto	Chf		;clear header flags and exit

; @@Cp	Print Calibration Values, Gain, and Duration in HEX	
	movlw	"p"		;is char a "p" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 6		;no, next header
	call	hpCD		;print Calibration Data
	call	hpIG		;print Interpolation Gain 
	call	hpCT		;print Calibration Time
	call	hpCL		;print Cal Limiter Data
	goto	Prtn		;clear header flags and exit

; @@Cr	Reset Calibration Peak Detectors
	movlw	"r"		;is char an "r" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	call	clrSC		;init peak detectors
	goto	Chf		;clear header flags and exit

; @@Ct	Set Calibration Time Duration xxxx HEX
	movlw	"t"		;is char a "t" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	movlw	Hcal		;move indirect address to W
	goto	G2B	

; @@Cu	Disable Cal Offset Limiter
	movlw	"u"		;is char a "u" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bcf	COLE
	goto	Chf		;clear header flags and exit

; @@Cv	Set Cal Limit Variance x HEX
	movlw	"v"		;is char a "v" ?
	call	RxChk
	btfss	Zflag
	goto	Chf		;no valid @@C command, exit
	bsf	LoByt		;set LoByt for 1 byte
	movlw	CalOL		;move indirect address to W
G1B	movwf	Dadr		;store in Dadr
	movlw	0x01		;get 1 registers (2 bytes)
	goto	GBR

H3	btfss	Hdr3		;header 3 flag set (@@B received)? 
	goto	H2		;no, test for next header

; @@Bc	Set Ch B Center Value xxx HEX
	movlw	"c"		;is char a "c" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HibC
	goto	G2B

; @@Bs	Set Ch B Span Value xxx HEX
	movlw	"s"		;is char an "s" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HibS
	goto	G2B

; @@Bz	Set Ch B Zero Value	xxx HEX
	movlw	"z"		;is char a "z" ?
	call	RxChk
	btfss	Zflag
	goto	Chf		;no valid @@B command, exit
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HibZ
	goto	G2B

H2	btfss	Hdr2		;header 2 flag set (@@A received)? 
	goto	H1		;no, test for next header

; @@Ac	Set Ch A Center Value xxx HEX
	movlw	"c"		;is char a "c" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HiaC
	goto	G2B

; @@As	Set Ch A Span Value	xxx HEX
	movlw	"s"		;is char an "s" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HiaS
	goto	G2B

; @@Az	Set Ch A Zero Value	xxx HEX
	movlw	"z"		;is char a "z" ?
	call	RxChk
	btfss	Zflag
	goto	Chf		;no valid @@A command, exit
	bsf	LoByt		;set LoByt for 1 byte
	movlw	HiaZ
G2B	movwf	Dadr		;store in Dadr
	movlw	0x02		;get 2 registers (4 bytes)
GBR	movwf	BytCt
	clrf	TSS
	bsf	Nrdy		;set not ready flag
	bsf	Hdr7		;set Header 7 flag
	return

H1	btfss	Hdr1		;header 1 flag set (@@ received)? 
	goto	H0		;no, test for header start

; @@A	Interpolator A Calibration 
	movlw	"A"		;is char an "A" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	Hdr2		;set Header 2 flag
	return

; @@B	Interpolator B Calibration 
	movlw	"B"		;is char a "B" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	Hdr3		;set Header 3 flag
	return

; @@C	Calibration Commands
	movlw	"C"		;is char a "C" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	Hdr4		;set Header 4 flag
	return

; @@D	Toggle Display Bits 
	movlw	"D"		;is char a "D" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	Hdr5		;set Header 5 flag
	return

; @@P	Print value to serial TX port 
	movlw	"P"		;is char a "P" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	Hdr6		;yes, set header 6 flag
	return

; @@R	Run Command - Reset and Enable Counter 
	movlw	"R"		;is char an "R" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 4		;no, next header
	bcf	HoldF		;clear hold flag
	call	clrTC		;reset counter
	goto	Chf		;clear header flags and exit

; @@S	Stop Command - Hold Counter 
	movlw	"S"		;is char an "S" ?
	call	RxChk
	btfss	Zflag
	goto	$ + 3		;no, next header
	bsf	HoldF
	goto	Chf		;clear header flags and exit

; @@U	Save data to EEPROM 
	movlw	"U"		;is char a "U" ?
	call	RxChk
	btfss	Zflag
	goto	Chf		;no valid @@ command, exit
	bsf	DUF		;set data update flag
	goto	Chf		;clear header flags and exit

H0	movlw	"@"		;is char a "@" ?
	call	RxChk
	btfsc	Zflag
	goto	$ + 4
	btfsc	Hdr0		;If @ rcvd but second char not @
	bcf	Hdr0		;clear header 0 flag and
	return			;exit
	btfsc	Hdr0		;was header 0 flag set?
	goto	$ + 3
	bsf	Hdr0		;no, set header 0 flag (@ rcvd)
	return			;exit
	bsf	Hdr1		;yes, set header 1 flag (@@ rcvd)	
	return			;exit

Chf	clrf	cHdr0		;clear header flags
	return

;*********************************************************************
; Check Received byte for match to character in W on call
; Set Zflag if match (used in background so use btmp)

RxChk	movwf	btmp
	movf	RX_BUF,W		
	subwf	btmp,W
	return

;*********************************************************************
; Hex Convert - convert ASCII 0-9 or A-F to number 0-15 in W
; converts small a-f to caps by clearing 32 bit first 
; so either caps or smalls for a-f work.
;(used in background, so use btmp) 

HexC	movf	RX_BUF,W	;get the byte
	movwf	btmp
	btfss	btmp,6		;number or char?
	goto	$ + 4
	bcf	btmp,5		;clear 32 bit (convert small to caps)
	movlw	D'7'		;subtract 55 to convert A-F to values 10-15
	subwf	btmp
	movlw	D'48'		;subtract 48 to value
	subwf	btmp
	movf	btmp,W
	andlw	0x0f		;discard high bits (if wrong char)
	return

;*********************************************************************
;Get Byte - fetch number of registers in BytCt and store
;at address starting at Dadr, clear Nrdy when finished.
;Revised to use temp storage until full byte is received.
;Clear TSS and set LoByt before call to fetch 1/2 byte. 

GetByt	call	HexC		;convert input to value
	btfsc	LoByt		;if LoByte set, 
	goto	$ + 5
	movwf	TSS		;load in temp storage	
	swapf	TSS		;move to hi nibble
	bsf	LoByt		;set low byte flag
	return
	addwf	TSS		;add to value in storage	
	movf	Dadr,W		;get storage address
	movwf	FSR
	movf	TSS,W		;move data to W
	movwf	INDF		;place in register	
	clrf	TSS		;clear temp reg
	bcf	LoByt		;clear low byte flag
	decfsz	BytCt		;got all the registers?
	goto	$ + 3
	bcf	Nrdy		;clear not ready flag	
	return
	incf	FSR		;point to next data
	movf	FSR,W
	movwf	Dadr		;and store in Dadr
	return

;*********************************************************************
;			  EEPROM Routines
;*********************************************************************
;			EEPROM Memory Usage

;  Address		  Function		Registers
;   0 - 1 	16 bit Ch A zero		HiaZ,LiaZ
;   2 - 3	16 bit Ch A Center		HiaC,LiaC
;   4 - 5	16 bit Ch A span		HiaS,LiaS
;   6 - 7	16 bit Ch B zero		HibZ,LibZ
;   8 - 9	16 bit Ch A Center		HibC,LibC
;   A - B	16 bit Ch B span		HibS,LibS
;   C - D	16 bit Interpolator Gain	HIgain,LIgain
;   E - F	16 bit Cal Duration (Samples)	Hcal,Lcal	
;   10		 8-bit Offset Limit Variance	CalOL
;   11		 8 bit Display Mode Bits	DMB
;   12		 8 bit Command Mode Bits	CMB

;*********************************************************************
;While EEPROM's offer unlimited reads, the number of write cycles 
;is limited to 1M before errors occur due to memory cell failure.
;To maximize EEPROM life, this program performs EEPROM writes once
;per hour giving a predicted life of > 100 years @ 25C. More frequent 
;writes should be avoided. Predicted EEPROM life updating once per
;minute is < 2 years @ 25C !! 
;
;Reads EEPROM data byte pointed to by address in Padr on call
;Returns with data in W

PromRd	movf	Padr,W 		; Address to read in Padr
	bcf	STATUS,RP1 	; select Bank 1
	bsf	STATUS,RP0
	movwf	EEADR		; store read address
	bcf	EECON1,EEPGD 	; Point to Data memory
	bsf	EECON1,RD 	; EE Read
	movf	EEDAT,W 	; W contains EEDATA
	bcf	STATUS,RP0	; back to Bank 0
	return

;*********************************************************************
;Writes data byte pointed to by FSR to EEPROM address pointed to by
;Padr on call. Use only during ISR unless interrupts disabled first!

PromWr	movf	INDF,W		;Data Value to write
	bcf	STATUS,RP1 	;select Bank 1
	bsf	STATUS,RP0 	
	movwf	EEDAT		;store data value
	bcf	STATUS,RP0	;select Bank 0
	movf	Padr,W		;Data Address to write
	bsf	STATUS,RP0 	;select Bank 1
	movwf	EEADR		;store address
	bcf	EECON1,EEPGD	;Point to DATA memory
	bsf	EECON1,WREN 	;Enable writes
	movlw	0x55
	movwf	EECON2		;Write 55h
	movlw	0xaa
	movwf	EECON2		;Write AAh
	bsf	EECON1,WR 	;Set WR bit to begin write
	bcf	EECON1,WREN 	;Disable writes
	btfsc	EECON1,WR 	;Wait for write
	goto	$ - 1 		;to complete
	bcf	STATUS,RP0	;back to bank 0
	return

;*********************************************************************
; Update PROM with 19 registers of current data

D2prom	bcf	DUF		;clear update flag
	clrf	Padr		;move start address to Padr
	movlw	HiaZ		;load addr of HiaZ as indir addr
	movwf	FSR
	movlw	0x13		;write 19 bytes at HiaZ -> HiaZ + 18
	movwf	temp
pWr	call	PromWr		;Write data
	incf	Padr		;point to next prom address
	incf	FSR		;point to next register
	decfsz	temp		;Done?
	goto	pWr		;no, get next word
	return

;*********************************************************************
; Initialize internal registers from PROM

InitCon	clrf	PORTA		;clear port output latches
	clrf	PORTC
	clrf	INTCON		;disable all interrupts for now
	clrf	T1CON 		;Stop Timer1
	movlw	0x07		;set PORTA pins as digital (not comparator inputs)
	movwf	CMCON0
	bcf CMCON1,T1GSS	;disable external gate on TMR1
	clrwdt			;clear watch dog & prescaler

;initialize bank 1 control regs

	bsf	STATUS,RP0	;select bank 1
	movlw	0x71		;select 8MHz internal clock for PIC
	movwf	OSCCON
;	movlw	0x00		;put the cal value in OSCTUNE
;	movwf 	OSCTUNE 	;to calibrate oscillator
	movlw	0x47		;int on rising edge of RA2
	movwf	OPTION_REG
	movlw	0x03		;PORTA bits 0,1 as analog, 2-5 as digital
	movwf	ANSEL
	movlw	0x50		;set A/D Clock = 16Tosc
	movwf	ADCON1
	movlw	0x2f		;PORTA bits 0,1 as analog, 2,3,5 as digital in, 4 as digital out
	movwf	TRISA		
	movlw	0x3f		;set PORTC bits 0-3 as inputs, 2 serial pins
	movwf	TRISC
	movlw	0x01		;enable interrupt on TMR1 OF
	movwf	PIE1
	movlw	0x08		;IOC on PORTA bit 3 
	movwf	IOCA		

;back to bank 0

	bcf	STATUS,RP0	;select bank 0
	movlw	0x81		;Analog on A0, Right Justified, Vdd Reference
	movwf	ADCON0		
	bsf	TXSTA,TXEN	;enable USART xmt (async mode)
	bsf	TXSTA,BRGH	;set USART hi speed mode
	movlw	D'51'		;set async rate at 9600 baud (51. for 8 MHz int, BRGH=1)
	movwf	SPBRG		
	bcf	BAUDCTL,SCKP 	;normal transmit polarity for RS-232 comm
	bsf	RCSTA,CREN	;enable USART rcv (async mode)
	bsf	RCSTA,SPEN	;enable serial port
	movlw	0x06		;set TMR1 as f_osc/1, async, no gate, ext clk
	movwf	T1CON
	clrf	TMR1L		;clear TMR1
	clrf	TMR1H
	clrf	HOFct		;clear overflow counter
	clrf	LOFct
	clrf	PIR1 		;clear peripheral interrupt flags
	bsf	INTCON,PEIE	;enable interrupt on TMR1 OF
	bsf	INTCON,INTE	;enable interrupt on RA2
	bsf	INTCON,RAIE	;enable interrupt on PORTA change
	clrf	flg0		;clear all flags 
	clrf	flg1
	clrf	cHdr0		;clear comm headers 
	clrw			;Read 19 registers from PROM 
	movwf	Padr		;move start address to Padr
	movlw	HiaZ		;load addr of HiaZ as indir addr
	movwf	FSR
	movlw	0x13		;get 19 bytes at HiaZ -> HiaZ + 18
	movwf	temp
	call	PromRd		;Read data
	movwf	INDF		;store data in register
	incf	Padr		;point to next prom address
	incf	FSR			;point to next register
	decfsz	temp		;Done?
	goto	$ - 5		;no, get next word
	btfsc	ITX	 	;invert transmit polarity if TTL comm
	bsf	BAUDCTL,SCKP
	movf	HiaC,W		;init peak detectors to center
	movwf	HiaM
	movwf	HiaO
	movf	LiaC,W
	movwf	LiaM
	movwf	LiaO
	movf	HibC,W
	movwf	HibM
	movwf	HibO
	movf	LibC,W
	movwf	LibM
	movwf	LibO
	movlw	0x01		;load cal timer with 300
	movwf	HcalT		;(5 min delay to stabilize)
	movlw	0x2c
	movwf	LcalT
	bsf	FpF		;set first cal flag
	movlw	0x04		;load cal limit timer
	bsf	STATUS,RP0
	movwf	CalLT
	bcf	STATUS,RP0
	bcf	OLEF		;clear limit enable flag
	bcf	adCH		;select ADC channel 0
	bcf	A1rd		;clear Ch 1 read
	movf	PORTA,W		;clear RA IOC flag
	bcf	RACF
	return

;*********************************************************************
	de "PICTIC II Ver 1.00, Copyright © Richard H McCorkle 2010"
	de " "
	END	
