;file: RF_mate.asm version 5

;Dale Hughes VK1DSH
;August 2007

;software for RF test system:
;							Power measurement
;							Frequency measurement
;							Return loss & SWR measurement
;							Phase angle measurements

;analog i/p's used
;							ADC0: phase angle via AD8302
;							ADC1; ratio db(A/B) via AD8302
;							ADC2: pwr via AD8307 (external)
;							ADC3: pwr via AD8307 (internal)

;							ADC7: PCB battery voltage 

;******************************************************************************

					.include "m8535def.inc"

				.eseg

				pwrrefsetE:		.dw 0		;reference power for ext' i/p
				pwrrefsetI:		.dw 0		;reference power for int' i/p
				phase_int: 		.dw 36000	;intercept of phase 
				phase_slope:	.dw 100		;slope of phase calibration

				.equ def_phs_int = 36000
				.equ def_phs_slp = 100

;registers
;reg R0 & R1 are used by the multiply instruction

				.def multemp0 = R0			;temp' regs for 16 x 16 multiply
				.def multemp1 = R1
				.def multemp2 = R2

				.def fBCD0 = 	R3			;BCD digits 1 & 0
				.def fBCD1 = 	R4			;BCD digits 2 & 3
				.def fBCD2 = 	R5			;BCD digits 4 & 5
				.def fBCD3 = 	R6			;BCD digits 6
				.equ AfBCD0 = 	3			;address of fBCD0
				.equ AfBCD3 = 	6			;address of fBCD3

				.def sign    = 	R7			;sign of number

				.def pwrrefEL = R8			;reference powers for power measurement
				.def pwrrefEH = R9
				.def pwrrefIL = R10
				.def pwrrefIH = R11

				.def swrrefL = 	R12			;reference for SWR measurements
				.def swrrefH = 	R13

				.def pwr_extint = R14		;0 = external, 1 = internal pwr sensor

				.def FROK = 	R15			;decremented by INT0 routine
											;used as timebase

				.def m16u0 = 	R16			;result byte 0 (LSB)
				.def m16u1 = 	R17			;result byte 1
				.def m16u2 = 	R18			;result byte 2
				.def m16u3 = 	R19			;result byte 3

				.def mc16uL = 	R20			;multiplicant L
				.def mc16uH	= 	R21			;multiplicand H
				.def mp16uL = 	R22			;multiplier L
				.def mp16uH = 	R23			;multiplier H

				.def drem16uL = R16			;divide remainder L
				.def drem16uH = R17			;divide remainder H
				.def dres16uL = R18			;divide result L
				.def dres16uH = R19			;divide result H
				.def dd16uL	= 	R18			;dividend L
				.def dd16uH	= 	R19			;dividend H
				.def dv16uL	= 	R20			;divisor L
				.def dv16uH	= 	R21			;divisor H

				.def tbinL	= 	R16			;these don't get used at the 
				.def tbinM = 	R17			;same time as the multiply
				.def tbinH = 	R18			;registers
				.def tmp24a = 	R19
				.def cnt24a = 	R20

				.def temp = 	R24			;use within individual routines
				.def out_char = R25

;these two regs share the same address as they won't be used together
;				.def in_char = 	R26			;serial i/p char
				.def pushbut = 	R26			;push button inputs


				.def Hchar = 	R27
				.def Achar = 	R28			;same as YL
				.def PORTCcopy = R29		;same as YH
;					ZL = R30
;					ZH = R31


;NOTE! regsister pair Y & Z are used by various rotuines but are saved saved on
;the stack before use and restored afterwards so the can be used for general use.
;			ZL & ZH used by print(LCD) & string input routines
;			YL & YH used by ADC routines

;SRAM usage

				.equ VbatL = $60			;battery voltage
				.equ VbatM = VbatL+1
				.equ VbatH = VbatM+1

				.equ freqL = VbatH+1		;freq counter contents
				.equ freqM = freqL+1		;loaded by interrupt routine
				.equ freqH = freqM+1

				.equ copyZL = freqH+1		;copy of Z reg pair
				.equ copyZH = copyZL+1

				.equ pwrcpyEL = copyZH+1	;copy of raw ext' measured power
				.equ pwrcpyEH = pwrcpyEL+1

				.equ pwrcpyIL = pwrcpyEH+1	;copy of raw int' measured power
				.equ pwrcpyIH = pwrcpyIL+1

				.equ freqtype = pwrcpyIH+1	;direct=1, prescaled=64

				.equ opsel = freqtype+1		;0=LCD, 1=RS232

				.equ meas_frq = opsel+1		;copy of measured freq string
											;sign + 8 chars' + null = 10
				.equ meas_phs = meas_frq+10 ;copy of phase data in BCD

				.equ meas_mag = meas_phs+10 ;copy of magnitude string

				.equ meas_pwr1 = meas_mag+10 ;copy of ext' power string

				.equ meas_pwr2 = meas_pwr1+10 ;copy of int' power string

				.equ RL_SWR = meas_pwr2+10	;Return loss / VSWR flag
				.equ tmpswrL= RL_SWR+1		;temporary SWR value
				.equ tmpswrH = tmpswrL+1

				.equ phaseintL = tmpswrH+1	;phase calibration registers
				.equ phaseintH = phaseintL+1
				.equ phaseslopL = phaseintH+1
				.equ phaseslopH = phaseslopL+1

				.equ string = phaseslopH+1	;where character string is stored	


;****************** Code section **********************************************

					.cseg					;code segment

				rjmp RESET					;Reset pointer

				rjmp meas_freq				;leave space for the various interrupt
				reti						;vectors. 16 locations used.
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti
				reti

				.org $011												

RESET:			ldi temp, high(RAMEND)		;initialise stackpointer to top of ram
				out SPH, temp
				ldi temp ,low(RAMEND)
				out SPL, temp					

				rcall setup_system			;setup hardware & initial settings				

;Main menu selection. Display options & battery voltage. Battery voltage
;updates every second. Second level main menu is selected by pressing the
;first two buttons together.
main_menu:		rcall read_buttons			;wait for function buttons to be
				cpi pushbut	, $0			;released if they're not
				brne main_menu
				rcall clrscn				;show options

main_menu0:		rcall line_one

				ldi temp,250
				mov FROK,temp				;set time for 1 sec update
				rcall print
				.db "Main ",0
				rcall disp_Vbat				;& battery voltage

				rcall line_two
				rcall print
				.db "Frq Pwr Phs Mag",0		;1, 2, 4, 8

main_menu1:		rcall read_buttons			;get selected function
				cpi pushbut, 1
				brne main_menu2
				rcall sel_freq				;select frequency menu
				rjmp main_menu

main_menu2:		cpi pushbut, 2			
				brne main_menu3
				rcall sel_pwr				;select power menu
				rjmp main_menu

main_menu3:		cpi pushbut, 4
				brne main_menu4
				rcall sel_phs				;select phase menu
				rjmp main_menu

main_menu4:		cpi pushbut, 8
				brne main_menu5
				rcall sel_mag				;select magnitude menu
				rjmp main_menu

main_menu5:		cpi pushbut, 3				
				brne main_menu6
				rcall second_funct			;select 2nd level functions
				rjmp main_menu
				
main_menu6:		nop							;future use...

				tst FROK					;if 1 sec done, then refresh
				breq main_menu0				;display, otherwise just loop
				rjmp main_menu1


;select power display from either internal or external power sensor.
;User can enter a reference level e.g. 1mW. user inputs
;RF at desired reference, then presses the 'Ref' button. Value is saved to
;EEPROM
sel_pwr:		rcall wait4release			;wait for buttons to be released
				rcall clrscn
				rcall print
				.db "Power sensor?",0
				rcall line_two
				rcall print
				.db "Int Ext",0				;1, 2

				rcall get_selection
				cpi pushbut, 1
				brne sel_pwr2
				rjmp sel_int_pwr			;select internal sensor

sel_pwr2:		cpi pushbut, 2
				brne sel_pwr3
				rjmp sel_ext_pwr			;select external sensor

sel_pwr3:		nop
				ret



;set pointer to internal power, then measure
sel_int_pwr:	clr pwr_extint			;set power sensor flag to 1
				inc pwr_extint			;for inernal sensor
				rjmp meas_Ipwr

sel_ext_pwr:	clr pwr_extint			;clr power sensor flag
				rjmp meas_Epwr			;for external sensor



;measure power or SWR using internal power sensor
meas_Ipwr:		rcall clrscn			;show options
				rcall clrscn
				rcall print
				.db "Function?",0
				rcall line_two
				rcall print
				.db "Main Frq Pwr SWR ",0	;1, 2, 4, 8

				rcall get_selection
				cpi pushbut,1
				brne meas_Ipwr_1
				ret
				
meas_Ipwr_1:	cpi pushbut,2
				brne meas_Ipwr_2
				rjmp sel_freq
				
meas_Ipwr_2:	cpi pushbut,4
				brne meas_Ipwr_4
				rjmp meas_Idb

meas_Ipwr_4:	cpi pushbut,8
				brne meas_Ipwr
				rjmp meas_Iswr


;measure power via internal AD8307 and display in db above user setable reference
meas_Idb:		rcall wait4release
				rcall clrscn			;show options

				rcall line_two
				rcall print
				.db "Main Frq SWR Ref ",0	;1, 2, 4, 8

meas_Idb_2:		mov temp, FROK			;get value of FROK
meas_Idb_3:		cp temp, FROK			;wait until it changes (via IRQ)
				breq meas_Idb_3
				
				rcall line_one			;switch to 1st line of LCD	
				rcall print
				.db "pwr  ",0						
				rcall disp_Ipwr			;and display power level

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;exit, ref' or continue
				brne meas_Idb_4
				ret

meas_Idb_4:		cpi pushbut,2
				brne meas_Idb_5
				rjmp sel_freq

meas_Idb_5:		cpi pushbut,4			;jump to display SWR
				brne meas_Idb_6	
				rjmp meas_Iswr

meas_Idb_6:		cpi pushbut,8			;set reference is selected
				brne meas_Idb_2
				rcall set_pwrref
				rjmp meas_Idb



;measure & display SWR via internal AD8307 pwr sensor. Reference set
;when Zu port is O/C or S/C
meas_Iswr:		rcall wait4release
				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Frq db Ref",0	;1, 2, 4,8

meas_Iswr_2:	mov temp, FROK			;get value of FROK
meas_Iswr_3:	cp temp, FROK			;wait until it changes (via IRQ)
				breq meas_Iswr_3
				
				rcall line_one			;switch to 1st line of LCD	
				rcall print
				.db "SWR  ",0			
				rcall disp_Iswr			;and display SWR

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;exit, ref' or continue
				brne meas_Iswr_4
				ret

meas_Iswr_4:	cpi pushbut,2
				brne meas_Iswr_5
				rjmp sel_freq

meas_Iswr_5:	cpi pushbut,4			;jmp to display db above ref'
				brne meas_Iswr_6	
				rjmp meas_Idb

meas_Iswr_6:	cpi pushbut,8			;set reference is selected
				brne meas_Iswr_2
				rcall set_swrref
				rjmp meas_Iswr


;measure power via external AD8307 and display in db above user setable reference
meas_Epwr:		rcall wait4release
				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Frq Ref ",0	;1, 2, 4

meas_Epwr_2:	mov temp, FROK			;get value of FROK
meas_Epwr_3:	cp temp, FROK			;wait until it changes (via IRQ)
				breq meas_Epwr_3
				
				rcall line_one			;switch to 1st line of LCD				
				rcall disp_Epwr			;and display power level

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;exit, ref' or continue
				brne meas_Epwr_4
				ret

meas_Epwr_4:	cpi pushbut,2
				brne meas_Epwr_5
				rjmp sel_freq

meas_Epwr_5:	cpi pushbut,4			;set reference is selected
				brne meas_Epwr_2	
				rcall set_pwrref
				rjmp meas_Epwr


;select phase measurement.
sel_phs:		rcall wait4release		;wait for buttons to be released
				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Frq Mag Cal ",0		;1, 2, 4, 8

sel_phs_2:		mov temp, FROK			;get value of FROK
sel_phs_3:		cp temp, FROK			;wait until it changes (via IRQ)
				breq sel_phs_3
				
				rcall line_one			;switch to 1st line of LCD
				rcall print
				.db "Phase: ",0							
				rcall disp_angle		;and display phase angle

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;exit or continue
				brne sel_phs_4
				ret

sel_phs_4:		cpi pushbut,2			;measure frequency if required
				brne sel_phs_5
				rjmp sel_freq

sel_phs_5:		cpi pushbut,4			;measure R.L/SWR if req'd
				brne sel_phs_6
				rjmp sel_mag

sel_phs_6:		cpi pushbut, 8			;chk if cal' required
				brne sel_phs_2
				rcall sel_phsslpcal
				rjmp sel_phs



;calibrate phase measurement with accurately known 1/8, 1/4 & 3/8 line
;assumes that the the test line IS an ACCURATE electrical length!
;adjusts slope & intercept & writes to EEPROM if required. A two step process
;is followed: 1st adjustment of slope using 1/8 & 3/8 lines, then intercept 
;using 1/4 line. Also with ability to switch between functions.

;adjust phase angle measurement intercept calibration
sel_phsslpcal:	rcall wait4release		;wait for buttons to be released
				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Int  +   -   Phs ",0 ;1 2 4 8

sel_phsslpcal2:	mov temp, FROK			;get value of FROK
sel_phsslpcal3:	cp temp, FROK			;wait until it changes (via IRQ)
				breq sel_phsslpcal3
				
				rcall line_one			;switch to 1st line of LCD	
				rcall print
				.db "set Slp: ",0		;show 'set Slope'			
				rcall disp_angle		;and display phase angle

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1
				brne sel_phsslpcal4		;if 1 then jump to intercept
				rjmp sel_phsintcal		;calibrate

sel_phsslpcal4:	cpi pushbut, 2			;increase phase slope?
				brne sel_phsslpcal5
				rcall sel_phsslp_inc
				rjmp sel_phsslpcal

sel_phsslpcal5:	cpi pushbut, 4			;decrease phase slope?
				brne sel_phsslpcal6
				rcall sel_phsslp_dec
				rjmp sel_phsslpcal

sel_phsslpcal6:	cpi pushbut, 8			;back to phase measurement?
				brne sel_phsslpcal2

sel_phsslpcalex: ret


;adjust phase angle measurement intercept calibration
sel_phsintcal:	rcall wait4release		;wait for buttons to be released
				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Slp  +   -   Set ",0 ;1 2 4 8

sel_phsintcal2:	mov temp, FROK			;get value of FROK
sel_phsintcal3:	cp temp, FROK			;wait until it changes (via IRQ)
				breq sel_phsintcal3
				
				rcall line_one			;switch to 1st line of LCD	
				rcall print
				.db "set Int: ",0		;show 'set intercept'		
				rcall disp_angle		;and display phase angle

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1
				brne sel_phsintcal4		;if 1 then jump to slope
				rjmp sel_phsslpcal		;calibrate

sel_phsintcal4:	cpi pushbut, 2			;increase phase intercept?
				brne sel_phsintcal5
				rcall sel_phsint_inc
				rjmp sel_phsintcal

sel_phsintcal5:	cpi pushbut, 4			;decrease phase intercept?
				brne sel_phsintcal6
				rcall sel_phsint_dec
				rjmp sel_phsintcal

sel_phsintcal6:	cpi pushbut, 8			;save slope & intercept to EEPROM?
				brne sel_phsintcal2		;also option of restoring default
				rcall sel_cal2eprom		;or doing nothing...
				ret


;increase phase intercept value, nominal value ~ 36000
sel_phsint_inc: push ZL				;save Z regs
				push ZH
				
				lds ZL, phaseintL	;get intercept
				lds ZH, phaseintH

				adiw ZL, 10			;inc' phase intercept
				sts phaseintL, ZL  	;save new value back in SRAM
				sts phaseintH, ZH

				pop ZH				;restore Z regs
				pop ZL
				ret


;decrease phase intercept value, nominal value ~ 36000
sel_phsint_dec: push ZL				;save Z regs
				push ZH

				lds ZL, phaseintL	;get intercept
				lds ZH, phaseintH

				sbiw ZL, 10			;dec' phase intercept
				sts phaseintL, ZL 	;save new value back in SRAM
				sts phaseintH, ZH

				pop ZH				;restore Z regs
				pop ZL
				ret


;increase phase slope value, nominal value ~ 100
sel_phsslp_inc: push ZL				;save Z regs
				push ZH
				
				lds ZL, phaseslopL	;get slope
				lds ZH, phaseslopH

				adiw ZL, 1			;inc' phase slop
				sts phaseslopL, ZL  ;save new value back in SRAM
				sts phaseslopH, ZH

				pop ZH				;restore Z regs
				pop ZL
				ret


;decrease phase slope value, nominal value ~ 100
sel_phsslp_dec: push ZL				;save Z regs
				push ZH

				lds ZL, phaseslopL	;get slope
				lds ZH, phaseslopH

				sbiw ZL, 1			;dec' phase slope
				sts phaseslopL, ZL 	;save new value back in SRAM
				sts phaseslopH, ZH

				pop ZH				;restore Z regs
				pop ZL
				ret


;either restore default settings or save new settings to EEPROM or return
;to cal menu without changing anything
sel_cal2eprom:		rcall wait4release		;wait for buttons to be released
					rcall clrscn			;show options
					rcall print
					.db "Save to EEPROM?",0	;restore default or set new
					rcall line_two			;slope & intercept? or return
					rcall print				;to Cal menu?
					.db "Def New       No ",0 ;1, 2  8

sel_cal2eprom2:		mov temp, FROK			;get value of FROK
sel_cal2eprom3:		cp temp, FROK			;wait until it changes (via IRQ)
					breq sel_cal2eprom3
				
					rcall read_buttons		;chk if buttons pressed
					cpi pushbut, 1
					brne sel_cal3eprom4		;restore default slope & intercept
					rcall sel_phs_def_set
					ret

sel_cal3eprom4:		cpi pushbut, 2
					brne sel_cal2eprom5
					rcall sel_phs_cal_set	;write new slope & intercept?
					ret

sel_cal2eprom5:		cpi pushbut, 4			;do nothing (future use)
					brne sel_cal2eprom6

sel_cal2eprom6:		cpi pushbut, 8			;no change, return to cal menu
					brne sel_cal2eprom2
					rcall sel_no_cal_set
					ret



;restore default phase slope & intercept to EEPROM
;defaults: slope = 100, intercept = 36000 (defined at start of code)
sel_phs_def_set:	ldi ZH,high(phase_int)		;initialise  pointer
					ldi ZL,low(phase_int)		;to phase_int area in EEPROM

					ldi temp, low(def_phs_int)	;copy data to EEPROM data reg
					rcall wr_eeprom				;lo byte first
					adiw ZL,1					;point to next location

					ldi temp, high(def_phs_int)		;hi byte next
					rcall wr_eeprom
					adiw ZL,1					;point to next location
				
					ldi temp, low(def_phs_slp)		;copy data to EEPROM data reg
					rcall wr_eeprom				;lo byte first
					adiw ZL,1					;point to next location

					ldi temp, high(def_phs_slp)		;hi byte next
					rcall wr_eeprom
			
					rcall clrscn				;show operation is complete
					rcall print
					.db "Phase defaults ",0
					rcall line_two
					rcall print
					.db "saved to EEPROM",0
					rcall wait_1sec
					rcall wait_1sec
					ret


;save calibrated phase slope & intercept to EEPROM
sel_phs_cal_set:	ldi ZH,high(phase_int)	;initialise  pointer
					ldi ZL,low(phase_int)	;to phase_int area in EEPROM

					lds temp, phaseintL		;copy data to EEPROM data reg
					rcall wr_eeprom			;lo byte first
					adiw ZL,1				;point to next location

					lds temp, phaseintH		;hi byte next
					rcall wr_eeprom
					adiw ZL,1				;point to next location
				
					lds temp, phaseslopL	;copy data to EEPROM data reg
					rcall wr_eeprom			;lo byte first
					adiw ZL,1				;point to next location

					lds temp, phaseslopH	;hi byte next
					rcall wr_eeprom
			
					rcall clrscn			;show operation is complete
					rcall print
					.db "Phase calibrate",0
					rcall line_two
					rcall print
					.db "saved to EEPROM",0
					rcall wait_1sec
					rcall wait_1sec
					ret


;no change to EEPROM contents
sel_no_cal_set:		rcall clrscn			;show operation is complete
					rcall print
					.db "No change to ",0
					rcall line_two
					rcall print
					.db "EEPROM settings",0
					rcall wait_1sec
					rcall wait_1sec
					ret



