;*****************************************************************************************
; 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.
;*****************************************************************************************
; SX Single I2C Multi-Master demo.
;                   
; Filename:	I2CMM_UART_TMR.src
;
; Author:	Bruce Wilson
;		Applications Engineer
;		Scenix Semiconductor Inc.
;
; Revision:	1.0
;
; Part:		SX28AC datecode 9929AA
; Freq:		50Mhz
; Compiled using Parallax SXKey and SASM compilers
;
; Date Written: Nov 12,1999
;
; Last Revised: Nov 18,1999
;
; Introduction:
;   This program was written with the intention to demonstate the implementation of running both an I2C 
; multi-master and UART virtual peripherals simultaniously and also to demonstrate the I2C arbitration process.  
; This code has been written and tested on both the Scenix I2C/UART demo board and also the SX28/52 Eval board.
;
; Program Description:                                                                                         
; This demo currently runs two VPs simultaniously: 
;	UART 			(2400 -> 57600 Bauds) Currently 19200
;	I2C Multi-Master 	Currently running at 154kHz
;
;   This program can be operated in one of two modes by simply changing the value of PROG_MODE in the constants
; section. 
;  Mode 1 : This mode will sequentially read each memory loaction within the first bank of an I2C EEPROM 
;	device and send each byte read serially at 19200 baud to a terminal program.
;  Mode 2 : Will prompt the user to send a character to the SX.  The characters received will be sequentially
;	written to bank 1 of the EEPROM device.
;  It is possible to run this program on two SX connected on the same I2C bus simultaniously. This will
;  demonstrate using the SX in an I2C multi-master situation. You could program one SX in mode 1 and one in 
;  mode 2 and simultaniously read data whilst writing data into the EEPROM. Try it!
; 	
;  The hardware setup is as for the Scenix I2C/UART demo board. Use the SX_NUM define to change between using
;  U1 and U2. If using the Scenix evaluation board change the target board define to EVAL_BD
;
;	 		    4.7k			  MAX232
;		    VCC	x--/\/\/\--------x 	      ----  RX  -------  RA2  
;	 	 	    4.7k	 |          PC            UART
;		    VCC	x--/\/\/\---x	 |  	      ----  TX  -------	 RA3
;				    |    |                           		SX2    
;				    |	 | SCL	  	    
;		    RA0	------------x------------------x---------------- RB0
;	       				 | SDA 	       |	  I2C
;		    RA1	-----------------x-----------------x------------ RB1
;						       |   |	
;	   SX1 		    MAX232		       |   |			
;		    RB2 ----- RX ------		       |   |	
;			 UART	      PC	      SCL SDA	
;		    RB3	----- TX ------		    EEPROM (24LC16B)
;
;  Once the SX has been programmed run the code at 50Mhz and connect a serial cable to your PC set at
; 	19200 Baud, No Parity, 1 Stop Bit, 8 Data Bits and no flow control.
;
; Revision History:
;   1.0	 Used I2C Multi-Master and UART VPs to implement multi-master/UART demo on SX I2C/UART board.
;

;*****************************************************************************************
; PROGRAM DEFINES
;*****************************************************************************************
		ID	'I2CM_URT'		; code identification
		reset	reset_entry		; set reset vector

multi_master					; Enable arbitration

;*****************************************************************************************
; 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.
;                
;*****************************************************************************************		
		device	SX28L,oschs2,turbo,stackx_optionx
		freq	50_000_000			


;*****************************************************************************************
; Macros
;*****************************************************************************************


;*****************************************************************************************
; Constants
;*****************************************************************************************

SX_NUM		equ	1 		; When using the Scenix I2C/UART demo board
					;  1 : when programming top SX (U1)
					;  2 : when programming bottom SX (U2)

PROG_MODE	equ	1		; Use to change between running program in either
					;  1 : Read entire EEPROM Bank and send to terminal	
					;  2 : Write data from terminal to EEPROM	

int_period	equ	108		; This sets the I2C bus frequency.  (154kHz)
					; Also used as seed for UART baud rate.
					; int_period = clk_speed/(3 x bus_speed)
	     ; eg. for 100kHz operation : int_period = 50Mhz/(3 x 100kHz) = 166

strt_delay	=	4		; This start delay is critical in the operation
					; of the multi-master system. The value of strt_delay
IFNDEF	multi_master			; specifies the number of ISR's that the scl line must
  strt_delay	=	1		; be held high for before a start bit will be sent.
ENDIF

_cr		equ	13	; ASCII value for carridge return
_line_feed	equ	10	; ASCII value for a line feed

	;*********************************************************************************
	; UART Constants
	; The following three 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	=	192			;for 2400 baud rate generation
start_del_24	=	baud_rate_24+(baud_rate_24/2)+1

; *** 9600 baud
baud_rate_96	=	48			;for 9600 baud rate generation
start_delay_96	= 	baud_rate_96+(baud_rate_96/2)+1

; *** 19200 baud
baud_rate_192	=	24			;for 19200 baud rate generation
start_delay_192	= 	baud_rate_192+(baud_rate_192/2)+1

; *** 38400 baud
baud_rate_384	=	12			;for 38400 baud rate generation
start_delay_384	= 	baud_rate_384+(baud_rate_384/2)+1

; *** 57600 baud
baud_rate_576	=	8			;for 57600 baud rate generation
start_delay_576	=	baud_rate_576+(baud_rate_576/2)+1

; *** 115200 baud
baud_rate_1152	=	4			;for 115200 baud rate generation
start_delay_1152=	baud_rate_1152+(baud_rate_1152/2)+1


;*****************************************************************************************
; Port Assignment
;   RX_io 	: sets direction registers
;   RX_init	: sets the ports initial value
;*****************************************************************************************

RA_init		equ	%11111111		;SX18/20/28/48/52 port A latch init
RA_io		equ	%11110111		;SX18/20/28/48/52 port A DDIR value
RB_init		equ	%11111111		;SX18/20/28/48/52 port B latch init
RB_io		equ	%11110111		;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

;*****************************************************************************************
; Pin Definitions 
;*****************************************************************************************
i2c_port	equ	ra
uart_port	equ	ra
led_port	equ	rc
scl		equ	0
sda		equ	1

scl_pin       	equ     i2c_port.scl	; I2C clock
sda_pin       	equ     i2c_port.sda	; I2C data I/O

rx_pin          equ     uart_port.2	;UART receive input
tx_pin          equ     uart_port.3	;UART transmit output

; Optional LEDs for debugging purposes...
green_led	equ	led_port.2	
red_led2	equ	led_port.1
red_led1	equ	led_port.0

;*****************************************************************************************
; Code Locations
;*****************************************************************************************
INTERRUPT_ORG		equ	$0   ; -> 9Ch  	This must always be 0 as this is the interrupt vector
UART_SUBS_ORG		equ	$200 ; -> 263h  UART subroutines stored here. These must be in first half of page
SEND_STRINGS_ORG	equ	$300 ; -> 35Fh  Strings to send to terminal MUST be stored here
I2C_ISR_FUNC_ORG	equ	$400 ; -> 48Ch  I2C Master state machine executed from ISR
I2C_SUBS_ORG		equ	$48C ; -> 50Dh	I2C Subroutines called from mainline

;*****************************************************************************************
; Data Memory address definitions
;*****************************************************************************************
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
;*****************************************************************************************
; Global Register definitions
; NOTE: Global data memory starts at $0A on SX48/52 and $08 on SX18/20/28.
;*****************************************************************************************
		org     global_org

main_temp	equ	global_org+1
isr_temp	equ	global_org+2		; Temporary variable used by the ISR.
flags		equ	global_org+3
uart_rx_flag	equ	flags.0		; flag set when data received by UART

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

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

	;*********************************************************************************
	; Bank 1 - I2C Master variables ram bank
	;*********************************************************************************
		org     bank1_org

I2CM		=	$	; I2CM bank
I2CM_state	ds	1	; This indicates the state that the I2CM master is currently in.
I2CM_sub_state	ds	1	; This indicates the substate that the I2C master is currently in.
I2CM_port_buf	ds	1	; This buffer holds the current state of the I2C port direction reg's
I2CM_bit_count	ds	1	; Indicates the number of bits left to process in read/write
I2CM_byte	ds	1	; The byte currently being written/read by the I2C master
I2CM_flags	ds	1
	I2CM_nack	equ	I2CM_flags.0	; This bit is set if the I2C master has received a NACK from the slave
	I2CM_rx_flag	equ	I2CM_flags.1 	; Indicates that the number of bytes requested have been received
	I2CM_lost_arb	equ	I2CM_flags.2	; indicates that this master has lost arbitration
	I2CM_doing_write equ	I2CM_flags.4	; Used to remember whether doing a read or write when recovering from loss of arbitration
	I2CM_in_control	equ	I2CM_flags.5	; Indicates that this master thinks it is in control of the I2C bus
I2CM_index 	ds	1	; The index into the I2CM buffer, used for writing	
I2CM_num_bytes 	ds	1	; The index into the I2CM buffer, used for reading
I2CM_strt_ctr	ds	1	; Counts the number of SCL high cycles before sending a start bit onto the i2c-bus
I2CM_buffer	=	$	; The buffer uses the last 7 registers of this bank (pre-increments, so put I2CM buffer here.)
I2CM_address	ds	1	; The address to read/write to.
I2CM_data_0 	ds	1	; Data buffer 
I2CM_data_1	ds	1	; Data buffer
I2CM_data_2	ds	1	; Data buffer
I2CM_data_3	ds	1	; Data buffer
I2CM_data_4	ds	1	; Data buffer
I2CM_data_5	ds	1	; Data buffer
; These data registers could easily be placed into another bank if a more are required

	;*********************************************************************************
	; Bank 2 - Serial bank
	;*********************************************************************************
		org     bank2_org
BANK2		=	$
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

TIMERS		=	$
timer_low	ds	1
timer_high	ds	1

VARS		=	$
bank_adr	ds	1		; points to bank address to read/write to
recv_byte	ds	1		; byte read from slave

	;*********************************************************************************
	; Bank 3 - Timer and other variable ram bank
	;*********************************************************************************
		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		=	$

;*****************************************************************************************
; 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
	;*********************************************************************************
	; Keep running 16bit timer... 
	;*********************************************************************************
		bank	TIMERS			;1 
		inc	timer_low		;1 
		snz				;1
		inc	timer_high		;1
						;=4
	;*********************************************************************************
	; I2C Master Virtual Peripheral
	;*********************************************************************************
		bank	I2CM			;1
		mov	w,#I2CM_buffer		;1 ; Switch to I2CM bank and load FSR for
		mov	fsr,w			;1 ; buffer look-up, if needed.
		call	@I2CM_ISR		;3 + 12/24
						;=18/30

	;*********************************************************************************
	; Update I2C port with buffered port data
	;*********************************************************************************
		mov	isr_temp,m		;2 ; Save the m register.

		mov	m,#$0f			;1
		mov	w,i2c_port		;1
		and	w,#%11111100		;1 ; Clear the data latches for SCL and SDA
		mov	i2c_port,w		;1

		bank	I2CM			;1 ; Select Master I2C bank
		mov	w,I2CM_port_buf		;1
		and	w,#%11110111		;1
		mov	!i2c_port,w		;1 ; Update the I2C port with the buffered port data

		mov	m,isr_temp		;2 ; Restore the m register.
						;=11
	;*********************************************************************************
	; I2C_arbitration
	; This code checks to see that the data written by I2CM is the same
	; as the data present on the bus.  If not, this master has lost arbitration.
	;*********************************************************************************
		IFDEF	multi_master
I2C_arbitration
		bank	I2CM			 ;1	
	; --- Check if arbitration required ---
		jb	I2CM_lost_arb,arb_done	 ;2/4 ; No need to check if lost arb. already lost
		jnb	I2CM_in_control,arb_done ;2/4 ; Only check arbitration when master is in control
  	; --- Check if data on bus matches data written by master ---
		jb	sda_pin,arb_done	 ;2/4 ; No master can have lost arb. when sda is high
		jnb	I2CM_port_buf.sda,arb_done ;2/4 check if buffer wants data high, if it does master loses arb.
 	; --- Arbitration has been lost. The master must now wait a 'random' time before trying again --- 
:arb_just_lost	setb	I2CM_lost_arb		 ;1 ; signal a loss of arbitration 
		clr	I2CM_state		 ;1 ; Put state machine into IDLE state
		clr	I2CM_sub_state		 ;1
arb_done					 ;=5/12
		ENDIF	

	;*********************************************************************************
	; 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 (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
	;
	;*********************************************************************************
		bank	SERIAL			;1 ;switch to serial register bank
:transmit	decsz	tx_divide		;1 ;only execute the transmit routine
		jmp	:receive		;1 
		mov	w,#baud_rate_192	;1 ;load UART baud rate (50MHz)
		mov	tx_divide,w		;1 
		test    tx_count		;1 ;are we sending?
		snz				;1 
		jmp	:receive		;3
		clc                             ;1 ;yes, ready stop bit
		rr      tx_high			;1 ; and shift to next bit
		rr      tx_low			;1 
		dec     tx_count		;1 ;decrement bit counter
		snb	tx_low.6		;1 ;output next bit
		clrb	tx_pin			;1 
		sb	tx_low.6		;1 
		setb	tx_pin			;1 
:receive        sb	rx_pin			;1 ;get current rx bit
	        clc				;1 
	        snb	rx_pin			;1 
	        stc				;1 
		test    rx_count                ;1 ;currently receiving byte?
		sz				;1 
		jmp	:rxbit                  ;3 ;if so, jump ahead
		mov     w,#9                    ;1 ;in case start, ready 9 bits
		sc				;1 ;skip ahead if not start bit
		mov     rx_count,w              ;1 ;it is, so renew bit count
		mov     w,#start_delay_192	;1 ;ready 1.5 bit periods (50MHz)
		mov     rx_divide,w		;1 
:rxbit          decsz	rx_divide		;1 ;middle of next bit?
          	jmp	:rxdone			;3 = 25/34
		mov	w,#baud_rate_192	;1 ;yes, ready 1 bit period (50MHz)
		mov	rx_divide,w		;1 

		dec     rx_count                ;1 ;last bit?
		sz                              ;1 ;if not
		rr      rx_byte                 ;1 ;  then save bit
		snz                             ;1 ;if so,
		setb    uart_rx_flag            ;1 = 29/40; then set flag
:rxdone					   	;else, exit

	;*********************************************************************************
	; Set Interrupt Rate
	;*********************************************************************************
isr_end
		mov	w,#-int_period		;1 ; refresh RTCC on return
		retiw				;3 ; return from the interrupt
				
;*****************************************************************************************
; 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
	;*********************************************************************************

		mov	w,#$0f			; point MODE to DDIR (SX18/20/28)
		mov	m,w			; Load m through w to ensure 5-bits written
						; in SX48/52
		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.

	;*********************************************************************************
	; Clear all Data RAM locations
	;*********************************************************************************
    					;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

	;*********************************************************************************
	; Initialize I2C program registers
	;*********************************************************************************
		bank	I2CM
		call	@I2CM_init

	;*********************************************************************************
	; Setup option register. 
	;*********************************************************************************

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 defalut
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_PS_OFF	;setup option register
		mov	!option,w

;*****************************************************************************************

;*****************************************************************************************
; MAIN PROGRAM CODE
; This can be changed as you like to suit your own application
; At present data is read from an EEPROM device and the data stored into the last RAM bank
;*****************************************************************************************

main
	;*********************************************************************************
	; Initialize variables...
	;*********************************************************************************
		bank	I2CM
		mov	I2CM_address,#$A0		; set address of slave to read. 
		bank	VARS
		clr	bank_adr
		clrb	green_led
	;*********************************************************************************
	; Send Introduction...
	;*********************************************************************************
		mov	w,#_hello			; Send a string to say Hello to the user
		call	@send_string
IF PROG_MODE == 1					; If using SXKey delete one of the '=' 
		mov	w,#_contents			; 'reading contents of E2 bank'
ELSE
		mov	w,#_prompt			; 'Enter Characters to store in bank '
ENDIF
		call	@send_string			
		bank	I2CM
		mov	w,I2CM_address			; Send address to read
		call	@send_hex
		mov	w,#_enter			; Send carridge return and line feed
		call	@send_string

IF PROG_MODE == 1					; If using SXKey delete one of the '=' 
	;*********************************************************************************
	; Read a character from EEPROM 'I2CM_address', at location 'read_adr'
	;*********************************************************************************
setup_I2C
do_I2C		bank	VARS
		mov	w,bank_adr			; get the address to read from
		bank	I2CM
		mov	I2CM_data_0,w			; load send register with data address		
		call	@I2CM_wait_not_busy		; wait for I2CM state machine to enter idle state
		call	@I2CM_send_byte			; send 'want to read' and bank addess to i2c slave
		call	@I2CM_wait_not_busy		; wait for I2CM state machine to enter idle state
		jb	I2CM_lost_arb,Ive_lost_arb	; if arbitration was lost then wait a while and try again
		snb	I2CM_NACK			; Check for no acknowledge
		jmp	got_NACK			; Set lights for no Ack and try again

		call	@I2CM_get_byte			; Gets the byte from slave and returns it in w
		jb	I2CM_lost_arb,Ive_lost_arb	; if arbitration was lost wait a while and then try again
		snb	I2CM_NACK			; Check for no acknowledge
		jmp	got_NACK			; Set lights for no ack and try again
		
bank	VARS
		mov	recv_byte,w
ELSE
	;*********************************************************************************
	; Write a character to EEPROM 'I2CM_address', at location 'write_adr'                  
	; See 24LC16B data sheet for more information on write sequence
	;*********************************************************************************
setup_I2C	call	@get_byte			; read a byte from hte terminal
		_bank	VARS
		mov	recv_byte,w			; store byte read
		mov	w,bank_adr			; get the address to read from

do_I2C		_bank	VARS                            ; Store data in EEPROM using byte write
		mov	main_temp,recv_byte		
		mov	w,bank_adr
		_bank	I2CM		                
		mov	I2CM_data_0,w                   ; EEPROM bank address
		mov	I2CM_data_1,main_temp           ; Byte to write to EEPROM
		mov	I2CM_num_bytes,#2
		call	@I2CM_wait_not_busy		; wait for I2CM state machine to enter idle state
		call	@I2CM_send_bytes		; Gets the byte from slave and returns it in w
		call	@I2CM_wait_not_busy		; wait for I2CM state machine to enter idle state
		jb	I2CM_lost_arb,Ive_lost_arb	; if arbitration was lost wait a while and then try again
		snb	I2CM_NACK			; Check for no acknowledge
		jmp	got_NACK			; Set lights for no ack and try again
ENDIF
	;*********************************************************************************
	; Set LEDs...
	;*********************************************************************************
		clrb	red_led1			
		clrb	red_led2
		setb	green_led			; indicate data received okay

	;*********************************************************************************
	; Send characters received to terminal...
	;*********************************************************************************
send_char	bank	VARS
		mov	w,bank_adr			; send EEPROM address 
		call	@send_hex

		mov	w,#':'				
		call	@send_byte
		mov	w,#' '				
		call	@send_byte

		bank	VARS
		mov	w,recv_byte			; send byte that was stored in that address 
		call	@send_byte			;  of the EEPROM

		mov	w,#_enter			; send CR + LF
		call	@send_string

		bank	VARS
		inc	bank_adr			; set to next address in bank
		jz	main				; finished entire bank... restart
		jmp	setup_I2C			; read next character from eeprom


	;*********************************************************************************
	; This Master will wait here when arbitration is lost
	;*********************************************************************************
Ive_lost_arb	clrb	green_led
		setb	red_led1
wait_here	bank	TIMERS				; Wait a 'random' time before trying again
		test	timer_low
		sz
		jmp	wait_here
		bank	I2CM
		clrb	I2CM_lost_arb
		jmp	do_I2C				; Retry I2C operation

	;*********************************************************************************
	; ERROR CONDITIONS:
	;*********************************************************************************
got_NACK	setb	green_led			; flash all LEDs
		setb	red_led1
		setb	red_led2
		jmp	do_I2C				; Retry I2C operation

;*****************************************************************************************
; I2C Master Interrupt Service Routines
;*****************************************************************************************
; I2C Virtual Peripheral...
org	I2C_ISR_FUNC_ORG

	;*********************************************************************************
	; Function: I2CM_ISR
	; I2C Master Interrupt-Driven State Machine
	; -------------------------------------------
	;	This is the I2C Master Interrupt Service Routine.  It is an interrupt-
	;	driven state machine which allows all of the actions of the I2C Master
	;	controller to be carried out, virtual peripheral style, with virtually
	;	no interaction from the mainline program.  
	;  I2CM_idle
	; 	This is the state that the I2C Master is usually in when it is not in use.
	; 	It just ensures that the I2C_port_buf SCL and SDA are both set high
	;  I2CM_start
	; 	When any mainline program wants to use the I2C master it puts the master into
	; 	start mode.  This mode creates a start condition on the I2C bus.  A start 
	;	condition is created when SDA goes from high to low while SCL stays high.
	;  I2CM_start_write
	;	This state performs some pre-processing which allows the I2CM_write state to
	;	do its work.  It sets up the bit count, gets the next piece of data from the
	;	buffer and prepares to send it.
	;  I2CM_write
	;	This state writes the data in I2CM_byte to the I2C bus
	;  I2CM_get_ack
	;	This state gets an ACK from the slave device.  If no ACK is received, the I2C
	;	Master state machine puts a stop condition on the bus and the I2CM_flags 
	;	register is loaded to indicate that a NACK has occurred.
	;  I2CM_write_repeat
	;	This state determines, after one byte of data is sent, whether or not
	; 	there is another byte to be sent.  If so, this state goes back to I2CM_start_write
	;	and sends the next byte.
	;  I2CM_stop
	;	This state puts a stop condition on the I2C bus and resets the state machine back
	;	to its idle state.  A stop condition is when SDA goes from low to high while SCL
	;	is high.
	;  I2CM_start_read
	;	This state simply loads the contents of the I2CM_address register into the I2CM_byte
	;	register and sets up the I2CM_write state to output the address of the slave to read.
	;  I2CM_read_data	
	; 	This state prepares the I2CM read routine so it can read from the slave device.  It
	;	initializes the bit count, etc.
	;  I2CM_read
	; 	This state read 8 bits of data from the slave device.
	;  I2CM_store_byte
	; 	This state stores the byte just read into the buffer.
	;  I2CM_send_ack
	; 	This state sends an ack if there is data left to write, and a NACK if there is
	;	no data left to write.
	;*********************************************************************************
I2CM_ISR
		mov	w,I2CM_state		;1
		add	PC,w			;3 ;Add the state to the program counter
						   ;and go to the state in the jump table.
		;*************************************************************************
		; States for I2C Master while idle
		;*************************************************************************
	I2CM_idle_loc		=	$
		jmp	I2CM_idle		;3 ;If I2C_state = 0, I2C is idle

		;*************************************************************************
		; States for I2C Master while writing
		;*************************************************************************
	I2CM_write_loc	=	$
		jmp	I2CM_start		;3 ;Make SDA go low while SCL is high
		jmp	I2CM_start_write	;3 ;Load a byte from the buffer and prepare to send. 
		jmp	I2CM_write		;3 ;Write it.
		jmp	I2CM_get_ack		;3 ;Get an ACK signal
		jmp	I2CM_write_repeat	;3 ;Check to see if we have finished sending 
						   ;(if buffer read index = buffer write index)
	I2CM_stop_loc	=	$
		jmp	I2CM_stop		;3 ;If write_repeat determines we're finished,
						   ;then send stop

		;*************************************************************************
		; States for I2C Master while reading
		;*************************************************************************
	I2CM_read_loc	=	$		   ;Load this state into state machine if we
						   ;are starting to read
		jmp	I2CM_start		;3 ;
		jmp	I2CM_start_read		;3 ;Write address used for reading
		jmp	I2CM_write 		;3 ;(writing address)
		jmp	I2CM_get_ack		;3 ;
	I2CM_read_rpt_loc =	$
		jmp	I2CM_read_data		;3 ;
		jmp	I2CM_read		;3 ;Keep doing this until done
		jmp	I2CM_store_byte		;3 ;
		jmp	I2CM_send_ack		;3 ;
		jmp	I2CM_stop		;3 ;
						
	;*********************************************************************************
	; State: I2CM_idle
	; 	This is the state that the I2C Master is usually in when it is not in use.
	; 	It just ensures that the I2C_port_buf SCL and SDA are both set high
	;*********************************************************************************
I2CM_idle	setb	I2CM_port_buf.sda	;1
		setb	I2CM_port_buf.scl	;1
		retp				;3 = 6 + 7 = 13

	;*********************************************************************************
	; State: I2CM_start
	; 	When any mainline program wants to use the I2C master it puts the master into
	; 	start mode.  This mode creates a start condition on the I2C bus.  A start 
	;	condition is created when SDA goes from high to low while SCL stays high.
	;*********************************************************************************
I2CM_start	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
		jmp	:state4			;3
		jmp	:state5			;3

:state1	 	setb	I2CM_port_buf.scl	;1
		setb	I2CM_in_control		;1
		setb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		mov	I2CM_strt_ctr,#strt_delay ;2 
		retp				;3 = 9 + 7 = 16

;	--- WAITING FOR STOP/START OR IDLE SCL ---
:state2		sb	scl_pin			;1 
		clr	I2CM_sub_state		;1 
		decsz	I2CM_strt_ctr		;1/2
		retp				;3
		inc	I2CM_sub_state		;1
		retp				;3 = 6/8 + 7 = 13/15
;	--- FINISHED CLOCK SYNCRONIZATION PERIOD ---

:state3		clrb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 5 + 7 = 12

:state4		clrb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 5 + 7 = 12

:state5		setb	I2CM_port_buf.sda	;1
		clr	I2CM_sub_state		;1
		inc	I2CM_state		;1
		retp				;3 = 6 + 7 = 13

	;*********************************************************************************
	; State: I2CM_start_write	
	;	This state performs some pre-processing which allows the I2CM_write state to
	;	do its work.  It sets up the bit count, gets the next piece of data from the
	;	buffer and prepares to send it.
	;*********************************************************************************
I2CM_start_write
		inc	I2CM_index		;1
		mov	w,I2CM_index		;1
		add	fsr,w			;1
		mov	w,indf			;1

		mov	I2CM_byte,w		;1
		mov	w,#8			;1
		mov	I2CM_bit_count,w	;1
		inc	I2CM_state		;1
		retp				;3 = 12 + 7 = 19

	;*********************************************************************************
	; State: I2CM_write
	;	This state writes the data in I2CM_byte to the I2C bus
	;*********************************************************************************
I2CM_write	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3

:state1		setb	I2CM_in_control
		setb	I2CM_port_buf.sda	;1
		rl	I2CM_byte		;1
		sc				;1
		clrb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 15 + 7 = 22

:state2		setb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 12 + 7 = 19

:state3		clrb	I2CM_port_buf.scl	;1
		dec	I2CM_bit_count		;1
		snz				;1
		jmp	:done			;3
		clr	I2CM_sub_state		;1
		retp				;3 = 14 + 7 = 21

:done		clr	I2CM_sub_state		;1
		inc	I2CM_state		;1
		clrb	I2CM_in_control
		retp				;3 = 18 + 7 = 25

	;*********************************************************************************
	; State: I2CM_get_ack
	;	This state gets an ACK from the slave device.  If no ACK is received, the I2C
	;	Master state machine puts a stop condition on the bus and the I2CM_flags 
	;	register is loaded to indicate that a NACK has occurred.
	;*********************************************************************************
I2CM_get_ack	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
		jmp	:state4			;3

:state1		setb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20

:state2		setb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 12 + 7 = 19

:state3		sb	sda_pin			;1
		inc	I2CM_sub_state		;1
		sb	sda_pin			;1
		retp				;3 = 13 + 7 = 20 ;if no ack,
		mov	w,#(I2CM_stop_loc-I2CM_idle_loc);1
		mov	I2CM_state,w		;1
		clr	I2CM_sub_state		;1 ;send a stop and indicate that this didn't work.
		setb	I2CM_NACK		;1 ;set I2CM_NACK to show that this did not go through.
		retp				;3 = 17 + 7 = 24

:state4		clrb	I2CM_port_buf.scl	;1
		clr	I2CM_sub_state		;1
		inc	I2CM_state		;1 ;move on to next state
		retp				;3 = 13 + 7 = 20


	;*********************************************************************************
	; State: I2CM_write_repeat
	;	This state determines, after one byte of data is sent, whether or not
	; 	there is another byte to be sent.  If so, this state goes back to 
	;	I2CM_start_write and sends the next byte.
	;*********************************************************************************
I2CM_write_repeat
		mov	w,I2CM_num_bytes	;1 ;test the read index to see if it is = to write index.
		xor	w,I2CM_index		;1 ;if it is, then we have finished writing the buffer via. I2C.
		snz				;1
		jmp	:I2CM_write_done	;3
		dec	I2CM_state		;1 ;back to get_ack
		dec	I2CM_state		;1 ;back to write
		dec	I2CM_state		;1 ;back to write_data
		retp				;3 = 16 + 7 =23

:I2CM_write_done
		inc	I2CM_state		;1 ;move on to next state (stop)
		retp				;3 = 17 + 7 = 24;and start the stop bit	

	;*********************************************************************************
	; State: I2CM_stop
	;	This state puts a stop condition on the I2C bus and resets the state machine back
	;	to its idle state.  A stop condition is when SDA goes from low to high while SCL
	;	is high.
	;*********************************************************************************
I2CM_stop	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3

:state1		setb	I2CM_in_control
		clrb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20

:state2		setb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20

:state3		setb	I2CM_port_buf.sda	;1
		clr	I2CM_sub_state		;1 ;put the state machine in Idle
		clr	I2CM_state		;1
		clr	I2CM_num_bytes		;1
		mov	w,#$ff			;1
		mov	I2CM_index,w		;1
		clrb	I2CM_in_control
		retp				;3 = 16 + 7 = 23 

;*****************************************************************************************
; End of I2C Master state machine
;*****************************************************************************************
	org	I2C_SUBS_ORG
;*****************************************************************************************
; Jump table
;*****************************************************************************************
I2CM_send_byte		jmp	I2CM_send_byte_		;3
I2CM_send_bytes		jmp	I2CM_send_bytes_	;3
I2CM_get_byte		jmp	I2CM_get_byte_		;3
I2CM_get_bytes		jmp	I2CM_get_bytes_		;3
I2CM_wait_not_busy	jmp	I2CM_wait_not_busy_	;3
I2CM_init		jmp	I2CM_init_		;3
;*****************************************************************************************
; I2C Master Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: I2CM_start_read
	;	This state simply loads the contents of the I2CM_address register into the 
	;	I2CM_byte register and sets up the I2CM_write state to output the address 
	;	of the slave to read.
	;*********************************************************************************
I2CM_start_read
		mov	w,I2CM_address
		mov	I2CM_byte,w
		inc	I2CM_index
		mov	w,#8
		mov	I2CM_bit_count,w
		inc	I2CM_state
		retp

	;*********************************************************************************
	; Function: I2CM_read_data	
	; 	This function prepares the I2CM read routine so it can read from the slave 
	;	device. It initializes the bit count, etc.
	;*********************************************************************************
I2CM_read_data
		mov	w,#8
		mov	I2CM_bit_count,w
		inc	I2CM_state
		;*************************************************************************
		; Read 8 bits of data from the slave device.
		;*************************************************************************
I2CM_read	mov	w,I2CM_sub_state
		add	pc,w
		jmp	:state1
		jmp	:state2
		jmp	:state3

:state1		clrb	I2CM_in_control
		setb	I2CM_port_buf.scl
		inc	I2CM_sub_state
		retp

:state2		sb	scl_pin
		retp
		stc
		sb	sda_pin
		clc
		rl	I2CM_byte
		inc	I2CM_sub_state
		retp

:state3		clrb	I2CM_port_buf.scl
		clr	I2CM_sub_state
		dec	I2CM_bit_count
		sz	
		retp
		inc	I2CM_state
		retp

	;*********************************************************************************
	; Function: I2CM_store_byte
	; 	This function stores the byte just read into the buffer.
	;*********************************************************************************
I2CM_store_byte
		mov	w,I2CM_byte
		mov	isr_temp,w
		inc	I2CM_index
		mov	w,I2CM_index
		add	fsr,w
		mov	w,isr_temp
		mov	indf,w
		inc	I2CM_state
		setb	I2CM_rx_flag
		retp

	;*********************************************************************************
	; Function: I2CM_send_ack
	; 	This function sends an ack if there is data left to write, and a NACK if 
	;	there is no data left to write.
	;*********************************************************************************
I2CM_send_ack
		mov	w,I2CM_sub_state
		add	pc,w
		jmp	:state1
		jmp	:state2
		jmp	:state3
		jmp	:state4	

:state1		setb	I2CM_in_control
		clrb	I2CM_port_buf.sda
		mov	w,I2CM_index
		xor	w,I2CM_num_bytes
		snz
		setb	I2CM_port_buf.sda
		inc	I2CM_sub_state
		retp

:state2		setb	I2CM_port_buf.scl	;Pulse the clock
		inc	I2CM_sub_state
		retp

:state3		sb	scl_pin
		retp
		clrb	I2CM_port_buf.scl
		inc	I2CM_sub_state
		retp

:state4		setb	I2CM_port_buf.sda
		clr	I2CM_sub_state
		mov	w,I2CM_index
		xor	w,I2CM_num_bytes
		snz
		jmp	:done
		mov	w,#(I2CM_read_rpt_loc - I2CM_idle_loc) ;back to read 
		mov	I2CM_state,w
		retp

:done		inc	I2CM_state		;If this was the last byte to be read, send a stop
		retp

;*****************************************************************************************
; I2C Master Subroutines:  Mainline Access routines (call from the mainline)
;*****************************************************************************************

	;*********************************************************************************
	; Function: I2CM_send_byte
	; 	This routine sets up the I2CM state machine to write the byte of data in
	; 	the I2CM_data_0 register.  Before entering this routine, make sure that
	;	the I2CM state machine is in its idle state (use the I2CM_wait_not_busy 
	;	subroutine) and that the I2CM_address is loaded with the address of the
	;	slave that this byte is going to, and that I2CM_data_0 is loaded with the
	;	data to send.
	;*********************************************************************************
I2CM_send_byte_
		mov	w,#1			;There is one byte of data in the buffer
		mov	I2CM_num_bytes,w
	;*********************************************************************************
	; Function: I2CM_send_bytes
	; 	This routine sets up the I2CM state machine to write the bytes of data in
	; 	the I2CM_data_0 - I2CM_data_n registers.  Before entering this routine, 
	;	make sure that the I2CM state machine is in its idle state (use the 
	;	I2CM_wait_not_busy subroutine) and that I2CM_address is loaded with the 
	;	address of the slave that this byte is going to, that I2CM_data_0 - I2CM_data_n
	;	are loaded loaded with the data to send, and that the I2CM_num_bytes register
	;	is loaded with the number of data bytes to send.
	;*********************************************************************************
I2CM_send_bytes_
		setb	I2CM_doing_write
		mov	w,#%11111110
		and	I2CM_address,w
		clrb	I2CM_nack
		mov	w,#(I2CM_write_loc-I2CM_idle_loc)
		mov	I2CM_state,w
		retp
	;*********************************************************************************
	; Function: I2CM_wait_not_busy
	; 	This routine polls the I2CM_state register until it is not busy.  It returns 
	;	when the I2C master state machine becomes idle.  It returns a (0) in the w
	;	register if the transfer appeared successful (ie.  The slave returned an ACK
	;	when addressed), and a (1) in the w register if the slave did not return
	;	an ack when addressed/written.
	;*********************************************************************************
I2CM_wait_not_busy_
:wait_loop	test	I2CM_state
		sz			; wait until I2C is idle

		jmp	:wait_loop
		mov	w,#1		; return (1) if we are idle because a NACK was
		snb	I2CM_nack	; received.  Return (0) if we are idle because
		retp			; this was successful
		clr	w		
		retp

	;*********************************************************************************
	; Function: I2CM_get_byte
	;	This routine gets one byte of data from the slave at address I2CM_address.
	;	before calling this routine, ensure that the I2C Master State Machine is
	;	in its idle state (use the I2CM_wait_not_busy subroutine) and that the
	;	I2CM_address register is loaded with a valid address.  The routine returns
	;	with the byte received in the w register and in the I2CM_data_0 register.
	;*********************************************************************************
I2CM_get_byte_
		clrb	I2CM_doing_write
:nack		mov	w,#%00000001
		or	I2CM_address,w
		mov	w,#1
		mov	I2CM_num_bytes,w
		clrb	I2CM_nack
		mov	w,#(I2CM_read_loc-I2CM_idle_loc)
		mov	I2CM_state,w
		call	I2CM_wait_not_busy
		and	w,$ff
		sz
		jmp	:nack
		clrb	I2CM_rx_flag
		mov	w,I2CM_data_0
		retp

	;*********************************************************************************
	; Function: I2CM_get_bytes
	;	This routine gets I2CM_num_bytes of data from the slave at address
	;	I2CM_address.  Before calling this routine, ensure that the I2CM state
	;	machine is idle by using the I2CM_wait_not_busy subroutine, that I2CM_address
	;	register contains the address of the slave to be read from, and that the
	;	I2CM_num_bytes register is loaded with the number of bytes of data to receive.
	;	The received bytes will be contained in registers I2CM_data_0 to I2CM_data_n.
	;*********************************************************************************
I2CM_get_bytes_
		clrb	I2CM_doing_write
		mov	w,#%00000001
		or	I2CM_address,w
		clrb	I2CM_nack
		clrb	I2CM_rx_flag
		mov	w,#(I2CM_read_loc-I2CM_idle_loc)
		mov	I2CM_state,w
		retp

	;*********************************************************************************
	; Function: I2CM_init
	;	This subroutine should be called on startup.  It initializes the registers
	;	which are critical to the operation of the I2CM state machine.
	;*********************************************************************************
I2CM_init_
		mov     w,#%10111111		;Set RB in/out directions 
		mov     I2CM_port_buf,w
		mov	w,#$ff
		mov	I2CM_num_bytes,w
		mov	w,#$ff
		mov	I2CM_index,w
		retp

	org	SEND_STRINGS_ORG
;*****************************************************************************************
; String data (for RS-232 output)
;*****************************************************************************************
_hello          dw      13,10,10,'SX I2C Multi-Master & UART Demo',0
_contents	dw	13,10,'Contents of EEPROM Bank $',0
_prompt		dw	13,10,'Enter Some Charaters to Store in Bank ',0
_enter		dw	13,10,0
_enter_hex	dw	13,10,' Enter Value : ',0

	org	UART_SUBS_ORG
;*****************************************************************************************
; 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	uart_rx_flag		;wait till byte is received
		jmp	get_byte
		clrb    uart_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
	;*********************************************************************************
	;*********************************************************************************
	; Function: send_hex
	; Output a hex number
	; INPUTS:
	;	w	-	The byte to be output
	; OUTPUTS:
	; 	outputs the hex byte via RS-232
	;*********************************************************************************
send_hex	bank	SERIAL
		mov	main_temp,w
		swap	wreg
		and	w,#$0f
		call	hex_table
		call	send_byte
		mov	w,main_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
		page	send_string
		call	send_string
		call	:get_valid_hex
		mov	w,byte			; send the received (good) byte
		call	send_byte
		swap	main_temp		; put the nybble in the upper nybble of
		mov	w,main_temp
		mov	hex,w			; hex register

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

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

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