please dont rip this site

"Digilight" SX Serial 6 Lamp Controller

By Timothy Stranex

;*****************************************************************************************
; Copyright © [24.02.2002] Timothy Stranex
;*****************************************************************************************
; 
; Filename:	digilight.src
;
; Authors:	Timothy Stranex (perspex@dream.za.net)
;
; Part:		SX52BD
; Freq:		50MHz
;
; Date Written	: Feb 24 2002
;
; Program Description:
;
;		Changes the intensity of upto 6 different lights using pwm. The intesity
;		values are changed through an RS232 port.
;
; Acknowlegments:
;		Ubicom - Vpg UART 1.04
;
; Interface Pins:
;		
;		rs232RxPin      equ     rd.0		;UART receive input
;		rs232TxPin      equ     rd.1		;UART transmit output
;		rts_pin		equ	rd.2		;UART 1 RTS input
;		cts_pin		equ	rd.3		;UART 1 CTS output
;
;		pwm_ch0		equ	rb.0
;		pwm_ch1		equ	rb.1
;		pwm_ch2		equ	rb.2
;		pwm_ch3		equ	rb.3
;		pwm_ch4		equ	rb.4
;		pwm_ch5		equ	rb.5
;
;*****************************************************************************************


;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC,SX20AC,SX28AC,SX48BD, SX52BD. 
;*****************************************************************************************

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

;*****************************************************************************************
; Uncomment one of the following to run the uart vp at the required baud rate
;*****************************************************************************************
;baud1200		;baud rate of 1.2 Kbps
;baud2400
;baud4800		;baud rate of 4.8 Kbps
baud9600		;baud rate of 9.6 kbps
;baud1920		;baud rate of 19.2kbps
;baud5760		;baud rate of 57.6kbps 


PWMDivide	equ	127


;*****************************************************************************************
;				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 SX28AC					;SX28AC device directives for SX-Key		
	device	SX28L,oschs2,turbo,stackx_optionx
  ENDIF


  IFDEF SX48_52					;SX48/52/BD device directives for SX-Key
	device	oschs2
  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 SX28AC					;SX28AC device directives for SASM
	device	SX28,oschs2,turbo,stackx,optionx
  ENDIF
  
  IFDEF SX48_52					;SX48BD or SX52BD device directives for SASM
	device	SX52,oschs2  
  ENDIF

ENDIF
		id	'1UART_VP'		;
		reset	resetEntry		; set reset vector


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

endm

	;*********************************************************************************
        ; INCP/DECP macros for incrementing/decrementing pointers to RAM
        ; used to compensate for incompatibilities between SX28AC and SX52BD
	;*********************************************************************************


 INCP macro 1				; Increments a pointer to RAM
		inc    \1
	IFNDEF	SX48_52
		setb  \1.4		; If SX18 or SX28AC,keep bit 4 of the pointer = 1
	ENDIF				; to jump from $1f to $30,etc
endm

DECP macro 1				; Decrements a pointer to RAM
	IFDEF	SX48_52
		dec    \1
	ELSE
		clrb   \1.4		; If SX18 or SX28AC, forces rollover to next bank
		dec    \1		; if it rolls over. (skips banks with bit 4 = 0)
		setb   \1.4             ; Eg: $30 ---> $20 ---> $1f ---> $1f 
	ENDIF                           ; AND: $31 ---> $21 ---> $20 ---> $30
endm

	;*********************************************************************************
        ; Error generating macros
        ; Used to generate an error message if the label is intentionally moved into the 
	; second page.
        ; Use for lookup tables.
	;*********************************************************************************

tableStart	macro	0		; Generates an error message if code that MUST be in
					; the first half of a page is moved into the second half
	if $ & $100
		ERROR      'Must be located in the first half of a page.'
	endif
endm

tableEnd	macro	0		; Generates an error message if code that MUST be in
					; the first half of a page is moved into the second half

	if $ & $100
		ERROR      'Must be located in the first half of a page.'
	endif
endm

;*****************************************************************************************************
;----------------------------------------------Memory Organization------------------------------------
;******************************************************************************************************


;*********************************************************************************************
;--------------------------------- Data Memory address definitions-------------------------------------
; These definitions ensure the proper address is used for banks 0 - 7 for 2K SX devices
; (SX18/20/28) and 4K SX devices (SX48/52). 
;*********************************************************************************************

IFDEF SX48_52

global_org	=	$0A
bank0_org	=	$00
bank1_org	=	$10
bank2_org	=	$20
bank3_org	=	$30
bank4_org	=	$40
bank5_org	=	$50
bank6_org	=	$60
bank7_org	=	$70

ELSE

global_org	=	$08
bank0_org	=	$10
bank1_org	=	$30
bank2_org	=	$50
bank3_org	=	$70
bank4_org	=	$90
bank5_org	=	$B0
bank6_org	=	$D0
bank7_org	=	$F0

ENDIF

;*********************************************************************************************
;-------------------------------- Global Register definitions----------------------------
; NOTE: Global data memory starts at $0A on SX48/52 and $08 on SX18/20/28.
;*********************************************************************************************

	org     global_org

flags0		equ	global_org + 0		; stores bit-wise operators like flags 
						; and function-enabling bits (semaphores)
;-----------------------------VP: RS232 Receive------------------------------------------
	rs232RxFlag	equ	flags0.0	;indicates the reception of a bit from the UART

isrTemp0	equ	global_org + 1		; Interrupt Service Routine's temp register.  
						; Don't use this register in the mainline.
localTemp0	equ	global_org + 2		; temporary storage register
						; Used by first level of nesting
						; Never guaranteed to maintain data
localTemp1	equ	global_org + 3		; temporary storage register	
						; Used by second level of nesting
						; or when a routine needs more than one 
						; temporary global register.	
localTemp2	equ	global_org + 4		; temporary storage register
						; Used by third level of nesting or by
						; main loop routines that need a loop 
						; counter, etc.

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

	;*********************************************************************************
	; Bank 0
	;*********************************************************************************

		org     bank0_org

bank0		=	$

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************

		org     bank1_org


bank1			=	$
rs232TxBank		=       $	;UART bank
rs232Txhigh		ds      1	;hi byte to transmit
rs232Txlow		ds      1	;low byte to transmit
rs232Txcount		ds      1	;number of bits sent
rs232Txdivide		ds      1	;xmit timing (/16) counter
rs232Txflag		ds	1

rs232RxBank		=	$
rs232Rxcount		ds      1	;number of bits received
rs232Rxdivide		ds      1	;receive timing counter
rs232Rxbyte		ds      1	;buffer for incoming byte
string			ds	1	;used by send_string to store the address in memory
rs232byte		ds	1	;used by serial routines
hex			ds	1

MultiplexBank		=	$
isrMultiplex 	  	ds      1  

	;*********************************************************************************
	; Bank 2
	;*********************************************************************************

		org     bank2_org

bank2			=	$
PWM_Bank		=	$
PWM_Divide		ds	1
PWM_Duty0		ds	1
PWM_Duty1		ds	1
PWM_Duty2		ds	1
PWM_Duty3		ds	1
PWM_Duty4		ds	1
PWM_Duty5		ds	1
PWM_Count		ds	1

	;*********************************************************************************
	; 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	%00000000		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11111111		;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	%00001100		;SX18/20/28/48/52 port A PLP value

RB_latch	equ	%00000000		;SX18/20/28/48/52 port B latch init;intial value affter reset
RB_DDIR		equ	%11000000		;SX18/20/28/48/52 port B DDIR value;0=Output,1=Input
RB_ST		equ	%11111111		;SX18/20/28/48/52 port B ST value;0=Enable,1=Disable
RB_LVL		equ	%00000000		;SX18/20/28/48/52 port B LVL value;0=CMOS,1=TTL
RB_PLP		equ	%00000000		;SX18/20/28/48/52 port B PLP value;0=Enable,1=Disable