;select magnitude (i.e. A/B), value can be displayed s return loss (db)
;or as VSWR
sel_mag:		rcall wait4release		;wait for buttons to be released

				lds temp,RL_SWR			;get which format to display
				cpi temp,0				;0 = RL (db), 1 = VSWR
				brne sel_mag_1

				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Frq SWR Ref ",0	;1, 2, 4, 8
				rjmp sel_mag_2

sel_mag_1:		rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Frq A/B Ref ",0	;1, 2, 4, 8

sel_mag_2:		mov temp, FROK			;get value of FROK
sel_mag_3:		cp temp, FROK			;wait until it changes (via IRQ)
				breq sel_mag_3
				
				rcall line_one			;switch to 1st line of LCD				

				lds temp,RL_SWR			;get which format to display
				cpi temp, 0				;0 = RL (db), 1 = VSWR
				brne sel_mag_4

				rcall print				;display ratio (db)
				.db "A/B  ",0   
				rcall disp_mag
				rjmp sel_mag_5

sel_mag_4:		rcall print				;display VSWR
				.db "VSWR ",0
				rcall disp_vswr

sel_mag_5:		rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;exit or continue
				breq sel_mag_ex

				cpi pushbut,2			;display frequency
				brne sel_mag_6
				rjmp sel_freq

sel_mag_6:		cpi pushbut,4			;toggle RL_SWR flag
				brne sel_mag_8
				lds temp, RL_SWR
				inc temp
				andi temp, $01
				sts RL_SWR, temp
				rjmp sel_mag

sel_mag_8:		cpi pushbut,8			;set ref' for SWR
				brne sel_mag_2
				rcall set_swrref
				rjmp sel_mag_2

sel_mag_ex:		ret



;select frequency display
sel_freq:		rcall wait4release			;wait for buttons to be released

				rcall clrscn				;show options
				rcall print
				.db "Frequency Menu ",0
				rcall line_two
				rcall print
				.db "n=1 n=64 ",0			;1, 2

				rcall get_selection			;get selected function
				cpi pushbut, 1
				brne sel_freq2
				rjmp sel_freq_n1			;select n=1 (i.e. direct i/p)

sel_freq2:		cpi pushbut, 2
				brne sel_freq
				rjmp sel_freq_n64			;select n=64 (i.e. prescaled i/p)


;select direct frequency input, no prescaling. This option has only one 
;input.
sel_freq_n1:	rcall wait4release
				rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Pwr Phs Mag ",0	;1, 2, 4, 8

				cbi PORTC,0				;de-energise relay to select external
				cbr PORTCcopy,1

sel_freq_n1_2:	mov temp, FROK			;get value of freq' meas' flag
sel_freq_n1_3:	cp temp, FROK			;wait until it changes (via IRQ)
				breq sel_freq_n1_3
				
				rcall line_one			;switch to 1st line of LCD				
				rcall disp_freq			;and display frequency

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;& continue with freq' display
				brne sel_freq_n1_D		;if not
				ret

sel_freq_n1_D:	cpi pushbut,2			;measure power if required
				brne sel_freq_n1_E
				rjmp sel_pwr

sel_freq_n1_E:	cpi pushbut,4			;measure phase if required
				brne sel_freq_n1_F
				rjmp sel_phs

sel_freq_n1_F:	cpi pushbut,8			;measure R.L/SWR if required
				brne sel_freq_n1_2
				rjmp sel_mag


;select prescaled frequency input. This option has two posible inputs:
;a) the external prescaler
;b) the internal prescaler
;these two inputs are selected by a relay via PORTC0
sel_freq_n64:	rcall wait4release
				rcall clrscn
				rcall print
				.db "Prescaler? ",0			
				rcall line_two			;show options
				rcall print
				.db "Int Ext",0			;1, 2

				rcall get_selection
				cpi pushbut,1
				brne sel_freq_n64_2

				sbi PORTC,0				;energise relay to select internal
				sbr PORTCcopy,1
				rjmp sel_freq_n64_9

sel_freq_n64_2:	cpi pushbut,2
				breq sel_freq_n64_3
				ret

sel_freq_n64_3:	cbi PORTC,0				;de-energise relay to select external
				cbr PORTCcopy,1

sel_freq_n64_9: rcall wait4release

;now measure prescaled frequency & allow other measurement types
sel_freq_n64_A:	rcall clrscn			;show options
				rcall line_two
				rcall print
				.db "Main Pwr Phs Mag ",0			;1, 2, 4, 8

sel_freq_n64_B:	mov temp, FROK			;get value of freq' meas' flag
sel_freq_n64_C:	cp temp, FROK			;wait until it changes (via IRQ)
				breq sel_freq_n64_C
				
				rcall line_one			;switch to 1st line of LCD				
				rcall disp_freq_prsc	;and display frequency

				rcall read_buttons		;chk if buttons pressed
				cpi pushbut, 1			;& continue with freq' display
				brne sel_freq_n64_B		;if not
				ret

sel_freq_n64_D:	cpi pushbut,2			;measure power if required
				brne sel_freq_n64_E
				rjmp sel_pwr

sel_freq_n64_E:	cpi pushbut,4			;measure phase if required
				brne sel_freq_n64_F
				rjmp sel_phs

sel_freq_n64_F:	cpi pushbut,8			;measure R.L/SWR if required
				brne sel_freq_n64_A
				rjmp sel_mag


;*********************************************************************************

;Second level functions. 
;Functions selected by pressing buttons 1 & 2 simultaneoulsy

second_funct:	rcall wait4release			;wait for buttons to be released

				rcall clrscn				;show options
				rcall print
				.db "log mag & phs? ",0
				rcall line_two
				rcall print
				.db "main log ",0			;1, 2

				rcall get_selection			;get selected function
				cpi pushbut, 2
				brne secfun2
				rjmp log_magphs				;log frequency, magnitude & phase

secfun2:		nop							;future entries...
				ret


;read the frequency, magnitude & phase information and send to serial port
;whenever the log button is pressed. Direct or prescaled frequency i/p
;is selected from the main menu level.
log_magphs:		rcall wait4release

				rcall clrscn				;show options
				rcall print
				.db "mag & phs log",0
				rcall line_two
				rcall print
				.db "main send",0			;1, 2	

				rcall get_selection			;get selected function
				cpi pushbut, 2
				brne log_magphs2

				rcall send_magphs			;send frequency magnitude & phase 
				rjmp log_magphs

log_magphs2:	ret


;measure frequency, magnitude & phase information and send to host PC whenever
;the 'send' button is pushed. Fields are separated by comma's
send_magphs:	ldi temp,1					;point to RS232 o/p
				sts opsel, temp

send_magphs2:	lds temp, freqtype
				cpi temp, 1
				breq send_magphs4
				rcall disp_freq_prsc		;send prescaled frequency
				rjmp send_magphs6

send_magphs4:	rcall disp_freq				;send direct frequency

send_magphs6:	ldi out_char,','
				rcall putchar

				rcall disp_mag				;send magnitude
				ldi out_char,','
				rcall putchar

				rcall disp_angle			;send phaseangle
				ldi out_char,','
				rcall putchar

				ldi out_char,$0d			;terminate line
				rcall putchar
				ldi out_char,$0a
				rcall putchar

				ldi temp,0					;point to LCD o/p
				sts opsel, temp

				ret

;*********************************************************************************

;sit & wait for function to be selected, then get selected function into pushbut, 
;then wait for buttons to be released. Returns 1, 2, 3, 4 or 8. 
get_selection:	rcall read_buttons			;wait for selection
				cpi pushbut, $0				;i.e. when not zero
				breq get_selection

				rcall wait_100ms			;wait for any switch bounce
				rcall read_buttons			;and read again
				push pushbut				;save selection for later

				rcall wait4release			;wait for button to be released
				
				pop pushbut					;get selection back when buttons
				ret							;released


;wait for buttons to be released. Use in get_selection but also for when
;read_buttons is used in multi level menus
wait4release:	rcall read_buttons			;wait for button to be released
				cpi pushbut, $0
				brne wait4release
				ret


;read function buttons and return selected input. Buttons return
;0, 1, 2, 4 or 8. Leave result in pushbut.	Second functions returns 3			
read_buttons:	in pushbut, PIND			;read buttons
				lsr pushbut					;shift right 3 places to line up 
				lsr pushbut					;with D0..D3
				lsr pushbut
				ldi temp, $0f				;mask inputs
				and pushbut, temp			;invert bits to make	
				eor pushbut, temp			;selection 0, 1, 2, 4 or 8

				rcall wait_30ms				;wait-a-while (& refresh WDR)
				ret


;*******************************************************************************

;setup hardware and initial settings

setup_system:	wdr							;reset watchdog prior to setting timer
				ldi temp, $0f				;enable timer and set to ~1.9sec
				out WDTCR, temp

				rcall setup_ports
				rcall setup_TC2				;TC2 is timebase for freq counter.
				rcall setup_freq_cntr		;always make this the last one!	

				rcall setup_usart
				rcall setup_adc
				rcall setup_lcd

				rcall read_pwrref			;get ref' power from EEPROM
				rcall read_phase_org		;get phase angle origin
				
				rcall boot_msg				
				ret


;setup data direction registers for all I/O ports
setup_ports:	ldi temp, $40				;PA0..PA5 & PA7 adc 7 i/p			
				out DDRA, temp				;PA6 LCD E.

				ldi temp, $0d				;PB0 LCD RS, PB1 cntr1 i/p
				out DDRB, temp				;PB2 ext cntr reset o/p
											;PB3 unused o/p
											;PB4..PB7 ext cntr i/p

				ldi temp, $f0				;PC0..PC3 unused i/p
				out DDRC, temp				;PC4..PC7 outputs for LCD
				clr PORTCcopy				;clear PORTC o/p
				out PORTC, PORTCcopy
								
				ldi temp, $82				;PD0 rxd i/p, PD1 txd o/p
				out DDRD, temp				;PD2 ext cntr gate i/p (interrupt)
											;PD3..PD6 push button inputs
											;PD7 32kHz o/p from OC2
				ret


;setup timer/counter 2 to o/p 32kHz on PD7 (OC2)
setup_TC2:		ldi temp, $19				;set OC2 to toggle on OCR match
				out TCCR2, temp
				ldi temp, $3f				;set output compare reg' to 63
				out OCR2, temp
				ret



;setup frequency measurment system, then enable interrupt. Interrupt occurs
;every 4ms. FROK is decrmented each interrupt
setup_freq_cntr:
				ldi temp, 1					;set default type to direct
				sts freqtype,temp
					
				ldi temp, $f0
				out PORTB, temp				;activate pullups on PORTB

				sbi PORTB, 2				;reset ext' counter
				nop
				cbi PORTB, 2

				clr temp
				mov FROK, temp

				ldi temp, $6				;enable external pulse counting
				out TCCR1B, temp			;for T1 (-ve edge)
				clr temp
				out TCNT1H, temp			;clr counter T1
				out TCNT1L, temp

				ldi temp, $03				;set INT0 to rising edge trigger
				out MCUCR, temp
				ldi temp, $40				;enable INT0
				out GIMSK, temp
				sei							;set global irq flag
				ret
	

;setup uart for serial communications. Set for 8bit, 1 stop Fck = 4.096 MHz
setup_usart:	clr temp
				out UBRRH, temp
				ldi temp,26					;set baud rate generator to 9600 baud
				out UBRRL, temp				;see P148 of data sheets

				ldi temp, $08				;enable the TX by setting the
				out UCSRB, temp				;TXEN bit
				ldi temp, $86				;enable transmitter & receiver
				out UCSRC, temp				;8 bit & no irq
				sbi PORTD,0					;enable pullup on rx input
				ret


;setup ADC to single conversion, F = 125kHz ie div = 64, no irq
setup_adc:		ldi temp, $86				;enable & setup ADC
				out ADCSRA, temp
				ldi temp, 7					;set adc mux to channel 7
				out ADMUX, temp
				rcall read_adc				;do initial conversion
				in temp, ADCL				;read lo byte & discard
				in temp, ADCH				;read hi byte & discard
				ret


;setup LCD to 4 bit mode
setup_lcd:			clr temp				;set LCD as default display device
					sts opsel,temp

					rcall wait_100ms		;wait for LCD reset
				
					ldi out_char, $28		;function set					
					rcall lcd_com
					rcall wait_4ms

					ldi out_char, $28		;function set					
					rcall lcd_com				
					rcall wait_50us
					rcall wait_50us	

					ldi out_char, $28		;function set					
					rcall lcd_com
					rcall wait_50us	

					ldi out_char, $0c		;display on/off
					rcall lcd_com
					rcall wait_50us	

					rcall clrscn			;display clear

					ldi out_char, $06		;entry mode set
					rcall lcd_com
					rcall wait_50us												
					ret



;display boot message
boot_msg:			rcall print				;display boot message
					.db "RF_mate V5 ",0			
					rcall line_two
					rcall print
					.db "DEH 8/2007 ",0
					rcall wait_1sec			;wait a while
					rcall wait_1sec				
					ret



;read phase intercept & slope from EEPROM into SRAM
read_phase_org:		cli
					ldi ZL,low(phase_int)	;point to stored phs' origin
					ldi ZH,high(phase_int)	;EEPROM adr

read_phase_org_2:	out EEARL, ZL			;set pointers to adr in eeprom
					out EEARH, ZH

					sbi EECR,0				;set read enable bit in eeprom
					in temp, EEDR			;control reg', then read eeprom
					sts phaseintL,temp		;and save byte

					rcall inc_Z_ptr
					sbi EECR,0				;set read enable bit in eeprom
					in temp, EEDR			;control reg', then read eeprom
					sts phaseintH,temp		;and save byte

					rcall inc_Z_ptr
					sbi EECR,0				;set read enable bit in eeprom
					in temp, EEDR			;control reg', then read eeprom
					sts phaseslopL,temp		;and save byte

					rcall inc_Z_ptr
					sbi EECR,0				;set read enable bit in eeprom
					in temp, EEDR			;control reg', then read eeprom
					sts phaseslopH,temp		;and save byte

					sei
					ret


;for above routine
inc_Z_ptr:			adiw ZL,1				;increment adr pointer
					out EEARL, ZL			;set pointers to adr in eeprom
					out EEARH, ZH
					ret


;************* frequency meter routines **************************************

;least sig nibble read via PINB from 4 bit counter
;Middle & most sig' bytes read from counter T1
;this code is triggered via interrupt 0, hence temp is saved
;FROK decrements every 4ms & this routine executes every 4ms.
;Note that FROK is used as a timebase by other routines as FROK 
;decrements every 4ms due to interrupt
meas_freq:			push temp
					in temp, SREG
					push temp

					in temp, PINB				;read external 4 bit counter
					sts freqL, temp				;only hi nibble is counter
					in temp, TCNT1L				;read T1 counter
					sts freqM, temp
					in temp, TCNT1H
					sts freqH, temp

					sbi PORTB, 2				;reset ext' counter
					nop							;by strobing 74HC393 clr line hi
					nop
					cbi PORTB, 2

					clr temp
					out TCNT1H, temp			;clr counter T1
					out TCNT1L, temp
					
					dec FROK					;flag that interrupt occured

					pop temp
					out SREG, temp
					pop temp
					reti



;calculate direct frequency input & display
disp_freq:			ldi temp, 1					;flag direct frequency input
					sts freqtype, temp
					clr sign

					rcall get_count

					lsr tbinM					;divide by two to make 
					ror tbinL					;count in 1ms

disp_freq2:			rcall bin2bcd24				;convert to BCD

					ldi temp,3					;display result as xxx.xxMHz
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "MHz  ",0														
					ret
					

;display prescaled frequency input. Prescale value is 64, 
;measure i/p n=128 times and divide by 4 for 1ms timebase, sum, then display
disp_freq_prsc:		ldi temp, 64				;flag prescaled frequency input
					sts freqtype, temp

					clr sign
					clr multemp0				;multemp is used to store the
					clr multemp1				;partial sum of frequency
					clr multemp2
					ldi cnt24a,128				;set n = wanted value

disp_freq_prsc1:	ldi temp,1					;set FROK for next count
					mov FROK, temp
disp_freq_prsc2:	tst FROK
					brne disp_freq_prsc2		;wait until done

					wdr							;refresh watchdog here
					rcall get_count				;get current count

					add multemp0, tbinL			;calculate partial sum
					adc multemp1, tbinM			;which remains in multemp
					adc multemp2, tbinH

					dec cnt24a					;do for 'n' measurments
					brne disp_freq_prsc1

					rcall div_multemp			;divide by 4
					rcall div_multemp			

					mov tbinL,multemp0			;copy sum to tbin for
					mov tbinM,multemp1			;conversion to BCD
					mov tbinH,multemp2
															
					rcall bin2bcd24				;convert to BCD

					ldi temp,3					;display result as xxxx.xxxMHz
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "MHz  ",0														
					ret


;right shift multemp to divide by two
div_multemp:		lsr multemp2
					ror multemp1
					ror multemp0
					ret


;get value from counter and format:
;get internal 16 bit counter & append nibble from external counter
;    freqH	 freqM	 freqL  (freqL is 4 bit value from external counter)
;    /   \   /   \   /
;tbinH   tbinM   tbinL    (which is a 20 bit value for conversion to BCD)
;NOTE: This is a value is based on a 2ms timebase!
get_count:			lds temp, freqH				;get highest nible
					andi temp, $f0
					swap temp
					mov tbinH, temp				;make lo nibble of tbinH

					lds temp, freqH
					andi temp, $0f
					swap temp
					mov tbinM, temp				;make hi nibble of tbinM

					lds temp, freqM				
					andi temp, $f0
					swap temp
					or tbinM,temp				;make lo nibble of tbinM

					lds temp, freqM				;make hi nibble of tbinL
					andi temp, $0f
					swap temp
					mov tbinL, temp

					lds temp, freqL				;make lo nibble of tbinL
					andi temp, $f0
					swap temp
					or tbinL, temp
					ret


;************************* USART driver routines ******************************
;PD1 = serial tx
;PD0 = serial rx

;send crlf sequence to terminate a sent line
;crlf:			ldi out_char, $0d			;send CR
;				rcall txd
;				ldi out_char, $0a			;send LF
;				rcall txd
;				ret


;get a CRLF terminated string from the serial port and save in RAM
;Z register (R30 & 31 used as pointers)
;get_string:		sts copyZH,ZH				;make copy of existing Z
;				sts copyZL,ZL

;				ldi ZH, high(string)		;initialise string pointer
;				ldi ZL, low(string)			;to the start of ram

;get_string2:	rcall rxd					;get serial char

;get_string3:	mov out_char, in_char		;display rec'd on LCD
;				rcall putchar
;				st Z+, in_char				;save in SRAM & post increment
;				cpi in_char, $0a			;chk if LF is received
;				brne get_string2

;				lds ZH,copyZH				;restore contents of Z
;				lds ZL,copyZL
;				ret


;receive byte from serial port
;rxd:			wdr							;reset watchdog timer while waiting
;				sbis UCSRA, 7				;if byte is received bit 7 is set
;				rjmp rxd					;and rjmp is skipped
;				in in_char, UDR				;get received byte
;				ret


;transmit byte from serial port	& refresh watchdog timer
putRS232:		sbis UCSRA, 5				;check if data reg' is empty, if so
				rjmp putRS232				;load with new char to send
				out UDR, out_char
				wdr
				ret


;************************* LCD driver routines ********************************		

;lcd_com	- send command codes to LCD
;line_one	- set LCD cursor to the first line
;line_two	- set LCD cursor to the second line
;clrscn		- clear the LCD
;putlcd		- display individual chars

;position cursor to start of first line
line_one:		ldi out_char, $80
				rcall lcd_com
				ret

;position cursor to start of second line
line_two:		ldi out_char, $c0
				rcall lcd_com
				ret
				
;clear lcd and reset cursor
clrscn:			ldi out_char, $01			;load clr command byte
				rcall lcd_com
				rcall wait_4ms				;wait for LCD to do it's thing				
				ret
									
