;******************************************************************************
; Copyright © [12/09/1998] 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.
;******************************************************************************
;
;	Designed for use with Scenix Modem Rev 1.2
;
; 	Filename:	simple_fsk_gen_1_03.src
;
; 	Authors:	Chris Fogelklou
;			Applications Engineer	
;			Scenix Semiconductor, Inc.
;	
;	Revision:	1.03
;
; 	Part:		SX28AC datecode 9929AA/ SX52BD datecode AB9919AA
; 	Freq:		50Mhz
;
; 	Compiled using Parallax SX-Key software v1.07 and SASM 1.40
;
;       Date Written: 	December 9, 1998
;	Last Revised: 	November 22, 1999
;
;	Program Description
;	This program demonstrates a simple FSK modulation scheme.  It contains
;	no UART, so the baud rate of the FSK modulation is set by the external
; 	Terminal's UART.  It simply converts high data to a low frequency
;	and low data to a high frequency.  The analog voltage is generated by
;	a 1-pin PDM D/A, and the sine wave values are generated by an artificial
;	sine wave generator.  This program could be improved by adding a UART to
;	the program, and sending the data out after it has been received.  Using
;	this method, the data can be manipulated.
;
;	The FSK modulation constants for other FSK standards besides bell202 are
;	also supported, but most require a baud rate generator and flow control.
;
;	BELL202:	   Supported with other BELL202 Modems.
;	V.23 Answer Mode:  Supported using a 1200bps connection to the modem board,
;			   but won't be autodetected because it doesn't output 2100Hz
;			   answer tone for 3 seconds after picking up.
;	V.23 Originate Mode: Can't be supported because requires a 75bps baud rate
;			   generator.
;	BELL103 Originate Mode: Supported using a 300bps connection to the board.
;			   must use another device to dial out.
;	BELL103 Answer Mode: Supported using a 300bps connection to the modem board,
;			   but can't be auto-detected for the same reasons as the
;			   V.23 Answer mode.
;
;	Uncomment the desired modulation type:
;
BELL202			
;V23_ORIGINATE_MODE
;V23_ANSWER_MODE		
;BELL103_ORIGINATE_MODE
;BELL103_ANSWER_MODE
;
; 	Revision History:
;	1.01 & 1.02:  Documentation Updates.
;	1.03: Updated for new SX template, and Modem Boards, Rev 1.2
;
;	Pins Used:
;		PDM_pin		equ	ra.0	; D/A output pin, connect to filter circuitry
;		rx_pin		equ	ra.1	; RS-232 reception pin
;		tx_pin		equ	ra.2	; RS-232 transmission pin, received characters
;						; are echoed on this pin.
;		led_pin		equ	rb.0	; LED pin... Flashes when characters are received
;		hook		equ	rb.4	; drive hook low to go off-hook
;		cts		equ	rb.7	; indicates to the PC that the SX is ready to 
;						; receive data
;
;******************************************************************************
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD/ES,
; SX48BD, SX52BD/ES or SX52BD. For SX48BD/ES and SX52BD/ES, uncomment both defines,
; SX48_52 and SX48_52_ES.
;*****************************************************************************************
;SX18_20
SX28
;SX48_52
;SX48_52_ES

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

	;*********************************************************************************
	; 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
  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_ES			;SX48BD/ES or SX52BD/ES device directives for SX-Key
		device	oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52				;SX48/52/BD device directives for SX-Key
		device	oschs2
    ENDIF
  ENDIF
		freq	50_000_000
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_ES			;SX48BD/ES or SX52BD/ES device directives for SASM
		device	SX52,oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52			;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2  
    ENDIF
  ENDIF
ENDIF
		ID	'FSK_TX10'			; Version = 1.01

		reset	reset_entry				; JUMP to start label on reset


;*****************************************************************************************
; 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.4 (for SX48/52BD/ES)
	; or FSR.7 (SX48/52bd production release) needs to be set appropriately, depending
	; on the bank address being accessed. This macro fixes this.
	;
	; So, instead of using the bank instruction to switch between banks, use _bank instead.
	; 
	;*********************************************************************************
_bank	macro	1
	bank	\1

	IFDEF SX48_52
	  IFDEF SX48_52_ES
	    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
		setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
	    ENDIF
	  ELSE
	    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
	ENDIF
	endm


	;*********************************************************************************
	; 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
		mov	w,#\1		;loads the M register correctly for the SX48BD and SX52BD
		mov	m,w
	ELSE
		mov	m,#\1		;loads the M register correctly for the SX18AC, SX20AC
					;and SX28AC
	ENDIF
	endm

	;*****************************************************************************************
	; Error generating macros
	;*****************************************************************************************

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

	flags0		equ	global_org + 0  ; bit-storage register
	flags1		equ	global_org + 1	; bit-storage register 2
	localTemp0	equ	global_org + 2	; temporary storage register
	localTemp1	equ	global_org + 3	; temporary storage register		
	localTemp2	equ	global_org + 4	; temporary storage register
	isrTemp0	equ	global_org + 5	; Interrupt Service Routine's temp register.  
						; Don't use this register in the mainline.


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

	;*********************************************************************************
	; Bank 0
	;*********************************************************************************
		org     bank0_org

bank0		=	$
	

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
		org     bank1_org

bank1		=	$

;VP: artificial sine generation

sin_gen_bank	=	$

freq_acc_low	ds	1	; 16-bit accumulator which decides when to increment the sine wave
freq_acc_high	ds	1	; 
freq_count_low	ds	1	; 16-bit counter which decides which frequency for the sine wave
freq_count_high	ds	1	; freq_count = Frequency * 6.83671552
sin		ds	1	; The current value of the imitation sin wave
sinvel		ds	1	; The velocity of the sin wave

;VP: PDM D/A VP

PDM_bank		=	$
	
PDM0_acc		ds	1		; PDM accumulator
PDM0_out		ds	1		; current PDM output (D/A)


	;*********************************************************************************
	; Bank 2
	;*********************************************************************************
		org     bank2_org

bank2		=	$


	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
		org     bank3_org

bank3		=	$


	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
		org     bank4_org

bank4		=	$


	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
		org     bank5_org

bank5		=	$

	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
		org     bank6_org

bank6		=	$


	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
		org     bank7_org

bank7		=	$


IFDEF SX48_52
	;*********************************************************************************
	; Bank 8
	;*********************************************************************************
		org	$80	;bank 8 address on SX52

bank8		=	$


	;*********************************************************************************
	; 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:  These are the pins on the Scenix Modem board.  Not all are
	; 			necessary.  Check the documentation at the top of this 
	;			program.
	;*********************************************************************************
PDM_pin		equ	ra.0	; D/A output pin
rx_pin		equ	ra.1	; RS-232 reception pin
tx_pin		equ	ra.2	; RS-232 transmission pin
nothing		equ	ra.3	; N/C
RA_latch	equ	%11111111		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11111010		;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

led_pin		equ	rb.0	; LED pin
rxa_pin		equ	rb.1	; FSK receive pin
cntrl_1		equ	rb.2	; drive cntrl_1 low to disable the output of the LPF
ring		equ	rb.3	; ring detection pin
hook		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
rts		equ	rb.6	; indicates to the SX that the PC wants to transmit data
cts		equ	rb.7	; indicates to the PC that the SX is ready to receive data
RB_latch	equ	%11011011		;SX18/20/28/48/52 port B latch init
RB_DDIR		equ	%01101110		;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

dtmf_in_pin	equ	rc.0	; DTMF input pin
dtmf_fdbk_pin	equ	rc.1	; Negative feedback output for DTMF input
AtoD_in_pin	equ	rc.2	; A/D input pin
AtoD_fdbk_pin	equ	rc.3	; Negative feedback for A/D input
imp_450_pin	equ	rc.4	; Set to an output to set hybrid for 450ohm line impedance.  Tristate otherwise.
imp_600_pin	equ	rc.5	; Set to an output to set hybrid for 600ohm line impedance.  Tristate otherwise.
imp_750_pin	equ	rc.6	; Set to an output to set hybrid for 750ohm line impedance.  Tristate otherwise.
imp_900_pin	equ	rc.7	; Set to an output to set hybrid for 900ohm line impedance.  Tristate otherwise.
RC_latch	equ	%00001111		;SX18/20/28/48/52 port C latch init
RC_DDIR		equ	%11010101		;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

