;Software for PicoC Plus/Super
;Author Jon Drury
;LCD interface uses 4bit mode
;based on reg set G,H,L - high, mid, low
;uses INT0/1 to stop and start counting
;With period timer
;With Sig Gen		29/8/11
;With Pulse gen		16/2/12
;stripped scrns down to suit 16x2 lcd 12/9/11

.EQU	True=0xff
.EQU	False=00
;**
;.EQU	PB=True		;If original PicoC PCB then PB=True
.EQU	PB=False	;Else for 110687 PB=False
;.EQU	T4313=True
.EQU	T4313=False


.NOLIST
.IF		T4313
.INCLUDE	"tn4313def.inc"
.ELSE
.INCLUDE	"tn2313def.inc"
.ENDIF

.INCLUDE	"Reg defs.inc"
.INCLUDE	"MacLib.inc"
.LIST

.EQU	CHG=150			;defines long press - reduce for quicker response

;***   PROGRAM START   ***

;Interrupt vectors
	RJMP		main	;1 Power-on/rst
	RJMP		Int_sub	;2 Ext INT0
	RJMP		Int_sub	;3 Ext INT1
	RJMP		main	;4 Timer/Counter1 Capture Event
	RJMP		main	;5 Timer/Counter1 Compare Match A
	RJMP		ovf_sub	;6 Timer/Counter1 Overflow
	RJMP		main	;7 Timer/Counter0 Overflow
	RJMP		main	;8 USART, Rx Complete
	RJMP		main	;9 USART Data Register Empty	
	RJMP		main	;10 USART Data Register Empty
	RJMP		main	;11 Analog Comparator
	RJMP		main	;12 PCint0
	RJMP		main	;13 OC1
	RJMP		main	;14 OC0A
	RJMP		main	;15 OC0B
	RJMP		main	;16 USI Start Condition
	RJMP		main	;17 USI Overflo
	RJMP		main	;18 ERDY
	RJMP		main	;19 Watchdog Timer Overflow
	RJMP		main	;20 PCInt1
	RJMP		main	;21 PCint2


;Interrupt service routine for INT0
;Note 'C' is used to control number of Periods to be timed
Int_sub:IN		A,SREG		;save SREG
		PUSH	A
		TST		icnt		;if icnt=0 use int to start count
		BREQ	i0strt

		CP		icnt,C		;else check for end of count
		BRCS	Int0x

		STOP_COUNT			;is end of count
		RJMP	Int0x

i0strt:	NOP					;keeps start and stop timing equal
		NOP
		START_COUNT			;If icnt=0

Int0x:	INC		icnt
		POP		A			;restore SREG
		OUT		SREG,A
		RETI

;ISR T1 overflow
Ovf_sub:IN		A,SREG
		PUSH	A
		MOV		icnt,D		;terminate measurement cycle
		STS		ovfg,D		;set overflow flag to non-zero
		POP		A			;restore SREG
		OUT		SREG,A
		RETI

;*** Initialisation

main:	CLI					;disable interrupts
		CLR		A
		OUT		CLKPR,A		;Config Clockdiv=1

;set up stack pointer for SRs
		LDI		A,low(Ramend)
		OUT		SPL,A

.IF		T4313
;		LDI		A,high(Ramend)	;** Only for 4313
;		OUT		SPH,A
.ENDIF

;set PORT direction
		SER		A	
		OUT		DDRB,A			;all o/ps for lcd

.IF	PB
		LDI		A,0b00110000	;OC0B and PD4 Out
.ELSE
		LDI		A,0b00100000	;OC0B Out, PD4/T0 i/p
.ENDIF
		OUT		DDRD,A
		LDI		A,0b00010001
		OUT		PORTD,A			;activate PU on sw & start 555

;initialise T0
		SER		A
		OUT		OCR0B,A			;set o/p compare register
		LDI		A,0b00010000
		OUT		TCCR0A,A		;toggle OC0B

;initialise T1 as counter
		LDI		A,0b01000000	;toggle OC1A
		OUT		TCCR1A,A
		LDI		A,0b00000110	;clock on falling edge of T1 input
		OUT		TCCR1B,A

;Set up Interrupt mode
		LDI		A,0x0f			;use rising edge trigger on INT0/INT1
		OUT		MCUCR,A
		LDI		A,0x80			;enable T1 overflow ints
		OUT		TIMSK,A

;initialise LCD
		RCALL	lcd_init

;**S1 - Splash screen and select mode
		DISPLAY	Scrn1		;Splash
		CLR		A
		OUT		GIMSK,A		;DISable INT0/1

		RCALL	dly1s
		RCALL	dly1s
		RCALL	dly1s

S1a:	DISPLAY	Scrn1a		;Select mode

ssrs:	CLR		E			;used as mode index

