please dont rip this site

Scenix Lib IO Dev Modem V_23_ORIGINATE_1_37.SRC

; ******************************************************************************
; Copyright © [05/15/1999] 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:	v_23_originate_1_37.src
;
; Author:	Chris Fogelklou
;		Applications Engineer
;		Scenix Semiconductor Inc.
;
; Revision:	1.37
;
; Part:		SX28AC datecode 9929AA/ SX52BD datecode AB9919AA
; Freq:		50Mhz
;
; Compiled Using: SX-Key v.1.07 and SASM V. 1.43
;
; Date:		May 23, 1999.
;
; Revised	November 30, 1999
;
; Program Description:
;		This program performs V.23 origination on the Scenix/IDC 
;		modem boards V.1.2.  These specifications are followed:
;
;	User Interface
;		- Software UART will provide the modem’s interface.
;			- 1200 baud
;			- No Parity
;			- 8 Data Bits
;			- 1 Stop Bit
;			- Hardware Flow Control (CTS, RTS)
;			- Compact AT command set
;			- 64-byte command buffer
;			- Dial: “ATDTxxxxxxxxx…”
;			- Switch from data mode to command mode: “+++”
;				To escape, wait at least 3 seconds from the last transmitted 
;				character, and type +++ with less than 1 second between each 
;				character.  The modem will return to command mode if another
;				character is not received in 3 seconds.
;			- Switch from command mode to data mode: “ATO”
;			- Hang up: “ATH”
;			- Initialize: “ATZ”
;			- Automatic Hybrid Adjustment: “ATY”
;
;	Signal Generation/Detection Software
;		- DTMF Generation for Dialing
;			- Tones generated: 697Hz, 770Hz, 852Hz, 941Hz, 1209Hz, 1336Hz, 1477Hz,1633Hz
;			- On time = 100ms
;			- Off time = 100ms
;			- Off-hook delay time before dialing = 4 s
;			- D/A conversion provided by filtered PDM output
;			- Data transmission and modulation
;		- FSK transmission data rate at 75bps
;			- Hardware flow control, 16-byte buffer, and 75bps asynchronous transmitter for
;			  data rate conversion from 1200bps to 75bps
;			- Logic ‘1’ (mark) modulated by 390 Hz
;			- Logic ‘0’ (space) modulated by 450 Hz
;			- Transmission power = -15dB
;			- D/A conversion provided by filtered PDM output
;			- Data reception and demodulation
;		- FSK reception data rate at 1200bps
;			- Logic ‘1’ (mark) demodulated from 1300Hz carrier
;			- Logic ‘0’ (space) demodulated from 2100Hz carrier
;			- Carrier detection
;			- Timed-Zero-Cross algorithm
;		- D/A conversion
;			- Pulse Position Modulation with maximum output frequency of 307kHz
;
;	Hardware Specifications
;		- Filtering
;			- Low pass filter on PDM output (fc = 1633Hz)
;			- High pass filter on FSK input (fc = 1300Hz)
;		- Hybrid (removes tx signal from rx signal)
;			- Four settings provided for automatic hybrid adjustment for various line
;			  impedance’s
;			- Hybrid adjusted by outputting signal onto line and measuring fed-back signal
;			  with a low-resolution sigma-delta A/D converter
;			- FSK input sensitivity = -30dB
;			- Auto-Hybrid removed in V.1.37
;		- UART
;			- RS-232 interface provided through MAX232 or similar IC
;			- Interface provided through RXD, TXD, RTS, and CTS lines
;
;	Testing Specifications
;			- Initial tests using function generator and off-the-shelf V.23 modems
;			- Second round of testing performed with IDC’s modem test equipment
;			- Tests performed:
;			- Input Sensitivity
;			- DTMF output level
;			- FSK output level
;			- Error rate
;			- FCC part 68 and FCC part 15 qualified
;			- CTR-21 ready
;			- All test results will be documented
;
; Program Instructions;
;		To use this program, the modem board must be connected to a serial port at
;		these settings:
;			1200 bps
;			No parity
;			8 Data Bits
;			1 stop bit
;			Hardware flow control ON!!! (CTS/RTS)
;
;		These AT commands can be used:
;
;			ATDT - Used to dial into a remote modem
;			ATH - Used to hang up a call
;			ATZ - Used to initialize the modem settings
;			ATO - Switches back to data mode from command mode
;			+++ - Switches from data mode to command mode.
;			?   - Re-prints the help screen to the terminal.
;
; Revision History:
;		1.10 Took semi-working V.23 code and cleaned it up.  Kept it working, but made
;		few improvements to the operation.
;		1.15 Finally got FSK receive to work error free!!! Whooopeee!!!
;		1.17 Added documentation.
;		1.20 Added carrier detection to the software
;		1.30 Added automatic hybrid adjustment to the software.
;		1.32 Automatic hybrid adjustment tweaked until working.  Component values for A/D:
;		     C = 470pF, R1 = 22k, R2 = 10k
;		1.35 Added guard times around the “+++” coming in.  
;		1.37 Removed Auto-Hybrid Adjust.  Made sure assembly worked in SASM.
;		     Removed CARRYX directive and modified source code accordingly.
;
;
;	RESOURCES:
;       Program memory: TBD
;       Data memory:	TBD
;       I/O Count:	TBD
;
;*****************************************************************************************
; 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

;*****************************************************************************************
; 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,oscxt4,turbo,stackx_optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oscxt4,turbo,stackx_optionx
  ENDIF
		freq	50_000_000
ELSE  					;SASM Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SASM
		device	SX18,oschs1,turbo,stackx,optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SASM
		device	SX28,oschs1,turbo,stackx,optionx
  ENDIF
ENDIF
		ID	'v23org13'			; Version = 1.1

		reset	reset_entry			; JUMP to reset_entry 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

	ERROR ' This program has not been ported to SX48/52'
					; Let the programmer know that this program
					; will only work on the SX28.
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

flags	ds	1
	dtmf_gen_en	equ	flags.0	; Signifies whether or not DTMF output is enabled
	sine_gen_en	equ	flags.1	; Enables the sine generator(s) for DTMF generation and
					; FSK generation
	timer_flag	equ	flags.2	; Set every time the timers roll over.
	fsk_tx_en	equ	flags.3	; enables the fsk transmission portion of the ISR
	fsk_rx_en	equ	flags.4	; enables the fsk reception portion of the ISR
	rx_flag		equ	flags.5	; this flag is set when a byte is received via the UART
	fsk_rx_bit	equ	flags.6	; this bit indicates the current state of the FSK being
	carrier_detected equ	flags.7	; indicates the presence of a carrier
						;  received.
flags2		ds	1	
	swCarryFlag	equ	flags2.0
temp		ds	1		; Temporary register
temp2		ds	1		; Temporary register
task_switcher	ds	1		; Used in the ISR to switch between tasks.
push_index	ds	1		; Used by the 64-byte buffer to store bytes.	
pop_index	ds	1		; Used by the 64-byte buffer to retrieve bytes.
command_index	ds	1		; Used by the string parser to remember the current
						; command being checked.
	
;******************************************************************************
; Bank 0 Variables
;******************************************************************************
		org     bank0_org

sine_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
sine_index		ds	1	; Index into the sine table for sine wave 1
sine_index2		ds	1	; Index into the sine table for sine wave 2
freq_count_low2		ds	1	; 16-bit counter which sets the sine wave frequency
freq_count_high2	ds	1	; freq_count = Frequency * 6.83671552
freq_acc_high2		ds	1	; 
freq_acc_low2		ds	1	; 16-bit accumulator which decides when to increment the sine wave
curr_sine		ds	1	; The current value of the sine wave
curr_sine2		ds	1	; The current value of sine wave 2
sine2_temp		ds	1	; This register is used to do a temporary shift/add register
PDM_bank		=	$

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

;******************************************************************************
; Bank 1 Variables
;******************************************************************************
		org     bank1_org
timers			=	$
timer_l		ds	1		; The low byte of the 24-bit timer
timer_h		ds	1		; the middle byte of the 24-bit timer
timer_hh	ds	1		; the high byte of the 24-bit timer

serial		=       $                       ;UART bank

tx_high		ds      1                       ;hi byte to transmit
tx_low		ds      1                       ;low byte to transmit
tx_count	ds      1                       ;number of bits sent
tx_divide	ds      1                       ;xmit timing (/16) counter
rx_count	ds      1                       ;number of bits received
rx_divide	ds      1                       ;receive timing counter
rx_byte		ds      1                       ;buffer for incoming byte
rx_count2	ds      1                       ;number of bits received
rx_divide2	ds      1                       ;receive timing counter
rx_byte2	ds      1                       ;buffer for incoming byte
string		ds	1			;the address of the string to be sent
byte		ds	1			;semi-temporary serial register
plus_count	ds	1			;stores the number of consecutive '+''s received during
						; FSK i/o mode.

;******************************************************************************
;	Bank 2 Variables
;******************************************************************************
		org     bank2_org
fsk_transmit_bank	=	$
fsk_receive_bank	=	$
fsk_serial_bank		=	$
	
fsk_tx_high		ds      1                       ;hi byte to transmit
fsk_tx_low		ds      1                       ;low byte to transmit
fsk_tx_count		ds      1                       ;number of bits sent
fsk_tx_divide		ds      1                       ;xmit timing (/16) counter
fsk_tx_divide_2		ds	1

fsk_trans_count		ds	1		; This register counts the number of counts 
						; between transitions at the pin
fsk_last_trans		ds	1		
fsk_rb_past_state 	ds	1		; This register keeps track of the previous 
						; state of port RB, to watch for transitions
fsk_temp_trans		ds	1		; Temporarily stores the transition count after
						; a transition has occurred, until it can be processed.
fsk_flags		ds	1
	fsk_answering		equ	fsk_flags.0
	fsk_tx_bit		equ	fsk_flags.1
	fsk_processing_required_1 equ	fsk_flags.2

;******************************************************************************
;	Bank 3 Variables
;******************************************************************************
		org     bank3_org
carrier_detect_bank	=	$
cd_trans_count		ds	1
cd_trans_avg_l		ds	1
cd_trans_avg_h		ds	1
cd_avg_count		ds	1
cd_rb_past_state	ds	1

;*************************************************************
; Bank 4, 5, 6, 7 (for 64-byte buffer, but can be reused.)
;*************************************************************
		org     bank4_org
buffer		=	$
		org     bank5_org
buffer2		=	$
		org     bank6_org
buffer3		=	$
		org     bank7_org
buffer4		=	$

;*************************************************************
; Equates for the FSK receive part of the modem
;*************************************************************
threshold	=	180	; How many counts to look for for a transition from high frequency to low frequency
fsk_hysterises	=	6	; The number of counts over/under the threshold to allow an actual transition
				; from high to low on RX-bit
;*************************************************************
;**************************************************************************
; Baud rate defines
;**************************************************************************
	; *** 150 baud 
;	baud_bit	=       7                       ;for 2400 baud
;	start_delay	=       128+64+1                ; "    "    "
;	int_period	=       163                     ; "    "    "
	; *** 600 baud 
;	baud_bit	=	5
;	start_delay	=	32+16+1
;	int_period	=	163
	; *** 1200 baud 
baud_bit	=	4
start_delay	=	16+8+1
int_period	=	163

;**************************************************************************
; Equates for common data comm frequencies
;**************************************************************************
f697_h		equ	$012	; DTMF Frequency
f697_l		equ	$09d

f770_h		equ	$014	; DTMF Frequency
f770_l		equ	$090

f852_h		equ	$016	; DTMF Frequency
f852_l		equ	$0c0

f941_h		equ	$019	; DTMF Frequency
f941_l		equ	$021

f1209_h		equ	$020	; DTMF Frequency
f1209_l		equ	$049

f1336_h		equ	$023	; DTMF Frequency
f1336_l		equ	$0ad

f1477_h		equ	$027	; DTMF Frequency
f1477_l		equ	$071

f1633_h		equ	$02b	; DTMF Frequency
f1633_l		equ	$09c
;******************************************************************************
; Equates for FSK generation
;******************************************************************************
f390_h		equ	$00a	; V.23 backchannel logic '1' (mark)
f390_l		equ	$06a

f450_h		equ	$00c	; V.23 backchannel logic '0' (space)
f450_l		equ	$004

f1300_h		equ	$022	; V.23 forward channel logic '1' (mark)
f1300_l		equ	$0b7

f2100_h		equ	$038	; V.23 forward channel logic '0' (space)
f2100_l		equ	$015

f2225_h		equ	$03b	; Bell 103 forward channel logic '1' (mark)
f2225_l		equ	$06b

f2025_h		equ	$036	; Bell 103 forward channel logic '0' (space)
f2025_l		equ	$014

f1070_h		equ	$01c	; Bell 103 backward channel logic '1' (mark)
f1070_l		equ	$093

f1270_h		equ	$021	; Bell 103 backward channel logic '0' (space)
f1270_l		equ	$0ea

	;*********************************************************************************
	; 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	%01101010		;SX18/20/28/48/52 port B DDIR value: HPF

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

fskRxPort	equ	rb
fskRxMask	equ	%00000010

rc_450_mask	equ	%11100101			; Hybrid set-up for 450 ohms
rc_600_mask	equ	%11010101			; Hybrid set-up for 600 ohms
rc_750_mask	equ	%10110101			; Hybrid set-up for 750 ohms
rc_900_mask	equ	%01110101			; Hybrid set-up for 900 ohms

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	rc_600_mask		;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
	;*********************************************************************************
	; 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
;************************ Beginning of program space ***************************
		org	$0			
;******************************************************************************
; Interrupt
; 
; With a retiw value of -163 and an oscillator frequency of 50MHz, this
; code runs every 3.26us.
;******************************************************************************
PDM_output
		bank	PDM_bank		; Update the PDM pin
		add	PDM0_acc,PDM0_out
		snc
		setb	PDM_pin
		sc
		clrb	PDM_pin
;**************************************************************************
		jmp	@ISR	; The ISR is in the second page, so go there.
;******************************************************************************
reset_entry
; Program Starts Here on Power Up
;******************************************************************************

	;**********************************************************************
	; First, call init to initialize the program
	;**********************************************************************
		call	@init

		mov	!option,#%00011111	; enable wreg and rtcc interrupt
		setb	tx_pin			; set the RS-232 tx_pin
		setb	CTS			; Don't allow PC to transmit
		mov	w,#25			; delay 250 milliseconds
		call	@delay_10n_ms

	;**********************************************************************
	; Send "hello" string
	;**********************************************************************
		mov	w,#_hello		; say 'hello'
		call	@send_string
		mov	w,#_help
		call	@send_string

	;**********************************************************************
	; Send prompt
	;**********************************************************************
main_2
_send_prompt	mov	w,#_prompt		; send prompt
		call	@send_string
		clrb	CTS			; Allow PC to transmit
		clr	push_index		; Clear the buffer_push pointer
		clr	pop_index		; Clear the buffer_pop pointer

	;**********************************************************************
	; Fill the command buffer with input characters.  Backspace will delete
	; the last value entered.  
	;**********************************************************************
_cmd_loop
		jnb	rx_flag,$			; Wait until we receive a byte via. RS-232
		clrb	rx_flag				; clear the flag
		bank	serial			
		mov	byte,rx_byte			; Move the received byte to 'byte' and
		call	@uppercase			; convert it to uppercase
		mov	w,#$20				; compare the byte to ' '
		xor	w,byte
		jz	_cmd_loop			; If byte == space, ignore it.
		mov	w,#$0a				; compare the byte to LF
		xor	w,byte		
		jz	_cmd_loop			; If byte == line feed, ignore it.
		mov	w,#$0d				; compare the byte to CR
		xor	w,byte
		jz	:enter				; if byte == CR, parse the string.
		mov	w,byte				; if it does not resemble the above characters, echo it.
		call	@send_byte			; send via. RS-232
		mov	w,#$08				; compare the byte to a backspace.
		xor	w,byte				
		jz	:backspace			; if it equals a backspace, delete one character in the buffer.
		call	@buffer_push			; otherwise, store it
		jmp	_cmd_loop			; and come back for more.
:backspace
		call	@buffer_backspace		
		jmp	_cmd_loop

:enter							; If the user presses enter, then parse the string.


	;**************************************************************************
	; String parser (Checks to see if buffer = any commands)
	; -Checks contents of ascii buffer against any commands stored in ROM
	; -If a command = the contents of the ascii buffer, a routine will be called
	; -Each routine MUST perform a retw 0 on exit, or parse_string will not 
	;  know that a routine has run and it should exit back to command mode.
	; -Exits back to command mode when it detects a zero after the table look-up.
	; -Outputs 'OK' if no commands are matched.
	;**************************************************************************
parse_string
		clr	pop_index			; Clear the index into the ascii buffer
		clr	command_index			; And the index into the commands
		
:loop		call	@buffer_get			; Get a vale from the buffer at ascii_index
		call	command_table			; Get a character from one of the commands
		test	wreg				; If the return value is 0, then this matched
		jz	:nothing			; the command and ran a routine.  Exit.
		bank	serial				
		xor	w,byte				; compare the command's character with the 
		jnz	:not_equal			; buffer's character.
		call	@inc_pop_index			; Increment the index into the buffer.
		jmp	:loop
:not_equal
		inc	command_index			; If the buffer did not equal the command,
		clr	pop_index			; start from the beginning of a new command 
		
		cjne	command_index,#5,:loop		; compare command_index with 5  (This number = # of commands)
:nothing	clrb	fsk_rx_en
		setb	tx_pin
		mov	w,#20
		call	@delay_10n_ms
		mov	w,#_CR
		call	@send_string
		mov	w,#_OK				; If we have checked all 5 commands, then this
		call	@send_string			; did not equal any so send an 'OK' message.
:done
		bank	buffer
		clr	pop_index
		clr	push_index
		clr	buffer
		jmp	_send_prompt

;**************************************************************************
command_table
		mov	w,command_index
		add	pc,w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		jmp	command_1
		jmp	command_2
		jmp	command_3
		jmp	command_4
		jmp	command_5
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
command_1					; Dial command
		mov	w,pop_index
		add	PC,w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		retw	'A'
		retw	'T'
		retw	'D'
		retw	'T'
		jmp	DIAL_MODE
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
command_2					; Hang up command
		mov	w,pop_index
		add	PC,w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		retw	'A'
		retw	'T'
		retw	'H'
		jmp	HANG_UP
tableEnd	; Will cause a compiler error if not located in the lower half of a page.

;**************************************************************************
command_3					; Initialize
		mov	w,pop_index
		add	PC,w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		retw	'A'
		retw	'T'
		retw	'Z'
		jmp	INITIALIZE
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
command_4					; Data mode
		mov	w,pop_index
		add	PC,w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		retw	'A'
		retw	'T'
		retw	'O'
		jmp	FSK_IO
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
command_5					; Hybrid Set-up
		mov	w,pop_index
		add	PC,w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		retw	'?'
		jmp	HELP
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
; END of String parser (Checks to see if buffer = any commands)
;**************************************************************************
	;**********************************************************************
	; Hang Up
	;**********************************************************************
HANG_UP
		call	@disable_o
		clrb	fsk_rx_en	; Disable fsk detection
		mov	w,#50
		call	@delay_100n_ms	; Pause for 5 seconds.
		setb	hook		; hang-up
		retw	0
	;**********************************************************************
	; Initialize
	;**********************************************************************
INITIALIZE
		mov	w,#10
		call	@delay_100n_ms	; Pause for 1 second
		call	@init
		clr	flags
		retw	0


	;**********************************************************************
	; Send Help string
	;**********************************************************************
HELP		mov	w,#_HELP
		call	@send_string
		retw	0
				
	;**************************************************************************
	; Dial Mode:
	; -Dials contents of ascii buffer, starting from location pointed
	;  to by ascii_index.
	; -Responds to these commands:
	; 	0-9, *, #	- Dials the specified number
	;	,		- Pause for 2 seconds
	; -Jumps to data mode after dialing.
	;**************************************************************************
DIAL_MODE
		clrb	sine_gen_en			; Disable sine generation
		clrb	fsk_tx_en			; Disable fsk generation
		clrb	dtmf_gen_en			; Disable dtmf generation
		clrb	hook				; go off-hook
		mov	m,#$0f
		mov	w,#%01101010			; rb.5 (cntrl_3) is tristate, rb.2 (cntrl_1) is low
		mov	!rb,w			
		clrb	cntrl_1				; Enable lowest low-pass filter on output
		mov	w,#40				; delay 4 seconds before dialing
		call	@delay_100n_ms
		mov	w,#_CR				; Send a carriage return
		call	@send_string			
		mov	w,#_DIALING			; send "Dialing" to screen
		call	@send_string
		bank	serial

:dial_loop	call	@buffer_get			; Get a character from the buffer
		call	@uppercase			; convert it to uppercase
		mov	w,byte				; test byte for zero
		snz
		jmp	:originate_mode			; If byte is zero, dialing is done.
		call	@send_byte
		cje	byte,#',',:pause		; if the character = ',', pause for 2s
		call	@digit_2_index			; convert the ascii digit to an 
							; index value
		call	@load_frequencies		; load the frequency registers
		call	@dial_it			; dial the number for 60ms and return.
:inc		call	@inc_pop_index			; increment the index into the table
		jmp	:dial_loop
:pause
		mov	w,#20				; delay 2s
		call	@delay_100n_ms
		jmp	:inc


:originate_mode
	;******************************************************************
	; Set/clear proper flags for origination
	;******************************************************************
		bank	fsk_transmit_bank	
		clr	fsk_tx_divide_2		; clear the transmit-divider
		clr	flags			; clear all flags.
		clrb	fsk_answering		; we are not answering.
		setb	fsk_tx_bit		; set the transmit bit to logic '1'
		setb	sine_gen_en		; enable the sine generators
		setb	fsk_tx_en		; enable the fsk transmitter
		mov	w,#50			; delay 5 seconds after dialing to wait for carrier
		call	@delay_100n_ms	
		jb	carrier_detected,FSK_IO	; if there still is no carrier, exit
		mov	w,#50			; delay 5 seconds after dialing to wait for carrier
		call	@delay_100n_ms	
		jb	carrier_detected,FSK_IO	; if there still is no carrier, exit
		mov	w,#100			; delay 10 seconds after dialing to wait for carrier
		call	@delay_100n_ms	
		jb	carrier_detected,FSK_IO
		mov	w,#150			; delay 15 seconds after dialing to wait for carrier
		call	@delay_100n_ms	
		jb	carrier_detected,FSK_IO
no_carrier
		clrb	fsk_rx_en
		setb	tx_pin
		mov	w,#80			; give carrier 8 more seconds to re-appear
		call	@delay_100n_ms
		jb	carrier_detected,FSK_IO_AGAIN
		mov	w,#_no_carrier
		call	@send_string
		jmp	INITIALIZE
				

	;******************************************************************
	; Once at FSK I/O mode, the program sends/receives data.  In 
	; originate mode, the send is at 75bps and the receive is at 1200bps.
	; Because of the difference in baud rates, hardware flow control
	; is used.  CTS is disabled when the buffer is close to capacity,
	; and re-enabled when the buffer is completely empty.
	;******************************************************************
FSK_IO


		mov	w,#_DATA_MODE		; Send "connect" message
		call	@send_string
FSK_IO_AGAIN	clr	plus_count		; clear the plus count
		clr	push_index		; clear the push pointer to buffer
		clr	pop_index		; clear the pop pointer to buffer
		setb	fsk_rx_en		; enable the FSK reception part of ISR
		mov	m,#$0f
		mov	w,#%01101010		; rb.5 (cntrl_3) is tristate, rb.2 (cntrl_1) is low
		mov	!rb,w			
		clrb	cntrl_1			; Enable lowest low-pass filter on output
		clrb	cts			; clear CTS to tell PC "ready for data"
	;******************************************************************
	; This is the main loop for FSK I/O.  Sends FSK bytes, and receives
	; bytes from the UART.  The FSK receive portion of FSK I/O is completely
	; handled by the ISR
	;******************************************************************

:loop2		
		
		jnb	timer_flag,:no_timeout	; if (timer_flag)
		bank	serial	
		test	plus_count		;	if (plus_count)
		jz	:no_timeout
		mov	w,plus_count		;		if (plus_count==3)
		xor	w,#3			;			return;
		snz
		retw	0
		jmp	:clr_plus_count		;		else clr_plus_count();
						;	else no_timout();
						; else no_timeout();
:no_timeout	jnb	carrier_detected,no_carrier
		jb	rx_flag,:got_byte	; Received a byte of data.  Handle it. 
		bank	fsk_transmit_bank	; If no byte, check to see if we need to transmit
		test	fsk_tx_count		; Are we transmitting anything?  
		sz				;	if no, then send next byte.
		jmp	:loop2			; 	else jump here forever (ISR does all the work)
		
		mov	w,pop_index		; If pop_index == push_index, everything in the buffer has been sent.
		xor	w,push_index
		sz
		jmp	:not_empty_yet		
	;******************************************************************
	; The buffer is empty: initialize the buffer and enable CTS.
	;******************************************************************

:empty
		clr	push_index		; so clear the buffer indexes
		clr	pop_index
		clrb	cts			; and clear cts to allow more data from DCE
		jmp	:loop2
	;******************************************************************
	; The buffer is not empty, keep sending stuff..
	;******************************************************************
:not_empty_yet
		call	@buffer_get		; if the buffer is not empty, get the next byte 
		call	@fsk_send_byte		; from the buffer and send it via. FSK
		call	@inc_pop_index		; and increment the pop index
		and	pop_index,#$0f
		jmp	:loop2
	;******************************************************************
	; The we just received a byte, so put it on the buffer.
	;******************************************************************
:got_byte

		bank	serial
		clrb	rx_flag
		mov	byte,rx_byte
		call	@buffer_push
	;******************************************************************
	; Check to see if the pop index is at (push index + 5)
	;******************************************************************
		and	push_index,#$0f
		mov	w,#5
		
		add	w,push_index
		and	w,#$0f			; keep push index < 16
		xor	w,pop_index		; if (push_index + 5 == pop_index, the buffer is almost full so indicate this)
		snz
		setb	cts			; If push index == pop index, disable CTS
		bank	serial
		mov	w,#'+'			; If the byte = '+', increment plus_count, otherwise, plus_count == 0
		xor	w,rx_byte		; If byte = '+'
		jz	:plus_received		;	plus_received();
		
:clr_plus_count					; Else
		clr	plus_count		; 	clr_plus_count();
		mov	w,#255			; 	
		call	@reset_timers		; 	
		jmp	:loop2			; 
		
:plus_received					; plus_received();
 		test	plus_count		; If !(plus_count)
		jz	:zero_plus_count	;	zero_plus_count();
:some_pluses	jb	timer_flag,:clr_plus_count; else if (timer_flag)
:inc_plus_count	inc	plus_count		;		clr_plus_count();
		mov	w,#200			;	else
		call	@reset_timers		;		inc_plus_count();
		jmp	:loop2

:zero_plus_count
		sb	timer_flag		; If (timer_flag)
		jmp	:clr_plus_count		;	clr_plus_count();
		jmp	:inc_plus_count		; else	inc_plus_count

;**************************************************************************
; Miscellaneous subroutines....
;**************************************************************************
org	$200
;**************************************************************************
reset_timers
; This subroutine times 'w' ticks, and returns with a '1' in w when 
; the specified time has timed out.  Each tick is 13.35296 ms.
; This subroutine uses the TEMP2 register.  Call this routine with w = 0
; to poll for a time_out.
;**************************************************************************
	bank	timers
	not	w
	inc	wreg
	mov	timer_h,w
	clr	timer_l
	clrb	timer_flag
	retp
	
;**************************************************************************
buffer_push
; This subroutine pushes the contents of byte onto the 64-byte ascii buffer. 
;**************************************************************************
	bank	serial			; Move the byte into the buffer
	mov	temp,byte
	mov	fsr,#buffer
	add	fsr,push_index
	mov	indf,temp
					; Increment index and keep it in range
	call	@inc_push_index
	mov	fsr,#buffer	; Null terminate the buffer.
	add	fsr,push_index
	clr	indf
	bank	serial
	retp
;**************************************************************************
;**************************************************************************
buffer_backspace
; This subroutine deletes one value of the buffer and decrements the index 
;**************************************************************************
	dec	push_index
	and	push_index,#%01101111

	mov	fsr,#buffer
	add	fsr,push_index
	clr	indf
	bank	serial
	retp
;**************************************************************************
inc_pop_index
;**************************************************************************
	mov	fsr,#pop_index
	jmp	inc_index
;**************************************************************************
inc_push_index
;**************************************************************************
	mov	fsr,#push_index
;**************************************************************************
inc_index
; This subroutine increments the index into the buffer
;**************************************************************************
	mov	w,indf
	and	w,#%00001111
	xor	w,#%00001111
	jnz	:not_on_verge
	inc	indf
	mov	w,#16
	add	w,indf
	and	w,#$7f
	mov	indf,w
	retp
:not_on_verge
	inc	indf
	retp
;**************************************************************************
buffer_get
; This subroutine retrieves the buffered value at index
;**************************************************************************
	mov	fsr,#buffer
	add	fsr,pop_index
	mov	w,indf
	bank	serial
	mov	byte,w
	
	retp
;**************************************************************************
delay_10n_ms
; This subroutine delays 'w'*10 milliseconds. (not exactly, but pretty close)
; This subroutine uses the TEMP register
; INPUT		w	-	w/10 milliseconds to delay for.
; OUTPUT	Returns after 10 * n milliseconds.
;**************************************************************************
	mov	temp,w
	bank	timers
:loop	clrb	timer_flag	; This loop delays for 10ms
	mov	timer_h,#$0ff
	mov	timer_l,#$041
	jnb	timer_flag,$
	dec	temp		; do it w-1 times.
	jnz	:loop
	clrb	timer_flag
	retp
;**************************************************************************
delay_100n_ms
; 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	temp,w
	bank	timers
:loop	clrb	timer_flag	; This loop delays for 10ms
	mov	timer_h,#$0f8
	mov	timer_l,#$083
	jnb	timer_flag,$
	dec	temp		; do it w-1 times.
	jnz	:loop
	clrb	timer_flag
	retp
;**************************************************************************
zero_ram
; Subroutine - Zero all ram.
; INPUTS:	None
; OUTPUTS:	All ram locations (except special function registers) are = 0
;**************************************************************************
		CLR	FSR
:loop	    	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
		IJNZ    FSR,:loop	        ;repeat until done
		retp
;**************************************************************************
; Subroutine - Disable the outputs
; Load DC value into PDM and disable the output switch.
;**************************************************************************
disable_o
		bank	PDM_bank	; input mode.
		mov	PDM0_out,#128	; put 2.5V DC on PDM output pin
		clrb	sine_gen_en
		clrb	dtmf_gen_en
		clrb	fsk_tx_en
		retp
;**************************************************************************
init
;	Initializes the program.
;**************************************************************************
		_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	

		_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	

		_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	

		_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			

		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		

		setb	hook			; go on hook.
		clrb	cts
		setb	led_pin			; turn on LED
		clr	flags			; Clear all flags
		call	zero_ram
		call	@disable_o

		retp
;**************************************************************************
; Subroutine - Get byte via serial port and echo it back to the serial port
; INPUTS:
;	-NONE
; OUTPUTS:
;	-received byte in rx_byte
;**************************************************************************
get_byte     	jnb     rx_flag,$		;wait till byte is received
		clrb    rx_flag			;reset the receive flag
		bank	serial			;switch to serial bank
		mov     byte,rx_byte		;store byte (copy using W)
						; & fall through to echo char back
;**************************************************************************
; Subroutine - Send byte via serial port
; INPUTS:
;	w 	-	The byte to be sent via RS-232
;**************************************************************************
send_byte    	bank    serial

:wait        	test    tx_count                ;wait for not busy
		jnz     :wait                   ;

		not     w                       ;ready bits (inverse logic)
		mov     tx_high,w               ; store data byte
		setb    tx_low.7                ; set up start bit
		mov     tx_count,#10            ;1 start + 8 data + 1 stop bit
		RETP                            ;leave and fix page bits

;**************************************************************************
; Subroutine - Send byte via serial port
; INPUTS:
;	w 	-	The byte to be sent via RS-232
;**************************************************************************
fsk_send_byte  	bank    fsk_serial_bank

:wait        	test    fsk_tx_count                ;wait for not busy
		jnz     :wait                   ;

		not     w                       ;ready bits (inverse logic)
		mov     fsk_tx_high,w               ; store data byte
		setb    fsk_tx_low.7                ; set up start bit
		mov     fsk_tx_count,#10            ;1 start + 8 data + 1 stop bit
		RETP                            ;leave and fix page bits

;**************************************************************************
; Subroutine - Send string pointed to by address in W register
; INPUTS:
;	w	-	The address of a null-terminated string in program
;			memory
; OUTPUTS:
; 	outputs the string via. RS-232
;**************************************************************************
send_string	bank	serial
 		mov     string,w                ;store string address
:loop        	mov     w,string                ;read next string character
		mov     m,#3                    ; with indirect addressing
		iread                           ; using the mode register
		mov     m,#$F                   ;reset the mode register
		test    w                       ;are we at the last char?
		snz                             ;if not=0, skip ahead
		RETP                            ;yes, leave & fix page bits
		call    send_byte               ;not 0, so send character
		inc     string                  ;point to next character
		jmp     :loop                   ;loop until done

;**************************************************************************
; Subroutine - Make byte uppercase
; INPUTS:
;	byte	-	The byte to be converted
; OUTPUTS:
;	byte	-	The uppercase byte
;**************************************************************************
uppercase    	
		csae	  byte,#'a'            	;if byte is lowercase, then skip ahead
		RETP
		
		sub     byte,#'a'-'A'           ;change byte to uppercase
		RETP                            ;leave and fix page bits
;**************************************************************************

org	$300
;**************************************************************************
; String data (for RS-232 output) and tables
;**************************************************************************
_hello          dw      13,10,'V.23 Originate V.1.37',13,10,0
_instructions	dw	'- ? For Help',0
_DIALING	dw	'DIAL ',0
_PROMPT		dw	13,10,'>',0
_OK		dw	'OK',13,10,0
_CR		dw	13,10,0
_DATA_MODE	dw	13,10,'CONNECT 1275',13,10,0
_no_carrier	dw	13,10,'NO CARRIER',0
_HELP		dw	13,10,'ATDT- Dial',13,10,'ATH - Hang Up',13,10,'ATO - Data Mode',13,10,'ATZ - Init',13,10,'+++ - Command Mode',0

;**************************************************************************
org	$400	; FSK subroutines and the Interrupt Service Routine.
;**************************************************************************
answer_tone
;	This subroutine sends out an answer tone of 2100Hz for 3 seconds.
;**************************************************************************
		bank	sine_gen_bank		; send out the answer tone for 3 seconds
		clr	curr_sine
		mov	freq_count_high2,#f2100_h 
		mov	freq_count_low2,#f2100_l
		setb	sine_gen_en		; enable the FSK transmitter
		mov	w,#30
		call	@delay_100n_ms		
		retp
;**************************************************************************
; THESE ROUTINES ARE RUN WITHIN THE ISR... DO NOT CALL THEM FROM THE MAINLINE.
;**************************************************************************
;******************************************************************************
carrier_detect
		bank	carrier_detect_bank
		inc	cd_trans_count
		jnz	:no_rollover
		dec	cd_trans_count
		jmp	:sample
:no_rollover
		mov	w,rb
		xor	w,cd_rb_past_state
		and	w,#%00000010
		snz
		retp
		xor	cd_rb_past_state,w
		sb	cd_rb_past_state.1
		retp
:sample
		mov	w,cd_trans_count
		add	cd_trans_avg_l,w
		snc
		inc	cd_trans_avg_h
		clr	cd_trans_count
		inc	cd_avg_count
		sz
		retp
		setb	carrier_detected
		mov	w,#-8
		add	w,cd_trans_avg_h
		sc
		clrb	carrier_detected
		mov	w,#-16
		add	w,cd_trans_avg_h
		snc
		clrb	carrier_detected
		clr	cd_trans_avg_h
		clr	cd_trans_avg_l
		retp
;**************************************************************************
fsk_receive_main	; This code is speed critical and runs in every
			; ISR.  It increments the FSK transition couters
			; and checks for a transition.  If a transition 
			; has occured, it sets a flag, and saves the 
			; transition count for later processing by the
			; fsk_receive_processing1 subroutine.
;**************************************************************************
		bank	fsk_receive_bank
		sb	fsk_rx_en
		retp
		inc	fsk_trans_count
		snz
		dec	fsk_trans_count
		mov	w,fsk_rb_past_state
		xor	w,rb
		and	w,#%00000010
		snz
		retp
		xor	fsk_rb_past_state,w
		setb	fsk_processing_required_1
		mov	fsk_temp_trans,fsk_trans_count
		clr	fsk_trans_count
		retp
;**************************************************************************
fsk_receive_main_2	; This code removes some of the jitter away from
			; the low frequency detection algorithm by
			; continuously checking the transition count
			; to see if it has now reached a point where it
			; is safe to say that there is no high frequency
			; present.
;**************************************************************************
		bank	fsk_receive_bank
		sb	fsk_rx_en
		retp
		mov	w,#-(threshold+fsk_hysterises)
		add	w,fsk_trans_count
		snc
;		setb	fsk_rx_bit
;		setb	test_pin
		setb	tx_pin
		add	w,fsk_last_trans
		snc
;		setb	test_pin
;		setb	fsk_rx_bit
		setb	tx_pin
		retp
;**************************************************************************
fsk_receive_processing1	; This subroutine runs only when a transition has
			; occurred.  It adds the last transition count
			; to the current one and checks this against the
			; high/low frequency threshold.  If the transition
			; count is below the threshold, the fsk_rx_bit
			; flag is cleared.
;**************************************************************************
	bank	fsk_receive_bank
	sb	fsk_processing_required_1
	retp					; Exit if disabled
	clrb	fsk_processing_required_1	
	mov	w,#-25				; compare the transition count with 5
	add	w,fsk_temp_trans		
	jnc	:glitch				; If the transition count is less than 5, handle the glitch.
	mov	w,#-(threshold-fsk_hysterises)	; compare the transition count with
	add	w,fsk_temp_trans		; the threshold (-hysterises)
	snc
	mov	w,#$ff
	add	w,fsk_last_trans		
	sc
;	clrb	test_pin
;	clrb	fsk_rx_bit
	clrb	tx_pin				; Clear the TX_PIN if the transition count is less than the threshold
	mov	fsk_last_trans,fsk_temp_trans	; save the last transition count.
	retp
:glitch
	mov	w,fsk_last_trans		; of the last transition
	add	w,fsk_temp_trans
	snc
	mov	w,#$ff
	mov	fsk_last_trans,w	
	retp
;**************************************************************************
task_manager
; This portion of the ISR allows 1 of 16 separate tasks to run in each
; interrupt.
;**************************************************************************
		inc	task_switcher
		mov	w,task_switcher
		and	w,#$0f
		jmp	pc+w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		;*** TASKS ***
		jmp	fsk_receive_main_2	;0
		jmp	transmit		;1
		jmp	receive			;2
		jmp	fsk_transmit_uart 	;3
		jmp	fsk_receive_main_2	;4
		jmp	transmit_fsk		;5
		jmp	do_timers		;6
		jmp	fsk_receive_processing1	;7
		jmp	fsk_receive_main_2	;8
		jmp	carrier_detect		;9
		retp				;10
		retp				;11
		jmp	fsk_receive_main_2	;12
		retp				;13
		retp				;14
		retp				;15
		jmp	fsk_receive_main_2	;16
		retp				; (just in case)	
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
fsk_transmit_uart
; This is an asynchronous RS-232 transmitter
; INPUTS:
;	tx_divide.baud_bit  -	Transmitter only executes when this bit is = 1
;	tx_high		    -	Part of the data to be transmitted
;	tx_low		    -	Some more of the data to be transmitted
;	tx_count	    -	Counter which counts the number of bits transmitted.
; OUTPUTS:
;	tx_pin		    -	Sets/Clears this pin to accomplish the transmission.
;**************************************************************************
		bank	fsk_serial_bank
		sb	fsk_answering
		inc	fsk_tx_divide_2
		and	fsk_tx_divide_2,#$0f	    ; Divide the 1200bps UART by 16 to
						    ; achieve 75bps
		sz	
		retp
		clrb    fsk_tx_divide.baud_bit      ;clear xmit timing count flag
		inc     fsk_tx_divide               ;only execute the transmit routine
		STZ                             ;set zero flag for test
		SNB     fsk_tx_divide.baud_bit      ; every 2^baud_bit interrupt
		test    fsk_tx_count                ;are we sending?
		snz
		retp			        ;if not, go to :receive
		clc                             ;yes, ready stop bit
		rr      fsk_tx_high                 ; and shift to next bit
		rr      fsk_tx_low                  ;
		dec     fsk_tx_count                ;decrement bit counter
		movb    fsk_tx_bit,/fsk_tx_low.6        ;output next bit
		retp
;**************************************************************************
transmit
; This is an asynchronous RS-232 transmitter
; INPUTS:
;	tx_divide.baud_bit  -	Transmitter only executes when this bit is = 1
;	tx_high		    -	Part of the data to be transmitted
;	tx_low		    -	Some more of the data to be transmitted
;	tx_count	    -	Counter which counts the number of bits transmitted.
; OUTPUTS:
;	tx_pin		    -	Sets/Clears this pin to accomplish the transmission.
;**************************************************************************
		bank	serial
		clrb    tx_divide.baud_bit      ;clear xmit timing count flag
		inc     tx_divide               ;only execute the transmit routine
		STZ                             ;set zero flag for test
		SNB     tx_divide.baud_bit      ; every 2^baud_bit interrupt
		test    tx_count                ;are we sending?
		snz
		retp			        ;if not, go to :receive
		clc                             ;yes, ready stop bit
		rr      tx_high                 ; and shift to next bit
		rr      tx_low                  ;
		dec     tx_count                ;decrement bit counter
		movb    tx_pin,/tx_low.6        ;output next bit
		retp
;**************************************************************************
receive
; This is an asynchronous receiver for RS-232 reception
; INPUTS:
;	rx_pin		   -	Pin which RS-232 is received on.
; OUTPUTS:
;	rx_byte		   -	The byte received
;	rx_flag		   -	Set when a byte is received.
;**************************************************************************
		bank	serial
		movb    c,rx_pin                ;get current rx bit
		test    rx_count                ;currently receiving byte?
		jnz     :rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc                              ;skip ahead if not start bit
		mov     rx_count,w              ;it is, so renew bit count
		mov     rx_divide,#start_delay  ;ready 1.5 bit periods
:rxbit		djnz    rx_divide,:rxdone       ;middle of next bit?
		setb    rx_divide.baud_bit      ;yes, ready 1 bit period
		dec     rx_count                ;last bit?
		sz                              ;if not
		rr      rx_byte                 ;  then save bit
		snz                             ;if so
		setb    rx_flag                 ;  then set flag
:rxdone
		retp
;**************************************************************************
do_timers
; The 24-bit timer increments every 52.16us when called by task_manager.
;**************************************************************************
		bank	timers			; Update the timers
		inc	timer_l
		snz
		inc	timer_h
		snz
		setb	timer_flag
		snz
		inc	timer_hh
		snz
		dec	timer_hh
		setb	led_pin
		sb	timer_h.2
		clrb	led_pin
		retp
;**************************************************************************
transmit_fsk
;**************************************************************************
		bank	fsk_transmit_bank
		sb	fsk_tx_en
		retp
		jb	fsk_answering,transmit_answer_tones
transmit_originate_tones
		jnb	fsk_tx_bit,:low_bit
:high_bit
		bank	sine_gen_bank
		mov	freq_count_high2,#f390_h
		mov	freq_count_low2,#f390_l
		retp
:low_bit
		bank	sine_gen_bank
		mov	freq_count_high2,#f450_h
		mov	freq_count_low2,#f450_l
		retp
transmit_answer_tones
		jnb	fsk_tx_bit,:low_bit
:high_bit
		bank	sine_gen_bank
		mov	freq_count_high2,#f1300_h
		mov	freq_count_low2,#f1300_l
		retp
:low_bit	
		bank	sine_gen_bank
		mov	freq_count_high2,#f2100_h
		mov	freq_count_low2,#f2100_l
		retp
;******************************************************************************
; Interrupt
; 
; With a retiw value of -163 and an oscillator frequency of 50MHz, this
; code runs every 3.26us.
;******************************************************************************
ISR
;******************************************************************************
FSK_output
		jnb	dtmf_gen_en,:dtmf_disabled
		call	@sine_generator1
		call	@DTMF_twist
		jmp	:task_switcher
:dtmf_disabled
		jnb	sine_gen_en,:task_switcher  ; Output the frequencies set by the freq_count registers
		call	@sine_generator2
		call	@SINE_out		; Output each discrete value of the sine table
		call	fsk_receive_main
:task_switcher
		call	task_manager
;******************************************************************************
ISR_DONE
; This is the end of the interrupt service routine.  Now load -163 into w and
; perform a retiw to interrupt 163 cycles from the start of this one.  
; (3.26us@50MHz)
;******************************************************************************
		mov	w,#-163		;1	; interrupt 163 cycles after this interrupt
		retiw			;3	; return from the interrupt
;******************************************************************************
; End of the Interrupt Service Routine
;******************************************************************************

;**************************************************************************
org	$600			; These subroutines are on page 3.
;**************************************************************************
; DTMF transmit functions/subroutines
;**************************************************************************
;**************************************************************************
DTMF_TABLE	; DTMF tone constants
; This routine returns with the constant used for each of the frequency
; detectors.
; INPUT:	w	-	Index into the table (0-15 value)
; OUTPUT:	w	-	Constant at that index
;**************************************************************************
		jmp	PC+w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		retw	f697_l
		retw	f697_h			
		retw	f770_l
		retw	f770_h
		retw	f852_l
		retw	f852_h	
		retw	f941_l
		retw	f941_h
		retw	f1209_l
		retw	f1209_h
		retw	f1336_l
		retw	f1336_h
		retw	f1477_l
		retw	f1477_h
		retw	f1633_l
		retw	f1633_h
tableEnd	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
ASCII_TABLE	; Ascii value at index (0-15)
; INPUT:	w	-	Index into the table (0-15 value)
; OUTPUT:	w	-	Constant at that index
;**************************************************************************
		jmp	PC+w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
		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	; Will cause a compiler error if not located in the lower half of a page.
;**************************************************************************
index_2_digit
; This subroutine converts a digit from 0-9 or a '*' or a '#' to a table 
; lookup index which can be used by the load_frequencies subroutine.  To use
; this routine, pass it a value in the 'byte' register.  No invalid digits
; are used. (A, B, C, or D)
;**************************************************************************
		call	ASCII_TABLE
		retp
;**************************************************************************
digit_2_index
; This subroutine converts a digit from 0-9 or a '*' or a '#' to a table 
; lookup index which can be used by the load_frequencies subroutine.  To use
; this routine, pass it a value in the 'byte' register.  No invalid digits
; are used. (A, B, C, or D)
;**************************************************************************
		bank	serial
		clr	temp
:loop
		mov	w,temp
		call	ASCII_TABLE
		xor	w,byte
		jz	:done
		inc	temp
		jb	temp.4,:done
		jmp	:loop

:done		mov	w,temp
		retp
		
;**************************************************************************
load_frequencies
; This subroutine loads the frequencies using a table lookup approach.
; The index into the table is passed in the byte register.  The DTMF table
; must be in the range of $400 to $500.
;**************************************************************************
		mov	temp,w
		bank	sine_gen_bank

		mov	w,>>temp
		and	w,#%00000110
		call	DTMF_TABLE
		mov	freq_count_low,w

		mov	w,>>temp
		and	w,#%00000110
		inc	wreg
		call	DTMF_TABLE
		mov	freq_count_high,w

		rl	temp
		setb	temp.3
		mov	w,temp
		and	w,#%00001110
		mov	temp,w
		call	DTMF_TABLE
		mov	freq_count_low2,w

		mov	w,temp
		inc	wreg
		call	DTMF_TABLE
		mov	freq_count_high2,w
		retp		
		
;**************************************************************************
dial_it		; This subroutine puts out whatever frequencies were loaded
		; for 100ms, and then stops outputting the frequencies.
;**************************************************************************
		bank	sine_gen_bank
		clr	sine_index
		clr	sine_index2
		mov	w,#1
		call	@delay_100n_ms		; delay 100ms
		setb	dtmf_gen_en
		mov	w,#1
		call	@delay_100n_ms		; delay 100ms
		clrb	dtmf_gen_en
		call	@disable_o		; now disable the outputs
:end_dial_it	retp
;**************************************************************************
sine_generator1 					;(Part of interrupt service routine)
; This routine generates a sine wave with values from the sine table
; at the end of this program.  Frequency is specified by the counter.  To set
; the frequency, put this value into the 16-bit freq_count register:
; freq_count = FREQUENCY * 6.83671552 (@50MHz)
;**************************************************************************
		bank	sine_gen_bank
		clrb	swCarryFlag
		add	freq_acc_low,freq_count_low
		snc
		setb	swCarryFlag
		add	freq_acc_high,freq_count_high
		snb	swCarryFlag
		inc	freq_acc_high
		snz
		jmp	:change			; if zero, this definetely caused a rollover.

		sc
		jmp	:no_change
:change		inc	sine_index
		mov	w,sine_index
		and	w,#$1f
		call	sine_table
		mov	curr_sine,w		;1	

:no_change

;**************************************************************************
sine_generator2						;(Part of interrupt service routine)
; This routine generates a sine wave with values from the sine table
; at the end of this program.  Frequency is specified by the counter.  To set
; the frequency, put this value into the 16-bit freq_count register:
; freq_count = FREQUENCY * 6.83671552 (@50MHz)
;**************************************************************************
		bank	sine_gen_bank
		clrb	swCarryFlag
		add	freq_acc_low2,freq_count_low2
		snc
		setb	swCarryFlag
		add	freq_acc_high2,freq_count_high2
		snb	swCarryFlag
		inc	freq_acc_high2
		snz
		jmp	:change			; if zero, this definetely caused a rollover.

		sc
		jmp	:no_change
:change		inc	sine_index2
		mov	w,sine_index2
		and	w,#$1f
		call	sine_table
		mov	curr_sine2,w
:no_change
		retp
;**************************************************************************
SINE_out
; This subroutine moves the FSK output to the PDM register
;**************************************************************************
		bank	sine_gen_bank
		
		mov	w,#127
		add	w,curr_sine2
		mov	PDM0_out,w
		retp
;**************************************************************************
DTMF_twist
; This subroutine adds twist to the high frequency of the DTMF output.
;**************************************************************************
		bank	sine_gen_bank
		mov	PDM0_out,curr_sine2			; mov sin2 into PDM0
		rr	wreg
		rr	wreg
		and	w,#$3f
		snb	wreg.5
		or	w,#$C0
		add	PDM0_out,w				; (1.25)(sin2) = sin2 + (0.25)(sin2)
		
		add	PDM0_out,curr_sine			; add the value of SIN into the PDM output
		
		add	PDM0_out,#128			; for result = PDM0 = 1.25*sin2 + 1*sin
		retp					; return with page bits intact
;******************************************************************************
sine_table
; The values in this table can be changed to increase/decrease the amplitude of
; the output sine wave.
; This sine table gives an output level of approximately -15dB into a 600 ohm
; impedance
;******************************************************************************
	
	jmp	pc+w
tableStart	; Will cause a compiler error if not located in the lower half of a page.
	retw	0
	retw	4
	retw	8
	retw	11
	retw	14
	retw	16
	retw	18
	retw	19
	retw	20
	retw	19
	retw	18
	retw	16
	retw	14
	retw	11
	retw	8
	retw	4
	retw	0
	retw	-4
	retw	-8
	retw	-11
	retw	-14
	retw	-16
	retw	-18
	retw	-19
	retw	-20
	retw	-19
	retw	-18
	retw	-16
	retw	-14
	retw	-11
	retw	-8
	retw	-4
tableEnd	; Will cause a compiler error if not located in the lower half of a page.			; Will cause a compiler error if not locat
;******************************************************************************
;        Copyright © 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.
;        
;        Scenix Semiconductor products are not authorized for use in life support
;        systems or under conditions where failure of the product would endanger
;        the life or safety of the user, except when prior written approval is
;        obtained from Scenix Semiconductor.
;******************************************************************************


file: /Techref/scenix/lib/io/dev/modem/v_23_originate_1_37.src, 68KB, , updated: 2001/10/27 13:18, local time: 2024/4/18 15:31,
TOP NEW HELP FIND: 
3.145.60.29:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://www.massmind.org/Techref/scenix/lib/io/dev/modem/v_23_originate_1_37.src"> scenix lib io dev modem v_23_originate_1_37</A>

Did you find what you needed?

 

Welcome to massmind.org!

 

Welcome to www.massmind.org!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .