;*****************************************************************************************
; Copyright © [01/26/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:	UART.SRC
;
; Authors:	Bruce Wilson
;		Application Engineer
;		Scenix Semiconductor		
;
; Revision:	1.00
;
; Part:		SX28AC datecode 9929AA
; Freq:		50Mhz
; Compiled using Parallax SX-Key software v1.07 and SASM 1.40
;
; Last Revised: 10/18/99
;
; Introduction:
;   The SX can easily implement a serial UART capable of running at baud rates from 2400 to 115200.
; This UART VP will handle all the low level UART functionality, all that is required from the user
; is to make calls to simple access subroutines to send and receive data.
;
; Program Description:
;   This program is currently running a very simple UART demo which can be used to test that the 
; UART is working. If you are using the Scenix I2C/UART demo board simply program U2 with this source 
; and conenct a serial cable to your PC.  Otherwise, connect a MAX232 tranciever chip between the SX 
; and your PC. Then load up hyperterminal and set up to run at 19200, no parity, 1 stop bit, 
; 8 data bits and no flow control.
; Your setup should look as follows:
;
;        SX	      |	   	 | 	MAX232 	   |          9-pin plug  
;   		RA.3 -|----------|- T1IN    T1OUT -|-----TD-----|-2-|----------------  SERIAL
;      PORT A	RA.2 -|----------|- T2IN    T2OUT -|-----CTS----|-8 |----------------	PORT
;		RA.1 -|----------|- R1OUT   R1IN  -|-----RD-----|-3 |----------------    ON
;		RA.0 -|----------|- R2OUT   R2IN  -|-----RTS----|-7 |----------------    PC
;		      |		 |         	   |
;
;	
;
; Revision History:
;   	1.0	Implemented the UART VP and tested at 2400 baud to 57600 baud. This was running
;		with an interrupt period set at 217. Also added a simple echo character demo.
;
;*****************************************************************************************

;*****************************************************************************************
; 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,oscxt4,turbo,stackx_optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oscxt4,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,stackx_optionx
    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,stackx,optionx   
    ENDIF
  ENDIF
ENDIF
		id	'  '			;
		reset	reset_entry		; set reset vector

;*****************************************************************************************
; 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
  noexpand
	bank	\1
	IFDEF SX48_52
	  IFDEF SX48_52_ES
	    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
  expand
		setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
  noexpand
	    ENDIF
	  ELSE
	    IF \1 & %10000000		;SX48BD and SX52BD (production release) bank instruction 
  expand
		setb	fsr.7		;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
  noexpand
	    ELSE
  expand
		clrb	fsr.7
  noexpand
	    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
  noexpand
	IFDEF SX48_52
  expand
		mov	w,#\1&$1F	;loads the M register correctly for the SX48BD and SX52BD
		mov	m,w
  noexpand
	ELSE
  expand
		mov	m,#\1&$0F	;loads the M register correctly for the SX18AC, SX20AC
					;and SX28AC
  noexpand
	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

function_temp	equ	global_org+0	; Temporary variable used in:
					; -send_hex function
global_temp	equ	global_org+1    ; Temporary variable used in main line code
flags		equ	global_org+2	; Flags to indicate various things..
	rx_flag		equ	flags.1	; Indicates the reception of a bit from the UART

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

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

bank0		=	$


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

bank1		=	$
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
string		ds	1	;used by send_string to store the address in memory
byte		ds	1	;used by serial routines
hex		ds	1


	;*********************************************************************************
	; 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
;*****************************************************************************************
; Port Assignment
;*****************************************************************************************
RA_latch	equ	%00001000		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11110111		;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

RB_latch	equ	%00000000		;SX18/20/28/48/52 port B latch init
RB_DDIR		equ	%11111111		;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

RC_latch	equ	%00000000		;SX18/20/28/48/52 port C latch init
RC_DDIR		equ	%11111010		;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

	;*********************************************************************************
	; Pin Definitions
	;*********************************************************************************
rts_pin		equ	ra.0	;UART 1 RTS input
cts_pin		equ	ra.1	;UART 1 CTS output
rx_pin          equ     ra.2	;UART receive input
tx_pin          equ     ra.3	;UART transmit output

;*****************************************************************************************
; Program constants
;*****************************************************************************************

int_period	equ	217	; Interrupt to operate at 20ns x 217 = 4.34us
_enter		equ	13	; ASCII value for carridge return
_line_feed	equ	10	; ASCII value for a line feed

	;*********************************************************************************
	; UART Constants
	; The following five values determine the UART baud rate.
	; The value of baud_rate and int_period affect the baud rate as follows:
	;  Baud rate = 50MHz/(baud_rate * int_period * RTCC_prescaler)
	;
	; Note: *int_period must <256 and longer than the length of the slowest possible
	; interrupt sequence in instruction cycles. Changing the value of int_period will
	; affect the rest of the virtual peripherals due to timing issues. The start delay
	; value must be set equal to (baud_rate)*1.5 + 1
	;*********************************************************************************

; *** 2400 baud (for slower baud rates, increase the RTCC prescaler)
baud_rate_24	=	96			;for 2400 baud rate generation
start_delay_24	=	145

; *** 9600 baud
baud_rate_96 	=	24			;for 9600 baud rate generation
start_delay_96	= 	37

; *** 19200 baud
baud_rate_192	=	12			;for 19200 baud rate generation
start_delay_192	= 	19

; *** 38400 baud
baud_rate_384	=	6			;for 38400 baud rate generation
start_delay_384	= 	10

; *** 57600 baud
baud_rate_576	=	4			;for 57600 baud rate generation
start_delay_576	=	6

IFDEF SX48_52
	;*********************************************************************************
	; SX48BD/52BD Mode addresses
	; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
	; exception of CMP and WKPND which do an exchange with W.
	;*********************************************************************************
; Timer (read) addresses
TCPL_R		equ	$00		;Read Timer Capture register low byte
TCPH_R		equ	$01		;Read Timer Capture register high byte
TR2CML_R	equ	$02		;Read Timer R2 low byte
TR2CMH_R	equ	$03		;Read Timer R2 high byte
TR1CML_R	equ	$04		;Read Timer R1 low byte
TR1CMH_R	equ	$05 		;Read Timer R1 high byte
TCNTB_R		equ	$06		;Read Timer control register B
TCNTA_R		equ	$07		;Read Timer control register A

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

; Port setup (read) addresses
WKED_R		equ	$0A		;Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R		equ	$0B		;Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R		equ	$0C		;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R		equ	$0D		;Read Port 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
CLR_TMR		equ	$10		;Resets 16-bit Timer
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

;*****************************************************************************************
; 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 -217 and an oscillator frequency of 50MHz, this
; code runs every 4.34us.
;*****************************************************************************************
		org     $0
interrupt					;3

	;*********************************************************************************
	; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART) 
	; These routines send and receive RS232 serial data, and are currently
	; configured (though modifications can be made) for the popular
	; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format.
	; RECEIVING: The rx_flag is set high whenever a valid byte of data has been
	; received and it is the calling routine's responsibility to reset this flag
	; once the incoming data has been collected.
	; TRANSMITTING: The transmit routine requires the data to be inverted
	; and loaded (tx_high+tx_low) register pair (with the inverted 8 data bits
	; stored in tx_high and tx_low bit 7 set high to act as a start bit). Then
	; the number of bits ready for transmission (10=1 start + 8 data + 1 stop)
	; must be loaded into the tx_count register. As soon as this latter is done,
	; the transmit routine immediately begins sending the data.
	; This routine has a varying execution rate and therefore should always be
	; placed after any timing-critical virtual peripherals such as timers,
	; adcs, pwms, etc.
	; Note: The transmit and receive routines are independent and either may be
	;	removed, if not needed, to reduce execution time and memory usage,
	;	as long as the initial "BANK serial" (common) instruction is kept.
	;
	;       Input variable(s) : tx_low (only high bit used), tx_high, tx_count
	;       Output variable(s) : rx_flag, rx_byte
	;       Variable(s) affected : tx_divide, rx_divide, rx_count
	;       Flag(s) affected : rx_flag
	;
	;*********************************************************************************
		_bank	SERIAL			;switch to serial register bank
:transmit	decsz	tx_divide		;only execute the transmit routine
		jmp	:receive		; 
		mov	w,#baud_rate_192	;load UART baud rate (50MHz)
		mov	tx_divide,w		; 
		test    tx_count		;are we sending?
		snz				; 
		jmp	:receive		;
		clc                             ;yes, ready stop bit
		rr      tx_high			; and shift to next bit
		rr      tx_low			; 
		dec     tx_count		;decrement bit counter
		snb	tx_low.6		;output next bit
		clrb	tx_pin			; 
		sb	tx_low.6		; 
		setb	tx_pin			; 
:receive        sb	rx_pin			;get current rx bit
	        clc				; 
	        snb	rx_pin			; 
	        stc				; 
		test    rx_count                ;currently receiving byte?
		sz				; 
		jmp	: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     w,#start_delay_192	;ready 1.5 bit periods (50MHz)
		mov     rx_divide,w		; 
:rxbit          decsz	rx_divide		;middle of next bit?
          	jmp	:rxdone			;
		mov	w,#baud_rate_192	;yes, ready 1 bit period (50MHz)
		mov	rx_divide,w		; 

		dec     rx_count                ;last bit?
		sz                              ;if not
		rr      rx_byte                 ; then save bit
		snz                             ;if so,
		setb    rx_flag                 ; then set flag
:rxdone						; else, exit

	; PUT YOUR OWN VPs HERE

	;*********************************************************************************
	; Virtual Peripheral: 
	;
	;
	;	Input variable(s): 
	;	Output variable(s): 
	;	Variable(s) affected: 
	;	Flag(s) affected: 
	;*********************************************************************************
label1

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

;*****************************************************************************************
; 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
	;*********************************************************************************
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 RTCC (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

		mov	w,#RTCC_ON | RTCC_PS_OFF	;setup option register
		mov	!option,w
		jmp	@main

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

	;*********************************************************************************
	; Main
	;*********************************************************************************
main		mov	w,#_hello		; Send greating to user on UART
		call	@send_string
		mov	w,#_instructions	; Send instructinos
		call	@send_string
keep_doing_it
		call	@get_byte		; Get a character from the users terminal
		mov	global_temp,w
		mov	w,#_enter		; Check if user pressed enter
		mov	w,global_temp-w
		jnz	echo_char		; if so then send a LF and CR
		mov	w,#_line_feed		
		call	@send_byte
echo_char
		mov	w,global_temp
		call	@send_byte
		jmp	keep_doing_it

org	$300
;*****************************************************************************************
; String data (for RS-232 output)
;*****************************************************************************************
_hello          dw      13,10,10,'SX UART Demo',0
_instructions	dw	13,10,'Enter some characters and they will be echod to your terminal',13,10,'>',0
_enter_hex	dw	13,10,' Enter Value : ',0

org	$400
;*****************************************************************************************
; UART Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: get_byte
	; Get byte via serial port and echo it back to the serial port
	; INPUTS:
	;	-NONE
	; OUTPUTS:
	;	-received byte in rx_byte
	;*********************************************************************************
get_byte	sb	rx_flag			;wait till byte is received
		jmp	get_byte
		clrb    rx_flag			;reset the receive flag
		_bank	SERIAL
		mov     w,rx_byte		;store byte (copy using W)
		mov     byte,w			; & fall through to echo char back
		retp

	;*********************************************************************************
	; Function: send_byte
	; Send byte via serial port
	; INPUTS:
	;	w 	-	The byte to be sent via RS-232
	; OUTPUTS:
	; 	outputs the byte via RS-232
	;*********************************************************************************
send_byte    	_bank    SERIAL

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

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

	;*********************************************************************************
	; Function: send_string
	; 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 (set to $300)
		iread                           ;using the mode register
		test    w                       ;are we at the last char?
		snz                             ;if not=0, skip ahead
		jmp	:exit			;yes, leave & fix page bits
		mov     m,#$0f                  ;reset the mode register to RX directions
		call    send_byte               ;not 0, so send character
		inc     string                  ;point to next character
		jmp     :loop                   ;loop until done
:exit		mov     m,#$0f                   ;reset the mode register
		retp
	;*********************************************************************************
	; Function: uppercase
	; Convert byte to uppercase.
	; INPUTS:
	;	byte - the byte to be converted
	; OUTPUTS:
	; 	byte - converted byte
	;*********************************************************************************
uppercase    	mov	w,#'a'            	;if byte is lowercase, then skip ahead
	    	mov	w,byte-w
	    	sc
		retp
		mov	w,#'a'-'A'		;change byte to uppercase
		sub	byte,w
		retp				;leave and fix page bits

	;*********************************************************************************
	; Function: send_hex
	; Output a hex number
	; INPUTS:
	;	w	-	The byte to be output
	; OUTPUTS:
	; 	outputs the hex byte via RS-232
	;*********************************************************************************
send_hex	mov	function_temp,w
		swap	wreg
		and	w,#$0f
		call	hex_table
		call	send_byte
		mov	w,function_temp
		and	w,#$0f
		call	hex_table
		call	send_byte
		retp

	;*********************************************************************************
	; Function: get_hex
	;	This routine returns with an 8-bit value in the W and in the hex 
	;	register.  It accepts a hex number from the terminal screen and
	;	returns. Remember to write a prompt to the screen before calling get_hex
	; OUTPUTS:
	;	-received byte in rx_byte
	;*********************************************************************************
get_hex		_bank	SERIAL
		mov	w,#_enter_hex
		call	@send_string
		call	:get_valid_hex
		mov	w,byte			; send the received (good) byte
		call	send_byte
		swap	function_temp			; put the nibble in the upper nibble of
		mov	w,function_temp
		mov	hex,w			; hex register

		call	:get_valid_hex
		mov	w,byte			; send the second received byte
		call	send_byte
		mov	w,function_temp			
		and	w,#$0f
		or	w,hex
		mov	hex,w
		retp

:get_valid_hex
:gh1		clr	function_temp
		jnb	rx_flag,$		; get a byte from the terminal
		clrb	rx_flag
		mov	byte,rx_byte
		call	uppercase		; uppercase it.

:loop		mov	w,function_temp		; get the value at temp (index)
		call	hex_table
		xor	w,byte
		snz				; compare it to the received byte
		ret
		inc	function_temp		; if they are equal, we have the
		jb	function_temp.4,:gh1	; upper nybble.  Continue if not.
		jmp	:loop
		ret

hex_table	add	pc,w
		retw	'0'
		retw	'1'
		retw	'2'
		retw	'3'
		retw	'4'
		retw	'5'
		retw	'6'
		retw	'7'
		retw	'8'
		retw	'9'
		retw	'A'
		retw	'B'
		retw	'C'
		retw	'D'
		retw	'E'
		retw	'F'


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