sslp:	MOV		A,E			;record no.
		GET_RECORD	modtab,4

		LPM		XL,Z+		;this is the address of the routine
		LPM		XH,Z		;for jump later

		MOV		ZH,H		;meantime deal with the msg
		MOV		ZL,L		;which has already been loaded into HL
		RCALL	nxch3		;display corresponding msg

		RCALL	wait_s1
		CPI		B,chg		;test press length
		BRCC	sacpt		;if long go accept
		INC		E			;else bump index
.IF	PB
		CPI		E,5			;test for end of table
.ELSE
		CPI		E,6
.ENDIF
		BRCS	sslp		;is OK
		RJMP	ssrs		;else go reset index

sacpt:	MOVW	Z,X			;recover address from X and put it in Z
		IJMP				;go there

;**S9 - Cap calibration
s9:	;	DISPLAY	scrn9
		LDIZ	2*scrn1f+1	;saves a few bytes by re-using scrn1f
		RCALL	lcd_msg

		RCALL	dly1s

		LDI		A,'J'
		STS		calfg,A
		RJMP	s2c

;	set flag to force calibration

;run straight into S2 with cal flag set
;NOT quite - need to clear flag on normal S2 entry
;if flag set then bypass check for blank eep

;**S2 - Capacitance measurement
S2:		LDI		A,0xff
		STS		calfg,A

s2c:	DISPLAY	scrn2		;multiplier

s2be:	LDI		A,(1<<INT1)
		OUT		GIMSK,A		;enable INT1

s20:	CLR		A			;initialise index 0-2
s21:	STS		Ndx,A
							;get the loop count and ref val
		GET_RECORD	RefTab,4
		STS		Nlp,L		;save multiplier - used by meas
		STS		Rval2,H
		LPM		A,Z+
		STS		Rval1,A
		LPM		A,Z+
		STS		Rval0,A		;store Ref C val

		WRNUM8	LN1+11		;show multiplier

s2chs:	RCALL	wait_s1
		CPI		B,CHG		;test press length
		BRCC	s2cal		;if long go accept and cal

		LDS		A,Ndx		;else bump index
		INC		A
		CPI		A,3	;4			;check Ndx limit=3
		BRCS	s21			;Ok if <4
		RJMP	s20			;else reset index

s2cal:	DISPLAY	Scrn2a		;check zero
		RCALL	dly1s
		RCALL	meas		;get period

		STS		pC0_0,L		;and store zero period
		STS		pC0_1,H
		STS		pC0_2,G

;		LDI		A,LN4		;Diagnostic
;		RCALL	lcd_cmd		;show result
;		LDS		A,pC0_2
;		RCALL	lcd_hex
;		LDS		A,pC0_1
;;		RCALL	lcd_hex
;		LDS		A,pC0_0
;		RCALL	lcd_hex

		LDS		A,ndx		;get index
		MOV		temp,A
		ADD		A,A
		ADD		A,temp		;3 x ndx

		LDIZ	pCrg1_0		;set up Z for rdeep
		CLR		temp
		ADD		ZL,A		;add offset to base
		ADC		ZH,temp
		MOVW	X,Z			;copy Z to X for wrxep

		LDS		A,calfg
		CPI		A,'J'
		BREQ	docal

		RCALL	rdeep
		STS		pCref0,A	;else store in pCref
		RCALL	rdeep
		STS		pCref1,A
		RCALL	rdeep
		STS		pCref2,A
		CPI		A,0xff		;do cal if 1st byte =0xff
		BRNE	meslp		;else start measuring		

;do calibration
docal:	DISPLAY	Scrn3		;measure Cref
		RCALL	Wait_s1

		RCALL	meas

		RCALL	adjz		;Period = Period - Period_C0

		STS		pCref0,L
		STS		pCref1,H
		STS		pCref2,G	;store Ref period

		MOV		A,L			;and write to eep
		RCALL	wrxep
		MOV		A,H
		RCALL	wrxep
		MOV		A,G
		RCALL	wrxep		;continue to S3

;**S3 now loop doing measurements
meslp:	DISPLAY	scrn4

mesl2:	LDS		L,Nlp		;get No. of loops
		WRNUM8	LN1+11		;show multiplier	

		RCALL	bbon
		RCALL	meas		;get reading
		TST		E
		BRNE	s22			;skip rest if overflow

		RCALL	adjz		;adjust for zero,result in GHL

		LDS		A0,Rval0
		LDS		A1,Rval1
		LDS		A2,Rval2
	
		MOV		B0,L		;and multiplier with adjusted reading
		MOV		B1,H
		MOV		B2,G

		RCALL	mul24		;do 24 x 24 bit multiplication

		LDS		A0,pCref0
		LDS		A1,pCref1
		LDS		A2,pCref2	;get Cref period into divisor

		RCALL	dv24		;Cx = (Px x Cf)/Pf 

		MOV		L,B0
		MOV		H,B1
		MOV		G,B2		;move result into GHL for conversion

;		LDS		C,Ndx		;set no. dps
;		DEC		C
;		LDI		B,8			;tot n dig
;		SUB		B,C			;pre-dp
;		WRNUM24	LN2+4

		RCALL	cnv7b		;convert
		LDI		A,LN2+4		;position
		LDS		C,Ndx		;set no. dps
		LDI		B,8			;tot n dig
		SUB		B,C			;pre-dp
		RCALL	wrnumb		;display
		DISPX	spf			;add pF

s22:	SBIS	PinD,s1
		RJMP	s23			;check switch
		RCALL	bbof
		RJMP	mesl2		;repeat if no press

S23:	RCALL	wait_s1		;check length if press
		RJMP	s1a			;else back to select

;**S4 Display period
S4:		Display scrn5
		LDI		A,(1<<INT0)
		OUT		GIMSK,A		;enable INT0

S40:	CLR		A			;initialise ndx & nlp
		STS		Ndx,A
		LDI		A,1
		STS		Nlp,A

s41:	SBIS	PinD,s1		;check for press
		RJMP	s4chg
	
s42:	LDS		L,Nlp		;get No. of loops
		WRNUM8	LN1+11		;show multiplier

		RCALL	gper		;get period
		LDS		A,ovfg		;check for overflow
		TST		A
		BRNE	s44

		RCALL	cnv7b		;convert
		LDI		A,LN2+4		;position
		LDS		C,Ndx		;no. dps
		INC		C
		LDI		B,8			;tot n dig
		SUB		B,C			;num pre dp in B - C varies!
		RCALL	wrnumb		;display
		DISPX	scrn5a		;add units

		RCALL	bbof
		RJMP	s41

S44:	DISPX	sover		;overflow
		RJMP	s41

s4chg:	RCALL	wait_s1
		CPI		B,CHG		;test press length
		BRCC	s43			;if long go select
		
		LDS		A,Ndx		;else bump index
		INC		A
		CPI		A,3			;check Ndx limit=2
		BRCS	stx
		CLR		A			;reset if >=3

stx:	STS		Ndx,A		;else update Ndx and get new value from tab
		LDIZ	Reftab*2	;point to table - table contains Cref values as well
		ADD		A,A
		ADD		A,A			;* 4
		ADD		ZL,A

		LPM		C,Z			;get multiplier
		STS		Nlp,C		;save a copy for later	
		RJMP	s41			;start again	

s43:	RJMP	s1a			;Exit to select mode

;get period - SR
gper:	RCALL	bbon
		CLR		A
		STS		ovfg,A
		
		RCALL	MeasA

		MOV		temp,A		;quick save

;		LDS		A,ovfg		;check for overflow
;		TST		A
;		BRNE	s44
		
		MOV		A,temp		;reduce 25 bit to 24bit by shifting OCOb into L - 0.1uS
		ROR		A			;lsb(OC0b) into cry
		ROR		L			;and shift old L - NB lose top bit
		RET

;**S5  Frequency generator
S5:	;	DISPLAY	Scrn6
		LDIZ	2*scrn1e+1	;saves a few bytes by re-using scrn1e
		RCALL	lcd_msg

		LDI		A,3			;set to default index
		RJMP	setf
		RCALL	Wait_s1

Stz:	CLR		A			;use Ftab to set frequency 
SetF:	STS		Ndx,A

		LDIZ	Ftab
		MOV		B,A			;copy A
		ADD		A,A			;x2
		ADD		A,B			;x3
		ADD		A,A			;x6
		CLR		B
		ADD		ZL,A		;add offset to table

		RCALL	rdeep
		TST		A
		BREQ	stz			;found end of table - reset index
		ORI		A,0x08		;0x08=CTC mode, OR in prescaler from table
		OUT		TCCR1B,A	;else set CTC prescaler			

		RCALL	rdeep
		OUT		OCR1AH,A	;set output compare register H	
		RCALL	rdeep		;get val for L		
		OUT		OCR1AL,A	;and set it

		RCALL	lde2hl		;get freq from table
		PUSH	ZL
		WRNUM16	LN2+7,5,0	;and display it

		POP		ZL
		RCALL	rdeep		;get Units chr
		RCALL	lcd_data	;display it
;		DISPX	sHz			;and 'Hz'
		LDIZ	2*skhz+1
		RCALL	nxch3

		RCALL	Wait_s1		;wait keypress, then inc Freq index
		CPI		B,CHG		;test press length
		BRCC	s43			;if long go select mode

		LDS		A,Ndx		;else bump index for next table entry
		INC		A
		RJMP	SetF		;and go set Frequency

;** S6  Programmable pulse gen
;uses table in flash - Ptab
S6:	;	SETCSR	LN1
		LDIZ	2*scrn1g+1
		RCALL	lcd_msg	

		CLR		A
		OUT		TCCR1A,A	;set T1 for normal port operation
		OUT		TCCR1B,A
		LDIZ	2*Ptab		;point to table start


;SET pulse primer block 
pset:	SBI		PortB,PB3	;SET o/p			;2			
		LPM		B,Z+		;get pulse count	;3
		TST		B								;1
		BREQ	prst		;restart if zero	;1 or 2 on jump (8T Total)	
		NOP										;1	
		RJMP	pplp		;loop for count		;2
										;Total  10T
;Pulse train restart
prst:	LDIZ	2*Ptab		;point to table start	;2
		LPM		B,Z+		;get first val			;3
		NOP					;must not be zero		;1	
		NOP											;1
											;Total	7T + 8T it took to get here from zero table terminator
;SET pulse timer loop
pplp:	DEC		B								;1							
		NOP										;1						
		NOP										;1
		BRNE	pplp							;2 on jump else 1
		NOP										;1 on exit to compensate for non-jump

;CLR pulse	primer
pclr:	CBI		PortB,PB3	;CLR o/p			;2
		LPM		B,Z+							;3
		NOP										;1

		SBIS	PinD,S1
		RJMP	s1a		;whilst testing S1

;		NOP										;1	9T to compensate for one extra in pmlp on JMP
;		NOP										;1
		NOP										;1
										;Total  9T
;CLR pulse timer loop
pmlp:	DEC		B			;5T per loop 6T on exit
		NOP
		NOP
		BRNE	pmlp
				
		RJMP	pset		;next SET seg

;** S7 - Frequency measurement
S7:		DISPLAY	scrn7
		LDI		A,07
		OUT		TCCR0B,A
s7s:	CLR		A			;initialise Ndx

s71:	STS		Ndx,A		;else update Ndx and get new value from tab

		GET_RECORD	Gtab,10;leaves gate time binary in HL
							;and Z pointing to msg
		RCALL	nxch3		;display corresponding msg
		
		STS		GtL,L		;save gate time for reload
		STS		GtH,H	

s74:	RCALL	bbon

		LDS		L,GtL		;reload gate time
		LDS		H,GtH

		CLI
		STOP_COUNT
		CLR_COUNT

		LDI		A,07
		OUT		TCCR0B,A	;start count external

		LDI		count1,100	;precision 40us delay starts here
dlx:	MOV		XH,H
		MOV		XL,L		;Delay for X

dx2:	SBIS	PinD,S1
		RJMP	tss1		;whilst testing S1
		SBIW	X,1
		NOP
		NOP
		BRNE	dx2
		DEC		count1
		BRNE	dlx

		LDI		count1,132	;delay trim	
d518:	DEC		count1
		BRNE	d518
		NOP
		NOP

		STOP_COUNT

		RCALL	mes2		;get reading
		RCALL	measB

		RCALL	cnv7b		;convert
		LDI		A,LN2+4		;position
		LDS		C,Ndx		;no. dps
		INC		C
		LDI		B,8			;tot n dig
		SUB		B,C			;num pre dp in B - C varies
		RCALL	wrnumb		;display

		LDIZ	2*skHz
		RCALL	nxch3		;add 'kHz' to lcd

		RCALL	bbof		;remove blob

		RJMP	s74

tss1:	RCALL	wait_s1
		CPI		B,CHG		;test press length
		BRCC	s77			;if long go select mode

		LDS		A,Ndx		;else bump index
		INC		A
		CPI		A,3			;check Ndx limit=2
		BRCS	s73

		RJMP	s7s			;reset if >=4

s73:	RJMP	s71
s77:	RJMP	s1a

;show blob
bbon:	SETCSR	LN1+15
		LDI		A,'*'
		RCALL	lcd_data
		RCALL	dlyts
		RET

;remove blob
bbof:	SETCSR	LN1+15
		LDI		A,0x20
		RCALL	lcd_data
		RCALL	dlyts
		RET

;Period measurement routine
;interrupt driven version
;on entry Nlp contains no of sample loops 
meas:	RCALL	measA
		RCALL	measB
		RET

;PartA of meas - get result in G,H,A and L
measA:	STOP_COUNT
		CLR_COUNT
		CLR		A
		STS		ovfg,A		;clear overflow flag
		
		CLR		icnt		;refresh 'C' from Nlp and calc 'D'
		LDS		C,Nlp		;set up loop count for ISR
		MOV		D,C
		INC		D			;set up flag for watn
		LDI		A,0xC0
		OUT		GIFR,A		;clear pending ext ints
		SEI					;with icnt=0

watn:	CP		icnt,D
		BRCS	watn		;sit and wait while interrupts are gating T0/T1

		CLI					;then stop interrupts

		RCALL	dly50u

mes2:	IN		H,TCNT1L	;and gather data	
		IN		G,TCNT1H

		LDI		A,0x30		;'0' - ASCII
		SBIC	PIND,PD5	;test OC0B	
		INC		A			;'1'

		IN		L,TCNT0

		RET

;Part B of Meas
;combine to 24bit - right justifed 
MeasB:	ROR		A			;lsb(OC0b) into cry
		ROL		H			;shift cry back into H
		ROL		G			;and shift old H - NB lose top bit

		CLR		E			;clear overflow marker
		BRCS	sov			;check for overflow from ROL G
		LDS		A,ovfg		;from overflow int
		TST		A
		BRNE	sov			
nov:	RET

sov:	DISPX	sover		;there is overflow
		SER		E			;set overflow marker
		RET

;Adjust reading (G,H,L) for zero value - by subtraction
adjz:	LDS		A,pC0_0	
		SUB		L,A
		LDS		A,pC0_1	
		SBC		H,A
		LDS		A,pC0_2		
		SBC		G,A

		BRCC	adr		;Not negative result
		CLR		L		;Else is negative
		CLR		H		;COMPROMISE - set result to zero
		CLR		G

adr:	RET

;Wait for switch to be pressed and debounce.
;Returns with length of time pressed in 'B'
Wait_s1:CLR		B			;loop counter

ws2:	SBIS	PinD,s1
		RJMP	swlo
		RJMP	ws2			;wait for switch to be pressed

swlo:	RCALL	dly5m		;wait 5mS
		SBIC	pinD,s1
		RJMP	swhi
		INC		B
		BRNE	swlo		;dead end at 255
		DEC		B
		RJMP	swlo		;wait for switch to be released

swhi:	CPI		B,4			;do 4 loops to make sure sw has stopped bouncing
		BRCS	Wait_s1		;if counter has not reached target, loop back to start

		RET					;else return
		
;Write number to display in format xx.yy
;on entry digits are in nbuf, A points to LCD position
;B is max pre-dp figures and C post-dp
;includes leading zero supression
wrnumb:	RCALL	lcd_cmd	;csr position	

		LDIZ	nbuf	;point Z to data
pdp:	LD		A,z+	;get char
		TST		A		;test for zero
		BRNE	dspw	;output non-zero

		LDI		A,0x20	;else do space
		RCALL	lcd_data
		DEC		B
		CPI		B,1
		BRNE	pdp		;repeat until non-zero OR only one digit left

dsp:	LD		A,Z+	;and resume
dspw:	RCALL	doutn	;and output
		DJNZ	dsp		;loop for all pre-dp

		TST		C
		BREQ	dunp	;exit if C=0

		LDI		A,'.'	;else show dp
		RCALL	lcd_data	

ppp:	LD		A,z+	;get next digit
		RCALL	doutn
		DEC		C
		BRNE	ppp		;loop for all post-dp

dunp:	RET

;read a byte from eep into A. Z points to 1st locn.
rdeep:	OUT		EEAR,ZL
		SBI		EECR,EERE	;read enable
		IN		A,EEDR
		INC		ZL
		RET

;write 'A' to eep. X points to location
wrxep:	SBIC	EECR,EEWE	;wait for write enable to clear
		RJMP	wrxep
		LDI		B,(0<<EEPM1)|(0<<EEPM0) ;set programming mode
		OUT		EECR,B
		OUT		EEAR,XL	;set address
		OUT		EEDR,A		;set data
		SBI		EECR,EEMWE	;master write
		SBI		EECR,EEWE	;write strobe
		INC		XL			;bump pointer
		RET

;convert BCD to ASCII and display
doutn:	ANDI	A,0x0f
		ORI		A,0x30		;form number
dout:	RJMP	lcd_data	;subr provides ret

;16 bit binary to bcd routine
;choose entry point to suit number of digits reqd
;on entry HL contains no for convn.  Result stored at nbuf using X.
cnv5b:	LDIX	nbuf
cnv5d:	LDIDE	10000	;10,000
		RCALL	rsub
cnv4d:	LDIDE	1000	;1,000
		RCALL	rsub
cnv3d:	LDIDE	100		;100
		RCALL	rsub
cnv2d:	LDIDE	10		;10
		RCALL	rsub
		ST		X,L		;units
		RET

rsub:
b2d:	CLR		A		;used as loop counter
b2da:	CP		H,D
		BRCS	b2dc	;MSB smaller than dec
		BRNE	b2db	;MSB bigger than dec
		CP		L,E
		BRCS	b2dc
b2db:	SUB		L,E		;subtract LSD dec
		SBC		H,D		;subtract MSD dec
		INC		A		;bump digit counter
		RJMP	b2da	;and repeat until no<dec
b2dc:	ST		X+,A
		RET

;convert 24 bit number.  Number is G,H,L and subtractant is C,D,E
;this is just a 24bit version of above sub-routine
cnv7b:	LDIX	nbuf
		LDI		C,0x98
		LDIDE	0x9680
		RCALL	dsub
		LDI		C,0x0f
		LDIDE	0x4240
		RCALL	dsub
cnv7d:	LDI		C,01
		LDIDE	0x86A0
		RCALL	dsub
cnv57d:	CLR		C
		LDIDE	10000
		RCALL	dsub
		RJMP	cnv4d

dsub:	CLR		temp	;used as loop counter
dsba:	CP		G,C
		BRCS	dsbc
		BRNE	dsbb
		CP		H,D
		BRCS	dsbc
		BRNE	dsbb
		CP		L,E
		BRCS	dsbc
dsbb:	SUB		L,E		;subtract LSD dec
		SBC		H,D		;subtract MSD dec
		SBC		G,C
		INC		temp		;bump digit counter
		RJMP	dsba	;and repeat until no<dec

dsbc:	ST		X+,temp
		RET

;read 2 bytes from eep into HL
lde2hl:	RCALL	rdeep
		MOV		H,A
		RCALL	rdeep
		MOV		L,A
		RET

;Subroutine to write message on LCD
lcd_msg:RCALL	lcd_clear	;clear display
nxch3:	LPM  	A,z+		;loads byte into A
		SBRS	A,7			;test bit7
		RJMP	stxt		;if clr is text

		RCALL	lcd_cmd		;else set csr
		RJMP	nxch3		;and do next byte

stxt:	TST	A				;test for NUL
		BREQ	dunlcd		;end if nul
		RCALL 	lcd_data    ;send the char
		RJMP 	nxch3       ;loop back
         
dunlcd: RET

;send a data byte to the LCD
lcd_data:
			mov 	temp, A		;save data for 2nd nibble
    	    andi 	A, 0xf0		;clear bottom bits
        	sbr 	A, 1<<1		;set RS
        	out 	pLCD, A    ;output top nibble
        	rcall 	lcd_en;		;Enable-Routine
                            ;2nd Nibble
        	swap 	temp		;get lower half of original data
			andi 	temp,0xf0	;clear bottom bits 
        	sbr 	temp, 1<<1  ;set RS
	        out 	pLCD, temp 	;output data
    	    rcall 	lcd_en		;Enable

        	rcall 	dly50u      ;Delay 50uSec
			ret                 ;go back

;send a command to the LCD
lcd_cmd:   mov 		temp, A		;as above, but without setting RS
           andi 	A,0xf0	
           out 		pLCD, A
           rcall 	lcd_en

		   swap 	temp
           andi 	temp,0xf0	
           out 		pLCD, temp
           rcall 	lcd_en

           rcall 	dly50u
           ret

 ;Generate enable-pulse
lcd_en:    nop				;data settling time
		   nop
		   nop
		   nop
		   sbi 		pLCD, 2 ;Enable high
           nop              ;wait a bit
           nop
           nop
		   nop
           cbi 		pLCD, 2 ;Enable low
           ret              ;and go back                    

 ;Initialise lcd display a good while after power up
lcd_init:  ldi		temp,10
pupwat:    rcall	dly5m
           dec		temp
           brne		pupwat
           ldi 		A,0b00110000        ;send system set three times
           out 		pLCD, A
           rcall 	lcd_en             ;1
           rcall 	dly5m
           rcall 	lcd_en             ;2
           rcall 	dly5m
           rcall 	lcd_en             ;3 times
           rcall 	dly5m
           ldi 		A,0b00100000       ;4bit mode ??
           out 		pLCD, A
           rcall 	lcd_en
           rcall 	dly5m
           ldi 		A,0b00101000		;4 bit mode, 2/4 lines
		   rcall 	lcd_cmd
           ldi 		A,0b00001100        ;display on, csr off
           rcall 	lcd_cmd
           ldi 		A,0b00000110        ;entry mode - auto-inc csr
           rcall 	lcd_cmd
           ret

 ;Send a command to clear lcd_msg
lcd_clear: ldi A, 0b00000001   ;lcd_msg clear
           rcall lcd_cmd
           rcall dly5m
           ret

;Delay for 50usec
dly50u:	LDI		count1,0xFF
d5ul:	DEC		count1
		NOP
		BRNE	d5ul
		RET

;Delay for 5msec
dly5m:	LDI		count1,168
d5ml:	LDI		count2,0xC5
d5l2:	DEC		count2
		BRNE	d5l2
		DEC		count1
		BRNE	d5ml
		LDI		count1,0x41
d513:	DEC		count1
		BRNE	d513
		RET	
		
;Delay for 1sec			
dly1s:	LDI		temp,0xC8
d1sl:	RCALL	dly5m
		DEC		temp
		BRNE	d1sl
		RET

;Delay for 100mSec			
dlyts:	LDI		temp,50
d1s3:	RCALL	dly5m
		DEC		temp
		BRNE	d1s3
		RET

;Gets first two bytes of record 'A' into L,H from table
gtrcd:	CLR		C
		TST		A
		BREQ	gld			;record zero - so skip multiplication
mulc:	ADD		C,A			;do multiplication by multiple addition
;		BRCS	error		;This SR only valid for small tables <256 bytes	
		DJNZ	mulc		;loop for mult - crude multiply
		ADD		ZL,C		;add offset to table base
		ADC		ZH,B		;after DJNZ B=0
gld:	LPM		L,Z+		;get first two bytes of record
		LPM		H,Z+		;first is in L as fits .DW
		RET

;24 x 24bit unsigned multiplication.
;start with numbers in registers A0-2 and B0-2.
;result ends up in B5-0
;Derived from Atmel AVR200
mul24:	CLR		B5			;clear top 24 bits of results register
		CLR		B4
		CLR		B3	
		LDI		C,24		;loop counter
		LSR		B2
		ROR		B1
		ROR		B0			;shift first bit of multiplicand into Cry
m241:	BRCC	noad		;if Cry is clear no need to add number in
		ADD		B3,A0		;else
		ADC		B4,A1
		ADC		B5,A2		;Add multiplicator into results reg
noad:	ROR		B5			;Cry is already clear and shift
		ROR		B4			;results and multiplicand to right
		ROR		B3
		ROR		B2
		ROR		B1
		ROR		B0
		DEC		C
		BRNE	m241		;loop for all 24 bits of multiplicand
		RET

;Divide 48bit by 24bit
;start with Dividend in B5-0, Divisor in A2-0
;Remainder is in in Rm2-0 and result ends up in B2-0
;Derived from Atmel AVR200
dv24:	CLR		Rm0			;Clear remainder and Cry
		CLR		Rm1
		CLR		Rm2
		CLR		Rm3
		CLR		A
		CLC
		LDI		B,49		;set up loop counter
dv24_1:	ROL		B0			;shift left dividend
		ROL		B1
		ROL		B2
		ROL		B3
		ROL		B4
		ROL		B5
		DJNZ	dv24_2		;loop until zero
		RET					;then return
dv24_2:	ROL		Rm0			;else shift one more bit of dividend into remainder
		ROL		Rm1
		ROL		Rm2
		ROL		Rm3
		SUB		Rm0,A0		;and subtract divisor from remainder
		SBC		Rm1,A1
		SBC		Rm2,A2
		SBC		Rm3,A
		BRCC	dv24_3
		ADD		Rm0,A0		;if result negative, restore remainder
		ADC		Rm1,A1
		ADC		Rm2,A2
		ADC		Rm3,A
		CLC					;clear Cry to be shifted into result
		RJMP	dv24_1		;and repeat
dv24_3:	SEC					;else, set Cry to be shifted into result
		RJMP	dv24_1		;and repeat


;*** Display screens

.IF		PB
scrn1:	.DB		LN1,"Pico C-Plus"
.ELSE
scrn1:	.DB		LN1,"Pico C-Super+"
.ENDIF
		.DB		LN2,"V2p",0,0

scrn1a:	.DB		LN1,"Select mode - S1",00

scrn1b:	.DB		LN2,"Period    ",00
scrn1c:	.DB		LN2,"Frequency ",00
scrn1d:	.DB		LN2,"Capacitance ",00	
scrn1e:	.DB		LN2,"Sig Gen.  ",00
scrn1f:	.DB		LN2,"Calibrate   ",00
scrn1g:	.DB		LN2,"Pulse gen.",00

scrn2:	.DB		LN1,"Multiplier = "
		.DB		LN2,"<S1>",00

scrn2a:	
		.DB		LN1,"Remove Caps"
		.DB		LN2,"Autozero",00

scrn3:	.DB		LN1,"Fit Ref Cap"
		.DB		LN2,"Cal: C=1nF",00

scrn4:	.DB		LN1,"Multiplier "
		.DB		LN2,"Cx= ",00

scrn5:	.DB		LN1,"Multiplier "
		.DB		LN2,"Per"
scrn5a:	.DB		LN2+14,"uS",00

;scrn6:	.DB		LN1,"Sig. Gen",00

scrn7:	.DB		LN1,"Gate",00


;scrn9:	.DB		LN1,"Calibrate",0,0

spf:	.DB		" pF",00

skHz:	.DB		"kHz",00

sHz:	.DB		LN2+14,"Hz",0

sover:	.DB		LN2+4," Overflow   ",0


;Mode table
ModTab:	.DW		(2*scrn1d),S2		;Capacitance
		.DW		(2*scrn1f),S9		;Calibration for cap
		.DW		(2*scrn1b),S4		;Period		
		.DW		(2*scrn1e),S5		;Sig Gen
		.DW		(2*scrn1g),S6		;Pulse Gen
		.DW		(2*scrn1c),S7		;Frequency

;scaled Ref values
;byte 1 = period multiplier
;byte 2-4 = multiplier x cap value
RefTab:	.DB		1,0,3,0xE8			;1, 1000pF
		.DB		10,0,0x27,0x10		;10,10000
		.DB		100,1,0x86,0xA0		;100,100000

;Table - Gate times
Gtab:	.DB		249,0,LN1+6,"010ms",0,0						;10ms
		.DB		low(2499),high(2499),LN1+6,"100ms",0,0		;100ms	
		.DB		low(24999),high(24999),LN1+6,"   1s",0,0	;1s

;Pulse generation table
;Duration = (2+n) x 0.25us
;But subtract 1 more from first table entry!
;Use zeros to mark end of sequence
Ptab:	.DB		1,2		;1us=	
		.DB		6,6		;2us=
		.DB		10,10	;3us=
		.DB		14,60	;4hi 7lo
		.DB		0,0		;loop back to start


.DSEG
nbuf:	.BYTE	10		;buffer for bin to bcd convn	
pC0_0:	.BYTE	1		;Period for residual cap only
pC0_1:	.BYTE	1
pC0_2:	.BYTE	1
Ndx:	.BYTE	1		;index for range selection
Nlp:	.BYTE	1		;number of loops
GtL:	.BYTE	1		;Gate time low/high
GtH:	.BYTE	1
pCref0:	.BYTE	1		;Ref cap period
pCref1:	.BYTE	1
pCref2:	.BYTE	1
Rval0:	.BYTE	1		;Ref cap value
Rval1:	.BYTE	1
Rval2:	.BYTE	1
Ndec:	.BYTE	1
Calfg:	.BYTE	1		;calibrate flag
ovfg:	.BYTE	1		;overflow flag


.ESEG
Marker:		.DB		0xff
pCrg1_0:	.DB		0xff	;calibration values
pCrg1_1:	.DB		0xff
PCrg1_2:	.DB		0xff
pCrg2_0:	.DB		0xff
pCrg2_1:	.DB		0xff
PCrg2_2:	.DB		0xff
pCrg3_0:	.DB		0xff
pCrg3_1:	.DB		0xff
PCrg3_2:	.DB		0xff

;Frequency definition table.
;Range 0.15Hz to 10MHz
;Bytes define:-
;1. Prescaler. 1=x1, 2=x8, 3=x64, 4=x256, 5=x1024
;2. High byte OCR1
;3. Low byte OCR1
;4. High byte displayed value
;5. Low byte ditto
;6. Prefix of displayed units ( Spc, k , M)

.ORG	0x10
Ftab:
;		.DB		5,high(48827),low(48827),0,2,0x64	;0.2Hz
		.DB		4,high(48827),low(48827),0,8,0x64	;8deci-Hz
		.DB		4,high(39063),low(39063),0,1,0x20	;1Hz
		.DB		4,high(19531),low(19531),0,2,0x20	;2Hz
		.DB		3,high(31249),low(31249),0,5,0x20	;5Hz	
		.DB		3,high(15624),low(15624),0,10,0x20	;10Hz
		.DB		2,high(62499),low(62499),0,20,0x20	;20Hz
		.DB		2,high(24999),low(24999),0,50,0x20	;50Hz
		.DB		2,high(12499),low(12499),0,100,0x20	;100Hz
;		.DB		2,high(6249),low(6249),0,200,0x20	;200Hz
;		.DB		2,high(2499),low(2499),high(500),low(500),0x20	;500Hz
		.DB		1,high(9999),low(9999),0,1,"k"	;1k	
;		.DB		1,high(8332),low(8332),high(1200),low(1200),0x20	;1200Hz	
;		.DB		1,high(6665),low(6665),high(1500),low(1500),0x20	;1500Hz	
;		.DB		1,high(4999),low(4999),0,2,"k"	;2k	
		.DB		1,high(1999),low(1999),0,5,"k"	;5k	
		.DB		1,high(999),low(999),0,10,"k"	;10k
;		.DB		1,high(499),low(499),0,20,"k"	;20k
;		.DB		1,high(226),low(226),0,44,"k"	;44k
;		.DB		1,high(199),low(199),0,50,"k"	;50k
		.DB		1,high(99),low(99),0,100,"k"	;100k
		.DB		1,0,49,0,200,"k"				;200k
;		.DB		1,0,19,high(500),low(500),"k"	;500k
		.DB		1,0,9,0,1,"M"	;1M
		.DB		1,0,4,0,2,"M"	;2M
		.DB		1,0,2,high(3333),low(3333),"k"	;3.3M
		.DB		1,0,1,0,5,"M"	;5M
		.DB		1,0,0,0,10,"M"	;10M!
		.DB		0,0				;end marker