;send command code byte in out_char to LCD as two 4 bit nibbles, hi 1st, then lo. 
;Strobe OE line (PA6) for each nibble. As PORTC is shared with the relay o/p's
;keep copy of port status and don't OUT directly		
lcd_com:		wdr
				cbi PORTB, 0				;set RS lo for control	
				andi PORTCcopy, $0f			;clr hi bits of copy reg'				
				mov temp, out_char			;make copy of char'
				andi temp,$f0				;strip lower 4 bits
				or PORTCcopy, temp			;dont disturb PC0.PC3
				out PORTC, PORTCcopy		;write hi nibble
				sbi PORTA, 6				;set OE bit
				rcall Swait					;wait for LCD
				cbi PORTA, 6				;reset OE

				rcall Swait					;wait a while ~ 3us

				andi PORTCcopy, $0f			;clr hi bits of copy reg'
				mov temp, out_char			;get byte back
				swap temp					;swap hi and lo nibbles
				andi temp, $f0				;strip lower 4 bits		
				or PORTCcopy, temp			;dont disturb PC0.PC3
				out PORTC, PORTCcopy		;write hi nibble
				sbi PORTA, 6				;set OE bit
				rcall Swait					;wait for LCD
				cbi PORTA, 6				;reset OE

				rcall wait_50us				;wait for LCD
				rcall wait_50us
				ret
			
;send character in out_char to LCD as two 4 bit nibbles, hi 1st, then lo. 
;rs (PB0) set to 1 for this, strobe (PA6) OE as req'd
putlcd:			wdr
				sbi PORTB, 0				;set rs hi to indicate display char'
				andi PORTCcopy, $0f			;clr hi bits of copy reg'
				mov temp, out_char			;make copy
				andi temp, $f0				;strip lower 4 bits
				or PORTCcopy, temp			;dont disturb PC0.PC3
				out PORTC, PORTCcopy		;write hi nibble
				sbi PORTA, 6				;set OE bit
				rcall Swait					;wait for LCD
				rcall Swait
				cbi PORTA, 6				;reset OE

				rcall Swait					;wait about 3uS

				andi PORTCcopy, $0f			;clr hi bits of copy reg'
				mov temp, out_char			;make copy
				swap temp					;swap hi and lo nibbles
				andi temp, $f0				;strip lower 4 bits
				or PORTCcopy, temp			;dont disturb PC0.PC3
				out PORTC, PORTCcopy		;write hi nibble
				sbi PORTA, 6				;set OE bit
				rcall Swait					;wait for LCD
				rcall Swait
				cbi PORTA, 6				;reset OE			

				rcall wait_50us				;wait for LCD
				ret

;*****************************************************************************************
;non specific output character & string output routines which send o/p to device
;selected by opsel:    opsel = 0 --> LCD, opsel = 1 --> RS232


;print 1 space char
space:			ldi out_char, $20
				rcall putchar
				ret


;print an immediate string to the output device. 
;The string must be an even number of bytes, ;including the null as the string is 
;stored in the 16bit program memory.
print:			cli							;disable irq since we're mucking
											;around with the stack
				sts copyZH, ZH				;save existing contents of Z
				sts copyZL, ZL

				pop ZH						;get next address from top of stack
				pop ZL						;this points to the start of the string

				lsl ZL						;mult by two since in 16 bit mem'
				rol ZH

print2:			lpm 						;get char into R0 and point to next char
				tst R0						;chk for terminating null
				breq print3

				mov out_char, R0			;send char' to o/p device
				rcall putchar

				adiw ZL, 1					;update pointer to char
				rjmp print2

print3:			lsr ZH						;divide by 2 to get back to 16bit space
				ror ZL
				push ZL						;restore stack
				push ZH

				lds ZH, copyZH				;restore Z
				lds ZL, copyZL

				sei							;allow irq now
				ret


;send null terminated string in SRAM to selected output device. This can be any sort of string.
;esc ($7f) chars are skipped over
putstr:			ldi ZL, low(string)			;point to start of string
				ldi ZH, high(string)
				
putstr2:		ld out_char, Z+				;get char's untill null
				cpi out_char,0
				breq putstrex
				cpi out_char,$7f			;skip over esc chars
				breq putstr2		

				rcall putchar				;display wanted chars
				rjmp putstr2				;until end of string

putstrex:		ret


;send char in out_char to selected output device:
;opsel = 0 --> LCD, opsel = 1 --> RS232
putchar:		push temp					;save temp

				lds temp, opsel				;get o/p director
				tst temp
				brne putchar2				;branch for RS232

				pop temp					;o/p via LCD
				rcall putlcd
				ret

putchar2:		pop temp					;o/p via RS232
				rcall putRS232
				ret


;***********************************************************************************
;routines to convert between hex & ascii in various formats. Output device
;is selected by opsel

;display byte in out_char as two hex digits, out_char not changed 
puthexB:		rcall puthexHN				;show hi nybble
				rcall puthexLN
				ret


;display hi nibble in out_char as hex digit, out_char not changed
puthexHN:		push out_char
				swap out_char
				rjmp puthexLN2

;display lo nibble in out_char as hex digit, out_char not changed
puthexLN:		push out_char
puthexLN2:		mov Hchar, out_char			;display lo nybble
				rcall hex2ascii
				mov out_char, Achar
				rcall putchar
				pop out_char				;restore out_char
				ret


;convert ascii code in Achar to equiv' hex nibble in Hchar, if char is not valid 
;hex char 0..F, then return FF if Hchar
ascii2hex:		mov Hchar, Achar
				sec
				subi Hchar, $30				;subtract ascii offset for char's
				cpi Hchar, 10				;0..9
				brlo ascii2hex_3
				subi Hchar, 7				;now sub' ascii offset for chars A..F
				cpi Hchar, 10				;error if less than $a
				brlo ascii2hex_2
				cpi Hchar, 16				;error if greater than $f
				brlo ascii2hex_3
ascii2hex_2:	ser Hchar					;set Hchar to $ff		
ascii2hex_3:	ret


;convert low nibble of Hchar to ascii, ascii code return in Achar, Hchar unchanged
hex2ascii:		push R0						;save r0
				push ZH						;save Z regs
				push ZL
				
				push Hchar					;save Hchar
				ldi ZH, high(ASCII_codes*2)	;point to ascii codes
				ldi ZL, low(ASCII_codes*2)	;Z hold the address used by the LPM ins
				andi Hchar, $0f				;ensure only low nibble is used
				add ZL, Hchar				;this adds offset to start of table
				push Hchar					;save  before clearing
				clr Hchar
				adc ZH, Hchar				;now add with carry 0 to R0	
				lpm							;R0 is implied, ascii code stored in R0
				mov Achar, R0				;return ascii code in Achar
				pop Hchar 					;restore Hchar
				pop Hchar

				pop ZL						;restore Z regs
				pop ZH
				pop R0						;restore R0
				ret
					
ASCII_codes:	.db "0123456789ABCDEF"


;create a signed null terminated numeric ascii string with decimal place at 
;specified place. Number should be in fBCD, with position of decimal
;place in /temp/. leave string in /string/. if /sign/ != 0 the put '-' char
;max number of chars = 10: +xxxx.xxx\n
;smallest number 	= x.xxxxxx : DP = 6
;largest number		= xxxxxxx : DP =0
;NOTE: bin2bcd24 only produces 7 digits out, the hi nibble of fBCD3 always
;returns zero and so gets written over by the - sign or space
format_fBCD:	ldi ZL, low(string)			;set pointers to /string/
				ldi ZH, high(string)

				adiw ZL, 9					;point to end of string
				ldi Achar,0					;& write term' null
				st Z, Achar
				
format_fBCD2:	mov Hchar, fBCD0			;write each digit
				rcall chr2str				;& checking for DP
				mov Hchar, fBCD1
				rcall chr2str
				mov Hchar, fBCD2
				rcall chr2str
				mov Hchar, fBCD3			;only the lo nibble contains
				rcall chr2str				;valid number

				rcall chksign				;suppress leading zeros
				ret							;& insert sign


;convert BCD pair to ascii char's & save in string & chk for D.P
chr2str:		rcall hex2ascii
				st -Z, Achar
				rcall chkDP

				swap Hchar
				rcall hex2ascii
				st -Z, Achar
				rcall chkDP
				ret
				
	
;check if decimal point is req'd & insert into string if so. once temp goes
;to FF etc then another decimal will never be placed because the string
;is only 9 digits (max) long
chkDP:			dec temp					;if temp not 0 then skip
				cpi temp,0
				brne chkDPex
								
				dec temp					;if temp = 0, set temp to $ff
				ldi Achar, '.'				;and place d.p in string
				st -Z, Achar

chkDPex:		ret



;chk if -ve sign is required, otherwise put space & make sure sign is
;cleared afterwards & suppress leading zero's with esc char which
;the put string rountine will skip over. This removes the fixed field
;width problem.
chksign:		ldi ZL, low(string)			;set pointers to /string/
				ldi ZH, high(string)

chksign2:		ld temp, Z
				cpi temp,'.'				;stop when DP reached
				breq chksign3
				cpi temp, '0'
				brne chksign4

				ldi Achar,$7f				;if char = 0 the replace with
				st Z, Achar					;esc
				adiw ZL,1					;point to next character
				rjmp chksign2

chksign3:		ldi Achar,'0'				;restore 0 on LS of DP
				st -Z, Achar

chksign4:		tst sign					;test for sign &
				breq chksign6				;set as appropriate
				ldi Achar,'-'
				st -Z, Achar
				clr sign					;then clr sign flag
				ret

chksign6:		ldi Achar,' '
				st -Z, Achar
				ret

	
;*****************************************************************************
;ADC routines.
;Inputs are:
;ADC0 = AD8302 phase (10mV/degree)
;ADC1 = AD8302 ratio (30mV/db)
;ADC2 = external AD8307 power (25mV/db)
;ADC3 = internal AD8307 power (25mV/db)
;ADC7 = Vbat/3
;m16u1 & m16u0 = avg readings from ADC
;mp16uL & mp16uH = 15
;m16u1 & m16u0 = 15 X avg ADC = tbiL & tbinH
;fBCD3.. fBCD0 = converted BCD values


;display power reading from external AD8307 log amp based on user set reference power.
;AD8307 output is 25mV/db
disp_Epwr:			push YL
					push YH

					ldi YL, 2				;select external AD8307
					out ADMUX, YL

					rcall avg_read_adc		;read pwr from selected AD8307

					rcall conv2pwr			;convert voltage to power above reference
					clr tbinH				;clr highest byte as only using 16 bits
					rcall bin2BCD24			;convert to BCD

					ldi temp,1
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "db ",0

					pop YH
					pop YL
					ret


;display power reading from internal AD8307 log amp based on user set reference power.
;AD8307 output is 25mV/db
disp_Ipwr:			push YL
					push YH

					ldi YL, 3				;select internal AD8307
					out ADMUX, YL

					rcall avg_read_adc		;read pwr from selected AD8307

					rcall conv2pwr			;convert voltage to power above reference
					clr tbinH				;clr highest byte as only using 16 bits
					rcall bin2BCD24			;convert to BCD

					ldi temp,1
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "db ",0

					pop YH
					pop YL
					ret


;display SWR reading from interal AD8307 log amp based on user set reference power.
;AD8307 output is 25mV/db
disp_Iswr:			push YL
					push YH

					ldi YL, 3				;select internal AD8307
					out ADMUX, YL

					rcall avg_read_adc		;read pwr from selected AD8307

					rcall Iconv2swr			;convert voltage SWR
					clr tbinH				;clr highest byte as only using 16 bits
					rcall bin2BCD24			;convert to BCD

					ldi temp,3
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "     ",0

					pop YH
					pop YL
					ret


;measure & display battery voltage
disp_Vbat:			push YL
					push YH

					ldi YL, 7				;read battery voltage/3
					out ADMUX, YL
					rcall avg_read_adc

					rcall conv2volts		;multiply by 15 to convert to volts
					clr tbinH				;clr highest byte as only using 16 bits
					rcall bin2BCD24			;convert to BCD

					clr sign
					ldi temp,3				;display battery voltage
					rcall format_fBCD		;as xx.xxx volts
					rcall putstr

					rcall print
					.db "Vdc",0

					pop YH
					pop YL
					ret


;display phase angle measured by AD8302. o/p is 10 mV/degree, centred on 900 mV, with span of
;0 to 1800 mV. Input is ADC0
disp_angle:			push YL
					push YH

					ldi YL, 0				;read phase i/p
					out ADMUX, YL
					rcall avg_read_adc

					rcall conv2deg			;multiply by 5 to convert to degrees
					clr tbinH				;clr highest byte as only using 16 bits
					rcall bin2BCD24			;convert to BCD

					ldi temp, 1
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "deg  ",0

					pop YH
					pop YL
					ret


;display ratio measured by AD8302, o/p is 30mV/db, centred on 900 mV = 0, and span
;is -30db..+30db. Input is ADC1
disp_mag:			push YL
					push YH

					ldi YL, 1				;read magnitude i/p
					out ADMUX, YL
					rcall avg_read_adc

					rcall conv2db			;convert voltage to db
					clr tbinH				;clr highest byte as only using 16 bits
					rcall bin2BCD24			;convert to BCD

					ldi temp,1
					rcall format_fBCD
					rcall putstr

					rcall print
					.db "db   ",0

					pop YH
					pop YL
					ret


;display ratio measured by AD8302 as VSWR. Assumes use of 10db attenuator
;on input B of AD8302
disp_vswr:			push YL
					push YH

					ldi YL, 1				;read magnitude i/p
					out ADMUX, YL
					rcall avg_read_adc

					rcall conv2swr			;then convert db to VSWR
					rcall bin2BCD24			;convert to BCD

					ldi temp,3				;now display VSWR
					rcall format_fBCD
					rcall putstr

					rcall print				;overwrite 'db' if exisiting
					.db "     ",0			;from previous data


					pop YH
					pop YL
					ret



;read ADC 32 times and return average of readings. Average of readings left in 
;mp16uH & mp16uL. Set ADMUX before calling
avg_read_adc:		clr mp16uL
					clr mp16uH
					ldi mc16uH, 32			;use mc16uH as it's free at the moment

avg_read_adc2:		rcall read_adc			;read input and add to existing
					in temp, ADCL
					add mp16uL, temp
					in temp, ADCH
					adc mp16uH, temp

					dec mc16uH
					brne avg_read_adc2
							
					asr mp16uH				;calc' average by dividing by num'				
					ror mp16uL				;of samples. ie 2*2*2*2*2 = 32									
					asr mp16uH
					ror mp16uL								
					asr mp16uH
					ror mp16uL									
					asr mp16uH
					ror mp16uL
					asr mp16uH
					ror mp16uL
					ret


;perform a single conversion, ADMUX must be set by calling routine to select 
;required input. Data must be read by the calling routine ADCL first,then ADCH.
read_adc:			sbi ADCSRA, 6			;start conversion
read_adc2:			sbis ADCSRA, 4			;wait until conversion is complete
					rjmp read_adc2
					sbi ADCSRA, 4			;reset conversion complete flag
					ret			



;convert ADC output to battery volts. Remember that the input is divided
;by 3 by a voltage divider, hence the factor of 15 instead of 5
;i/p value already in mp16u0 & mp16u1, leave result in m16u0 & m16u1
conv2volts:			
					ldi mc16uL, 15
					clr mc16uH
					rcall mpy16U
					ret


;convert ADC reading to degrees.
;i/p value already in mp16u0 & mp16u1, leave result in m16u0 & m16u1. The 
;AD8302 o/p scale is ~1800 mV = 0 deg' to 0 mV = 180 deg.
;raw binary value is multiplied by ~100, then offset ~36000 is subtracted
;the result is divided by 20 to leave the answer in degrees.
;Phase intercept & slope are set by a calibrate routine using an accuratly known quarter wave line.
conv2deg:			lds mc16uL, phaseslopL	;slope of the phase function ~ 100
					lds mc16uH, phaseslopH
					rcall mpy16U

					lds temp, phaseintL		;intercept ~36000
					sub m16u0,temp			;Subtract low bytes
					lds temp, phaseintH
					sbc m16u1,temp			;Subtract high byte with carry

					brmi conv2deg2			;if negative, OK

					clr m16u0				;if positive, set to zero
					clr m16u1				;so that we dont get wrong
					rjmp conv2deg3			;value

conv2deg2:			com	m16u0				;Invert low byte	;Calculated by 
					com	m16u1				;Invert high byte	;inverting all 
					subi m16u0,low(-1)		;Add 0x0001, low byte bits then adding
					sbci m16u1,high(-1)		;Add high byte		;one (0x0001)

conv2deg3:			mov dd16uL, m16u0		;divide by 20 to give correct angle
					mov dd16uH, m16u1
					ldi dv16uL, low(20)
					ldi dv16uH, high(20)

					rcall div16u 
												
					mov tbinL,dres16uL		;move result for conversion to BCD
					mov tbinM,dres16uH					
					clr sign
					ret


;convert ADC reading from AD8302 to db. Steps are:
;1 - multiply by 10 to keep resolution
;2 - subtract 1800 for offset
;3 - divide by 6 to db
;leave result in tbinL & tbinH
conv2db:			ldi mc16uL, 10
					clr mc16uH
					rcall mpy16U			;leaves result in m16u0 & m16u1

					clr sign				;this will be set if res'l is -ve

					subi m16u0,low(1800)	;Subtract low bytes
					sbci m16u1,high(1800)	;Subtract high byte with carry

					brpl conv2db_2			;branch if result is positive

					com	m16u0				;Invert low byte	;Calculated by 
					com	m16u1				;Invert high byte	;inverting all 
					subi m16u0,low(-1)		;Add 0x0001, low byte bits then adding
					sbci m16u1,high(-1)		;Add high byte		;one (0x0001)

					clr sign				;use this reg' as negative flag
					inc sign				;but cant set it immediately...

conv2db_2:			mov dd16uL, m16u0		;divide ratio by 6
					mov dd16uH, m16u1
					ldi dv16uL, low(6)
					ldi dv16uH, high(6)

					rcall div16u 
												
					mov tbinL,dres16uL		;move result for conversion to BCD
					mov tbinM,dres16uH					
					ret



;convert the AD8302 magnitude output to db & convert to SWR via lookup table
;SWR returned in tbinL & tbinM. Table data is stored in hi..lo byte order
;ADC value * 5 = mV, & 1db = 30 mV so (ADC value * 2)/6 = db
;Reference is set with Zu port O/C or S/C & 50 ohm terminator on Zo port.
;In this case the voltage o/p increases with decreasing SWR (or R/L) as
;the external hardware is configured that way.
conv2swr:			ldi mc16uL, low(10)		;convert to mv*2
					ldi mc16uH, high(10)
					rcall mpy16U			;leaves result in m16u0 & m16u1

					mov dd16uL, m16u0		;divide by 6 to convert to db
					mov dd16uH, m16u1		;since we multiplied by 10 at start
					ldi dv16uL, low(6)
					ldi dv16uH, high(6)

					rcall div16u 

					sts tmpswrL,dres16uL	;save value for possible
					sts tmpswrH,dres16uH	;reference use

					sub dres16uL, swrrefL	;subtract reference from current
					sbc dres16uH, swrrefH
					brpl conv2swr_3			;chk for undeflow

					clr dres16uL			;if underflow set result to zero
					clr dres16uH
					
conv2swr_3:			ldi ZL, low(swr_table)	;point to start of table
					ldi ZH, high(swr_table)
					add ZL, dres16uL		;add db value to point to
					adc ZH, dres16uH		;swr entry

					lsl ZL					;mult' by 2 since we're loading
					rol ZH					;from program memory

					lpm	tbinL, Z+			;get low byte & save & point 2 next
					lpm	tbinM, Z			;get high byte & save
					clr tbinH

					ret


;set value of swr reference from previously stored value. 
;NOTE: Should be set with 50 ohm load on RL bridge.
set_swrref:			lds swrrefL,tmpswrL		;copy of swr value for setting
					lds swrrefH,tmpswrH		;new reference
					ret


;convert ADC reading from AD8307 to db above reference. Steps are:
;1 - multiply by 50 to convert to mV keep resolution
;2 - subtract reference for offset
;3 - divide by 25 to convert from millivolts to db
conv2pwr:			ldi mc16uL, low(50)		;convert to mv*10
					ldi mc16uH, high(50)
					rcall mpy16U			;leaves result in m16u0 & m16u1

					rcall set_pwr_src		;set pwr ref' and subtract

					brpl conv2pwr_2			;branch if result is positive

					com	m16u0				;Invert low byte	;Calculated by 
					com	m16u1				;Invert high byte	;inverting all 
					subi m16u0,low(-1)		;Add 0x0001, low byte bits then adding
					sbci m16u1,high(-1)		;Add high byte		;one (0x0001)

					clr sign				;use this reg' as negative flag
					inc sign				;but can't set it immediately..

conv2pwr_2:			mov dd16uL, m16u0		;divide by 25 to convert to db
					mov dd16uH, m16u1
					ldi dv16uL, low(25)
					ldi dv16uH, high(25)

					rcall div16u 
												
					mov tbinL,dres16uL		;move result for conversion to BCD
					mov tbinM,dres16uH					
					ret


;convert the internal AD8307 output to db & convert to SWR via lookup table
;SWR returned in tbinL & tbinM. Table data is stored in hi..lo byte order
;ADC value * 50 = mV, & 1db = 25 mV
;Reference is set with Zu port O/C or S/C & 50 ohm terminator on Zo port
; no sign chking done as SWR is always +ve. In this case the o/p voltage
;from the log amp decreases with decreasing SWR (or R/L) so the calculated value
;has to be converted to a positive value by complementing.

Iconv2swr:			ldi mc16uL, low(50)		;convert to mv*10
					ldi mc16uH, high(50)
					rcall mpy16U			;leaves result in m16u0 & m16u1

					sts tmpswrL, m16u0		;copy of raw swr value for setting
					sts tmpswrH, m16U1		;new swr reference if req'd

					clr sign				;this will be set if res'l is -ve

					sub m16u0,swrrefL		;Subtract low bytes
					sbc m16u1,swrrefH		;Subtract high byte with carry

					com	m16u0				;Invert low byte	;Calculated by 
					com	m16u1				;Invert high byte	;inverting all 
					subi m16u0,low(-1)		;Add 0x0001, low byte bits then adding
					sbci m16u1,high(-1)		;Add high byte		;one (0x0001)

					mov dd16uL, m16u0		;divide by 25 to convert to db
					mov dd16uH, m16u1		;since we multiplied by 10 at start
					ldi dv16uL, low(25)
					ldi dv16uH, high(25)

					rcall div16u 
					
Iconv2swr_3:		ldi ZL, low(swr_table)	;point to start of table
					ldi ZH, high(swr_table)
					add ZL, dres16uL		;add db value to point to
					adc ZH, dres16uH		;swr entry

					lsl ZL					;mult' by 2 since we're loading
					rol ZH					;from program memory

					lpm	tbinL, Z+			;get low byte & save & point 2 next
					lpm	tbinM, Z			;get high byte & save
					clr tbinH

					ret


;setup power reference base on whether sensor is external or internal
;0 = external, 1 = internal pwr sensor
set_pwr_src:		tst pwr_extint			;chk flag that selects sensor
					brne set_pwr_src2		;source
					rcall ext_pwr_ref
					ret
set_pwr_src2:		rcall int_pwr_ref
					ret


;setup for external power measurements
ext_pwr_ref:		sts pwrcpyEL, m16u0		;copy of raw pwr value for setting
					sts pwrcpyEH, m16U1		;new power reference if req'd

					clr sign				;this will be set if res'l is -ve

					sub m16u0,pwrrefEL		;Subtract low bytes
					sbc m16u1,pwrrefEH		;Subtract high byte with carry
					ret


;setup for internal power measurements
int_pwr_ref:		sts pwrcpyIL, m16u0		;copy of raw pwr value for setting
					sts pwrcpyIH, m16U1		;new power reference if req'd

					clr sign				;this will be set if res'l is -ve

					sub m16u0,pwrrefIL		;Subtract low bytes
					sbc m16u1,pwrrefIH		;Subtract high byte with carry
					ret



;read external & internal reference powers from EEPROM at boot. 
;Sequence is:
;pwrrefsetE(L) -> pwrrefE(L)	#0
;pwrrefsetE(H) -> pwrrefE(H)	#1
;pwrrefsetI(L) -> pwrrefI(L)	#2
;pwrrefsetI(H) -> pwrrefI(H)	#3
;also set RL_SWR display flag to default to return loss
read_pwrref:		cli
					ldi ZL,low(pwrrefsetE)	;point to stored ref' power
					ldi ZH,high(pwrrefsetE)	;EEPROM adr

					ldi XL,low(pwrcpyEL)	;point to SRAM equivalents
					ldi XH,high(pwrcpyEL) 

					ldi cnt24a, 4			;# bytes to read
read_pwrref2:		out EEARL, ZL			;set pointers to adr in eeprom
					out EEARH, ZH

					sbi EECR,0				;set read enable bit in eeprom
					in temp, EEDR			;control reg', then read eeprom
					st X+,temp				;and save byte

					adiw ZL,1				;increment adr pointer
					dec cnt24a				;and decrement # bytes until 0
					brne read_pwrref2

					lds pwrrefEL,pwrcpyEL	;copy power regs into regs
					lds pwrrefEH,pwrcpyEH
					lds pwrrefIL,pwrcpyIL
					lds pwrrefIH,pwrcpyIH

					sei

					clr temp				;set Return Loss / VSWR display
					sts RL_SWR,temp			;flag to R.L
					sts tmpswrL,temp		;set swr ref' to 0 default
					sts tmpswrH,temp
					mov swrrefL,temp
					mov swrrefH,temp
					ret


;set new temporary(SRAM) or permanent(EEPROM) refernce settings depending
;on sensor source flag
;pwr_extint: 0 = external, 1 = internal pwr sensor
set_pwrref:			tst pwr_extint
					brne set_pwrref2		;branch for int' sensor
							
					rcall set_pwrrefE		;set external references
					ret
set_pwrref2:		rcall set_pwrrefI		;set internal references
					ret


;set new external reference power SRAM & in EEPROM if required
set_pwrrefE:		rcall wait4release		;wait for buttons to be released

					lds pwrrefEL,pwrcpyEL	;set reference to new value
					lds pwrrefEH,pwrcpyEH

					rcall clrscn
					rcall print
					.db "Save to EEPROM?",0
					rcall line_two
					rcall print
					.db "Yes No ",0			;1, 2

					rcall get_selection		;get selected function
					cpi pushbut, 1
					breq set_pwrrefE2

					rcall clrscn			;show operation is complete
					rcall print
					.db "New temp ref ",0
					rcall wait_1sec
					rcall wait_1sec
					ret

;write new reference to EEPROM when required
set_pwrrefE2:		ldi ZH,high(pwrrefsetE)	;initialise  pointer
					ldi ZL,low(pwrrefsetE)	;to pwrref area in EEPROM

					lds temp, pwrcpyEL		;copy data to EEPROM data reg
					rcall wr_eeprom			;lo byte first
					adiw ZL,1				;point to next location

					lds temp, pwrcpyEH		;hi byte next
					rcall wr_eeprom

					rcall clrscn			;show operation is complete
					rcall print
					.db "New external ",0
					rcall line_two
					rcall print
					.db "ref to EEPROM  ",0
					rcall wait_1sec
					rcall wait_1sec
					ret



;set new internal reference power SRAM & in EEPROM if required
set_pwrrefI:		rcall wait4release		;wait for buttons to be released

					lds pwrrefIL,pwrcpyIL	;set reference to new value
					lds pwrrefIH,pwrcpyIH

					rcall clrscn
					rcall print
					.db "Save to EEPROM?",0
					rcall line_two
					rcall print
					.db "Yes No ",0			;1, 2

					rcall get_selection		;get selected function
					cpi pushbut, 1
					breq set_pwrrefI2

					rcall clrscn			;show operation is complete
					rcall print
					.db "New temp ref ",0
					rcall wait_1sec
					rcall wait_1sec
					ret

;write new reference to EEPROM when required
set_pwrrefI2:		ldi ZH,high(pwrrefsetI)	;initialise  pointer
					ldi ZL,low(pwrrefsetI)	;to pwrref area in EEPROM

					lds temp, pwrcpyIL		;copy data to EEPROM data reg
					rcall wr_eeprom			;lo byte first
					adiw ZL,1				;point to next location

					lds temp, pwrcpyIH		;hi byte next
					rcall wr_eeprom

					rcall clrscn			;show operation is complete
					rcall print
					.db "New internal ",0
					rcall line_two
					rcall print
					.db "ref to EEPROM  ",0
					rcall wait_1sec
					rcall wait_1sec
					ret


;write contents of 'temp' reg to address in eeprom pointed to by Z
wr_eeprom:			wdr						;refresh watchdog timer
					cli						;disable irq for duration
wr_eeprom2:			sbic EECR,1				;wait for eeprom WE goes clr
					rjmp wr_eeprom2
				
					out EEARH, ZH			;set EEPROM adr reg's
					out EEARL, ZL
					out EEDR, temp			;set EEPROM data reg

					ldi temp, $04			;set EEMWE and clr EEWE
					out EECR, temp
					sbi EECR, 1				;set EEWE

					sei						;re-enable irq
					ret


;************* maths & base conversion routines ******************************
;*
;* this code is based on the AVR application notes: AVR204
;* "bin2BCD16" - 16-bit Binary to BCD conversion
;* and has been modified to do 24 bit conversions
;*
;* This subroutine converts a 24-bit number (tbinH:tbinM:tbinL) to a 7-digit 
;* packed BCD number represented by 4 bytes (fBCD3:fBCD2:fBCD1:fBCD0).
;* MSD of the 7-digit number is placed in the lowermost nibble of fBCD3.
;*  
;* Low registers used	:4 (fBCD0,fBCD1,fBCD2, fBCD3) 
;* High registers used  :5(tbinL,tbinM,tbinH,cnt24a,tmp24a)	
;* Pointers used	:Z
;*
;***************************************************************************

bin2BCD24:		push tbinL				;save these for later
				push tbinM
				push tbinH
				push ZH
				push ZL
				
				ldi	cnt24a,24			;Init loop counter
				clr fBCD3	
				clr	fBCD2				;clear result (4 bytes)
				clr	fBCD1		
				clr	fBCD0		
				clr	ZH					;clear ZH 

bBCDx_1:		lsl	tbinL				;shift input value
				rol tbinM
				rol	tbinH				;through all bytes
				rol	fBCD0				;
				rol	fBCD1
				rol	fBCD2
				rol fBCD3
				dec	cnt24a				;decrement loop counter
				brne bBCDx_2			;if counter not zero

				pop ZL					;restore Z reg
				pop ZH
				pop tbinH				;restore initial binary values
				pop tbinM
				pop tbinL

				ret						;return

bBCDx_2:		ldi	r30,AfBCD3+1		;Z points to result MSB + 1
bBCDx_3:		ld	tmp24a,-Z			;get (Z) with pre-decrement
				subi tmp24a,-$03		;add 0x03
				sbrc tmp24a,3			;if bit 3 not clear
				st Z,tmp24a				;store back
				ld tmp24a,Z				;get (Z)
				subi tmp24a,-$30		;add 0x30
				sbrc tmp24a,7			;if bit 7 not clear
				st Z,tmp24a				;store back
				cpi ZL,AfBCD0			;done all three?
				brne bBCDx_3			;loop again if not
				rjmp bBCDx_1		



;******************************************************************************
;*From AVR201.asm application note & modified to suit this application
;*
;* FUNCTION
;*	mul16x16_32, renamed mpy16u
;* DECRIPTION
;*	Unsigned multiply of two 16bits numbers with 32bits result.
;* USAGE
;*	r19:r18:r17:r16 	   = r23:r22 * r21:r20
;* m16u3:m16u2:m16u1:m16u0 = mp16uH:mp16uL * mc16uH:mc16uL
;* STATISTICS
;*	Cycles :	17 + ret
;*	Words :		13 + ret
;*	Register usage: r2 to r4 and r16 to r23 (11 registers)
;*  Register usage:multemp0..multemp2 & c16uL..m16u3
;* NOTE
;*	Full orthogonality i.e. any register pair can be used as long as
;*	the 32bit result and the two operands does not share register pairs.
;*	The routine is non-destructive to the operands.
;*
;******************************************************************************

mpy16u:			clr	multemp2
				mul mp16uH, mc16uH	; ah * bh
				movw m16u3:m16u2, multemp1:multemp0
				mul mp16uL, mc16uL	; al * bl
				movw m16u1:m16u0,multemp1:multemp0
				mul mp16uH, mc16uL	; ah * bl
				add m16u1,multemp0
				adc m16u2,multemp1
				adc m16u3,multemp2
				mul mc16uH,mp16uL	; bh * al
				add m16u1,multemp0
				adc m16u2,multemp1
				adc m16u3,multemp2
				ret


;******************************************************************************
;*	from app note AVR200b, a 16bit/16bit routine
;*
;* "div16u" - 16/16 Bit Unsigned Division
;*
;* This subroutine divides the two 16-bit numbers 
;* "dd16uH:dd16uL" (dividend) and "dv16uH:dv16uL" (divisor). 
;* The result is placed in "dres16uH:dres16uL" and the remainder in
;* "drem16uH:drem16uL".
;*  
;* Number of words		:196 + return
;* Number of cycles		:148/173/196 (Min/Avg/Max)
;* Low registers used	:2 (drem16uL,drem16uH)
;* High registers used  :4 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH)
;*
;***************************************************************************

div16u:			clr	drem16uL			;clear remainder Low byte
				sub	drem16uH,drem16uH	;clear remainder High byte and carry

				rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_1				;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_2				;else
d16u_1:			sec						;set carry to be shifted into result

d16u_2:			rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_3				;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_4				;else
d16u_3:			sec						;set carry to be shifted into result

d16u_4:			rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_5				;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_6				;else
d16u_5:			sec						;set carry to be shifted into result

d16u_6:			rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_7				;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_8				;else
d16u_7:			sec						;set carry to be shifted into result

d16u_8:			rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_9				;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_10			;else
d16u_9:			sec						;set carry to be shifted into result

d16u_10:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_11			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_12			;else
d16u_11:		sec						;set carry to be shifted into result

d16u_12:		rol dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_13			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp	d16u_14			;else
d16u_13:		sec						;set carry to be shifted into result

d16u_14:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_15			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp	d16u_16			;else
d16u_15:		sec						;set carry to be shifted into result

d16u_16:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_17			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_18			;else
d16u_17:		sec						;set carry to be shifted into result

d16u_18:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_19			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_20			;else
d16u_19:		sec						;set carry to be shifted into result

d16u_20:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_21			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_22			;else
d16u_21:		sec						;set carry to be shifted into result

d16u_22:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_23			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_24			;else
d16u_23:		sec						;set carry to be shifted into result

d16u_24:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH
				brcc d16u_25			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_26			;else
d16u_25:		sec						;set carry to be shifted into result

d16u_26:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc	d16u_27			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_28			;else
d16u_27:		sec						;set carry to be shifted into result

d16u_28:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH	
				brcc d16u_29			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_30			;else
d16u_29:		sec						;set carry to be shifted into result

d16u_30:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				rol	drem16uL			;shift dividend into remainder
				rol	drem16uH
				sub	drem16uL,dv16uL		;remainder = remainder - divisor
				sbc	drem16uH,dv16uH
				brcc d16u_31			;if result negative
				add	drem16uL,dv16uL		;restore remainder
				adc	drem16uH,dv16uH
				clc						;clear carry to be shifted into result
				rjmp d16u_32			;else
d16u_31:		sec						;set carry to be shifted into result

d16u_32:		rol	dd16uL				;shift left dividend
				rol	dd16uH
				ret

 
;************** Software time delay loops *************************************

;wait approx 50us @ 4.096MHz (1+16*13+7 = 216 cycles)
wait_50us:		ldi temp, 16			;1 cycle
wait_50us_2:	rcall Swait				;3 + 13 = 16 cycles
				cpi temp,0				;1 cycle
				brne wait_50us_2		;2 cycles except for last
				ret						;4 cycles
				

; execution time = 13 cycles ~  3us @ 4.096MHz, decrement temp every time through
Swait:			push temp				;2 cycles
				pop temp				;2 cycles
				push temp				;2 cycles
				pop temp				;2 cycles
				dec temp				;1 cycle	
				ret						;4 cycles


;wait between 4 & 8 ms
wait_4ms:		ldi temp,2		
				rjmp wait_1

;wait approx 30ms by waiting for 7 4ms interuppts
wait_30ms:		ldi temp, 7
				rjmp wait_1

;wait approx 100ms by waiting for 25 4ms interuppts
wait_100ms:		ldi temp, 25
				rjmp wait_1	

;wait approximately 1 second by waiting for 250 4ms interrputs
wait_1sec:		ldi temp,250
wait_1:			mov FROK, temp
				
wait_2:			wdr
				tst FROK
				brne wait_2
				ret



;***************************************************************************************


;swr lookup table. Table is in order of return loss from 0 db to 50.0db
;SWR is found by using the RL*10 as an entry into the table. The table values
;are swr*1000 so need to be displayed with 3 decimal points.

swr_table:	
.dw	65535,65535,65535,57912,43437,34753,28964,24830,21730,19319
.dw 17391,15814,14500,13388,12435,11610,10888,10251,9686,9179
.dw 8724,8313,7938,7597,7284,6997,6731,6486,6258,6046
.dw 5848,5663,5490,5327,5174,5030,4894,4766,4644,4529
.dw	4419,4315,4216,4122,4032,3946,3864,3786,3711,3639
.dw	3570,3504,3440,3379,3320,3263,3209,3156,3106,3057
.dw	3010,2964,2920,2877,2836,2796,2758,2720,2684,2649
.dw	2615,2581,2549,2518,2488,2458,2430,2402,2375,2348
.dw	2323,2298,2274,2250,2227,2204,2182,2161,2140,2120
.dw	2100,2080,2062,2043,2025,2007,1990,1973,1957,1941
.dw	1925,1910,1894,1880,1865,1851,1837,1824,1811,1798
.dw	1785,1772,1760,1748,1737,1725,1714,1703,1692,1681
.dw	1671,1661,1651,1641,1631,1622,1612,1603,1594,1586
.dw	1577,1568,1560,1552,1544,1536,1528,1521,1513,1506
.dw	1499,1491,1484,1478,1471,1464,1458,1451,1445,1439
.dw	1433,1427,1421,1415,1409,1404,1398,1393,1387,1382
.dw	1377,1372,1367,1362,1357,1352,1347,1343,1338,1333
.dw	1329,1325,1320,1316,1312,1308,1304,1300,1296,1292
.dw	1288,1284,1281,1277,1273,1270,1266,1263,1259,1256
.dw	1253,1250,1246,1243,1240,1237,1234,1231,1228,1225
.dw	1222,1219,1217,1214,1211,1208,1206,1203,1201,1198
.dw	1196,1193,1191,1188,1186,1184,1181,1179,1177,1175
.dw	1173,1170,1168,1166,1164,1162,1160,1158,1156,1154
.dw	1152,1151,1149,1147,1145,1143,1141,1140,1138,1136
.dw	1135,1133,1131,1130,1128,1127,1125,1124,1122,1121
.dw	1119,1118,1116,1115,1114,1112,1111,1109,1108,1107
.dw	1106,1104,1103,1102,1101,1099,1098,1097,1096,1095
.dw	1094,1092,1091,1090,1089,1088,1087,1086,1085,1084
.dw	1083,1082,1081,1080,1079,1078,1077,1076,1075,1074
.dw	1074,1073,1072,1071,1070,1069,1068,1068,1067,1066
.dw	1065,1065,1064,1063,1062,1062,1061,1060,1059,1059
.dw 1058,1057,1057,1056,1055,1055,1054,1053,1053,1052
.dw 1052,1051,1050,1050,1049,1049,1048,1047,1047,1046
.dw	1046,1045,1045,1044,1044,1043,1043,1042,1042,1041
.dw 1041,1040,1040,1039,1039,1038,1038,1038,1037,1037
.dw 1036,1036,1035,1035,1035,1034,1034,1033,1033,1033
.dw	1032,1032,1031,1031,1031,1030,1030,1030,1029,1029
.dw	1029,1028,1028,1028,1027,1027,1027,1026,1026,1026
.dw	1025,1025,1025,1025,1024,1024,1024,1024,1023,1023
.dw	1023,1022,1022,1022,1022,1021,1021,1021,1021,1020
.dw	1020,1020,1020,1020,1019,1019,1019,1019,1018,1018
.dw 1018,1018,1018,1017,1017,1017,1017,1017,1016,1016
.dw 1016,1016,1016,1015,1015,1015,1015,1015,1015,1014
.dw 1014,1014,1014,1014,1014,1013,1013,1013,1013,1013
.dw 1013,1013,1012,1012,1012,1012,1012,1012,1012,1011
.dw 1011,1011,1011,1011,1011,1011,1011,1010,1010,1010
.dw 1010,1010,1010,1010,1010,1010,1009,1009,1009,1009
.dw 1009,1009,1009,1009,1009,1008,1008,1008,1008,1008
.dw 1008,1008,1008,1008,1008,1008,1007,1007,1007,1007
.dw 1007,1007,1007,1007,1007,1007,1007,1007,1006,1006
.dw	1006



;****************     the end     ***********************************************
