;*****************************************************************************************
; Copyright © [4/01/2000] Scenix Semiconductor, Inc. All rights reserved.
;
; Scenix Semiconductor, Inc. assumes no responsibility or liability for
; the use of this [product, application, software, any of these products].
; Scenix Semiconductor conveys no license, implicitly or otherwise, under
; any intellectual property rights.
; Information contained in this publication regarding (e.g.: application,
; implementation) and the like is intended through suggestion only and may
; be superseded by updates. Scenix Semiconductor makes no representation
; or warranties with respect to the accuracy or use of these information,
; or infringement of patents arising from such use or otherwise.
;*****************************************************************************************
;
; Filename:
;		DTMF_det.src
;
; Authors:
;		Chris Fogelklou
;		Applications Engineer	
;		Scenix Semiconductor, Inc.
;
; Revision:
;		1.01
;
; Target Part(s):
;		AB9929AA
;
; Default Oscillator Frequency:
;		50MHz
;
; Assemblers:
;		SXKey28L Version 1.09 for SX18/20/28AC
;		SASM Version 1.45.2		
;		SXKey52 Version 1.19 for SX48/52BD (only assembled, not _properly_ tested)
;
; Date Written:
;		Feb 24, 2000
;
; Program Description:
;		This DTMF Detection code contains a new reference algorithm. It is a ported 
;		customer code, and it is not a "revised" version of any of the previous versions 
;		of the DTMF Detection.
;		It contains a 9600bps UART for output of received digits to a terminal screen.
;		This program is ready to run on V.23 Modem board (tested on Rev. 1.2) with a minor
;		modification to the board; the input filters has to be disabled;
;		The LP filter has to be disabled by moving the top lead of R25 to pin 14 of U4. 
;		By doing this, the two OP-amps doing the LP-filtering is bypassed. The HP-filter 
;		is taken care of in the program by setting the "cntrl_3" pin on RB.5 low. This action
;		forces the output from this filter to gnd.
;		
; Interface Pins:
;	      	rs232RxPin	equ	ra.1	; RS-232 reception pin
;		rs232TxPin	equ	ra.2	; RS-232 transmission pin
;		ledPin		equ	rb.0	; LED flasher pin 
;		dtmfHook	equ	rb.4	; drive hook low to go off-hook
;		cntrl_3		equ	rb.5	; drive cntrl_3 low to disable the output of the HPF
;		dtmfdADInPin	equ	rc.2	; A/D Input pin 
;		dtmfdADFdbkPin	equ	rc.3	; A/D feedback pin
;		dtmfdImp600Pin	equ	rc.5	; To cancel output signals on a 600ohm line
;	
; Revision History:
;
;	1.0  	Ported customer DTMF detection code to a demo program.
;	1.01 	Implemented according to VP guide 1.06, checkPowers routine updated to 
;		check for low level and high level according to Bellcore. Improved A/D 
;		routine by adding offset, tri-stating feedback pin when not active and removed 
;		hardcoding of pins (not "destroying" the whole port for this routine). 
;		dtmfDetectSine is rewritten to reduce cyclecount/efficiency. DTMF_SAMPL_HI is 
;		increased from 105 to 115 cycles for better results at high frequencies.
;		Modified dtmfdGetValid so that it requieres 30ms of silence to detect a valid 
;		digit. Also, the ":redoHighFrequencies" is modified to only check that
;		the freq. detected is the same as the previous -if not start all over again...
;		JEE/AEH/OG, SX-Design, Aug 11, 2000 
;
; Virtual Peripherals:
;		UART transmitt 
;		LED flasher
;		5ms Timer
;		DTMF detection routines including a simple Sigma Delta A/D module. 
;		The simple Sigma delta A/D that takes 36 samples @5MHz (1-bit) and with a Nyquist
;		frequency of Fs=10kHz. This routine is not the same as the Sigma Delta 
;		Virtual Peripheral.
;		
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, 
; SX48BD, or SX52BD.
;*****************************************************************************************
;SX18_20
;SX28
SX48_52

;*****************************************************************************************
; Assembler Used
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
;SX_Key

;*****************************************************************************************
; Runnable Demo?
; Uncomment the following line to enable the main program, creating a runnable demo
;*****************************************************************************************
;DEMO

	;*********************************************************************************
	; Assembler directives:
	;	high speed external osc, turbo mode, 8-level stack, and extended option reg.
	;
	;	SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default.
	;	SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default.
	;
	;*********************************************************************************

IFDEF SX_Key 				;SX-Key Directives
;VP_BEGIN: DTMF Detection
	watch	dtmfdSinAcc1Lo,16,sdec
	watch	dtmfdCosAcc1Lo,16,sdec
	watch	dtmfdSinAcc2Lo,16,sdec
	watch	dtmfdCosAcc2Lo,16,sdec
	watch	dtmfdSinAcc3Lo,16,sdec
	watch	dtmfdCosAcc3Lo,16,sdec
	watch	dtmfdSinAcc4Lo,16,sdec
	watch	dtmfdCosAcc4Lo,16,sdec
	watch	wreg,1,fstr
	watch	wreg,8,sdec
	watch	wreg,8,udec
	watch	dtmfdAtoDVal,8,sdec
	watch	dtmfdHiFreqPow,8,udec
	watch	dtmfdLoFreqPow,8,udec
	watch	dtmfdRefAcc1Lo,8,udec
	watch	dtmfdSampleCnt,8,udec
	watch	dtmfdResult1,8,udec
	watch	dtmfdResult2,8,udec
	watch	dtmfdResult3,8,udec
	watch	dtmfdResult4,8,udec
	watch	dtmfdHighScore,8,udec
	watch	dtmfdRunUpScore,8,udec
	watch	dtmfdRunUpIndex,8,udec
	watch	dtmfdWinnIndex,8,udec
	watch	dtmfdSampleLows,1,ubin
	;VP_END: DTMF Detection
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SX-Key
		device	SX18L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX48_52				;SX48/52/BD device directives for SX-Key
		device	oschs2
  ENDIF

		freq	50_000_000	; Debugger frequency

ELSE					;SASM Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SASM
		device	SX18,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SASM
		device	SX28,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX48_52				;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2
  ENDIF
ENDIF
		
		id	'DTMFDET4'	
		reset	resetEntry	; set reset vector

;*****************************************************************************************
; Macros
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	To support compatibility between source code written for the SX28 and the SX52,
	;	use macros.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	;*********************************************************************************
	; Macro: _bank
	; Sets the bank appropriately for all revisions of SX.
	;
	; This is required since the bank instruction has only a 3-bit operand, it cannot
	; be used to access all 16 banks of the SX48/52. For this reason, FSR.7
	; (SX48/52bd production release) needs to be set appropriately, depending
	; on the bank address being accessed.
	;
	; So, instead of using the bank instruction to switch between banks, use _bank instead.
	; 
	;*********************************************************************************

_bank	macro	1
	bank	\1

	IFDEF SX48_52
	    IF \1 & %10000000		;SX48BD and SX52BD (production release) bank instruction 
		setb	fsr.7		;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
	    ELSE
		clrb	fsr.7
	    ENDIF
	ENDIF
	endm


	;*****************************************************************************************
	; Macros for SX28/52 Compatibility
	;*****************************************************************************************
	;*********************************************************************************
	; Macro: _mode
	; Sets the MODE register appropriately for all revisions of SX.
	;
	; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand. 
	; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have 
	; the added ability of reading or writing some of the MODE registers, and therefore use
	; 5-bits of the MODE register. The  MOV M,W instruction modifies all 8-bits of the 
	; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE
	; register is written with the correct value. This macro fixes this.
	;
	; So, instead of using the MODE or MOV M,# instructions to load the M register, use
	;  _mode instead.
	; 
	;*********************************************************************************
_mode	macro	1
	IFDEF SX48_52
  expand
		mov	w,#\1		;loads the M register correctly for the SX48BD and SX52BD
		mov	m,w
  noexpand
	ELSE
  expand
		mov	m,#\1		;loads the M register correctly for the SX18AC, SX20AC
  noexpand					;and SX28AC
	ENDIF
	endm

	;*****************************************************************************************
	; INCP/DECP macros for incrementing/decrementing pointers to RAM
	; used to compensate for incompatibilities between SX28 and SX52
	;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	To support compatibility between source code written for the SX28 and the SX52,
	;	use macros.  This macro compensates for the fact that RAM banks are contiguous in
	;	the SX52, but separated by 0x20 in the SX18/28.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

INCP	macro	1
		inc	\1
	IFNDEF	SX48_52
		setb	\1.4		; If SX18,20 or SX28, keep bit 4 of the pointer = 1
	ENDIF				; to jump from $1f to $30, etc.
endm
		
DECP	macro	1
	IFDEF	SX48_52
		dec	\1
	ELSE
		clrb	\1.4		; If SX18,20 or SX28, forces rollover to next bank
		dec	\1		; if it rolls over.  (Skips banks with bit 4 = 0)
		setb	\1.4		; Eg:  $30 --> $20 --> $1f --> $1f
	ENDIF				; AND: $31 --> $21 --> $20 --> $30
endm

	;*****************************************************************************************
	; Error generating macros
	; Used to generate an error message if the label is unintentionally moved into the 
	; second half of a page.  Use for lookup tables.
	;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	Surround lookup tables with the tableStart and tableEnd macros.  An error will
	;	be generated on assembly if part of the table is in the second half of a page.
	;	
	;	Example:
	;		lookupTable1
	;			add	pc,w	; Add w register to program counter
	;		tableStart
	;			retw	0
	;			retw	4
	;			retw	8
	;			retw	4
	;		tableEnd
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

tableStart	macro 0			; Generates an error message if code that MUST be in
					; the first half of a page is moved into the second half.
	if $ & $100
		ERROR  'Must be located in the first half of a page.'
	endif
endm

tableEnd	macro 0			; Generates an error message if code that MUST be in
					; the first half of a page is moved into the second half.
	if $ & $100
		ERROR  'Must be located in the first half of a page.'
	endif
endm

;*****************************************************************************************
; Data Memory address definitions
; These definitions ensure the proper address is used for banks 0 - 7 for 2K SX devices
; (SX18/20/28) and 4K SX devices (SX48/52). 
;*****************************************************************************************
IFDEF SX48_52

global_org	=	$0A
bank0_org	=	$00
bank1_org	=	$10
bank2_org	=	$20
bank3_org	=	$30
bank4_org	=	$40
bank5_org	=	$50
bank6_org	=	$60
bank7_org	=	$70

ELSE

global_org	=	$08
bank0_org	=	$10
bank1_org	=	$30
bank2_org	=	$50
bank3_org	=	$70
bank4_org	=	$90
bank5_org	=	$B0
bank6_org	=	$D0
bank7_org	=	$F0

ENDIF
;*****************************************************************************************
; Global Register definitions
; NOTE: Global data memory starts at $0A on SX48/52 and $08 on SX18/20/28.
;*****************************************************************************************

		org	global_org

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	Use only these defined label types for global registers.  If an extra temporary 
	;	register is required, adhere to these label types.  For instance, if two temporary
	;	registers are required for the Interrupt Service Routine, use the labels isrTemp0
	;	and isrTemp1.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

flags0		equ	global_org + 0  ; stores bit-wise operators like flags 
					; and function-enabling bits (semaphores)
	;VP_BEGIN: DTMF detection
	dtmfdDetectEn	equ	flags0.1
	dtmfdSign	equ	flags0.2 ; Flag that indicates the sign of the mult.result
	dtmfdInputSign  equ	flags0.3 ; used by DTMF detection for A/D sample
	dtmfdSampleLows	equ	flags0.4 ; To sample low frequencies
	dtmfdDetectDone	equ	flags0.5 ; Flag that indicates to main program that the 
					 ; sampling period is over.
	;VP_END: DTMF detection

	;VP_BEGIN : 5ms Timer
	timer5msFlag	equ	flags0.6
	;VP_END : 5ms Timer


flags1		equ	global_org + 1  ; stores bit-wise operators like flags 
					; and function-enabling bits (semaphores)
localTemp0	equ	global_org + 2	; temporary storage register
					; Used by first level of nesting
					; Never guaranteed to maintain data
localTemp1	equ	global_org + 3	; temporary storage register	
					; Used by second level of nesting
					; or when a routine needs more than one 
					; temporary global register.	
localTemp2	equ	global_org + 4	; temporary storage register
					; Used by third level of nesting or by
					; main loop routines that need a loop 
					; counter, etc.
isrTemp0	equ	global_org + 5	; Interrupt Service Routine's temp register.  
					; Don't use this register in the mainline.
isrTemp1	equ	global_org + 6	; Interrupt Service Routine's temp register.
					; Don't use this register in the mainline.

;*****************************************************************************************
; RAM Bank Register definitions
;*****************************************************************************************

	;*********************************************************************************
	; Bank 0
	;*********************************************************************************
		org     bank0_org
bank0		=	$

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Avoid using bank0 in programs written for SX48/52.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
		org     bank1_org
bank1		=	$
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	Tip 1:
	;	Indicate which Virtual Peripherals a portion of source code or declaration belongs
	;	to with a ;VP_BEGIN: VirtualPeripheralName and VP_END: comment.
	;
	;	Tip 2:
	;	All RAM location declaration names should be
	;   	- left justified
	;	- less than 2 tabs in length
	;	- written in hungarian notation
	;	- prefixed by a truncated version of the Virtual Peripheral's name
	;	
	;	Examples:
	;
	;	;VP_BEGIN: RS232 Transmit
	;	rs232TxBank	=       $	;UART Transmit bank
	;	rs232TxHigh	ds      1	;hi byte to transmit
	;	rs232TxLow	ds      1	;low byte to transmit
	;	rs232TxCount	ds      1	;number of bits sent
	;	rs232TxDivide	ds      1	;xmit timing (/16) counter
	;	;VP_END: RS232 Transmit
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

;VP_BEGIN: VP Scheduler
isrMultiplex	ds	1	; The isrMultiplex register is used to switch to a new
;VP_END: VP Scheduler		; execution thread on each pass of the ISR.

;VP_BEGIN : 5ms Timer
Timer5msBank	=	$
Timer5ms	ds	1
Timer5msDiv	ds	1
;VP_END : 5ms Timer

;VP_BEGIN: RS232 Transmit
rs232TxBank	=       $	;UART Transmit bank
rs232TxHigh	ds      1	;hi byte to transmit
rs232TxLow	ds      1	;low byte to transmit
rs232TxCount	ds      1	;number of bits sent
rs232TxDivide	ds      1	;xmit timing (/16) counter
;VP_END: RS232 Transmit

	;*********************************************************************************
	; Bank 2
	;*********************************************************************************
		org	bank2_org
bank2		=	$
	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
		org	bank3_org
bank3		=	$
	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
		org	bank4_org
bank4		=	$
	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
		org	bank5_org
bank5		=	$
;VP_BEGIN: DTMF Detection
dtmfdRefBank	= 	$	; Accumulator/counters used to run the references.
dtmfdRefAcc1Lo	ds	1	; the low byte of the phase accumulator for the lowest frequency being generated
dtmfdRefAcc1Hi	ds	1	
dtmfdRefAcc2Lo	ds	1
dtmfdRefAcc2Hi	ds	1
dtmfdRefAcc3Lo	ds	1
dtmfdRefAcc3Hi	ds	1
dtmfdRefAcc4Lo	ds	1
dtmfdRefAcc4Hi	ds	1	; the high byte of the phase accumulator for the highest frequency being generated
dtmfdHiFreqInd	ds	1
dtmfdLoFreqInd	ds	1
dtmfdHiFreqPow	ds	1
dtmfdLoFreqPow	ds	1
dtmfdSampleCnt	ds	1
dtmfdDigitIndex	ds	1	; the index that points to a valid DTMF digit 
;VP_END: DTMF Detection

	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
		org	bank6_org
bank6		=	$
;VP_BEGIN: DTMF Detection
dtmfdCh1	=	$
dtmfdSinAcc1Lo	ds	1	; Sine and cosine accumulators for accumulating the results of the DFT
dtmfdSinAcc1Hi	ds	1
dtmfdCosAcc1Lo	ds	1
dtmfdCosAcc1Hi	ds	1	
dtmfdSinAcc2Lo	ds	1
dtmfdSinAcc2Hi	ds	1
dtmfdCosAcc2Lo	ds	1
dtmfdCosAcc2Hi	ds	1
dtmfdSinAcc3Lo	ds	1
dtmfdSinAcc3Hi	ds	1	
dtmfdCosAcc3Lo	ds	1
dtmfdCosAcc3Hi	ds	1	
dtmfdSinAcc4Lo	ds	1
dtmfdSinAcc4Hi	ds	1	
dtmfdCosAcc4Lo	ds	1
dtmfdCosAcc4Hi	ds	1	
	dtmfdResult1	equ	dtmfdSinAcc1Lo
	dtmfdResult2   	equ	dtmfdSinAcc1Hi
	dtmfdResult3   	equ	dtmfdCosAcc1Lo
	dtmfdResult4	equ	dtmfdCosAcc1Hi
	dtmfdHighScore	equ	dtmfdSinAcc2Lo
	dtmfdRunUpScore	equ	dtmfdSinAcc2Hi
	dtmfdRunUpIndex	equ	dtmfdCosAcc2Lo
	dtmfdWinnIndex	equ	dtmfdCosAcc2Hi
;VP_END: DTMF Detection
	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
		org	bank7_org
bank7		=	$

;VP_BEGIN: DTMF Detection
dtmfdMathBank		=	$
dtmfdLoopCount		ds	1
dtmfdMathFlags		ds	1
	dtmfdNeg		equ	dtmfdMathFlags.0
dtmfdRootMask		ds	1
dtmfdALo		ds	1
dtmfdAHi		ds	1
dtmfdBLo		ds	1
dtmfdBHi		ds	1
dtmfdInput		ds	1
dtmfdAtoDVal		ds	1
dtmfdAtoDCount		ds	1
;VP_END: DTMF Detection

IFDEF SX48_52
	;*********************************************************************************
	; Bank 8
	;*********************************************************************************
		org	$80	;bank 8 address on SX52
bank8		=	$
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- This extra memory is not available in the SX18/28, so don't use it for Virtual
	;	  Peripherals written for both platforms.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	;*********************************************************************************
	; Bank 9
	;*********************************************************************************
		org	$90	;bank 9 address on SX52
bank9		=	$
	;*********************************************************************************
	; Bank A
	;*********************************************************************************
		org	$A0	;bank A address on SX52
bankA		=	$
	;*********************************************************************************
	; Bank B
	;*********************************************************************************
		org	$B0	;bank B address on SX52
bankB		=	$
	;*********************************************************************************
	; Bank C
	;*********************************************************************************
		org	$C0	;bank C address on SX52
bankC		=	$
	;*********************************************************************************
	; Bank D
	;*********************************************************************************
		org	$D0	;bank D address on SX52
bankD		=	$
	;*********************************************************************************
	; Bank E
	;*********************************************************************************
		org	$E0	;bank E address on SX52
bankE		=	$
	;*********************************************************************************
	; Bank F
	;*********************************************************************************
		org	$F0	;bank F address on SX52
bankF		=	$
ENDIF
	;*********************************************************************************
	; Pin Definitions:
	;*********************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Store all initialization constants for the I/O in the same area, so
	;	  pins can be easily moved around.
	;	- Pin definitions should follow the same format guidelines as RAM definitions
	;		- Left justified
	;		- No underscores.  Indicate word separation with capital letters
	;		- Less that 2 tabs in length
	;		- Indicate the Virtual Peripheral the pin is used for
	;		- All pin definitions for a specific VP must have the same prefix
	;	- Only use symbolic names to access a pin/port in the source code.
	;	- Example:
	;		; VP_BEGIN: RS232 Transmit
	;			rs232TxPin	equ	ra.3
	;		; VP_END: RS232 Transmit
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	;VP_BEGIN: rs232 Receive
	rs232RxPin	equ	ra.1	; RS-232 reception pin
	;VP_END: rs232 Receive

	;VP_BEGIN: rs232 Transmit
	rs232TxPin	equ	ra.2	; RS-232 transmission pin
	;VP_END: rs232 Transmit

RA_latch	equ	%11111111		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11111011		;SX18/20/28/48/52 port A DDIR value
RA_LVL		equ	%00000000		;SX18/20/28/48/52 port A LVL value
RA_PLP		equ	%11111111		;SX18/20/28/48/52 port A PLP value

	;VP_BEGIN: LED flasher
	ledPin		equ	rb.0	; LED flasher pin 
	;VP_END: LED flasher

      	;VP_BEGIN: DTMF Detection
	dtmfHook	equ	rb.4	; drive hook low to go off-hook
	cntrl_3		equ	rb.5	; drive cntrl_3 low to disable the output of the HP-Filter
	;VP_END: DTMF Detection

RB_latch	equ	%00010000		;SX18/20/28/48/52 port B latch init
RB_DDIR		equ	%11001110		;SX18/20/28/48/52 port B DDIR value
RB_ST		equ	%11111111		;SX18/20/28/48/52 port B ST value
RB_LVL		equ	%00000000		;SX18/20/28/48/52 port B LVL value
RB_PLP		equ	%11111111		;SX18/20/28/48/52 port B PLP value


IFNDEF	SX18_20	; There is no C port on SX18/20
	;VP_BEGIN: DTMF Detection
	dtmfdPort	equ	rc	; Port used for DTMF
	ADIn		equ	2	; Pin number AD Input pin (pin 2 has low gain)
	ADFdbk		equ	3	; Pin number for ADFeedback pin
	Imp450		equ	4	; Make low impedance to cancel output signals on a 450ohm line
	Imp600		equ	5	; Make low impedance to cancel output signals on a 600ohm line
	Imp750		equ	6	; Make low impedance to cancel output signals on a 750ohm line
	Imp900		equ	7	; Make low impedance to cancel output signals on a 900ohm line

	; Do changes above
	dtmfdADInPin	equ	dtmfdPort.ADIn	
	dtmfdADFdbkPin	equ	dtmfdPort.ADFdbk	
	dtmfdImp450Pin	equ	dtmfdPort.Imp450	
	dtmfdImp600Pin	equ	dtmfdPort.Imp600	
	dtmfdImp750Pin	equ	dtmfdPort.Imp750	
	dtmfdImp900Pin	equ	dtmfdPort.Imp900	
	;VP_END: DTMF Detection

RC_latch	equ	%00000000		;SX18/20/28/48/52 port C latch init
RC_DDIR		equ	%11010111		;SX18/20/28/48/52 port C DDIR value
RC_ST		equ	%11111111		;SX18/20/28/48/52 port C ST value
RC_LVL		equ	%00000000		;SX18/20/28/48/52 port C LVL value
RC_PLP		equ	%11111111		;SX18/20/28/48/52 port C PLP value

IFDEF SX48_52	;SX48BD/52BD Port initialization values
RD_latch	equ	%00000000		;SX48/52 port D latch init
RD_DDIR		equ	%11111111		;SX48/52 port D DDIR value
RD_ST		equ	%11111111		;SX48/52 port D ST value
RD_LVL		equ	%00000000		;SX48/52 port D LVL value
RD_PLP		equ	%11111111		;SX48/52 port D PLP value

RE_latch	equ	%00000000		;SX48/52 port E latch init
RE_DDIR		equ	%11111111		;SX48/52 port E DDIR value
RE_ST		equ	%11111111		;SX48/52 port E ST value
RE_LVL		equ	%00000000		;SX48/52 port E LVL value
RE_PLP		equ	%11111111		;SX48/52 port E PLP value
ENDIF	;(SX18_20)
ENDIF	;(SX48_52)
;*****************************************************************************************
; Program constants
;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	To calculate the interrupt period in cycles:
	;	- First, choose the desired interrupt frequency
	;		- Should be a multiple of each Virtual Peripherals sampling frequency.
	;		- Example:  19200kHz UART sampling rate * 16 = 307.200kHz
	;	- Next, choose the desired oscillator frequency.
	;		- 50MHz, for example.
	;	- Next calculate the 
	;	- Perform the calculation intPeriod = (cyclesPerSecond / interruptFrequency)
	;				  		= (50MHz / 307.2kHz)
	;						= 162.7604
	;	- Round intPeriod to the nearest integer:
	;						= 163
	;	- Now calculate your actual interrupt rate:
	;						= cyclesPerSecond / intPeriod
	;						= 50MHz / 163
	;						= 306.748kHz
	;	- This interrupt frequency will be the timebase for all of the Virtual 
	;	  Peripherals
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
				; Prescaler is set to 4, this can be changed to 2 if the 
				; intPeriod is set to 250.
intPeriod	=	125	; Gives an interrupt period at 50MHz of (125 * 4 * (1/50000000)s) = 10us
				; Which gives an interrupt frequency of (1/10us)Hz = 100.000kHz

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	- Include all calculations for Virtual Peripheral constants for any sample 
	;	  rate.
	;	- Relate all Virtual Peripheral constants to the sample rate of the Virtual
	;	  Peripheral.
	;	- Example:
	;		; VP_BEGIN: 5ms Timer
	;		TIMER_DIV_CONST	equ 192	; This constant = timer sample rate/200Hz = 192
	;		; VP_END: 5ms Timer
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

;VP_BEGIN: RS232 Transmit

RS232TX_FS	=	10000	; Thread rate of UART; called on every 10th pass of the Interrupt Service Routine
RS232TX_BAUD	=	9600	; 

RS232TX_DIVIDE	=	RS232TX_FS/RS232TX_BAUD		; Divide rate constant used by the program

;VP_END: RS232 Transmit


;VP_BEGIN: DTMF Detection
;**************************************************************************
; CALCULATING CONSTANTS FOR DTMF DETECTION
;
; The constant = 2^n * Ts * FREQ, 
; where n is the number of bits in the phase accumulator for each
; signal generator, Ts is the sample rate, and FREQ is the desired
; output frequency.
; We know that the phase accumulator (phaseAcc) is 16 bits, so n = 16
;
; = 1/([cyclesperinterrupt] * [instructiontime] * [ISR passes]) 
;
; Therefore, the constant = 2^n * (1/DTMF_FS) * FREQ
;
; Convert the result of the calculation to a hexadecimal number and load
; the upper byte into the phaseAccHi register and the lower byte 
;**************************************************************************
DTMF_FS		= 10000		; sampling frequency for DTMF detection (interrupt rate of DTMF Detection threads)
DTMF_BITS	= 65536		; 2^16 is the value of the phase accumulator
TIMER_COUNTER	= 5*DTMF_FS/1000; Counts to 5ms

DTMF_FREQ697HI	equ	((DTMF_BITS * 697)/DTMF_FS) >> 8	; high 8 bits of 16 bit constant
DTMF_FREQ697LO	equ 	((DTMF_BITS * 697)/DTMF_FS) & $0ff	; low 8 bits of 16 bit constant

DTMF_FREQ770HI	equ	((DTMF_BITS * 770)/DTMF_FS) >> 8
DTMF_FREQ770LO	equ	((DTMF_BITS * 770)/DTMF_FS) & $0ff

DTMF_FREQ852HI	equ	((DTMF_BITS * 852)/DTMF_FS) >> 8
DTMF_FREQ852LO	equ	((DTMF_BITS * 852)/DTMF_FS) & $0ff

DTMF_FREQ941HI	equ	((DTMF_BITS * 941)/DTMF_FS) >> 8
DTMF_FREQ941LO	equ	((DTMF_BITS * 941)/DTMF_FS) & $0ff

DTMF_FREQ1209HI	equ	((DTMF_BITS * 1209)/DTMF_FS) >> 8
DTMF_FREQ1209LO	equ	((DTMF_BITS * 1209)/DTMF_FS) & $0ff

DTMF_FREQ1336HI	equ	((DTMF_BITS * 1336)/DTMF_FS) >> 8
DTMF_FREQ1336LO	equ	((DTMF_BITS * 1336)/DTMF_FS) & $0ff

DTMF_FREQ1477HI	equ	((DTMF_BITS * 1477)/DTMF_FS) >> 8
DTMF_FREQ1477LO	equ	((DTMF_BITS * 1477)/DTMF_FS) & $0ff

DTMF_FREQ1633HI	equ	((DTMF_BITS * 1633)/DTMF_FS) >> 8
DTMF_FREQ1633LO	equ	((DTMF_BITS * 1633)/DTMF_FS) & $0ff



DTMF_SCALING	equ	4		; The number of bits to shift the accumulated results
					; to the right to squeeze them into 8 bits.  Decrease this
					; for sensitivity to smaller signals. If so, consider doubling DTMF_MIN_SCORE.

DTMF_ADOFFSET	equ	7		; Offset correction value for ADC. Adjust this value only when there is no
					; input signal,and  poll the code and check the dtmfdAtoDVal to be near zero.

DTMF_ADNOSAMPL	equ	36		; Count 36 feedback bits per sample. Higher values take longer to process, 
					; and the minimum time to detect a valid digit will increase.
					; Also, if this value is decreased further, the dynamic range of the A/D 
					; will also be decreased.

DTMF_MIN_SCORE	equ	50		; The minimum score required to detect a digit. However, a valid digit 
					; requires the measured value in ResultN to be twice this value...
					; To decrease the scaling factor, increase this value as well.
					; (approx. double the value if the scaling factor is increased by 1)
					; Accumulated results will be higher.
DTMF_SAMPL_LO	equ	140		; The number of samples to take to detect a low frequency
DTMF_SAMPL_HI	equ	115		; The number of samples to take to detect a high frequency
;VP_END: DTMF Detection
;-------------------------------------------------------------------------------------
IFDEF SX48_52
	;*********************************************************************************
	; SX48BD/52BD Mode addresses
	; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
	; exception of CMP and WKPND which do an exchange with W.
	;*********************************************************************************
; Timer (read) addresses
TCPL_R		equ	$00		;Read Timer Capture register low byte
TCPH_R		equ	$01		;Read Timer Capture register high byte
TR2CML_R	equ	$02		;Read Timer R2 low byte
TR2CMH_R	equ	$03		;Read Timer R2 high byte
TR1CML_R	equ	$04		;Read Timer R1 low byte
TR1CMH_R	equ	$05 		;Read Timer R1 high byte
TCNTB_R		equ	$06		;Read Timer control register B
TCNTA_R		equ	$07		;Read Timer control register A

; Exchange addresses
CMP		equ	$08		;Exchange Comparator enable/status register with W
WKPND		equ	$09		;Exchange MIWU/RB Interrupts pending with W

; Port setup (read) addresses
WKED_R		equ	$0A		;Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R		equ	$0B		;Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R		equ	$0C		;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R		equ	$0D		;Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R		equ	$0E		;Read Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_R		equ	$0F		;Read Port Direction

; Timer (write) addresses
TR2CML_W	equ	$12		;Write Timer R2 low byte
TR2CMH_W	equ	$13		;Write Timer R2 high byte
TR1CML_W	equ	$14		;Write Timer R1 low byte
TR1CMH_W	equ	$15 		;Write Timer R1 high byte
TCNTB_W		equ	$16		;Write Timer control register B
TCNTA_W		equ	$17		;Write Timer control register A

; Port setup (write) addresses
WKED_W		equ	$1A		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	$1B		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	$1C		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	$1D		;Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W		equ	$1E		;Write Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_W		equ	$1F		;Write Port Direction

ELSE

	;*********************************************************************************
	; SX18AC/20AC/28AC Mode addresses
	; *On SX18/20/28, all registers addressed via mode are write only, with the exception of
	; CMP and WKPND which do an exchange with W.
	;*********************************************************************************
; Exchange addresses
CMP		equ	$08		;Exchange Comparator enable/status register with W
WKPND		equ	$09		;Exchange MIWU/RB Interrupts pending with W

; Port setup (read) addresses
WKED_W		equ	$0A		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	$0B		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	$0C		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	$0D		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W		equ	$0E		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W		equ	$0F		;Write Port Direction
ENDIF	;(SX48_52)

;*****************************************************************************************
; Program memory ORG defines
;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Place a table at the top of the source with the starting addresses of all of
	;	  the components of the program.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

INTERRUPT_ORG		equ	$0	; Interrupt must always start at location zero
RESET_ENTRY_ORG		equ	$1FB	; The program will jump here on reset.
SUBROUTINES_ORG		equ	$200	; The subroutines are in this location
STRINGS_ORG		equ	$300	; The strings are in location $300
DTMF_SUBS_ORG		equ	$400	; DTMF calculation subroutines
MAIN_PROGRAM_ORG	equ	$600	; The main program is in the last page of program memory.










;****************************** Beginning of program space *******************************
	org	INTERRUPT_ORG			; First location in program memory.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Interrupt Service Routine
;------------------------------------------------------------------------------
; Note: The interrupt code must always originate at address $0.
;
; Interrupt Frequency = OSC Frequency / ((-retiw value)*RTCC Prescaler)  For example:
; With a retiw value of -163, a prescaler of 1, and an oscillator frequency of 50MHz,
; this Interrupt Routine runs every 3.26us.
;------------------------------------------------------------------------------
ISR					;3	The interrupt service routine... 

;------------------------------------------------------------------------------
;VP_BEGIN: VP Scheduler

;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; - Schedule tasks in the Interrupt Service Routine
	;	- Produces a FAR smaller worst-case cycle time count, and enables a larger number
	;	  of VP's to run simultaneously.  Also produces "empty" slots that future VP's 
	;	  can be copied and pasted into easily.
	;	- Determine how often your tasks need to run.  (9600bps UART can run well at a 
	;	  sampling rate of only 38400Hz, so don't run it faster than this.)
	;	- Strategically place each "module" into the threads of the Scheduler.  If a 
	;	  module must be run more often, just call it's module at double the rate or
	;	  quadruple the rate, etc.…
	;	- Split complicated Virtual Peripherals into several modules, keeping the 
	;	  high-speed portions of the Virtual Peripherals as small and quick as possible, 
	;	  and run the more complicated, slower processing part of the Virtual Peripheral 
	;	  at a lower rate.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	;------------------------------------------------------------------------------
	; Virtual Peripheral Scheduler:  up to 16 individual threads, each running at
	;				   the interrupt rate/16.  Change the table
	;				   to modify the rate at which each of the threads
	;				   is run.
	;
	;	Input variable(s): 		isrMultiplex: variable used to choose threads
	;	Output variable(s): 		None, executes the next thread
	;	Variable(s) affected: 		isrMultiplex
	;	Flag(s) affected: 		None
	;	Program Cycles:			10 cycles (turbo mode SX52)
	;------------------------------------------------------------------------------
	_bank	isrMultiplex		;1/2
	inc	isrMultiplex		;1	; toggle interrupt rates
	mov	w,isrMultiplex		;1
; The code between the tableStart and tableEnd statements MUST be 
; completely within the first half of a page.  The routines 
; it is jumping to must be in the same page as this table.
tableStart				; Start all tables with this macro.
	add	pc,w			;3
	jmp	isrThread3		;3, 12/13 cycles + 16/17 + 392/400 + 7 	= 415/424 cycles
					;  ;Thread3 contains UART, A/D, 5ms Timer and LED flasher
	jmp	isrThread1		;3, 12/13 cycles +         490 + 7 	= max 510 cycles 
					;  ; NOTE: For the SX18/28 		= max 483 cycles
					;  ;Thread1 contains DTMF
	jmp	isrThread2		;
	jmp	isrThread4		;
	jmp	isrThread5		;
	jmp	isrThread6		;
	jmp	isrThread7		;
	jmp	isrThread8		; 
	jmp	isrThread9		;
	jmp	isrThread16		; ; table threading ends here
	jmp	isrThread8		;
	jmp	isrThread9		;
	jmp	isrThread1		;
	jmp	isrThread10		;
	jmp	isrThread3		;
	jmp	isrThread11		;
	jmp	isrThread1		;
	jmp	isrThread2		;
	jmp	isrThread12		;
	jmp	isrThread13		;
	jmp	isrThread1		;
	jmp	isrThread14		;
	jmp	isrThread15		;
	jmp	isrThread16		;
tableEnd				; End all tables with this macro.
;VP_END: VP Scheduler

;VP_BEGIN: DTMF Detection
;******************************************************************************
; Portions of DTMF Detection ISR that must be in the first 1/2 page...  (Tables, etc.)
;******************************************************************************
;------------------------------------------------------------------------------
sineTableTiny
;------------------------------------------------------------------------------
tableStart				; trap an error if this is not in the first half
				;3	; of a page
	and	w,#%00000111	;1 keep w within range
	add	pc,w		;3
	retw	0		;3	;3,10
	retw	1
	retw	1
	retw	2
	retw	2
	retw	2
	retw	1
	retw	1
tableEnd				; trap an error if this is not in the first half
					; of a page

;------------------------------------------------------------------------------
; Jump table for the DTMF detection routines
;------------------------------------------------------------------------------
tableStart				; trap an error if this is not in the first half
					; of a page
dtmfDetectSine		jmp	_dtmfDetectSine
dtmfDetectCosine	jmp	_dtmfDetectCosine
tableEnd				; trap an error if this is not in the first half
					; of a page
;------------------------------------------------------------------------------
;VP_END: DTMF Detection


;------------------------------------------------------------------------------
;VP_BEGIN: VP Scheduler
; ISR TASKS
;------------------------------------------------------------------------------
isrThread1		; Serviced at ISR rate / 10
;------------------------------------------------------------------------------
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	The sample rate of this section of code is the isr rate / 4, because it is jumped
	;	to in every 4th entry in the VP Schedulers table.  To increase the
	;	sample rate, put more calls to this thread in the Scheduler's jump table.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

;VP_BEGIN: DTMF Detection

	sb	dtmfdDetectEn			;1,
	jmp	dtmfdDone			;3, If DTMF Detection is not enabled, skip the DTMF detection ISR


;******************************************************************************
;******************************************************************************
;******************************************************************************
; Portions of DTMF Detection ISR that can be in the second half of a page...
;******************************************************************************
dtmfDetIsr

;------------------------------------------------------------------------------
; Update Reference frequencies
;------------------------------------------------------------------------------

; NOW UPDATE THE REFERENCES!!!
		_bank	dtmfdRefBank		;1/2
		snb	dtmfdSampleLows		;1
		jmp	:doLowFrequencies	;3,8

		mov	w,#DTMF_FREQ1209LO	;1,7 ;High Band Frequencies
		add	dtmfdRefAcc1Lo,w	;1 ;1209 Hz
		snb	c			;1
		inc	dtmfdRefAcc1Hi		;1
		mov	w,#DTMF_FREQ1209HI	;1
		add	dtmfdRefAcc1Hi,w	;1,12

		mov	w,#DTMF_FREQ1336LO	;1 ;1336 Hz
		add	dtmfdRefAcc2Lo,w	;1
		snb	c			;1
		inc	dtmfdRefAcc2Hi		;1
		mov	w,#DTMF_FREQ1336HI	;1
		add	dtmfdRefAcc2Hi,w	;1,18

		mov	w,#DTMF_FREQ1477LO	;1 ;1477
		add	dtmfdRefAcc3Lo,w	;1
		snb	c			;1
		inc	dtmfdRefAcc3Hi		;1
		mov	w,#DTMF_FREQ1477HI	;1
		add	dtmfdRefAcc3Hi,w	;1,24

		mov	w,#DTMF_FREQ1633LO	;1 ;1633
		add	dtmfdRefAcc4Lo,w	;1
		snb	c			;1
		inc	dtmfdRefAcc4Hi		;1
		mov	w,#DTMF_FREQ1633HI	;1
		add	dtmfdRefAcc4Hi,w	;1
		jmp	dtmfdCalcFreq	;3, 33 cycles

:doLowFrequencies				;0,8

		mov	w,#DTMF_FREQ697LO	;1 ;Low Band Frequencies
		add	dtmfdRefAcc1Lo,w	;1 ;697 Hz
		snb	c			;1
		inc	dtmfdRefAcc1Hi		;1
		mov	w,#DTMF_FREQ697HI	;1
		add	dtmfdRefAcc1Hi,w	;1,14

		mov	w,#DTMF_FREQ770LO	;1 ;770 Hz
		add	dtmfdRefAcc2Lo,w	;1
		snb	c			;1
		inc	dtmfdRefAcc2Hi		;1
		mov	w,#DTMF_FREQ770HI	;1
		add	dtmfdRefAcc2Hi,w	;1,20

		mov	w,#DTMF_FREQ852LO	;1 ;852 Hz
		add	dtmfdRefAcc3Lo,w	;1
		snb	c			;1
		inc	dtmfdRefAcc3Hi		;1
		mov	w,#DTMF_FREQ852HI	;1
		add	dtmfdRefAcc3Hi,w	;1,26

		mov	w,#DTMF_FREQ941LO	;1 ;941 Hz
		add	dtmfdRefAcc4Lo,w	;1
		snb	c			;1
		inc	dtmfdRefAcc4Hi		;1
		mov	w,#DTMF_FREQ941HI	;1
		add	dtmfdRefAcc4Hi,w	;1, 32 cycles

;------------------------------------------------------------------------------
; Update Reference frequencies
;------------------------------------------------------------------------------
dtmfdCalcFreq  			;32/33cycles to here in thread...starts over from zero 
:loop

		_bank	dtmfdMathBank		;1/2
		;test	dtmfdAtoDVal		;1 ; TODO: Can be left out?
		;snb	z			;1
		;jmp	dtmfdDone		;3

		mov	w,#dtmfdSinAcc1Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1/2
		mov	w,dtmfdRefAcc1Hi	;1,5
		call	dtmfDetectSine		;3 + 25/45 (25/43cycles for SX18/28)

		mov	w,#dtmfdCosAcc1Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1/2
		mov	w,dtmfdRefAcc1Hi	;1
		call	dtmfDetectCosine	;3 + 31/51 (31/49cycles for SX18/28)

		mov	w,#dtmfdSinAcc2Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1/2
		mov	w,dtmfdRefAcc2Hi	;1	
		call	dtmfDetectSine		;3 + 25/45 (25/43cycles for SX18/28)

		mov	w,#dtmfdCosAcc2Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1/2
		mov	w,dtmfdRefAcc2Hi	;1
		call	dtmfDetectCosine	;3 + 31/51 (31/49cycles for SX18/28)

		mov	w,#dtmfdSinAcc3Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1/2
		mov	w,dtmfdRefAcc3Hi	;1	
		call	dtmfDetectSine		;3 + 25/45 (25/43cycles for SX18/28)

		mov	w,#dtmfdCosAcc3Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1
		mov	w,dtmfdRefAcc3Hi	;1
		call	dtmfDetectCosine	;3 + 31/51 (31/49cycles for SX18/28)

		mov	w,#dtmfdSinAcc4Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1
		mov	w,dtmfdRefAcc4Hi	;1   
		call	dtmfDetectSine		;3 + 25/45 (25/43cycles for SX18/28)

		mov	w,#dtmfdCosAcc4Lo	;1
		mov	fsr,w			;1
		_bank	dtmfdRefBank		;1
		mov	w,dtmfdRefAcc4Hi	;1
		call	dtmfDetectCosine	;3 + 31/51 (31/49cycles for SX18/28)

		; Total max cycle count in dtmfdCalcFreq = 5 + 53*4 + 59*4 = 453 cycles
		; For the SX18/28 the max count is 	 = 4 + 50*4 + 56*4 = 428 cycles
		; To here in thread the max count is = 32 + 453 = 485 cycles

		_bank	dtmfdSampleCnt		;1/2
		dec	dtmfdSampleCnt		;1
		snb	z			;1
		setb	dtmfdDetectDone		;1,4/5 + 485 = max 490 cycles total 
						; ; For the SX18/28, max=464cycles
;VP_END: DTMF Detection	
dtmfdDone	jmp	isrOut			;7





						
;------------------------------------------------------------------------------
isrThread2		; Serviced at ISR rate / 5
;------------------------------------------------------------------------------

	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread3		; Serviced at ISR rate / 10
;------------------------------------------------------------------------------
;VP_BEGIN: RS232 Transmit
;------------------------------------------------------------------------------
; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART) 
; These routines send and receive RS232 serial data, and are currently
; configured (though modifications can be made) for the popular
; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format.
; TRANSMITTING: The transmit routine requires the data to be inverted
; and loaded (tx_high+tx_low) register pair (with the inverted 8 data bits
; stored in tx_high and tx_low bit 7 set high to act as a start bit). Then
; the number of bits ready for transmission (10=1 start + 8 data + 1 stop)
; must be loaded into the tx_count register. As soon as this latter is done,
; the transmit routine immediately begins sending the data.
; This routine has a varying execution rate and therefore should always be
; placed after any timing-critical virtual peripherals such as timers,
; adcs, pwms, etc.
; Note: The transmit and receive routines are independent and either may be
;	removed, if not needed, to reduce execution time and memory usage,
;	as long as the initial "BANK serial" (common) instruction is kept.
;
;       Input variable(s) : tx_low (only high bit used), tx_high, tx_count
;       Variable(s) affected : tx_divide
;	Program cycles: 17 worst case
;	Variable Length?  Yes.
;
;------------------------------------------------------------------------------
rs232Transmit
		_bank	rs232TxBank		;1/2 switch to serial register bank

		decsz	rs232TxDivide		;1 ; only execute the transmit routine
		jmp	:rs232TxOut		;3,5/6 
		mov	w,#RS232TX_DIVIDE	;1 ; load UART baud rate (50MHz)
		mov	rs232TxDivide,w		;1  
		test    rs232TxCount		;1 ; are we sending?
		snz				;1  
		jmp	:rs232TxOut		;1 
:txbit		clc				;1 ; yes, ready stop bit
		rr      rs232TxHigh		;1 ; and shift to next bit
		rr      rs232TxLow		;1  
		dec     rs232TxCount		;1 ; decrement bit counter
		snb	rs232TxLow.6		;1 ; output next bit
		clrb	rs232TxPin		;1  
		sb	rs232TxLow.6		;1  
		setb	rs232TxPin		;1,16/17  
:rs232TxOut
;VP_END: RS232 Transmit

	;------------------------------------------------------------------------------
	; This A/D module comes after the UART because the UART only uses 17cycles out of
	; 500 possible. This UART module is inserted in this thread ahead of this A/D 
	; module to avoid waste of MIPS and ensure timing of the UART
	;------------------------------------------------------------------------------

;VP_BEGIN: DTMF Detection
	;------------------------------------------------------------------------------
	; Do A/D Conversion:
	;------------------------------------------------------------------------------
						
		_bank	dtmfdMathBank		;1/2
		mov	w,#DTMF_ADNOSAMPL	;1 
		mov	dtmfdAtoDCount,w	;1 ; numbers of sample to take

		mov	w,#DTMF_ADOFFSET	;1
		mov	dtmfdAtoDVal,w		;1 ; clear and correct AtoDVal for minor DC offset

		mov	isrTemp0,m		;1 ; Store Mode register
		_mode	DDIR_W			;1/2 ; point MODE to write DDIR register
		mov	w,#RC_DDIR		;1 ; load direction setting for port C
		clrb	wreg.ADFdbk		;1 ; set ADfdbkpin to output
		mov	!dtmfdPort,w		;1, 10/12 

:AtoDLoop2	
		snb	dtmfdADInPin		;1 ; check AD input pin
		jmp	:above			;3 ; if input is above threshold

:below		nop				;1 ; Make :below and :above symmetrical with equal length
		nop				;1 ;
		setb	dtmfdADFdbkPin		;1 ; -if not: feedback pin=1 (inverted AD input)
		dec	dtmfdAtoDVal		;1 ; decrement AD value
		decsz	dtmfdAtoDCount		;1 ; decrement number of samples to take
		jmp	:AtoDLoop2		;3,10 ; if not done, do again
		jmp	:loopDone		;3 ; when finished
:above		
		clrb	dtmfdADFdbkPin		;1 ; feedback pin=0 (inverted AD input)
		inc	dtmfdAtoDVal		;1 ; increment AD value
		decsz	dtmfdAtoDCount		;1 ; decrement number of samples to take
		jmp	:AtoDLoop2		;3,10; if not done, do it again
:loopDone					; loop time = (10 * ADNOSAMPL)+3 = 10*36+3=363 cycles

		clrb	dtmfdADFdbkPin		;1 ; Make the A/D feedback pin low to next time
		clrb	dtmfdInputSign		;1 
		sb	dtmfdAtoDVal.7		;1 ; check the sign bit of dtmfdAtoDVal
		jmp	:positive		;3
:negative	
		setb	dtmfdInputSign		;1 ; set inputsign to negative
		not	dtmfdAtoDVal		;1
		inc	dtmfdAtoDVal		;1 ; 2's compliment (make negative positive)

:positive	
		mov	w,#RC_DDIR		;1 ; load direction setting for port C
		setb	wreg.ADFdbk		;1 ; set direction of ADfdbkpin to input (tristate)
		mov	!dtmfdPort,w		;1 ;
		_mode	isrTemp0		;1/2; Restore mode register
						; = 10/12+363+10/12=383/387cycles 
;VP_END: DTMF Detection				    

;------------------------------------------------------------------------------
;Do timers and return from interrupt.
;------------------------------------------------------------------------------

;VP_BEGIN : 5ms Timer
		_bank	Timer5msBank		;1/2
		decsz	Timer5msDiv		;1
		jmp	:noInc			;3,5/6  Interrupt rate is 1/100_000
		mov	w,#TIMER_COUNTER		;1	This interrupt occurs every 100uS,
		mov	Timer5msDiv,w		;1	There are 50 * 20uS in 5mS
		inc	Timer5ms		;1
		snb	z			;1
		setb	timer5msFlag		;1
:noInc						;0,5/9
;VP_END : 5ms Timer

;VP_BEGIN : LED flasher
		sb	Timer5ms.3		;1 ; Use these timers to flash the LED
		setb	ledPin			;1
		snb	Timer5ms.3		;1
		clrb	ledPin			;1,9/13 = 383/387 + 9/13 = 392/400 cycles
;VP_END : LED flasher				; 



	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread4		; Serviced at ISR rate / 5
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread5		; Serviced at ISR rate / 5
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread6		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread7		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread8		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread9		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread10		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread11		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread12		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread13		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread14		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread15		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread16		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	The final thread in the ISR Multi-Threader must load the isrMultiplex 
	; 	register with a value of 255, so it will roll-over to 0 on the next ISR.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	_bank	isrMultiplex
	mov	w,#255			; Reload isrMultiplex so isrThread1 will be run
	mov	isrMultiplex,w		; on next interrupt.
	jmp	isrOut
;VP_END: VP Scheduler
;------------------------------------------------------------------------------
isrOut
;------------------------------------------------------------------------------
	mov	w,#-intPeriod	;1	; return and add -intPeriod to the RTCC
	retiw			;3	; using the retiw instruction.

;VP_BEGIN: DTMF Detection
;------------------------------------------------------------------------------
; DTMF DETECT ISR SUBROUTINES HERE.
;------------------------------------------------------------------------------
;------------------------
_dtmfDetectCosine
; INPUTS:  WREG: 	High byte of cosine-index in w register
;	   FSR:		Location of low byte of accumulator
;------------------------
		mov	isrTemp0,w		;1,4 (jmp after call; 3cycles)
		mov	w,#$40			;1
		add	w,isrTemp0		;1
						; =6 ; These cycles are added to sine cycle-count
;------------------------
_dtmfDetectSine
; INPUTS:  WREG: 	High byte of sine/cosine-index in w register
;	   FSR:		Location of low byte of accumulator
;------------------------
						 
		sb	wreg.7			;1,4 ; Sine-reference sign
 		clrb	dtmfdSign		;1 ; Keep track of the reference sign. dtmfdSign is always set when 
						;    leaving/entering this routine, see below (saves one cycle)
		swap	wreg			;1 ; Bits 6-4 are the sine-index, for table index use bits 2-0
		call	sineTableTiny		;10; Use bit 2-0 to get the value from the tiny sine table
		test	wreg			;1 ; Check if sine-reference is zero
		snb	z			;1
		jmp	:next			;3,21 ;if reference is zero, skip this

		snb	dtmfdSign		;1,20
		jmp	:negRef			;3,23 ; reference is negative
:posRef	
		snb	dtmfdInputSign		;1,22
		setb	dtmfdSign		;1 ; if input sample value is negative=> pos*neg=neg
		jmp	:refSignOut		;3,26 ; dtmfdSign have now the sign of the multiplication
:negRef
		snb	dtmfdInputSign		;1
		clrb	dtmfdSign		;1,25 ; input sample value is negative=> neg*neg=pos
						; dtmfdSign have now the sign of the multiplication

:refSignOut					;0,25/26
		_bank	dtmfdMathBank		;1/2
		mov	isrTemp0,w		;1 reference to isrTemp0
		mov	w,dtmfdAtoDVal		;1 A/D sample value to wreg, 
		snb	isrTemp0.1		;1 If reference is one, skip multiply by 2(sine-reference is either 1 or 2)
		add	w,dtmfdAtoDVal		;1 wreg=2*AtoDVal (never exceeds FF; #$FF > (DTMF_ADNOSAMPL+ADOFFSET)*2)
		_bank	dtmfdCh1		;1/2 ; 
		snb	dtmfdSign		;1,32/35 check if we shall add or subtract
		jmp	:decrease		;3 ;35/38

:increase
		add	indf,w			;1 ; add the input value * reference to the accumulator
		inc	fsr			;1
		snb	c			;1
		inc	indf			;1
:next		setb	dtmfdSign		;1 ; Always set before leaving this routine, see above
		ret				;3,25/44 ; return to isr-thread (25/42cycles for SX18/28)

:decrease	
		sub	indf,w			;1 ; add the input value * reference to the accumulator (negative)
		inc	fsr			;1
		sb	c			;1 ; the result is negative if C is cleared
		dec	indf			;1,
		ret				;3,42/45 ;and return to caller. (42/43cycles for SX18/28)

						; Total for sineCalcs  = 25/45 (25/43cycles for SX18/28)
						; Total for cosineCalcs= 31/51 (31/49cycles for SX18/28)
;VP_END: DTMF Detection











;*****************************************************************************************
org	RESET_ENTRY_ORG
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	The main program operation should be easy to find, so place it at the end of the 
	;	program code.  This means that if the first page is used for anything other than 
	;	main program source code, a resetEntry must be placed in the first page, along 
	;	with a 'page' instruction and a 'jump' instruction to the beginning of the 
	;	main program, wherever that may be.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
resetEntry					; Program starts here on power-up
	page	_resetEntry
	jmp	_resetEntry
;------------------------------------------------------------------------------








	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	ORG statements should use predefined labels rather than literal values.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

org	SUBROUTINES_ORG
;*****************************************************************************************
; Subroutines
;*****************************************************************************************

;VP_BEGIN: RS232 Transmit
;
rs232TxSendByte
;		Description:
;			Sends the byte in w via the Asynchrounous Serial Transmit VP.
;			Will block if the transmit VP is still busy.
;			Load W with the byte to send before calling rs232TxSendByte
;
;		Global Ram Destroyed:
;			none
;
;		Inputs:
;			w - The byte to be sent
;
;		Outputs:
;			Initializes the Asynchrounous Serial Transmit VP and sends the
;			byte out serially.
;------------------------------------------------------------------------------

		_bank	rs232TxBank

:wait        	test	rs232TxCount		; wait for not busy
		sz
		jmp	:wait

		not	w			; ready bits (inverse logic)
		mov	rs232TxHigh,w		; store data byte
		setb	rs232TxLow.7		; set up start bit
		mov	w,#10			; 1 start + 8 data + 1 stop bit
		mov	rs232TxCount,w
		retp				; leave and fix page bits

;VP_END: RS232 Transmit
;------------------------------------------------------------------------------
;VP_BEGIN : RS232 Transmit
;
rs232TxSendString
;		Description:
;			Sends the null-terminated string at location "w" serially.
;
;		Global Ram Destroyed:
;			localTemp1, mode
;
;		Inputs:
;			w - The location of the string to be sent.  The string must
;			be located in the same half of a page as the STRINGS_ORG label.
;		Outputs: 
;			Terminal - Outputs the string serially.
;------------------------------------------------------------------------------

		_bank	rs232TxBank
 		mov     localTemp1,w		; store string address
:loop
		_mode	STRINGS_ORG>>8		; with indirect addressing
		mov     w,localTemp1		; read next string character
		iread				; using the mode register
		test    w			; are we at the last char?
		snz				; if not=0, skip ahead
		jmp	:out			; yes, leave & fix page bits
		call    rs232TxSendByte		; not 0, so send character
		_bank	rs232TxBank
		inc     localTemp1		; point to next character
		jmp     :loop			; loop until done

:out		_mode	DDIR_W			; reset the mode register to point 
						; to the data latches
		retp

;VP_END: RS232 Transmit

;VP_END: DTMF Detection
;------------------------------------------------------------------------------
clearBank	; Clears an entire bank of RAM.
		; To use, first load the FSR with the starting address 
		; of the bank to clear.
;--------------------------------------------------------------------------
:loop  
		clr	indf
		inc	fsr
		mov	w,fsr
		and	w,#%00001111
		snb	z
		retp
		jmp	:loop
;VP_END: DTMF Detection

;VP_BEGIN : 5ms Timer
;--------------------------------------------------------------------------
delayW100Ms
; This subroutine delays 'w'*100 milliseconds. (not exactly, but pretty close)
; This subroutine uses the TEMP register
; INPUT		w	-	w/100 milliseconds to delay for.
; OUTPUT	Returns after 100 * n milliseconds.
;--------------------------------------------------------------------------
		mov	localTemp0,w
		_bank	Timer5msBank
		mov	w,#48		; This loop delays for 100ms
		mov	Timer5msDiv,w	;
:loop	
		mov	w,#-20		; delay 100ms
		mov	Timer5ms,w	;
		clrb	timer5msFlag	; clear the flag and
:wait		sb	timer5msFlag	;   wait until the timer5msFlag is set.
		jmp	:wait
		decsz	localTemp0	; and do this w times.
		jmp	:loop
		retp
;VP_END : 5ms Timer










;*****************************************************************************************
org	STRINGS_ORG		; This label defines where strings are kept in program space.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Put String Data Here
;------------------------------------------------------------------------------

;VP_BEGIN: DTMF Detection
_hello		dw	13,10,'Press any key to detect DTMF>',0
_DTMFstring	dw	13,10,'DTMF>',0
;VP_END: DTMF Detection

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Routines that use location-dependant data, such as in example below, should
	;	  use a LABEL rather than a literal value as their input.  Example:
	;	  instead of
	;		mov	m,#3		 ; move upper nibble of address of strings into m
	;	  use
	;		mov	m,#STRINGS_ORG>>8; move upper nibble of address of strings into m
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?










;*****************************************************************************************
org	DTMF_SUBS_ORG
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	To ensure that several Virtual Peripherals, when pasted together, do not cross 
	;	a page boundary without the integrator's knowledge, put an ORG statement and one 
	;	instruction at every page boundary.  This will generate an error if a pasted 
	;	subroutine moves another subroutine to a page boundary.  
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	jmp	$	; This instruction will cause an assembler error if the source code before
			; the org statement inadvertantly crosses a page boundary.

	tableStart

dtmfdGetValid	jmp	_dtmfdGetValid

	tableEnd

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
asciiTable	; Ascii value at index (0-15)
; INPUT:	w	-	Index into the table (0-15 value)
; OUTPUT:	w	-	Constant at that index
;--------------------------------------------------------------------------
	tableStart
		add	pc,w
		retw	'1'
		retw	'2'			
		retw	'3'
		retw	'A'
		retw	'4'
		retw	'5'	
		retw	'6'
		retw	'B'
		retw	'7'
		retw	'8'
		retw	'9'
		retw	'C'
		retw	'*'
		retw	'0'
		retw	'#'
		retw	'D'
	tableEnd
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
index2digit
; This subroutine converts a digit from 0-9, '*' or '#' or the 
; characters 'A'-'D' to a table. 
;--------------------------------------------------------------------------
		call	asciiTable
		retp
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
dtmfdSample
; Get the result of one accumulation.  Store the result 
; in dtmfdResult1, dtmfdResult2, dtmfdResult3 and dtmfdResult4
; inputs:	before calling:
;			- Disable PWM output
;			- Set or clear the dtmfdSampleLows bit
; outputs:	These are affected:
;			- ALL temporary globals are changed.
;			- FSR is destroyed
;			- calculated results of all accumulations
;			  in the result registers.
;			- w register returns an index to the winning frequency.
;			  if no frequency won, wreg will = #4 (bit 2 set.)
;			- high score in localTemp0 register
;--------------------------------------------------------------------------
		_bank	dtmfdSampleCnt		; use isrMultiplex as a sample counter
		mov	w,#DTMF_SAMPL_LO	; if we are sampling low frequencies, take lowSamples samples
		sb	dtmfdSampleLows		;
		mov	w,#DTMF_SAMPL_HI	; else take highSamples samples.
		mov	dtmfdSampleCnt,w	; move this value from w
		mov	w,#dtmfdCh1		; point to DTMF channel 1's accumulators
		mov	fsr,w
		page	clearBank
		call	clearBank		; and clear it

		clrb	dtmfdDetectDone		; clear the dtmfdDone indicator
		setb	dtmfdDetectEn		; enable DTMF detection
:wait		sb	dtmfdDetectDone		; wait until the accumulation cycle is finished.
		jmp	:wait	
		clrb	dtmfdDetectEn		; disable DTMF Detection

		mov	w,#dtmfdSinAcc1Lo	; do the calculations on acc1
		page	dtmfDetectCalcs
		call	dtmfDetectCalcs
		_bank	dtmfdCh1
		mov	dtmfdResult1,w		; and store result in dtmfdResult1

		mov	w,#dtmfdSinAcc2Lo	; ditto for 2
		page	dtmfDetectCalcs
		call	dtmfDetectCalcs
		_bank	dtmfdCh1
		mov	dtmfdResult2,w

		mov	w,#dtmfdSinAcc3Lo	; ditto
		page	dtmfDetectCalcs
		call	dtmfDetectCalcs
		_bank	dtmfdCh1
		mov	dtmfdResult3,w

		mov	w,#dtmfdSinAcc4Lo	; ditto
		page	dtmfDetectCalcs
		call	dtmfDetectCalcs
		_bank	dtmfdCh1
		mov	dtmfdResult4,w
	
		mov	w,#dtmfdCh1		; point to dtmfdCh1
		page	dtmfGetWinner	
		call	dtmfGetWinner		; and get the winner in this bank.
		mov	w,dtmfdWinnIndex	; store the index to the winner in w.
		retp
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
absolute16	;	absolutes the 16-bit value passed to it
		; inputs:	-pointer to the high-byte in w
		; 		-must have wreg enabled in !option register
		; outputs:	-absolute 16-bit value in passed pointer location
		;		-FSR points to the high byte
;--------------------------------------------------------------------------
		mov	fsr,w
		sb	indf.7		; return if high byte is positive
		retp		
		mov	w,indf		; else store high byte in w
		xor	w,#$ff		; and negate w
		dec	fsr		; point to low byte
		not	indf		; 1's complement low byte
		inc	indf		; and increment it
		snb	z			
		inc	wreg		; increment high byte if there was roll-over
		inc	fsr		
		mov	indf,w		; and store the new high-byte
		retp
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
scale16ByN	; Shifts right the 16-bit value passed to it by n bits
 		; If the MSByte is not equal to zero after the final shift,
		; it is cleared and the lower byte is set to 255.
		; inputs:	-#high-byte in FSR
		;		-n= bits to shift right in w
		;		-must have wreg enabled in !option register
		; outputs:	-value shifted right n bits
		;		-fsr = high-byte
		; destroys:	-localTemp0
;--------------------------------------------------------------------------
		mov	localTemp0,w
		mov	w,indf		; from the "absoluteScaleAndSquare"->"absolute16", fsr contains #dtmfdBHi
		clr	indf		; clear the upper byte since we want EVERYTHING in the lower byte
		dec	fsr
		test	localTemp0	; check the input variable for zero
:loop
		snb	z	
		jmp	:done		; jump to :done if finished
		clrb	c
		rr	wreg		; rotate the MSbyte right
		rr	indf		; rotate the LSbyte right
		dec	localTemp0	; decrement the counter
		jmp	:loop
:done
		test	wreg		; test the upper byte for zero.
		snb	z	
		retp
		mov	w,#255		; if the upper byte was not = 0, make the lower byte = 255	
		mov	indf,w		;
		retp
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
add16Plus16	; adds the 16-bit value pointed to by w to the location
		; in w+2 and divides by two.
		;
		; inputs:	- #valuel in w
		; outputs:	- the location in memory passed in w is added
		;		  the next contiguous 16 bits and the result
		;		  is stored in the next contiguous 16 bits.
		;		  The result is divided by two to keep in range.
		;		- fsr = value2h
;--------------------------------------------------------------------------
		clrb	dtmfdNeg
		mov	fsr,w		; indf = valuel
		mov	w,indf
		inc	fsr		; indf = valueh
		inc	fsr		; indf = value2l
		add	indf,w
		dec	fsr		; indf = valueh
		mov	w,indf		
		inc	fsr		; indf = value2l
		inc	fsr		; indf = value2h
		snb	c
		inc	indf
		snb	z
		setb	dtmfdNeg	; indicate the rollover
		add	indf,w
		snb	dtmfdNeg	; if there was a roll-over, set the carry bit
		setb	c
		rr	indf		; divide by two to keep in range.
		dec	fsr		; indf = value2l
		rr	indf		; divide by two to keep in range.
		inc	fsr		; fsr = value2h
		retp
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
Multiply8x8 ; Multiply W by dtmfdInput
;	INPUTS:		W * dtmfdInput
;	OUTPUTS: 	16-bit output value in dtmfdBLo and dtmfdBHi
;--------------------------------------------------------------------------
		clr	dtmfdLoopCount
		setb	dtmfdLoopCount.3; move 8 to dtmfdLoopCount without affecting w
		clr	dtmfdBLo	;1
		clr	dtmfdBHi	;1 ;3
:loop
		clrb	c		;1
		snb	dtmfdInput.0	;1
		add	dtmfdBHi,w	;1
		rr	dtmfdBHi	;1
		rr	dtmfdBLo	;1
		rr	dtmfdInput	;1
		decsz	dtmfdLoopCount	;1	
		jmp	:loop		;3	10=looptime (78 on exit)
					;	78 + 3 = 81
		retp			;3	82 + 3 = 84	ALWAYS!!!
					;	for 16 bit result.
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
;	sqRoot 		; By John Keenan
;	Routine to take the square root of a 16 bit unsigned number
;	entry: 	dtmfdBLo - low byte of number
;		dtmfdBHi - high byte of number
;	exit:	W register returns 8 bit result
;--------------------------------------------------------------------------

sqRoot16	mov	w,#$c0			; initialise dtmfdRootMask
		mov	dtmfdRootMask,w		;
		mov	w,#$40			; initialise answer
:sq1		setb	c
		sub	dtmfdBHi,w		; subtract the root develped so far
		sb	c			; restore subtraction if carry cleared
		jmp	:sq5
:sq2		or	w,dtmfdRootMask		; set the current bit
:sq3		nop
		rl	dtmfdBLo		; shift number left one position
		rl	dtmfdBHi
		rr	dtmfdRootMask		; picks up ms bit of input2
		snb	dtmfdRootMask.7
		jmp	:sq6
		xor	w,dtmfdRootMask		; append 01 to the root developed so far
		sb	c			; if the lsb of dtmfdRootMask was shifted into carry,
		jmp	:sq1			; then we're done. Otherwise loop again
		setb	c
		sub	dtmfdBHi,w
		sb	c
		retp
		snb	z
		snb	dtmfdBLo.7
		xor	w,#1
		retp
:sq6		snb	c
		retp
		clrb	dtmfdRootMask.7
		xor	w,dtmfdRootMask
		setb	c
		sub	dtmfdBHi,w
		jmp	:sq2
:sq5		add	dtmfdBHi,w		; carry=0 here
		jmp	:sq3
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
dtmfDetectCalcs		;	         _____________
			; OUTPUT = W = \/(a^2) + (b^2) 
			; Do all processing for DTMF detection, using FSR
			; as index into the bank to process in w register
			; output result of this calculation in the w register
;--------------------------------------------------------------------------
		mov	localTemp1,w
		call	absoluteScaleAndSquare	; a = a^2
		_bank	dtmfdMathBank	
		mov	w,dtmfdBLo		; store result in a
		mov	dtmfdALo,w
		mov	w,dtmfdBHi
		mov	dtmfdAHi,w
		inc	localTemp1		; increment pointer to register location
		inc	localTemp1
		mov	w,localTemp1		
		call	absoluteScaleAndSquare	; b = b^2
		mov	w,#dtmfdALo		; now add a^2 and b^2
		call	add16Plus16		; result in dtmfdBLo and dtmfdBHi
						; y = a^2 + b^2
		call	sqRoot16		; and get the square root of the result.
						; result returned in w register.
						;	_____________
						; y = \/(a^2) + (b^2)
		retp				; return with result in w register
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
absoluteScaleAndSquare
			; absolute, scale, and square the 16-bit value pointed
			; to by the w register.  Results are in dtmfdBLo and dtmfdBHi of
			; of the math bank
;--------------------------------------------------------------------------
		mov	fsr,w
		mov	w,indf			; and store value at this location in localTemp2
		mov	localTemp2,w
		inc	fsr			; get the high byte
		mov	w,indf			; and move it to w
		_bank	dtmfdMathBank
		mov	dtmfdBHi,w		; store high byte in dtmfdBHi
		mov	w,localTemp2		; store low byte in dtmfdBLo
		mov	dtmfdBLo,w
		mov	w,#dtmfdBHi		; w-->dtmfdBHi
		call	absolute16		; absolute b 
		mov	w,#DTMF_SCALING		; and shift b right by DTMF_SCALING
		call	scale16ByN		;
		mov	w,dtmfdBLo
		mov	dtmfdInput,w
		call	Multiply8x8		; square b
		retp				; return with result in dtmfdBLo and dtmfdBHi
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
checkPowers	; Lo-frequency power (L) and high frequency power (H)
		; shall, according to Bellcore, be within: 
		;		L-8 =< H <= L+4 [dB] 
		; This routine narrows the check to: 
		;		L-6 =< H <= L+3.5 [dB]
		; which is the same as 2*H > L and 1.5*L > H in power levels (voltage)
		; Disabling this routine increases sensitivity to DTMF digits.
		; INPUTS:  dtmfdHiFreqPow and dtmfdLoFreqPow are
		;	   already loaded with their corresponding
		;	   results from the last sample period.
		; OUTPUTS: Returns with 0 in the W register if the values
		; 	   are within 1.5 of each other, and $ff if they
		;	   are not.
;--------------------------------------------------------------------------
		_bank	dtmfdRefBank
:loLimit					; Verify that Hi-freq power not too small; 2*H > L 
		mov	w,dtmfdLoFreqPow
		clrb	c			; instead of 2*dtmfdHiFreqPow:
		rr	wreg			; = (dtmfdLoFreqPow/2); to avoid overflow 

		mov	w,dtmfdHiFreqPow-w	; if 2*H < L then this is invalid detection
		sb	c
		jmp	:invalid

:hiLimit	mov	w,dtmfdLoFreqPow	; Verify that Lo-freq power not too small; 1.5*L > H
		mov	localTemp0,w
		clrb	c
		rr	wreg			; w=0.5*dtmfdLoFreqPow
		add	localTemp0,w		; = (1.5 * dtmfdLoFreqPow) might overflow
		rr	localTemp0		; = (1.5 * dtmfdLoFreqPow)/2 in case of overflow
		clrb	c
		
		mov	w,dtmfdHiFreqPow
		mov	localTemp1,w
		rr	localTemp1		; = (dtmfdHiFreqPow/2) to make it fair.
		mov	w,localTemp1		; if 1.5 * the lower power is not greater
		mov	w,localTemp0-w		; than or equal to the higher power, this is invalid
		sb	c
		jmp	:invalid
:valid
		clr	w
		retp
:invalid
		mov	w,#$ff
		retp	
;VP_END: DTMF Detection

;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
dtmfGetWinner	; Compare the 4 frequencies and find the winner and 
		; runner-up.  Pre-load the high-score to create a threshold.
		; The winner must be at least 2 times larger than the threshold
		; or than the next highest score, whichever is larger.
		; INPUTS:  Load a pointer to the first result register into W
		; OUTPUTS: - dtmfdHighScore, dtmfdRunUpScore indicate power present
		;	     at each frequency
		;	   - W holds an index to the winning result.  
		;	     W = 4 if no result was valid.
;--------------------------------------------------------------------------
		mov	fsr,w
		mov	w,#DTMF_MIN_SCORE	; DTMF_MIN_SCORE is the score to beat
		mov	dtmfdHighScore,w
		clr	dtmfdRunUpScore
		mov	w,#4			; if this is still 4 at the end, then nothing beat the high score.
		mov	dtmfdWinnIndex,w

		clr	localTemp0		; clear the index

:loop
		mov	w,dtmfdHighScore	; check if the result is higher than the high score
		mov	w,indf-w
		sb	c
		jmp	:checkRunnerUp
						; if yes, save the result.
		mov	w,dtmfdWinnIndex	; save as the runner-up
		mov	dtmfdRunUpIndex,w
		mov	w,dtmfdHighScore
		mov	dtmfdRunUpScore,w
		mov	w,localTemp0		; save the winner and new high score
		mov	dtmfdWinnIndex,w
		mov	w,indf
		mov	dtmfdHighScore,w
		jmp	:next

:checkRunnerUp
		mov	w,dtmfdRunUpScore
		mov	w,indf-w
		sb	c
		jmp	:next
		mov	w,indf			; this one beat the runner-up
		mov	dtmfdRunUpScore,w
		mov	w,localTemp0
		mov	dtmfdRunUpIndex,w
:next
		inc	fsr			; point to the next result
		inc	localTemp0		; and increment the register (index)
		sb	localTemp0.2		; if localTemp0.2 is set, we are done. (we've done 4 results)
		jmp	:loop
		mov	w,dtmfdRunUpScore	; multiply the runner-up by 2.

		add	dtmfdRunUpScore,w	; winner's score must be 2 times larger than the runner up to win.
		snb	c
		jmp	:invalid		; if carry, the high score must be invalid.
		mov	w,dtmfdHighScore	; store the high score
		mov	localTemp0,w
		mov	w,dtmfdRunUpScore	; and return if the high score is above or equal to the (dtmfdRunUpScore * 2)
		mov	w,dtmfdHighScore-w
		snb	c
		retp	
:invalid

		mov	w,#4			; else move 4 into the winner index
		mov	dtmfdWinnIndex,w	; to indicate that nothing won.
		retp
;VP_END: DTMF Detection


;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
_dtmfdGetValid
; This routine waits for a valid DTMF digit, followed by silence.  Once 
; a valid DTMF digit is found, the power difference between the two components 
; are checked (according to Bellcore spec.). Then it must wait for silence 
; (invalid frequency) before a index to the digit can be calculated.
; 
;--------------------------------------------------------------------------

:getHighFrequencies				; wait until a valid high frequency comes in.
		clrb	dtmfdSampleLows
		page	dtmfdSample
		call	dtmfdSample
		snb	wreg.2			; if invalid (=4), start over
		jmp	:getHighFrequencies
		_bank	dtmfdRefBank
		mov	dtmfdHiFreqInd,w	; store result in the highFrequency storage register


:redoHighFrequencies				; now check it to make sure it is still the
		page	dtmfdSample		; same frequency.
		call	dtmfdSample
		_bank	dtmfdRefBank
		xor	w,dtmfdHiFreqInd	; compare with result in the highFrequency storage register
		sb	z			; if the same freq. is detected, do low freq.
		jmp	:getHighFrequencies

:getLowFrequencies				; now get the low frequency component
		mov	w,localTemp0		; store the power in the high frequency
		mov	dtmfdHiFreqPow,w
		setb	dtmfdSampleLows
		page	dtmfdSample
		call	dtmfdSample
		snb	wreg.2			; if invalid, start over
		jmp	:getHighFrequencies
		_bank	dtmfdRefBank
		mov	dtmfdLoFreqInd,w	; store result in the highFrequency storage register
		mov	w,localTemp0		; store the power in the high frequency
		mov	dtmfdLoFreqPow,w
		page	checkPowers
		call	checkPowers
		snb	wreg.2
		jmp	:getHighFrequencies

:getSilence					; if valid up to this point, wait until there is silence
						; before returning
		clrb	dtmfdSampleLows
		page	dtmfdSample
		call	dtmfdSample
		sb	wreg.2			; check if valid
		jmp	:getSilence		; if not, recheck

		page	dtmfdSample
		call	dtmfdSample
		sb	wreg.2
		jmp	:getSilence		; If valid freq., wait some more for silence

		page	dtmfdSample
		call	dtmfdSample
		sb	wreg.2
		jmp	:getSilence		; If valid freq., wait some more for silence

		_bank	dtmfdRefBank
		clrb	c
		mov	w,<<dtmfdLoFreqInd	; create the index to the valid digit
		rl	wreg
		or	w,dtmfdHiFreqInd
		mov	dtmfdDigitIndex,w	; and save it

		retp
;VP_END: DTMF Detection










;*****************************************************************************************
org	MAIN_PROGRAM_ORG
;*****************************************************************************************



;------------------------------------------------------------------------------
; RESET VECTOR 
;------------------------------------------------------------------------------

	;------------------------------------------------------------------------------
	; Program execution begins here on power-up or after a reset
	;------------------------------------------------------------------------------

_resetEntry		
	;------------------------------------------------------------------------------
	; Initialize all port configuration
	;------------------------------------------------------------------------------

		_mode	ST_W			;point MODE to write ST register
		mov     w,#RB_ST            	;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rb,w		
IFNDEF 	SX18_20
		mov     w,#RC_ST            	;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rc,w	
ENDIF 	;(SX18_20)
IFDEF 	SX48_52
		mov     w,#RD_ST            	;Setup RD Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rd,w		
		mov     w,#RE_ST            	;Setup RE Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!re,w		
ENDIF 	;(SX48_52)
		_mode	LVL_W			;point MODE to write LVL register
		mov     w,#RA_LVL            	;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!ra,w		 
		mov     w,#RB_LVL            	;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rb,w		
IFNDEF 	SX18_20
		mov     w,#RC_LVL            	;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rc,w	
ENDIF 	;(SX18_20)
IFDEF 	SX48_52
		mov     w,#RD_LVL            	;Setup RD CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rd,w		
		mov     w,#RE_LVL            	;Setup RE CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!re,w		
ENDIF	;(SX48_52)
		_mode	PLP_W			;point MODE to write PLP register
		mov     w,#RA_PLP            	;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!ra,w		 
		mov     w,#RB_PLP            	;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rb,w		
IFNDEF 	SX18_20
		mov     w,#RC_PLP            	;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rc,w	
ENDIF	;(SX18_20)
IFDEF 	SX48_52
		mov     w,#RD_PLP            	;Setup RD Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rd,w		
		mov     w,#RE_PLP            	;Setup RE Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!re,w		
ENDIF	;(SX48_52)
		_mode	DDIR_W			;point MODE to write DDIR register
		mov	w,#RA_DDIR		;Setup RA Direction register, 0 = output, 1 = input		
		mov	!ra,w	
		mov	w,#RB_DDIR		;Setup RB Direction register, 0 = output, 1 = input
		mov	!rb,w			
IFNDEF 	SX18_20
		mov	w,#RC_DDIR		;Setup RC Direction register, 0 = output, 1 = input
		mov	!rc,w			
ENDIF	;(SX18_20)
IFDEF SX48_52
		mov	w,#RD_DDIR		;Setup RD Direction register, 0 = output, 1 = input
		mov	!rd,w			
		mov	w,#RE_DDIR		;Setup RE Direction register, 0 = output, 1 = input
		mov	!re,w			
ENDIF	;(SX48_52)
		mov     w,#RA_latch          	;Initialize RA data latch
		mov     ra,w		
		mov     w,#RB_latch         	;Initialize RB data latch
		mov     rb,w		
IFNDEF 	SX18_20
		mov     w,#RC_latch          	;Initialize RC data latch
		mov     rc,w		
ENDIF	;(SX18_20)
IFDEF SX48_52
		mov     w,#RD_latch         	;Initialize RD data latch
		mov     rd,w			
		mov     w,#RE_latch         	;Initialize RE data latch
		mov     re,w			
ENDIF	;(SX48_52)


	;------------------------------------------------------------------------------
	; Clear all Data RAM locations
	;------------------------------------------------------------------------------

zeroRam
IFDEF SX48_52   				;SX48/52 RAM clear routine
		mov	w,#$0a			;reset all ram starting at $0A
		mov	fsr,w
:zeroRam	clr	indf			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zeroRam

		_bank	bank0			;clear bank 0 registers
		clr	$10
		clr	$11
		clr	$12
		clr	$13
		clr	$14
		clr	$15
		clr	$16
		clr	$17
		clr	$18
		clr	$19
		clr	$1a
		clr	$1b
		clr	$1c
		clr	$1d
		clr	$1e
		clr	$1f

ELSE	;(SX48_52)				;SX18/20/28 RAM clear routine
		clr	fsr			;reset all ram banks
:zeroRam	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	indf			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zeroRam
ENDIF	;(SX48_52)
	;------------------------------------------------------------------------------
	; Initialize program/VP registers
	;------------------------------------------------------------------------------

	;------------------------------------------------------------------------------
	; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
	;------------------------------------------------------------------------------

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	The suggested default values for the option register are:
	;	- Bit 7 set to 0: location $01 addresses the W register (WREG)
	;	- Bit 3 set to 1: Prescaler assigned to WatchDog Timer
	;
	;	If a routine must change the value of the option register (for example, to access
	;	the RTCC register directly), then it should restore the default value for the 
	;	option register before exiting.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?


RTCC_ON		=	%10000000	;Enables RTCC at address $01 (RTW hi)
					;*WREG at address $01 (RTW lo) by default
RTCC_ID		=	%01000000	;Disables RTCC edge interrupt (RTE_IE hi)
					;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT	=	%00100000	;Sets RTCC increment on RTCC pin transition (RTS hi)
					;*RTCC increment on internal instruction (RTS lo) is default
RTCC_FE		=	%00010000	;Sets RTCC to increment on falling edge (RTE_ES hi)
					;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON	=	%00000000	;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF	=	%00001000	;Assigns prescaler to WDT (PSA hi)
PS_000		=	%00000000	;RTCC = 1:2, WDT = 1:1
PS_001		=	%00000001	;RTCC = 1:4, WDT = 1:2
PS_010		=	%00000010	;RTCC = 1:8, WDT = 1:4
PS_011		=	%00000011	;RTCC = 1:16, WDT = 1:8
PS_100		=	%00000100	;RTCC = 1:32, WDT = 1:16
PS_101		=	%00000101	;RTCC = 1:64, WDT = 1:32
PS_110		=	%00000110	;RTCC = 1:128, WDT = 1:64
PS_111		=	%00000111	;RTCC = 1:256, WDT = 1:128

OPTIONSETUP	equ	PS_001			; the default option setup for this program.
		mov	w,#OPTIONSETUP		; setup option register for RTCC interrupts enabled 
		mov	!option,w		; and prescaler assigned to WDT.
		page	mainLoop
		jmp	mainLoop

;------------------------------------------------------------------------------
; MAIN PROGRAM CODE 
;------------------------------------------------------------------------------

mainLoop
IFDEF	DEMO				; Put source code here that would create a runnable
					; demo using this Virtual Peripheral

		mov	w,#_hello		; Send out string.
		call	@rs232TxSendString

:wait	snb	rs232RxPin			; Wait for the rxPin to go low (start bit)
		jmp	:wait
		mov	w,#_DTMFstring
		call	@rs232TxSendString
	
		clrb	dtmfHook		; go off hook
		mov	w,#10
		call	@delayW100Ms		; delay 1s.

:dtmfLoop
		call	@dtmfdGetValid		; Wait for a valid DTMF digit
		_bank	dtmfdRefBank
		mov	w,dtmfdDigitIndex	; Get the ASCII equivalent
		call	@index2digit
		call	@rs232TxSendByte	; Send it
		jmp	:dtmfLoop		; get another digit
ENDIF
		jmp	mainLoop





;*****************************************************************************************
END		;End of program code
;*****************************************************************************************
