;*****************************************************************************************
; 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:	DUAL_UART.SRC
;
; Authors:	Bruce Wilson
;		Application Engineer
;		Scenix Semiconductor		
;
; Revision:	1.1
;
; Part:		SX28AC datecode 9929AA
; Freq:		50Mhz
; Compiled using Parallax SX-Key software v1.07 and SASM 1.40
;
; Last Revised: 10/19/99
;
; Introduction:
;   The SX can easily implement dual UARTs capable of running at baud rates from 2400 to 115200.
; This DUAL 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 and check flags set in the ISR.
;
; Program Description:
;   This program is currently running a very simple dual UART demo which can be used to test that the 
; UARTS are working. If you are using the Scenix I2C/UART demo board simply program U1 with this source 
; and conenct serial cables from you PC to either UART 1 or UART 2 or both (if you have two cables and two 
; serial ports).
;  Otherwise if you do not have the Scenix I2C/UART demo board, connect a MAX232 tranciever chip between 
; the SX and a serial cable to 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:
;		______		  _________________	
;        	      |	   	 |    MAX232 1 	   |          9-pin plug  
;   		RB.7 -|----------|- T1IN    T1OUT -|-----TD-----|-2-|---------------x  COM
;      		RB.6 -|----------|- T2IN    T2OUT -|-----CTS----|-8 |---------------x  PORT
;		RB.5 -|----------|- R1OUT   R1IN  -|-----RD-----|-3 |---------------x  ON PC
;		RB.4 -|----------|- R2OUT   R2IN  -|-----RTS----|-7 |---------------x    
;    SX		      |		 |_________________|          	    
;    PORT B    	      |	   	 |    MAX232 2 	   |          9-pin plug  
;   		RB.3 -|----------|- T1IN    T1OUT -|-----TD-----|-2-|---------------x  
;      		RB.2 -|----------|- T2IN    T2OUT -|-----CTS----|-8 |---------------x  COM
;		RB.1 -|----------|- R1OUT   R1IN  -|-----RD-----|-3 |---------------x  PORT 
; 		RB.0 -|----------|- R2OUT   R2IN  -|-----RTS----|-7 |---------------x  ON PC  
;	        ______|		 |_________________|
;	
;   This demo will echo characters received from your PC via the same UART which received them.
; This was done to avoid the use of two serial cables. If you wished, it could easily be done so
; that characters received on one UART were sent out to the other UART.
;
; 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.
;	1.1	Modified Single UART VP to run dual UARTS on Scenix I2C/UART board
;*****************************************************************************************

;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD/ES,
; SX48BD, SX52BD/ES or SX52BD. For SX48BD/ES and SX52BD/ES, uncomment both defines,
; SX48_52 and SX48_52_ES.
;*****************************************************************************************
;SX18_20
SX28
;SX48_52
;SX48_52_ES

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

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

IFDEF SX_Key 				;SX-Key Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SX-Key
		device	SX18L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX48_52_ES			;SX48BD/ES or SX52BD/ES device directives for SX-Key
		device	oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52				;SX48/52/BD device directives for SX-Key
		device	oschs2
    ENDIF
  ENDIF
		freq	50_000_000
ELSE  					;SASM Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SASM
		device	SX18,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SASM
		device	SX28,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX48_52_ES			;SX48BD/ES or SX52BD/ES device directives for SASM
		device	SX52,oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52			;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2  
    ENDIF
  ENDIF
ENDIF
		id	'DUART'			;
		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
	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

;*****************************************************************************************
; Global Register definitions
;*****************************************************************************************
IFDEF SX48_52
		org     $0A			;global data memory starts at $0A on SX48/52
ELSE
		org     $08			;global data memory starts at $08 on SX18/20/28
ENDIF

temp		equ	$0E		; Temporary variable
flags		equ	$0F		; Flags to indicate various things..
	active_uart_isr_flag 	equ	flags.0 ; used by isr to switch between uart1 and uart2.

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

	;*********************************************************************************
	; Bank 0	This bank is used to store the UART variables
	;*********************************************************************************
IFDEF SX48_52
		org     $00	;bank 0 address on SX52
ELSE
		org	$10	;bank 0 address on SX28
ENDIF

SERIAL1		=       $	;UART bank 1
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
rx_pin_buf	ds	1	;used as a buffer of the actual rx_pin
ser_flags	ds	1
	rx_flag	equ	ser_flags.1	; Indicates the reception of a bit from the UART

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
IFDEF SX48_52
		org     $10	;bank 1 address on SX52
ELSE
		org	$30	;bank 1 address on SX28
ENDIF

SERIAL2		=       $	;UART bank 2
; This bank will be used as a mirror of SERIAL1. This bank will contrain all the same registers
; as SERIAL1 as when a bank switch occurs the reqisters are simply an offset.

	;*********************************************************************************
	; Bank 2
	;*********************************************************************************
IFDEF SX48_52
		org     $20	;bank 2 address on SX52
ELSE
		org	$50	;bank 2 address on SX28
ENDIF

bank2		=	$


	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
IFDEF SX48_52
		org     $30	;bank 3 address on SX52
ELSE
		org	$70	;bank 3 address on SX28
ENDIF

bank3		=	$


	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
IFDEF SX48_52
		org     $40	;bank 4 address on SX52
ELSE
		org	$90	;bank 4 address on SX28
ENDIF

bank4		=	$


	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
IFDEF SX48_52
		org     $50	;bank 5 address on SX52
ELSE
		org	$B0	;bank 5 address on SX28
ENDIF

bank5		=	$


	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
IFDEF SX48_52
		org     $60	;bank 6 address on SX52
ELSE
		org	$D0	;bank 6 address on SX28
ENDIF

bank6		=	$


	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
IFDEF SX48_52
		org     $70	;bank 7 address on SX52
ELSE
		org	$F0	;bank 7 address on SX28
ENDIF

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_init		equ	%11111111		;SX18/20/28/48/52 port A latch init
RA_io		equ	%11111111		;SX18/20/28/48/52 port A DDIR value
RB_init		equ	%10001000		;SX18/20/28/48/52 port B latch init
RB_io		equ	%01110111		;SX18/20/28/48/52 port B DDIR value
RC_init		equ	%11111000		;SX18/20/28/48/52 port C latch init
RC_io		equ	%11111000		;SX18/20/28/48/52 port C DDIR value

IFDEF SX48_52
RD_init		equ	%11111111		;SX48/52 port D latch init
RD_io		equ	%11111111		;SX48/52 port D DDIR value
RE_init		equ	%11111111		;SX48/52 port E latch init
RE_io		equ	%11111111		;SX48/52 port E DDIR value
ENDIF

	;*********************************************************************************
	; Pin Definitions
	;*********************************************************************************
;UART1
rts_pin1	equ	rb.4	;UART 1 RTS input
cts_pin1	equ	rb.5	;UART 1 CTS output
rx_pin1         equ     rb.6	;UART 1 receive input
tx_pin1         equ     rb.7	;UART 1 transmit output

;UART2
rts_pin2	equ	rb.0	;UART 2 RTS input 
cts_pin2	equ	rb.1	;UART 2 CTS output
rx_pin2         equ     rb.2	;UART 2 receive input
tx_pin2         equ     rb.3	;UART 2 transmit output


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

int_period	equ	108	; Interrupt to operate at 20ns x 108 = 2.16us
_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


;*****************************************************************************************
; 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
	;	Size : Transmit - 15 bytes + 1 byte shared with receive code
	;		Receive - 20 bytes + 1 byte shared with transmit code
	;       Timing For Single UART pass (turbo) : 
	;	       Transmit - (a) [not sending] 9 cycles
	;			  (b) [sending] 19 cycles
	;			  + 1 cycle shared with RX code ("bank" instr.)
	;		Receive - (a) [not receiving] 9 cycles
	;			  (b) [start receiving] 16 cycles
	;			  (c) [receiving, awaiting bit] 13 cycles
	;			  (d) [receiving, bit ready] 17 cycles
	;*********************************************************************************
; SERIAL bank 1 and SERIAL bank2 are used to switch between active UART
; This VP will alternate every ISR between acting on UART 1 and UART 2
; bacause each is run only every alternate ISR the timing of the UART remains
; the same as running every ISR with an interrupt period of 217.

		jb	active_uart_isr_flag,do_uart2
		setb	active_uart_isr_flag
do_uart1	_bank	SERIAL1			;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_pin1			; 
		sb	tx_low.6		; 
		setb	tx_pin1			; 
:receive        sb	rx_pin1			;get current rx bit
	        clc				; 
	        snb	rx_pin1			; 
	        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
		jmp	UARTs_done	

do_uart2	clrb	active_uart_isr_flag
		_bank	SERIAL2			;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_pin2			; 
		sb	tx_low.6		; 
		setb	tx_pin2			; 
:receive        sb	rx_pin2			;get current rx bit
	        clc				; 
	        snb	rx_pin2			; 
	        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
UARTs_done

	;*********************************************************************************
	; Virtual Peripheral: 
	;
	;
	;	Input variable(s): 
	;	Output variable(s): 
	;	Variable(s) affected: 
	;	Flag(s) affected: 
	;*********************************************************************************
label1
	; PUT YOUR OWN VPs HERE

	;*********************************************************************************
	; 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	$1f			; point MODE to write DDIR
						;*(5-bit value used to ensure correct value
						; for SX48/52, only 4-bits of this value are
						; used on SX18/20/28)
		mov     w,#RA_init            	; Initialize data latches for
		mov     ra,w			; port A.
		mov	w,#RA_io		; Initialize data direction register for		
		mov	!ra,w			; port A.
		mov     w,#RB_init          	; Initialize data latches for
		mov     rb,w			; port B.
		mov	w,#RB_io		; Initialize data direction register for
		mov	!rb,w			; port B.
		mov     w,#RC_init          	; Initialize data latches for
		mov     rc,w			; port C.
		mov	w,#RC_io		; Initialize data direction register for
		mov	!rc,w			; port C.

IFDEF SX48_52
		mov     w,#RD_init          	; Initialize data latches for
		mov     rd,w			; port D.
		mov	w,#RD_io		; Initialize data direction register for
		mov	!rd,w			; port D.
		mov     w,#RE_init          	; Initialize data latches for
		mov     re,w			; port E.
		mov	w,#RE_io		; Initialize data direction register for
		mov	!re,w			; port E.
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		_bank	SERIAL1
		mov	w,#_hello		; Send greating to user on UART
		call	@send_string
		mov	w,#_instructions	; Send instructinos
		call	@send_string
		_bank	SERIAL2
		mov	w,#_hello		; Send greating to user on UART
		call	@send_string
		mov	w,#_instructions	; Send instructinos
		call	@send_string

wait_for_byte	_bank	SERIAL1			; Select UART port 1
		jb	rx_flag,got_data	; check if data has been recvd
		_bank	SERIAL2			; Select UART port 2
		jnb	rx_flag,wait_for_byte	; check if data has been recvd

got_data	call	@get_byte		; Get a character from the users terminal
		mov	temp,w
		mov	w,#_enter		; Check if user pressed enter
		mov	w,temp-w
		jnz	echo_char		; if so then send a LF and CR
		mov	w,#_line_feed		
		call	@send_byte

echo_char	mov	w,temp			; The charecter recvd will be echod to the
		call	@send_byte		;  users terminal via the SAME uart. 
		jmp	wait_for_byte

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
		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 
: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	
 		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	temp,w
		swap	wreg
		and	w,#$0f
		call	hex_table
		call	send_byte
		mov	w,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
		call	:get_valid_hex
		mov	w,byte			; send the received (good) byte
		call	send_byte
		swap	temp			; put the nybble in the upper nybble of
		mov	w,temp
		mov	hex,w			; hex register

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

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

:loop		mov	w,temp			; get the value at temp (index)
		call	hex_table
		xor	w,byte
		snz				; compare it to the received byte
		ret
		inc	temp			; if they are equal, we have the
		jb	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
;*****************************************************************************************