;*****************************************************************************************
; Program constants
;*****************************************************************************************
;-------------------------------------------------------------------------------------
; 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 (freq_count) is 16 bits, and each
; time that the phase accumulator rolls over, the sine wave is advanced
; another of 32 states, giving an effective bit count in the phase
; accumulator of 21, so n = 21
; We will choose a phase update rate of 306.7kHz for the FSK Generation
; = 1/([cyclesperinterupt] * [instructiontime] * [ISR passes]) 
; = 1/(163 * 20ns * 1 passes)
; Therefore, the constant = 2^n * (1/Fs) * FREQ
; Convert the result of the calculation to a hexadecimal number and load
; the upper byte into the freq_count_high register and the lower byte
; into the freq_count_low register.
;-------------------------------------------------------------------------------------
;VP: FSK Generation using the artificial sine generator

f390_l		equ	$6a	; V.23 originating mode channel logic '1' (mark)
f390_h		equ	$0a
f450_l		equ	$04	; V.23 originating mode channel logic '0' (space)
f450_h		equ	$0c
f1300_l		equ	$b7	; V.23 answer mode & Bell202 channel logic '1' (mark)
f1300_h		equ	$22
f2100_l		equ	$15	; V.23 answer mode & Bell202 channel logic '0' (space)
f2100_h		equ	$38
f2225_l		equ	$6b	; Bell 103 answer mode channel logic '1' (mark)
f2225_h		equ	$3b
f2025_l		equ	$14	; Bell 103 answer mode channel logic '0' (space)
f2025_h		equ	$36
f1270_l		equ	$ea	; Bell 103 originating mode channel logic '1' (mark)
f1270_h		equ	$21
f1070_l		equ	$93	; Bell 103 originating mode channel logic '0' (space)
f1070_h		equ	$1c

IFDEF	V23_ORIGINATE_MODE
	F0_l	equ	f450_l
	F0_h	equ	f450_h
	F1_l	equ	f390_l
	F1_h	equ	f390_h
	;----------------------------------------------------
	; *** Sample_rate/128 = 9600/128 = 75bps
	;----------------------------------------------------
	fsk_tx_baud_bit	=       7			;
ENDIF
IFDEF	V23_ANSWER_MODE
	F0_l	equ	f2100_l
	F0_h	equ	f2100_h
	F1_l	equ	f1300_l
	F1_h	equ	f1300_h
	;----------------------------------------------------
	; *** Sample_rate/8 = 9600/8 = 75bps
	;----------------------------------------------------
	fsk_tx_baud_bit	=       3			;
ENDIF
IFDEF	BELL202
	F0_l	equ	f2100_l
	F0_h	equ	f2100_h
	F1_l	equ	f1300_l
	F1_h	equ	f1300_h
	;----------------------------------------------------
	; *** Sample_rate/8 = 9600/8 = 75bps
	;----------------------------------------------------
	fsk_tx_baud_bit	=       3			;
ENDIF
IFDEF	BELL103_ORIGINATE_MODE
	F0_l	equ	f1070_l
	F0_h	equ	f1070_h
	F1_l	equ	f1270_l
	F1_h	equ	f1270_h
	;----------------------------------------------------
	; *** Sample_rate/128 = 9600/32 = 300bps
	;----------------------------------------------------
	fsk_tx_baud_bit	=       5			;
ENDIF
IFDEF	BELL103_ANSWER_MODE
	F0_l	equ	f2025_l
	F0_h	equ	f2025_h
	F1_l	equ	f2225_l
	F1_h	equ	f2225_h
	;----------------------------------------------------
	; *** Sample_rate/128 = 9600/32 = 300bps
	;----------------------------------------------------
	fsk_tx_baud_bit	=       5			;
ENDIF
	
;VP:  Interrupt Setup.
;-------------------------------------------------------------------------------------
int_period	equ	163		; Gives an interrupt period at 50MHz of (163 * (1/50000000)s) = 3.26us
					; Which gives an interrupt frequency of (1/3.26u)Hz = 306.75kHz
;-------------------------------------------------------------------------------------
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	$02		;Read Timer Capture register low byte
TCPH_R		equ	$02		;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 Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_R		equ	$0E		;Read Port Schmitt Trigger 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 Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W		equ	$1E		;Write Port Schmitt Trigger 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


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

INTERRUPT_ORG		equ	$0	; Interrupt must always start at location zero
RESET_ENTRY_ORG		equ	$1FB	; The program will jump here on reset.
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 = (Cycle Frequency / -(retiw value))  For example:
; With a retiw value of -163 and an oscillator frequency of 50MHz, this
; code runs every 3.26us.
;------------------------------------------------------------------------------
ISR					;3	The interrupt service routine...

;VP: PDM D/A Conversion

PDM_output

	;------------------------------------------------------------------------------
	; Virtual Peripheral: Jitter-Free, Deterministic Pulse Density Modulation
	;
	;	Input variable(s): PDM0_out:  	Value from 0 - 255 corresponding to DC
	;					voltage
	;	Output variable(s): 		Ra.0 pin is PDM output pin
	;	Variable(s) affected: 		PDM0_acc variable
	;	Flag(s) affected: 		None
	;	Program Cycles:			11 cycles (turbo mode)
	;------------------------------------------------------------------------------
	_bank	PDM_bank		;1		; Update the PDM pin
	
	add	PDM0_acc,PDM0_out	;2,3		; set PDM pin if carry
	snc				;1,4	
	jmp	:set_pin		;1,5 or 3,7 (carry)
:clear_pin
	nop				;1,6		; clear PDM pin (no carry)
	nop				;1,7
	clrb	PDM_pin			;1,8
	jmp	:PDM_out		;3,11

:set_pin				;7		; set PDM pin (carry)
	setb	PDM_pin			;1,8
	jmp	:PDM_out		;3,11

:PDM_out				; 11 PDM cycles total.
;------------------------------------------------------------------------------
; TOTAL CYCLE COUNT: 14
;------------------------------------------------------------------------------
	;------------------------------------------------------------------------------
	; Virtual Peripheral: Artificial Sine Wave Generation
	;
	;	Input variable(s): 		freq_count_l, freq_count_h
	;	Output variable(s): 		sin,w returned as current value of sine
	;					wave.
	;	Variable(s) affected: 		freq_acc_high, freq_acc_low, sinvel, sin
	;	Flag(s) affected: 		None
	;	Program Cycles:			20 cycles worst case(turbo mode)
	;	Variable Length?:		Yes
	;------------------------------------------------------------------------------
		_bank	sin_gen_bank	;1

		mov	w,freq_count_low;1
		add	freq_acc_low,w	;1	;advance sine at frequency
		sc			;1
		jmp	:no_carry	;1,3	; if lower byte rolls over
		inc	freq_acc_high	;1	; carry over to upper byte
		sz			;1
		jmp	:no_carry	;1,3	; if carry causes roll-over
		mov	w,freq_count_high;1
		mov	freq_acc_high,w	;1	; then add freq counter to accumulator (which should be zero,
						; so move will work)
						; and update sine wave
		jmp	:change_sin	;3
:no_carry				
		mov	w,freq_count_high;1
		add	freq_acc_high,w	;1	; add the upper bytes of the accumulators
		sc			;1
		jmp	:no_change	;1,14
:change_sin

					; 14 cycles at this point
		mov	w,++sinvel	;1	; if the velocity of sine
		sb	sin.7		;1	; is positive, accelerate
		mov	w,--sinvel	;1	; it.  Otherwise, decelerate it.
		mov	sinvel,w	;1	
		add	sin,w		;1,19	; add the velocity to sin

:no_change
		
sine_generator_out
		mov	w,sin		;1,20 cycles worst case
;------------------------------------------------------------------------------
; TOTAL CYCLE COUNT: 14 + 20wc	=	34 cycles worst case
;------------------------------------------------------------------------------
		_bank	PDM_bank	;1
		mov	PDM0_out,w	;1	; mov the value of SIN into the PDM output
		mov	w,#128		;1
		add	PDM0_out,w	;1 add 128 to put it in the center of the PDM output

;------------------------------------------------------------------------------
ISR_out					; ISR goes here when finished.
;------------------------------------------------------------------------------

isr_end						;3
		mov	w,#-int_period		;1   ; refresh RTCC on return
		retiw				;3,7 ; return from the interrupt
						;    ; = 1/(int_period*RTCC prescaler*1/50MHz)
						;    ; = 1/(163*1*20ns) = 3.26us
;*****************************************************************************************
; End of the Interrupt Service Routine
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************









;*****************************************************************************************
org	RESET_ENTRY_ORG
;*****************************************************************************************
;------------------------------------------------------------------------------
reset_entry					; Program starts here on power-up
	page	_reset_entry
	jmp	_reset_entry
;------------------------------------------------------------------------------




;*****************************************************************************************
org	MAIN_PROGRAM_ORG
;*****************************************************************************************
;*****************************************************************************************
; RESET VECTOR 
;*****************************************************************************************

	;*********************************************************************************
	; Program execution begins here on power-up or after a reset
	;*********************************************************************************
_reset_entry		
	;*********************************************************************************
	; Initialise 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		
		mov     w,#RC_ST            	;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rc,w	
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
		_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		
		mov     w,#RC_LVL            	;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rc,w	
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
		_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		
		mov     w,#RC_PLP            	;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rc,w	
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
		_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			
		mov	w,#RC_DDIR		;Setup RC Direction register, 0 = output, 1 = input
		mov	!rc,w			
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
		mov     w,#RA_latch          	;Initialize RA data latch
		mov     ra,w		
		mov     w,#RB_latch         	;Initialize RB data latch
		mov     rb,w		
		mov     w,#RC_latch          	;Initialize RC data latch
		mov     rc,w		
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


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

		_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     					;SX18/20/28 RAM clear routine
		clr	fsr			;reset all ram banks
:zero_ram	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram
ENDIF
	;*********************************************************************************
	; Initialize program/VP registers
	;*********************************************************************************


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

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 lo)
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	RTCC_PS_OFF|PS_111	; the default option setup for this program.
		jmp	@main

;*****************************************************************************************
; MAIN PROGRAM CODE 
;*****************************************************************************************

	;*********************************************************************************
	; Main
	;*********************************************************************************
main		
		_bank	sin_gen_bank			
;		mov	sin,#32			; init variables.  A sine starts at 1, a cos wave starts at 0. 
;		mov	sinvel,#0		
		mov	sin,#-4			; use these values for a wave which is 90 degrees out of phase.
		mov	sinvel,#-8

		clrb	CTS			; enable transmission from the PC if hardware flow
						; control is enabled.
		mov	w,#OPTIONSETUP		; setup option register for RTCC interrupts enabled 
		mov	!option,w		; and no prescaler.
	
		_bank	sin_gen_bank
		mov	freq_count_high,#f2100_h; output a frequency of 2100Hz
		mov	freq_count_low,#f2100_l	; (bell202)

		clrb	hook			; go off-hook
		jb	rx_pin,$		; wait until data is received (Start bit is a low)

		mov	w,#OPTIONSETUP		; setup option register for RTCC interrupts enabled 
		mov	!option,w		; and no prescaler.
		
		clrb	hook			; go off-hook
main_loop
		jb	rx_pin,:rx_is_high	; If the rs_232 receive pin is currently a low

:rx_is_low	clrb	led_pin			; Turn the LED on
		clrb	tx_pin			; Echo the character
		mov	freq_count_high,#f0_h	; output a frequency of 2100Hz
		mov	freq_count_low,#f0_l	; (bell202)
		jmp	main_loop			
:rx_is_high	setb	led_pin			; Else turn the LED off
		setb	tx_pin			; turn Echo the character
		mov	freq_count_high,#f1_h	; output a frequency of 1300Hz
		mov	freq_count_low,#f1_l	; (bell202)
		jmp	main_loop			



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