RC_latch	equ	%00000000		;SX18/20/28/48/52 port C latch init;intial value affter reset
RC_DDIR		equ	%11111111		;SX18/20/28/48/52 port C DDIR value;0=Output,1=Input
RC_ST		equ	%11111111		;SX18/20/28/48/52 port C ST value;0=Enable,1=Disable
RC_LVL		equ	%00000000		;SX18/20/28/48/52 port C LVL value;0=CMOS,1=TTL
RC_PLP		equ	%00000000		;SX18/20/28/48/52 port C PLP value;0=Enable,1=Disable

IFDEF SX48_52		

RD_latch	equ	%00000000		;SX48/52 port D latch init;intial value affter reset
RD_DDIR		equ	%11111101		;SX48/52 port D DDIR value;0=Output,1=Input
RD_ST		equ	%11111111		;SX48/52 port D ST value;0=Enable,1=Disable
RD_LVL		equ	%00000000		;SX48/52 port D LVL value;0=CMOS,1=TTL
RD_PLP		equ	%00000000		;SX48/52 port D PLP value;0=Enable,1=Disable

RE_latch	equ	%00000000		;SX48/52 port E latch init;intial value affter reset
RE_DDIR		equ	%11111111		;SX48/52 port E DDIR value;0=Output,1=Input
RE_ST		equ	%11111111		;SX48/52 port E ST value;0=Enable,1=Disable
RE_LVL		equ	%00000000		;SX48/52 port E LVL value;0=CMOS,1=TTL
RE_PLP		equ	%00000000		;SX48/52 port E PLP value;0=Enable,1=Disable

ENDIF

;*****************************************************************************************
;--------------------------------- Pin Definitions----------------------------------------
;*****************************************************************************************

rs232RTSpin	equ	rb.4	;UART RTS input
rs232CTSpin	equ	rd.3	;UART CTS output
rs232Rxpin      equ     rd.0	;UART receive input
rs232Txpin      equ     rd.1	;UART transmit output

CH0_MASK	equ	%00000001	;PWM0 output
CH1_MASK	equ	%00000010	;PWM1 output
CH2_MASK	equ	%00000100	;PWM2 output
CH3_MASK	equ	%00001000	;PWM3 output
CH4_MASK	equ	%00010000	;PWM4 output
CH5_MASK	equ	%00100000	;PWM5 output

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

_enter		equ	13	; ASCII value for carridge return
_linefeed	equ	10	; ASCII value for a line feed

;*****************************************************************************************
;	UART Constants values
;*****************************************************************************************

intPeriod	= 217

UARTfs		= 230400
	
Num		= 4	

IFDEF baud1200
	UARTBaud	= 1200
ENDIF
	
IFDEF baud2400
	UARTBaud	= 2400
ENDIF
	
IFDEF baud4800
	UARTBaud	= 4800
ENDIF
	
IFDEF baud9600
	UARTBaud	= 9600
ENDIF
	
IFDEF baud1920
	UARTBaud	= 19200
ENDIF

IFDEF baud5760
	UARTBaud	= 57600
ENDIF

UARTDivide	= (UARTfs/(UARTBaud*Num))
UARTStDelay 	= UARTDivide +(UARTDivide/2)+1


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, 1 = falling, 0 = 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, 1 = falling, 0 = 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, 1 = falling, 0 = rising
WKEN_W		equ	$0B		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	$0C		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	$0D		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W		equ	$0E		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W		equ	$0F		;Write Port Direction

ENDIF

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

INTERRUPT_ORG		equ	$0	; Interrupt must always start at location zero
RESETENTRY_ORG		equ	$1FB	; The program will jump here on reset
SUBROUTINES_ORG		equ	$200	; The subroutines are in this location
STRINGS_ORG		equ	$300	; The strings are in the location $300
PAGE3_ORG		equ	$400	; Page 3 is empty
MAINPROGRAM_ORG		equ	$600	; The main program is in the lastpage of program memory


;*****************************************************************************************
		org	INTERRUPT_ORG        ; First location in program memory.
;*****************************************************************************************

;*****************************************************************************************
;--------------------------------------------Interrupt Service Routine--------------------
; Note 1: 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.32us.
; Note 2: Mode Register 'M' is not saved in SX 28 but saved in SX 52 when an Interrupt
;	  occurs. If the code is to run on a SX 28 and 'M' register is used in the ISR, 
;	  then the 'M' register has to be saved at the Start of ISR and restored at the 
;	  End of ISR.
;*****************************************************************************************

		org     $0
interrupt					;3

;*****************************************************************************************
; Interrupt
; 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.32us.
;*****************************************************************************************

;*****************************************************************************************
;--------------------------------------------VP:VP Multitasker----------------------------
; Virtual Peripheral Multitasker : up to 16 individual threads, each running at the 
; (interrupt rate/16). Change them below:
; Input variable(s): isrmultiplex: variable used to choose threads
; Output variable(s): None,executes the next thread
; Variable(s) affected: isrmultiplex
; Flag(s) affected: None
; Program Cycles: 9 cycles (turbo mode)
;*****************************************************************************************

                _bank          Multiplexbank        ;
                inc            isrMultiplex         ; toggle interrupt rate
                mov            w,isrMultiplex       ;

;*****************************************************************************************
; The code between the tableStart and tableEnd statements MUST be completely within the first
; half of a page. The routines it is jumping to must be in the same page as this table.
;*****************************************************************************************

         tableStart                                 ; Start all tables with this macro
                jmp            pc+w                 ;
                jmp            isrThread1           ;
                jmp            isrThread2           ;
                jmp            isrThread3           ;  
                jmp            isrThread4           ; 
                jmp            isrThread1           ;
                jmp            isrThread5           ;
                jmp            isrThread6           ;  
                jmp            isrThread7           ; 
                jmp            isrThread1           ; 
                jmp            isrThread8           ;
                jmp            isrThread9           ;
                jmp            isrThread10          ;  
                jmp            isrThread1           ; 
                jmp            isrThread11          ; 
                jmp            isrThread12          ;
                jmp            isrThread13          ;
         tableEnd                                   ; End all tables with this macro.   

;*****************************************************************************************
;VP: VP Multitasker
; ISR TASKS
;*****************************************************************************************

isrThread1                                          ; Serviced at ISR rate/4

;---------------------------------------------VP: RS232 Transmit--------------------------

;*****************************************************************************************
; 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 rs232Rxflag 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 (rs232Txhigh+rs232Txlow) register pair (with the inverted 8 data bits
; stored in rs232Txhigh and rs232Txlow 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 rs232Txcount 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) : rs232Txlow (only high bit used), rs232Txhigh, rs232Txcount
;       Output variable(s) : rs232Rxflag, rs232Rxbyte
;       Variable(s) affected : rs232Txdivide, rs232Rxdivide, rs232Rxcount
;       Flag(s) affected : rs232Rxflag
;       Variable(s) affected : Txdivide
;	Program cycles: 17 worst case
;	Variable Length?  Yes.
;*****************************************************************************************

rs232Transmit
		_bank	rs232TxBank		;2 switch to serial register bank

		decsz	rs232Txdivide		;1 only execute the transmit routine
		jmp	:rs232TxOut		;1 
		mov	w,#UARTDivide		;1 load UART baud rate (50MHz)
		mov	rs232Txdivide,w		;1  
		test    rs232Txcount		;1 are we sending?
		snz				;1  
		jmp	:rs232TxOut		;1 

:txbit		clc                             ;1 yes, ready stop bit
		rr      rs232Txhigh		;1  and shift to next bit
		rr      rs232Txlow		;1  
		dec     rs232Txcount		;1 decrement bit counter
		snb	rs232Txlow.6		;1 output next bit
		clrb	rs232TxPin		;1  
		sb	rs232Txlow.6		;1  
		setb	rs232TxPin		;1,17  
:rs232TxOut


;*****************************************************************************************
;----------------------------------------VP: RS232 Receive--------------------------------
; 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.
;       Output variable(s) : rx_flag, rx_byte
;       Variable(s) affected : tx_divide, rx_divide, rx_count
;       Flag(s) affected : rx_flag
;	Program cycles: 23 worst case
;	Variable Length?  Yes.
;*****************************************************************************************

rs232Receive
		_bank	rs232RxBank		;2
	        sb	rs232RxPin		;1 get current rx bit
	        clc				;1  
	        snb	rs232RxPin		;1  
	        stc				;1  
		test    rs232Rxcount            ;1 currently receiving byte?
		sz				;1  
		jmp	:rxbit                  ;1 if so, jump ahead
		mov     w,#9                    ;1 in case start, ready 9 bits
		sc				;1 skip ahead if not start bit
		mov     rs232Rxcount,w		;1 it is, so renew bit count
		mov     w,#UARTStDelay		;1 ready 1.5 bit periods (50MHz)		
		mov     rs232RxDivide,w		;1  
:rxbit          decsz	rs232Rxdivide		;1 middle of next bit?
          	jmp	:rs232RxOut		;1 
		mov	w,#UARTDivide		;1 yes, ready 1 bit period (50MHz)
		mov	rs232Rxdivide,w		;1  
		dec     rs232Rxcount            ;1 last bit?
		sz                              ;1 if not
		rr      rs232Rxbyte             ;1  then save bit
		snz                             ;1 if so,
		setb    rs232RxFlag             ;1,23  then set flag
:rs232RxOut

;*****************************************************************************************
;===================================== PUT YOUR OWN VPs HERE==============================
; Virtual Peripheral: 6 channel 8bit PWM
;
;	Input variable(s): PWM_Duty0 - PWM_Duty5, PWM_Count, PWM_Divide
;	Output variable(s): 
;	Variable(s) affected: PWM_Count, PWM_Divide
;	Flag(s) affected: 
;*****************************************************************************************
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread2					; Serviced at ISR rate/16

	_bank	PWM_Bank			; 2

	decsz	PWM_Divide			; 1
	jmp	:END
	mov	w, #PWMDivide			; 1
	mov	PWM_Divide, w			; 1
	
	incsz	PWM_Count			; 1
	jmp	:CH0				; 1
	clr	rb				; 1

:CH0	mov	w, CH0_MASK			; 1
	incsz	PWM_Duty0			; 1
	jmp	:CH0				; 1
	or	rb, w				; 1

:CH1	mov	w, CH1_MASK			; 1
	incsz	PWM_Duty1			; 1
	jmp	:CH2				; 1
	or	rb, w				; 1

:CH2	mov	w, CH2_MASK			; 1
	incsz	PWM_Duty2			; 1
	jmp	:CH3				; 1
	or	rb, w				; 1

:CH3	mov	w, CH3_MASK			; 1
	incsz	PWM_Duty3			; 1
	jmp	:CH4				; 1
	or	rb, w				; 1

:CH4	mov	w, CH4_MASK			; 1
	incsz	PWM_Duty4			; 1
	jmp	:CH5				; 1
	or	rb, w				; 1

:CH5	mov	w, CH5_MASK			; 1
	incsz	PWM_Duty5			; 1
	jmp	:END				; 1
	or	rb, w				; 1, 32

END:
	
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread3                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread4                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread5                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread6                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread7                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread8                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread9                                      ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread10                                     ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread11                                     ; Serviced at ISR rate/16
;-----------------------------------------------------------------------------------------
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread12                                     ; Serviced at ISR rate/16
		jmp		isrOut		; 7 cycles until mainline program resumes execution
;-----------------------------------------------------------------------------------------
isrThread13                                     ; Serviced at ISR rate/16
                                               	; This thread must reload the isrMultiplex register
               _bank	Multiplexbank
                mov	isrMultiplex,#255	;reload isrMultiplex so isrThread1 will be run on the
                                                ; next interrupt.
                jmp	isrOut           	; 7 cycles until mainline program resumes execution
						; This thread must reload the isrMultiplex register
                                                ; since it is the last one to run in a rotation.
;-----------------------------------------------------------------------------------------
isrOut

;*****************************************************************************************
; Set Interrupt Rate
;*****************************************************************************************

isrend	
		mov	w,#-intperiod		;refresh RTCC on return
						;(RTCC = 217-no of instructions executed in the ISR)
		retiw				;return from the interrupt
						
;*****************************************************************************************
; End of the Interrupt Service Routine
;*****************************************************************************************

;*****************************************************************************************
; RESET VECTOR 
;*****************************************************************************************

;*****************************************************************************************
;----------------------------------------Reset Entry--------------------------------------
;*****************************************************************************************

	org 	RESETENTRY_ORG

resetEntry					; Program starts here on power-up
		page 	_resetEntry
		jmp	_resetEntry

;*****************************************************************************************
;-----------------------------------------UART Subroutines--------------------------------
;*****************************************************************************************

	org	SUBROUTINES_ORG

;*****************************************************************************************
; 	Function	: getbyte
; 	INPUTS		: NONE
; 	OUTPUTS		: Received byte in rs232Rxbyte
; 	Get byte via serial port and echo it back to the serial port
;*****************************************************************************************

getbyte		jnb     rs232RxFlag,$		; wait till byte is received
		clrb    rs232RxFlag		; reset the receive flag
		_bank	rs232RxBank		; switch to rs232 bank

		mov     rs232byte,rs232Rxbyte	; store byte (copy using W)

		retp

;*****************************************************************************************
; 	Function	: sendbyte
;	INPUTS		: 'w' - the byte to be sent via RS-232
; 	OUTPUTS		: Outputs The byte via RS-232
; 	Send byte via serial port
;*****************************************************************************************

sendbyte    	mov	localTemp0,w
    		_bank    rs232TxBank

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

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

;*****************************************************************************************
; 	Function	: sendstring     
;			  Care should be taken that the srings are located within program 
;			  memory locations $300-$3ff as the area 
; 	INPUTS		: 'w' -	the address of a null-terminated string in program memory
; 	OUTPUTS		: Outputs the string via RS-232
; 	Send string pointed to by address in W register
;*****************************************************************************************

sendstring	_bank	rs232TxBank
 		mov     localTemp1,w            ; store string address
:loop
		mov	w,#STRINGS_ORG>>8	; with indirect addressing
		mov	m,w
		mov     w,localTemp1            ; read next string character
		iread                           ; using the mode register
		test    w                       ; are we at the last char?
		snz                             ; if not=0, skip ahead
		jmp	:out                    ; yes, leave & fix page bits
		call    sendbyte               	; not 0, so send character
		_bank	rs232TxBank
		inc     localTemp1              ; point to next character
		jmp     :loop                   ; loop until done

:out		mov	w,#$1F			; reset the mode register
		mov	m,w
		retp

;*****************************************************************************************
; 	Function	: uppercase
; 	INPUTS		: byte - the byte to be converted
; 	OUTPUTS		: byte - converted byte
; 	Convert byte to uppercase.
;*****************************************************************************************

uppercase    	mov	w,#'a'            	;if byte is lowercase, then skip ahead
	    	mov	w,rs232byte-w
	    	sc
		retp
		mov	w,#'a'-'A'		;change byte to uppercase
		sub	rs232byte,w
		retp				;leave and fix page bits

;*****************************************************************************************
;	Function	: sendhex
;	INPUTS		: 'w' - the byte to be output
;	OUTPUTS		: Outputs the hex byte via RS-232
;	Output a hex number
;*****************************************************************************************

sendhex		mov	localTemp1,w
		swap	wreg
		and	w,#$0f
		call	hextable
		call	sendbyte
		mov	w,localTemp1
		and	w,#$0f
		call	hextable
		call	sendbyte
		retp

;*****************************************************************************************
;	Function	: gethex
;	Inputs		: None
;	OUTPUTS		: Received HEX value is in 'hex' register.
;	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
;*****************************************************************************************

gethex		_bank	rs232RxBank		;2
		mov	w,#_enterhex
		call	@sendstring
		call	:getvalidhex
		mov	w,rs232byte		; send the received (good) byte
		call	sendbyte
		swap	localTemp2		; put the nibble in the upper nibble of
		mov	w,localTemp2
		mov	hex,w			; hex register

		call	:getvalidhex
		mov	w,rs232byte		; send the second received byte
		call	sendbyte
		mov	w,localTemp2
		and	w,#$0f
		or	w,hex
		mov	hex,w
		retp
:getvalidhex

:gh1		clr	localTemp2
		jnb	rs232Rxflag,$		; get a byte from the terminal
		clrb	rs232Rxflag
		mov	rs232byte,rs232Rxbyte
		call	uppercase		; uppercase it.

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

hextable	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'

;*****************************************************************************************
org    STRINGS_ORG	; This label defines where strings are kept in program space.  
			; all the following strings must be within the same half page of 
			; theprogram memory for sendstring to work,and they must be 
			; preceeded by this label.
;*****************************************************************************************

;*****************************************************************************************
;---------------------------------------String Data---------------------------------------
;*****************************************************************************************

;VP: RS232 Transmit

_welcome	dw	13,10,'Welcome to Tim's Digiligt Controller', 0
_error		dw	13,10,'ERR, I don't speak french', 0
_ok		dw	13,10,'OK', 0
_hex		dw	13,10,'0x', 0

	org	PAGE3_ORG
		jmp	$

;*****************************************************************************************
;---------------------------------------- Main Program -----------------------------------
;	 Program execution begins here on power-up or after a reset
;*****************************************************************************************

	org	MAINPROGRAM_ORG
     
_resetEntry

;*****************************************************************************************
;---------------------------- 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, 1 = TTL, 0 = CMOS
		mov	!ra,w		 
		mov     w,#RB_LVL            	;Setup RB CMOS or TTL levels, 1 = TTL, 0 = CMOS,0,1 = TTL, 2..7 = CMOS
		mov	!rb,w		
		mov     w,#RC_LVL            	;Setup RC CMOS or TTL levels, 1 = TTL, 0 = CMOS
		mov	!rc,w	
 IFDEF SX48_52
		mov     w,#RD_LVL            	;Setup RD CMOS or TTL levels, 1 = TTL, 0 = CMOS
		mov	!rd,w		
		mov     w,#RE_LVL            	;Setup RE CMOS or TTL levels, 1 = TTL, 0 = 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
:zeroRam	clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zeroRam

		_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
:zeroRam	sb	fsr.4			;are we on low half of bank?
						;If so, don't touch regs 0-7
		setb	fsr.3			; To clear from 08 - Global Registers
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zeroRam
ENDIF

;*****************************************************************************************
; Initialize program/VP registers
;*****************************************************************************************

		_bank 	rs232TxBank		;select rs232 bank
		mov 	w,#UARTDivide		;load Txdivide with UART baud rate
		mov 	rs232TXdivide,w

		_bank	PWM_Bank
		mov	w, #PWMDivide
		mov	PWM_Divide, w

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

RTCC_ON		=	%10000000		;Enables RTCC at address $01 (RTW hi)
						;*WREG at address $01 (RTW lo) by default
RTCC_ID		=	%01000000		;Disables RTCC edge interrupt (RTE_IE hi)
						;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT	=	%00100000		;Sets RTCC increment on RTCC pin transition (RTS hi)
						;*RTCC increment on internal instruction (RTS lo) is default
RTCC_FE		=	%00010000		;Sets RTCC to increment on falling edge (RTE_ES hi)
						;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON	=	%00000000		;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF	=	%00001000		;Assigns prescaler to WDT (PSA hi)
PS_000		=	%00000000		;RTCC = 1:2, WDT = 1:1
PS_001		=	%00000001		;RTCC = 1:4, WDT = 1:2
PS_010		=	%00000010		;RTCC = 1:8, WDT = 1:4
PS_011		=	%00000011		;RTCC = 1:16, WDT = 1:8
PS_100		=	%00000100		;RTCC = 1:32, WDT = 1:16
PS_101		=	%00000101		;RTCC = 1:64, WDT = 1:32
PS_110		=	%00000110		;RTCC = 1:128, WDT = 1:64
PS_111		=	%00000111		;RTCC = 1:256, WDT = 1:128

OPTIONSETUP	equ	RTCC_PS_OFF	        ;the default option setup for this program.
		mov	w,#OPTIONSETUP		;setup option register for RTCC interupts enabled
		mov	!option,w               ;and no prescaler.
		jmp	@mainLoop

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

;Send hello message
;Check for received byte
;	

		;Send welcome msg
		mov	w, #_welcome
		call	@sendstring

		;Wait for command
:loop		call	@getbyte
		cje	rs232Rxbyte, #'S', :set
		cje	rs232Rxbyte, #'G', :get
		jmp	:loop


:get		call	@getbyte
		mov	w, #hex
		call	@sendstring
		cje	rs232Rxbyte, #'0', :get_ch0
		cje	rs232Rxbyte, #'1', :get_ch1
		cje	rs232Rxbyte, #'2', :get_ch2
		cje	rs232Rxbyte, #'3', :get_ch3
		cje	rs232Rxbyte, #'4', :get_ch4
		cje	rs232Rxbyte, #'5', :get_ch5
:gotit		call	@sendhex
		jmp	:loop
		
:get_ch0	mov	w, PWM_Duty0
		jmp	:gotit
:get_ch1	mov	w, PWM_Duty1
		jmp	:gotit
:get_ch2	mov	w, PWM_Duty2
		jmp	:gotit
:get_ch3	mov	w, PWM_Duty3
		jmp	:gotit
:get_ch4	mov	w, PWM_Duty4
		jmp	:gotit
:get_ch5	mov	w, PWM_Duty5
		jmp	:gotit
		

:set		call	@getbyte
		cje	rs232Rxbyte, #'0', :set_ch0
		cje	rs232Rxbyte, #'1', :set_ch1
		cje	rs232Rxbyte, #'2', :set_ch2
		cje	rs232Rxbyte, #'3', :set_ch3
		cje	rs232Rxbyte, #'4', :set_ch4
		cje	rs232Rxbyte, #'5', :set_ch5
:setedit	mov	w, #_ok
		call	@sendstring
		jmp	:loop
		
:set_ch0	call	@getbyte
		mov	PWM_Duty0, w
		jmp	:setedit
:set_ch1	call	@getbyte
		mov	PWM_Duty1, w
		jmp	:setedit
:set_ch2	call	@getbyte
		mov	PWM_Duty2, w
		jmp	:setedit
:set_ch3	call	@getbyte
		mov	PWM_Duty3, w
		jmp	:setedit
:set_ch4	call	@getbyte
		mov	PWM_Duty4, w
		jmp	:setedit
:set_ch5	call	@getbyte
		mov	PWM_Duty5, w
		jmp	:setedit

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

;That's All Folks


file: /Techref/scenix/lib/io/dev/lamp/digilight.htm, 50KB, , updated: 2002/3/4 16:48, local time: 2024/11/24 18:30,
TOP NEW HELP FIND: 
52.15.185.147:LOG IN

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

<A HREF="http://massmind.org/techref/scenix/lib/io/dev/lamp/digilight.htm"> Digilight</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?

 

Welcome to massmind.org!

 

Welcome to massmind.org!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .