;;======================================================================;;
;;			PANsound					;;
;;======================================================================;;
;;									;;
;; Program:         PANsound -- DCC Crossing with Sound generator	;;
;; Code:            Paco Caņada						;;
;; Platform:        Microchip PIC16F628, 4 Mhz				;;
;; Date:            18.07.2004						;;
;; First release:   28.07.2004						;;
;; LastDate:        19.07.2005						;;
;;									;;
;;======================================================================;;
;
;
; Train crossing barriers with sound
; Minimal external components, uses internal oscilator at 4 MHz, 
; Uses TMR2 as PWM generator for sound, Vref module for volume control, 
; TXREG for Envelope timing, TMR0 for DCC signal decoding, 
; internal comparator for mixing sound and noise.
; RB0 is DCC input, RB3 (PWM) conected to RA1 (comparator), RA2 outputs to a buffer OPAMP
; RA4 to OPAMP output throught a resistor and to another OPAMP buffer for output to a buzzer.
; Servo output for moving barriers, two flasing lights.
;
; This program is distributed as is but WITHOUT ANY WARRANTY
; I hope you enjoy!!
;
; Revisions:
; 01.08.2006	Start of writting code
; 09.08.2006	Writed movement sequence
; 11.08.2006	Added pushbuttons to control moving sequence and save servo position
; 15.08.2006	Changed DCC decode routine


; ----- Definitions

#define		__VERNUM	D'1'
#define		__VERDAY	0x15
#define		__VERMONTH	0x08
#define		__VERYEAR	0x06


                list    p=16F628,r=hex

		errorlevel	-302		; suppress Bank warnings

	        INCLUDE "P16F628.INC"


                __FUSES _BODEN_ON & _CP_OFF & _PWRTE_OFF & _WDT_OFF & _LVP_OFF & _MCLRE_OFF  & _INTRC_OSC_NOCLKOUT


#define		RAMINI0		0x020		; 80 bytes
#define		RAMINI1		0x0A0		; 80 bytes
#define		RAMINI2		0x120		; 48 bytes
#define		RAMINT		0x070		; 16 bytes


; --- Macros

; --- Macros

SEL_BANK_0:	macro	
		bcf	STATUS,RP0		; macros for data access
		bcf	STATUS,RP1
		endm

SEL_BANK_1:	macro	
		bsf	STATUS,RP0
		bcf	STATUS,RP1
		endm

SEL_BANK_2:	macro	
		bcf	STATUS,RP0
		bsf	STATUS,RP1
		endm

SEL_BANK_3:	macro	
		bsf	STATUS,RP0
		bsf	STATUS,RP1
		endm

#define		SEL_IBANK_0	bcf	STATUS,IRP

#define		SEL_IBANK_1	bsf	STATUS,IRP

#define		DNOP		goto	$+1



SND_JUMP:	macro	jumpto		
		retlw	HIGH (jumpto)		; b'0 000 AAAA''AAAAAAAA'
		retlw	LOW  (jumpto)
		endm

SND_FLAGS:	macro	setflags		
		retlw	(0x10 | setflags)	; b'0 001 EPSN'
		endm


;#define	FL_NOISE	0x01		; Activate noise generator
#define		FL_SOUND	0x02		; Activate sound generator
;#define	FL_SPEED	0x04		; Speed dependent pitch
;#define	FL_END		0x08		; End of sound
#define		FL_NONE		0x00


SND_SAMPLES:	macro	samplerate
		retlw	0x20			; b'0 010 ----''SSSSSSSS'
		retlw	samplerate
		endm
		
SND_FREQ:	macro 	noisesound, freq
		retlw	(0x30 | noisesound)	; b'0 011 ---N''FFFFFFFF'
		retlw	freq
		endm

;#define	FQ_NOISE	0
#define		FQ_SOUND	1


; --- Constant values

FXTAL		equ	D'4000000'		; internal oscilator

RA_TRIS         equ     0xA7			; RA0,RA1,RA2,RA5,RA6,RA7: inputs
RB_TRIS         equ     0xC7			; RB0,RB1,RB2,RB6,RB7: inputs
RA_INI          equ     0x00			; all zero
RB_INI          equ     0x00			; all zero
OPTION_INI	equ	0x88			; Option register: no pull-up, falling GP2, no prescaler, wdt 1:1   ********
INTC_INI	equ	0xD0			; GIE, INTE enable, PEIE enable
PIE1_INI	equ	0x01			; interrupt TMR1

						; Port B
#define		DCCIN	PORTB,0			; DCC input pin
;		RXD	1
;		TXD	2
;		CCP1	3			; PWM output
#define		FL_LED	PORTB,4			; Lights
#define		BL_LED	PORTB,5			;
#define		KEY_UP	PORTB,6			; key up
#define		KEY_DWN	PORTB,7			; key down

#define		OUT1A	4			; Semaphore. Port B
#define		OUT1B	5	

						; Port A
;		AN0	0			; comparators
;		AN1	1
;		VREF	2
;		CMP1	3
;		CMP2	4
;		RA5	5			; free. only input
#define		SERVO	PORTA,6			; Servo output
;		RA7	7			; free


REACH_PULS	equ	0x04			; aditional pulses when reached position


; --- EEPROM Section

#define		EE_INI		0x00

E_CV513		equ	EE_INI+0x00		; CV513	Primary Adress low
E_CV515		equ	EE_INI+0x01		; CV515	
E_CV516		equ	EE_INI+0x02		; CV516	
E_CV517		equ	EE_INI+0x03		; CV517	
E_CV518		equ	EE_INI+0x04		; CV518	
E_CV7		equ	EE_INI+0x05		; Manufacturer Version
E_CV8		equ	EE_INI+0x06		; Manufacturer ID
E_CV521		equ	EE_INI+0x07		; CV521	Primary Adress high
E_CV541		equ	EE_INI+0x08		; config
E_CV545		equ	EE_INI+0x09		; CV545 Slope
E_CV546		equ	EE_INI+0x0A		; CV546	Spacing

EE_OUT		equ	EE_INI+0x7F		; saved outputs


; ----- Variables

; --- Internal RAM Section

; --- Top on all banks

INT_W		equ	RAMINT+0x00		; Interrupt variables.
INT_STAT	equ	RAMINT+0x01

SHIFT0		equ	RAMINT+0x02
DATA1		equ	RAMINT+0x03
DATA2		equ	RAMINT+0x04
DATA3		equ	RAMINT+0x05
DATA4		equ	RAMINT+0x06

PREAMBLE	equ	RAMINT+0x08
DCCSTATE	equ	RAMINT+0x09
DCCBYTE		equ	RAMINT+0x0A

EEDATA0		equ	RAMINT+0x0B		; EEPROM shadow variables
EEADR0		equ	RAMINT+0x0C

; --- Bank 0

;SHIFT0		equ	RAMINI0+0x02
;SHIFT1		equ	RAMINI0+0x03		; interrupt shift register
;SHIFT2		equ	RAMINI0+0x04	
;SHIFT3		equ	RAMINI0+0x05
;SHIFT4		equ	RAMINI0+0x06
;SHIFT5		equ	RAMINI0+0x07
;DATA00		equ	RAMINI0+0x08
;DATA0		equ	RAMINI0+0x09		; received packet
;DATA1		equ	RAMINI0+0x0A
;DATA2		equ	RAMINI0+0x0B
;DATA3		equ	RAMINI0+0x0C

PAGEREG		equ	RAMINI0+0x0D		; Page register

CV513		equ	RAMINI0+0x10		; Primary Adress low byte
CV515		equ	RAMINI0+0x11		; range
CV516		equ	RAMINI0+0x12		; speed
CV517		equ	RAMINI0+0x13		; max. bright
CV518		equ	RAMINI0+0x14		; start delay
CV519		equ	RAMINI0+0x15		; Manufacturer version
CV520		equ	RAMINI0+0x16		; Manufacturer ID
CV521		equ	RAMINI0+0x17		; Primary Adress high byte
CV541		equ	RAMINI0+0x18		; Config
CV545		equ	RAMINI0+0x19		; Slope
CV546		equ	RAMINI0+0x1A		; Spacing


CV516CNT	equ	RAMINI0+0x20		; speed
PULSE		equ	RAMINI0+0x21		; pulse duration
MOVING		equ	RAMINI0+0x22		; moving flags
POSITION	equ	RAMINI0+0x23		; current position flags
STATE		equ	RAMINI0+0x24		; current state
REACHED		equ	RAMINI0+0x25		; position reached
SPACE_H		equ	RAMINI0+0x26		; spacing duration
SPACE_L		equ	RAMINI0+0x27

DEBOUNCE1	equ	RAMINI0+0x2A		; key up
DEBOUNCE2	equ	RAMINI0+0x2B		; key down
DEBOUNCE3	equ	RAMINI0+0x2C		; both keys
FLASH		equ	RAMINI0+0x2D		; flash counter

FLAGS		equ	RAMINI0+0x30
SND_FLG		equ	RAMINI0+0x31
COUNT		equ	RAMINI0+0x32
TEMP		equ	RAMINI0+0x33

DIR_UP		equ	RAMINI0+0x38		; bright / fade
LIGHT_UP	equ	RAMINI0+0x39		; light up pending
TIMER		equ	RAMINI0+0x3A		; timer 1:16
PWM1A		equ	RAMINI0+0x3B		; current bright
PWM1B		equ	RAMINI0+0x3C
SPEED1A		equ	RAMINI0+0x3D		; final bright
SPEED1B		equ	RAMINI0+0x3E
SLOPE_CNT	equ	RAMINI0+0x3F		; slope timer

EnvValue	equ	RAMINI0+0x40		; Envelope current volume
EnvPage		equ	RAMINI0+0x41		; Envelope page table
EnvStep		equ	RAMINI0+0x42		; Envelope current step
EnvSamples	equ	RAMINI0+0x43		; Envelope times per sample
SampleCnt	equ	RAMINI0+0x44		; samples counter
CurrentSound	equ	RAMINI0+0x45		; Current playing sound

MOVE_PHASE	equ	RAMINI0+0x48		; Phase of movement
START_DLY	equ	RAMINI0+0x49		; Delay before servo down
START_PRE	equ	RAMINI0+0x4A		; Delay prescaler

; --- Flags

#define		NEW_PACKET	FLAGS,0		; New 3 byte packet received
#define		NOCV		FLAGS,1		; No CV finded
#define		RDONLY		FLAGS,2		; CV read only
#define		DCC4BYTE	FLAGS,3		; DCC command 4 bytes
#define		KEY_PROG	FLAGS,4		; Key pressed for programming
#define		DO_PULSE	FLAGS,5		; do pulse, TMR1 end
#define		PROG_2X		FLAGS,6		; 2x prog
#define		RESET_FLG	FLAGS,7		; reset packet

KEYPRG_MSK	equ	0x10

;#define	NOISE_ON	SND_FLG,0	; noise on
#define		SOUND_ON	SND_FLG,1	; sound on
;#define	VAR_PITCH	SND_FLG,2	; Variable pitch
;#define	SOUND_END	SND_FLG,3	; end of sound samples 



; --------------- Program Section --------------------------------------


		org	0x000

PowerUp:
		clrf	STATUS			; Bank 0 default
		clrf	INTCON			; Disable all interrupts
		clrf	PCLATH			; tables on page 0
		goto	INIT

; ----------------------------------------------------------------------

		org	0x004

Interrupt:
		movwf	INT_W			; save context registers		;1
		swapf	STATUS,w							;2
		movwf	INT_STAT							;3
		clrf	STATUS			; interrupt uses bank 0			;4

		btfss	PIR1,TMR1IF		; end of servo pulse?			;+1
		goto	Int_DCC								;+2,3
		bcf	SERVO			; yes, clear pulse			;+3
		bcf	PIR1,TMR1IF							;+4
		bsf	DO_PULSE							;+5

Int_DCC:
		btfss	INTCON,INTF		; RB0 interrupt?			;+4,6
		goto	EndInt								;+5,6, +7,8

		btfss	DCCIN								;5
		goto	Int_Low_Half							;6,7

Int_High_Half:
		movf	DCCSTATE,w								; 8
		addwf	PCL,f								; 9

		goto	Preamble							; 10,11
		goto	WaitLow
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadBit
		goto	ReadLastBit
		goto	EndByte1
		goto	EndByte2
		goto	EndByte3
		goto	EndByte4

Int_Low_Half:
		movlw	d'256' - d'80'		; 77us: between 64us (one) and 90us (zero);8
		movwf	TMR0								;9
		bcf	INTCON,T0IF		; clear overflow flag for counting	;10
		bcf	INTCON,INTF							;11
		bsf	STATUS,RP0							;12
		bsf	OPTION_REG,INTEDG	; next interrupt on rising edge GP2	;13
		swapf	INT_STAT,w		; restore context registers		;14
		movwf	STATUS								;15
		swapf	INT_W,f								;16
		swapf	INT_W,w								;17
		retfie									;18,19

EndHighHalf:
		bcf	INTCON,INTF							;21
		bsf	STATUS,RP0							;22
		bcf	OPTION_REG,INTEDG	; next interrupt on falling edge GP2	;23
EndInt:
		swapf	INT_STAT,w		; restore context registers		;24
		movwf	STATUS								;25
		swapf	INT_W,f								;26
		swapf	INT_W,w								;27
		retfie									;28,29


Preamble:
		btfss	NEW_PACKET		; wait until last decoded		;12
		incf	PREAMBLE,f		;					;13
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;14
		clrf	PREAMBLE		;					;15
		movlw	0xF6			; 10 preamble bits?			;16
		addwf	PREAMBLE,w		;					;17
		btfsc	STATUS,C		;					;18
		incf	DCCSTATE,f		; yes, next state			;19
		goto	EndHighHalf		;					;20,21
		

WaitLow:
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;12
		incf	DCCSTATE,f		; then state				;13
		clrf	DCCBYTE			;					;14
		clrf	PREAMBLE		;					;15
		clrf	DATA4			;					;16
		goto	EndHighHalf		;					;17,18


ReadBit:
		bsf	STATUS,C							;12
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;13
		bcf	STATUS,C							;14
		rlf	SHIFT0,f		; receiver shift register		;15
		incf	DCCSTATE,f		;					;16
		goto	EndHighHalf		;					;17,18
			
ReadLastBit:
		bsf	STATUS,C							;12
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;13
		bcf	STATUS,C							;14
		rlf	SHIFT0,f		; receiver shift register		;15
		incf	DCCBYTE,w							;16
		addwf	DCCSTATE,f							;17
		goto	EndHighHalf		;					;18,19

EndByte1:
		movlw	0x00			;					;12
		btfsc	INTCON,T0IF		; End bit=1, invalid packet		;13
		movlw	0x02			;					;14
		movwf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA1			;					;17
		incf	DCCBYTE,f		;					;18
		goto	EndHighHalf		;					;19,20

EndByte2:
		movlw	0x00			;					;12
		btfsc	INTCON,T0IF		; End bit=1, invalid packet		;13
		movlw	0x02			;					;14
		movwf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA2			;					;17
		incf	DCCBYTE,f		;					;18
		goto	EndHighHalf		;					;19,20


EndByte3:
		btfss	INTCON,T0IF		; End bit=1, end of packet		;12
		goto	EndByte3x		;					;13,14
		movlw	0x02			;					;14
		movwf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA3			;					;17
		incf	DCCBYTE,f		;					;18
		bsf	DCC4BYTE		;					;19
		goto	EndHighHalf		;					;20,21
EndByte3x:
		clrf	DCCSTATE		;					;15
		movf	SHIFT0,w		;					;16
		movwf	DATA3			;					;17
		bsf	NEW_PACKET		;					;18
		bcf	DCC4BYTE		;					;19
		goto	EndHighHalf		;					;20,21

EndByte4:
		clrf	DCCSTATE		;					;12
		btfsc	INTCON,T0IF		; End bit=1, end of packet		;13
		goto	EndInt			; End bit=0, invalid packet		;14,15
		movf	SHIFT0,w		;					;15
		movwf	DATA4			;					;16
		bsf	NEW_PACKET		;					;17
		goto	EndHighHalf		;					;18,19


; ----- Tables on first 256 bytes --------------------------------------

BitPos:
		clrf	PCLATH
		addwf PCL,f
		retlw	b'00000001'		; 0	
		retlw	b'00000010'		; 1	
		retlw	b'00000100'		; 2	
		retlw	b'00001000'		; 3	
		retlw	b'00010000'		; 4	
		retlw	b'00100000'		; 5	
		retlw	b'01000000'		; 6	
		retlw	b'10000000'		; 7	


SoundDecode:
		clrf	PCLATH
		swapf	EnvValue,w		; macro decoding
		andlw	0x03
		addwf	PCL,f

		goto	SoundJump
		goto	SoundFlags
		goto	SetSamples
		goto	SetFreq

PhaseMove:
		clrf	PCLATH
		movf	MOVE_PHASE,w		; movement squence
		andlw	0x03
		addwf	PCL,f

		goto	MoveStop
		goto	SndLightOn
		goto	ServoDown
		goto	ServoUp


;------ Sounds --------------------------------------------------------------

GetEnv:
		andlw	0x03
		addwf	PCL,f
		retlw	HIGH (Silence)		; 0
		retlw	LOW  (Silence)
		retlw	HIGH (SND_Crossing)	; 1
		retlw	LOW  (SND_Crossing)

Silence:
		SND_FLAGS	FL_NONE		; no sound
		SND_SAMPLES	d'1'		; 1 times sample
		retlw	0xE0			; off
		SND_JUMP	Silence

SND_Crossing:					; |\ envelope
		SND_FLAGS	FL_SOUND	; Sound on
		SND_FREQ	FQ_SOUND, d'126'; Sound base freq 62500 / 126 = 496 Hz ( C - do )
		SND_SAMPLES	d'14'		; 20ms time sample
SND_CrossLoop:
		retlw		0xE8
		retlw		0XEF
		retlw		0XEE
		retlw		0XED
		retlw		0XEC
		retlw		0XED
		retlw		0XEC
		retlw		0XEA
		retlw		0XE8
		retlw		0XE7
		retlw		0XE6
		retlw		0XE5
		retlw		0XE5
		retlw		0XE4
		retlw		0XE4
		retlw		0XE3
		retlw		0XE3
		retlw		0XE2
		retlw		0XE2
		retlw		0XE2
		retlw		0XE1
		retlw		0XE1
		retlw		0XE1
;		retlw		0XE0
		SND_JUMP	SND_CrossLoop


;		SND_FLAGS	FL_SOUND	; Sound on
;		SND_FREQ	FQ_SOUND, d'82'	; Sound base freq 62500 / 82 = 762 Hz
;		SND_SAMPLES	d'14'		; 14ms time sample
; ----------------------------------------------------------------------

Accessory:
		btfsc	KEY_PROG		; pressed switch?
		goto	AccessProg		; yes, program new address

		movf	CV521,w			; check high address bits
		iorlw	0x80			;'1AAAxxxx'
		xorwf	DATA2,w
		andlw	0xF0
		btfss	STATUS,Z
		goto	ExitDecode
		movf	DATA1,w			; check low address bits
		andlw	0x3F
		xorwf	CV513,w
		btfss	STATUS,Z
		goto	ExitDecode

		clrf	PCLATH
		movf	DATA2,w			; activate outputs
		andlw	0x0F			; 'xxxxCDDD'
		addwf	PCL,f

		goto	F1A_Clear		; 
		goto	F1B_Clear
		goto	F2A_Clear
		goto	F2B_Clear
		goto	F3A_Clear		; not used
		goto	F3B_Clear		; not used
		goto	F4A_Clear		; not used
		goto	F4B_Clear		; not used
		goto	F1_Off			; All off
		goto	F1_Set			; All on
		goto	F2_Up			; Servo up   *
		goto	F2_Down			; Servo down *
		goto	F3_Off			; Light off  *
		goto	F3_Set			; Light on   *
		goto	F4_Off			; Sound off  *
		goto	F4_On			; Sound on   *


AccessProg:
		btfss	DATA2,7			; '10AAAAAA'1AAAxPxx'? accesory operation
		goto	ExitDecode
		bcf	FL_LED			; clear output flashing
		bcf	BL_LED
		movf	DATA1,w			; get 6 low bits
		andlw	0x3F
		movwf	EEDATA0
		movwf	CV513
		movlw	E_CV513
		call	SetParm

		swapf	DATA2,w			; get 3 high bits
		andlw	0x07
		movwf	CV521
		swapf	CV521,f
		xorlw	0x07			; complement bits
		movwf	EEDATA0
		movlw	E_CV521
		call	SetParm

		bcf	KEY_PROG
		goto	Accessory


; ----------------------------------------------------------------------

; ----- Low Level Hardware access routines


; ----- Initialization

INIT:
		movlw   RA_INI			; Set ports
		movwf   PORTA                         
		movlw   RB_INI
		movwf   PORTB
		SEL_BANK_1
		clrf	PIE1			; no peripheral interrupts
		movlw	RB_TRIS
		movwf   TRISB
		movlw   RA_TRIS         	; Set port A I/O configuration
		movwf   TRISA
		movlw   OPTION_INI      	; PORTB pull-ups disabled, Timer configuration
		movwf   OPTION_REG
		movlw	0x0B			; internal oscillator to 4MHz
		movwf	PCON
		movlw	0xCF			; VRR on, Vref on RA2, high volume
		movwf	VRCON

		movlw	d'25'			; envelope 1ms (10000 baud)
		movwf	SPBRG
		movlw	0x24			; TXEN, BRGH=1;   Baud = Fosc / (16 * BRGH +1)
		movwf	TXSTA

		SEL_BANK_0
		movlw	0x06			; Two comparators, two outputs
		movwf	CMCON                          

		movlw	0x80			; Enable serial port, pins can't be used
		movwf	RCSTA
		movwf	TXREG			; first send, sets TRMT at the end

		movlw	0x01			; Timer 1. Run, set 1:1 Prescaler
		movwf	T1CON
		movlw	0x06			; Timer 2. Run with 1:16 Prescaler
		movwf	T2CON

		movlw	0x20			; clear RAM
		movwf	FSR
ClearRAM:
		clrf	INDF
		incf	FSR,f
		movlw	0x70
		xorwf	FSR,w
		btfss	STATUS,Z
		goto	ClearRAM

		movlw	PIE1
		movwf	FSR
		movlw	PIE1_INI		; Set interrupt (none)
		movwf	INDF
		movlw	INTC_INI		; Set interrupts RB0
		movwf	INTCON

		movlw	d'150'			; init servos default
		movwf	PULSE
;		clrf	MOVING
;		clrf	POSITION
;		clrf	STATE

		clrf	PAGEREG			; page register default

		call	Clear			; clear sound variables
		call	LoadCV			; Load CV in SFR

		call	LoadOutputs		; set servo to last position

		movlw	d'2'			; init slope counter
		movwf	SLOPE_CNT
		clrf	MOVE_PHASE
		movlw	0xFF
		movwf	DEBOUNCE1
		movwf	DEBOUNCE2

; ----------------------------------------------------------------------


MainLoop:
		btfsc	NEW_PACKET		; new packet?
		call	Decode			; yes, decode


		btfsc	DO_PULSE		; end of servo pulse?
		call	DoServo			; yes, next pulse

		bcf	STATUS,C		; check keys
		btfsc	KEY_UP
		bsf	STATUS,C
		rlf	DEBOUNCE1,f

		bcf	STATUS,C
		btfsc	KEY_DWN
		bsf	STATUS,C
		rlf	DEBOUNCE2,f

		bcf	STATUS,C
		btfss	KEY_UP
		btfsc	KEY_DWN
		bsf	STATUS,C
		rlf	DEBOUNCE3,f


		movlw	0xC0			; check both key pressed
		xorwf	DEBOUNCE3,w
		movlw	KEYPRG_MSK
		btfsc	STATUS,Z
		xorwf	FLAGS,f

		btfsc	KEY_PROG
		goto	DoFlashProg

		movlw	0x03			; check up key released
		xorwf	DEBOUNCE1,w
		btfsc	STATUS,Z
		call	Key_Up

		movlw	0x03			; check down key released
		xorwf	DEBOUNCE2,w
		btfsc	STATUS,Z
		call	Key_Down


MainSound:
		bsf	STATUS,RP0		; bank1
		btfss	TXSTA,TRMT		; TSR empty (counter overflow)
		goto	MainSoundEnd
		bcf	STATUS,RP0		; bank0
		movwf	TXREG			; reload envelope timer

		call	DoLight
		call	PhaseMove

PlaySound:
		decfsz	SampleCnt,f		; waiting samples
		goto	MainLoop
		movf	EnvSamples,w
		movwf	SampleCnt

SoundGet:
		call	GetEnvValue		; get envelope value
		movwf	EnvValue		; and save it
		incf	EnvStep,f		; next value

		btfss	EnvValue,7		; sample or macro?
		goto	SoundDecode
PutVol:
		bsf	STATUS,RP0		; bank 1
		movwf	VRCON			; 
MainSoundEnd:
		bcf	STATUS,RP0		; bank 0
		goto	MainLoop

GetEnvValue:
		movf	EnvPage,w		; jump to EnvPage:EnvStep
		movwf	PCLATH			;
		movf	EnvStep,w		;
		movwf	PCL			;

SoundJump:
		call	GetEnvValue		; get new position
		movwf	EnvStep
		movf	EnvValue,w
		movwf	EnvPage
		goto	SoundGet

SoundFlags:
		movf	EnvValue,w
		andlw	0x0F
		movwf	SND_FLG

		movlw	0x0C			; 50% duty cycle
		btfss	SOUND_ON
		movlw	0x00			; stop
		movwf	CCP1CON			; sound	
		goto	SoundGet

SetSamples:
		call	GetEnvValue		; get value
		incf	EnvStep,f		; next value
		movwf	EnvSamples
		movwf	SampleCnt
		goto	SoundGet

SetFreq:
		call	GetEnvValue
		incf	EnvStep,f		; next value
		movwf	TEMP
		bsf	STATUS,RP0		; bank 1
		movwf	PR2
		bcf	STATUS,RP0		; bank 0
		bcf	STATUS,C
		rrf	TEMP,w			; 50% duty cycle
		movwf	CCPR1L
		goto	SoundGet


DoFlashProg:
		btfss	FLASH,3			; 0,64 sec
		goto	MainLoop

		bcf	FLASH,3

		movlw	(1<<OUT1A | 1<<OUT1B)	; blink LEDs
		xorwf	PORTB,f

		goto	MainLoop


; ----------------------------------------------------------------------


DoServo:
		bcf	DO_PULSE
		btfsc	INTCON,INTE		; disabled interrupts?
		goto	DoServoJump
		clrf	DCCSTATE		; yes, clear for decoding
		bsf	INTCON,INTE		; re-enable interrupts
DoServoJump:
		btfss	STATE,0
		goto	PulseServo1
		goto	Spacing


PulseServo1:
		movf	CV546,w			; init spacing value
		sublw	0x00
		movwf	SPACE_H
		clrf	SPACE_L

		movf	PULSE,w			; 200: 2ms, 100: 1ms
		call	PulseServo

		btfsc	MOVING,0
		bcf	INTCON,INTE		; disable DCC interrupts for time accuracy

		btfsc	MOVING,0
		bsf	SERVO

		bsf	T1CON,TMR1ON		; run timer 1
		incf	STATE,f
;		goto	EndSpacing
EndSpacing:
		incf	FLASH,f			; timer for flashing
		return


PulseServo:
		bcf	T1CON,TMR1ON		; stop timer 1
		clrf	TMR1H			; do PULSE x10
		movwf	TEMP
		movwf	TMR1L
		rlf	TMR1L,f
		rlf	TMR1H,f
		rlf	TMR1L,f			; x4
		rlf	TMR1H,f
		rlf	TMR1L,f			; x8
		rlf	TMR1H,f
		movlw	0xF8
		andwf	TMR1L,f
		movf	TEMP,w			; x8 + x1 = x9
		addwf	TMR1L,f
		btfsc	STATUS,C
		incf	TMR1H,f
		addwf	TMR1L,f			; x9 + x1 = x10
		btfsc	STATUS,C
		incf	TMR1H,f

		movf	TMR1L,w			; correct spacing
		addwf	SPACE_L,f
		btfsc	STATUS,C
		incf	SPACE_H,f
		movf	TMR1H,w
		addwf	SPACE_H,f

		comf	TMR1L,f			; negative for incrementing
		comf	TMR1H,f
		movlw	0x01
		addwf	TMR1L,f
		btfsc	STATUS,C
		incf	TMR1H,f
;		bcf	PIR1,TMR1IF
		return


Spacing:
		bcf	T1CON,TMR1ON
		movf	SPACE_H,w
		movwf	TMR1H
		movf	SPACE_L,w
		movwf	TMR1L
;		bcf	PIR1,TMR1IF
		bsf	T1CON,TMR1ON		; run timer 1
		clrf	STATE
EndServo1:
		btfss	MOVING,0		; moving servo?
		return				; no

		btfss	POSITION,0		; yes	*************
		goto	EndServo1Nxt
		
		btfsc	REACHED,0
		goto	EndServo1WW1

		decfsz	CV516CNT,f		; speed
		goto	EndServo1W1
		movf	CV516,w
		movwf	CV516CNT
		decf	PULSE,f
EndServo1W1:
		comf	CV515,w			; range (negative)
		addlw	0x01
		addlw	d'150'
		xorwf	PULSE,w
		btfss	STATUS,Z
		return
		movlw	REACH_PULS
		movwf	CV516CNT
		bsf	REACHED,0
		return
EndServo1WW1:
		decfsz	CV516CNT,f
		return
		bcf	POSITION,0		; **********************
EndServoSave:
		bcf	REACHED,0
		bcf	MOVING,0

		movf	POSITION,w		; save new output state
;		iorwf	DIR_UP,w
;		iorwf	LIGHT_UP,w
;		movf	CurrentSound,f
;		btfss	STATUS,Z
;		iorlw	0x80
		xorlw	0x01	
		movwf	EEDATA0
		movlw	EE_OUT
		goto	SetParm

EndServo1Nxt:
		btfsc	REACHED,0
		goto	EndServo1WW2

		decfsz	CV516CNT,f		; speed
		goto	EndServo1W2
		movf	CV516,w
		movwf	CV516CNT
		incf	PULSE,f
EndServo1W2:
		movlw	d'150'			; range
		addwf	CV515,w
		xorwf	PULSE,w
		btfss	STATUS,Z
		return
		movlw	REACH_PULS
		movwf	CV516CNT
		bsf	REACHED,0
		return
EndServo1WW2:
		decfsz	CV516CNT,f
		return
		bsf	POSITION,0		; *****************
		goto	EndServoSave




; ----------------------------------------------------------------------


DoLight:
		movlw	0x10
		addwf	TIMER,f

		decfsz	SLOPE_CNT,f		; slope
		goto	SpeedOn

		movf	CV545,w			; reload slope CV
		movwf	SLOPE_CNT

DoLight1:
		movf	PWM1A,w			; all off?
		iorwf	PWM1B,w
		btfss	STATUS,Z
		goto	DoPWM1A
DoLight1A:
		btfss	LIGHT_UP,OUT1A
		goto	DoLight1B
		bcf	LIGHT_UP,OUT1A
		bsf	DIR_UP,OUT1A
		movf	CV517,w
		movwf	SPEED1A
DoLight1B:		
		btfss	LIGHT_UP,OUT1B
		goto	DoPWM1A
		bcf	LIGHT_UP,OUT1B
		bsf	DIR_UP,OUT1B
		movf	CV517,w
		movwf	SPEED1B


DoPWM1A:
		movf	PWM1A,w			; calc PWM acc/dec
		xorwf	SPEED1A,w
		btfsc	STATUS,Z
		goto	DoPWM1Amax		; max. light, invert
		movlw	0x01
		btfss	DIR_UP,OUT1A
		movlw	0xFF
		addwf	PWM1A,f
		goto	DoPWM1B
DoPWM1Amax:
		movf	CV517,w
		xorwf	SPEED1A,w		; max?
		btfss	STATUS,Z
		goto	DoPWM1B
		clrf	SPEED1A			; turn off
		bcf	DIR_UP,OUT1A
		bsf	LIGHT_UP,OUT1B		; next light other

DoPWM1B:
		movf	PWM1B,w
		xorwf	SPEED1B,w
		btfsc	STATUS,Z
		goto	DoPWM1Bmax
		movlw	0x01
		btfss	DIR_UP,OUT1B
		movlw	0xFF
		addwf	PWM1B,f
		goto	SpeedOn
DoPWM1Bmax:
		movf	CV517,w
		xorwf	SPEED1B,w		; max?
		btfss	STATUS,Z
		goto	SpeedOn
		clrf	SPEED1B			; turn off
		bcf	DIR_UP,OUT1B
		bsf	LIGHT_UP,OUT1A		; next light other

SpeedOn:
		movf	TIMER,w
		addwf	PWM1A,w			; do PWM of outputs
		btfsc	STATUS,C
		bsf	FL_LED
		btfss	STATUS,C
		bcf	FL_LED

		movf	TIMER,w
		addwf	PWM1B,w
		btfsc	STATUS,C
		bsf	BL_LED
		btfss	STATUS,C
		bcf	BL_LED

		return


; ----------------------------------------------------------------------

F1A_Clear:
F1B_Clear:
F2A_Clear:
F2B_Clear:
F3A_Clear:
F3B_Clear:
F4A_Clear:
F4B_Clear:
		goto	ExitFunction

F1_Off:
		btfss	POSITION,0		; servo move up
		bsf	MOVING,0
		movf	CV516,w
		movwf	CV516CNT
		movlw	0x03
		movwf	MOVE_PHASE
		goto	F4_Off			; turn off sound


F1_Set:
		movlw	0x01			; init movement
		movwf	MOVE_PHASE
		clrf	SPEED1B			; turn off one & on other
		bcf	DIR_UP,OUT1B
		bcf	LIGHT_UP,OUT1B
		btfss	DIR_UP,OUT1A		; if lights, nothing to do
		bsf	LIGHT_UP,OUT1A		; next light this
		goto	F4_On			; turn on sound


F2_Up:
		btfss	POSITION,0		; servo move up
		bsf	MOVING,0
		movf	CV516,w			; speed
		movwf	CV516CNT
		goto	ExitFunction

F2_Down:
		btfsc	POSITION,0		; servo move down
		bsf	MOVING,0
		movf	CV516,w			; speed
		movwf	CV516CNT
		goto	ExitFunction


F3_Off:						; Light off
		clrf	SPEED1A			; turn off
		bcf	DIR_UP,OUT1A
		bcf	LIGHT_UP,OUT1A
		clrf	SPEED1B			; turn off
		bcf	DIR_UP,OUT1B
		bcf	LIGHT_UP,OUT1B
		goto	ExitFunction

F3_Set:						; Light on (only one)
		clrf	SPEED1B			; turn off
		bcf	DIR_UP,OUT1B
		bcf	LIGHT_UP,OUT1B
		btfss	DIR_UP,OUT1A		; if lights, nothing to do
		bsf	LIGHT_UP,OUT1A		; next light this
		goto	ExitFunction

F4_Off:
		movlw	0x00			; silence
		call	EnvelopeSelect
		goto	ExitFunction
F4_On:
		movf	CurrentSound,w		; if sound started, nothing to do
		btfss	STATUS,Z
		goto	ExitFunction
		movlw	0x01
		call	EnvelopeSelect
		goto	ExitFunction
		
EnvelopeSelect:
		movwf	CurrentSound		; select new envelope
		movwf	TEMP
		movlw	HIGH (GetEnv)		; load envelope 
		movwf	PCLATH
		bcf	STATUS,C		; w * 2
		rlf	TEMP,f
		movf	TEMP,w
		call	GetEnv
		movwf	EnvPage			; PCLATH
		incf	TEMP,w	
		call	GetEnv
		movwf	EnvStep			; PCL

		movlw	0x01			; SoundInitSample
		movwf	SampleCnt
		movwf	EnvSamples
		return


ExitFunction:
		bcf	RESET_FLG
		bsf	INTCON,INTE		; enable DCC interrupts
		return

; ----------------------------------------------------------------------

MoveStop:
		movf	CV518,w			; no movement
		movwf	START_DLY
		clrf	START_PRE
		return

SndLightOn:
		decfsz	START_PRE,f		; lights & sound is on
		return
		decfsz	START_DLY,f		; wait start delay
		return
		btfsc	POSITION,0		; servo move down
		bsf	MOVING,0
		movf	CV516,w			; speed
		movwf	CV516CNT
		movlw	0x02
		movwf	MOVE_PHASE
		return

ServoDown:
		btfsc	MOVING,0		; end of movement?
		return
		movlw	0x00
		call	EnvelopeSelect		; yes, sound off
		clrf	MOVE_PHASE
		return

ServoUp:
		btfsc	MOVING,0		; end of movement?
		return
		clrf	SPEED1A			; turn off lights
		bcf	DIR_UP,OUT1A
		bcf	LIGHT_UP,OUT1A
		clrf	SPEED1B			
		bcf	DIR_UP,OUT1B
		bcf	LIGHT_UP,OUT1B
		movlw	0x00
		call	EnvelopeSelect		; sound off to be sure
		clrf	MOVE_PHASE
		return



Key_Up:
		btfss	POSITION,0		; servo move up
		bsf	MOVING,0
		movf	CV516,w
		movwf	CV516CNT
		movlw	0x03
		movwf	MOVE_PHASE

;		movlw	0x00
;		call	EnvelopeSelect		; sound off to be sure

		return


Key_Down:
		movlw	0x01			; init movement
		movwf	MOVE_PHASE
		movf	CV518,w			; start delay movement
		movwf	START_DLY
		clrf	START_PRE

		clrf	SPEED1B			; turn off one & on other
		bcf	DIR_UP,OUT1B
		bcf	LIGHT_UP,OUT1B
		btfss	DIR_UP,OUT1A		; if lights, nothing to do
		bsf	LIGHT_UP,OUT1A		; next light this

		movlw	0x01
		movf	CurrentSound,f		; if sound started, nothing to do
		btfsc	STATUS,Z
		call	EnvelopeSelect
		return


; ----------------------------------------------------------------------

Decode:
		bcf	NEW_PACKET		; prepare for next packet
		bcf	INTCON,INTE		; disable interrupts for more speed

		movf	DATA1,w			; exclusive or check
		xorwf	DATA2,w
		xorwf	DATA3,w
		xorwf	DATA4,w

		btfss	STATUS,Z		; valid packet?
		goto	ExitDecode		; no, return

; 'AAAAAAAA''DDDDDDDD''EEEEEEEE'		; 3 byte packet
;   DATA1     DATA2     DATA3
; '0111CCAA''AAAAAAAA''DDDDDDDD''EEEEEEEE'	; 4 byte packet. Direct mode
;    DATA1    DATA2     DATA3     DATA4

		movf	DATA1,w			; address = '00000000' ?
		btfsc	STATUS,Z
		goto	Broadcast
;		movf	DATA1,w
		andlw	0xF0
		xorlw	0x70			; '0111xxxx'?
		btfsc	STATUS,Z
		goto	CheckSM			; yes, may be service mode

		movf	DATA1,w
		andlw	0xC0
		xorlw	0x80			;'10xxxxxx'? accessory operation
		btfsc	STATUS,Z
		goto	Accessory

		incfsz	DATA1,w			;'11111111' idle packet
		goto	ExitDecode
		bsf	INTCON,INTE		; yes, don't clear reset flag
		return


; -----------------------------------------------------------------------

Broadcast:
		movf	DATA2,w			; reset packet?
		btfss	STATUS,Z
		goto	ExitDecode
		bcf	PROG_2X
		bsf	RESET_FLG

Clear:						; reset decoder
		bcf	FL_LED			; light off
		bcf	BL_LED

		clrf	SND_FLG			; noise off
		clrf	CCP1CON			; sound off
		clrf	CurrentSound		; Silence
		movlw	HIGH(Silence)		; default envelope
		movwf	EnvPage
		movlw	LOW (Silence)
		movwf	EnvStep
		movlw	0x01			; start sampling
		movwf	SampleCnt
		movwf	EnvSamples

;		bcf	SERVO			; servo disable

		bsf	INTCON,INTE		; enable interrupts
		return


ExitDecode:
		bcf	RESET_FLG
		bsf	INTCON,INTE		; enable interrupts
		return


;************* SM Mode *******************************************
; Service Mode

CheckSM:
		btfss	RESET_FLG		; check for SM, reset packet has to come first
		goto	ServModeError
		btfss	PROG_2X
		goto	SetSM_Flag

		btfsc	DCC4BYTE		; 3 or 4 byte packet?
		goto	ChkProg4

; 0111CRRR DDDDDDDD EEEEEEEE

		bcf	PROG_2X
		movf	DATA2,w				; save data
		movwf	EEDATA0

		movf	DATA1,w				; 3 byte programming
		andlw	b'11110111'
		xorlw	b'01110101'			; Reg6
		btfsc	STATUS,Z		
		goto	REG6
		xorlw	(b'01110101')^(b'01110100')	; Reg5
		btfsc	STATUS,Z		
		goto	REG5
		xorlw	(b'01110100')^(b'01110110')	; reg7
		btfsc	STATUS,Z
		goto	REG7	
		xorlw	(b'01110110')^(b'01110111')	; reg8
		btfsc	STATUS,Z
		goto	REG8	

		movf	DATA1,w
		andlw	0x03
		addwf	PAGEREG,w
		call	FindCV
		btfsc	NOCV
		goto	ExitProg
ProgReg:
		btfss	DATA1,3
		goto	EEVERI
		goto	EEPROG

REG5:
		movlw	E_CV541			; CV541 configuration
		goto	ProgReg

REG6:
		btfss	DATA1,3			; read or write
		goto	REG6RD
		decf	DATA2,f			; Page register
		rlf	DATA2,f
		rlf	DATA2,w
		andlw	b'11111100'		; page 1 and 129 are the same. CV1 & CV513
		movwf	PAGEREG
		goto	ExitProg
REG6RD:
		decf	DATA2,f			; read page register
		rlf	DATA2,f
		rlf	DATA2,w
		andlw	b'11111100'		; page 1 and 129 are the same. CV1 & CV513
		xorwf	PAGEREG,w
		goto	EEVERIP

REG7:
		movlw	E_CV7			; only read
		btfss	DATA1,3
		goto	EEVERI
		goto	ExitProg

REG8:
		movlw	E_CV8			; only read
		btfss	DATA1,3
		goto	EEVERI
		goto	CheckResetCV		; if CV8 = 33 reset CV

	
EEPROG:
		btfsc	RDONLY
		goto	CheckCV8
		call	SetParm			; program EEPROM
		call	AckPulse		; do ACK
		bcf	PROG_2X
		bcf	NEW_PACKET
		call	LoadCV
		goto	ExitProg


EEVERI:
		call	EE_Read			; check data
		xorwf	DATA2,w
EEVERIP:
		btfss	STATUS,Z
		goto	ExitProg
DoAck:
		call	AckPulse		; equal, do ACK
		bcf	PROG_2X
;		bcf	NEW_PACKET
		goto	ExitProg


SetSM_Flag:
		bsf	PROG_2X
		goto	ExitProg

ServModeError:
		bcf	RESET_FLG
		bcf	PROG_2X
		goto	ExitProg

CheckCV8:
		xorlw	E_CV8			; CV8?
		btfss	STATUS,Z
		goto	ExitProg
CheckResetCV:
		movlw	d'33'			; CV8 = 33 -> reset CV
		xorwf	DATA2,w
		btfss	STATUS,Z
		goto	ExitProg
		call	ResetCV			; program CV defaults
		call	AckPulse		; do ACK
		bcf	PROG_2X
		bcf	NEW_PACKET
		call	LoadCV
;		goto	ExitProg

ExitProg:
		bsf	INTCON,INTE		; enable interrupts
		return
		
; -----------------------------------------------------------------------------------

AckPulse:
		movlw	0xCF			; max. volume
		bsf	STATUS,RP0		; bank 1
		movwf	VRCON
		bcf	STATUS,RP0		; bank 0
		movlw	0x0C			; 50% duty cycle
		movwf	CCP1CON			; start PWM
		bsf	FL_LED			; set FL/FR and motor on
		bsf	BL_LED

		movlw	d'6'			; 6ms pulse
		movwf	TEMP
		movlw	0x00
AckNext:
		addlw	0xFF			;1
		btfss	STATUS,Z		;2
		goto	$-2			;3,4
		decfsz	TEMP,f
		goto	AckNext

		clrf	CCP1CON			; shut off
		bcf	FL_LED
		bcf	BL_LED
		return

; -----------------------------------------------------------------------------------

; '0111CCAA''AAAAAAAA''DDDDDDDD''EEEEEEEE'	; Direct mode
;    DATA1    DATA2     DATA3     DATA4

ChkProg4:
		bcf	PROG_2X
;		bcf	RESET_FLG
		movf	DATA3,w			; save data
		movwf	EEDATA0

		btfsc	DATA1,0			; CV513.. or CV1..
		goto	ExitProg

		movf	DATA2,w			; x0AAAAAAAA
		call	FindCV
		btfsc	NOCV
		goto	ExitProg
ProgDirect:
		btfsc	DATA1,3	
		goto	RomNxt
		btfss	DATA1,2
		goto	ExitProg		;00 not defined
RomNxt:
		btfss	DATA1,2
		goto	BitMan			;10 Bit Manipulation
		btfss	DATA1,3
		goto	EEVERI4			;01 Verify byte
WriteDirect:					;11 Write byte
		btfsc	RDONLY
		goto	CheckCV8
		call	SetParm
		call	AckPulse
		bcf	PROG_2X
		bcf	NEW_PACKET
		call	LoadCV
		goto	ExitProg

EEVERI4:
		call	EE_Read			; check data
		xorwf	DATA3,w
		goto	EEVERIP

; 0111CCAA AAAAAAAA 111KDBBB EEEEEEEE

BitMan:
		call	EE_Read
		movwf	EEDATA0
		movlw	EEDATA0
		movwf	FSR
		movlw	b'00000111'
		andwf	DATA3,w
		call	BitPos
		btfss	DATA3,4			; K
		goto	Vbit			; K=0,verify bit
		btfsc	DATA3,3
		iorwf	INDF,f			; D=1,set bit
		xorlw	0xFF
		btfss	DATA3,3
		andwf	INDF,f			; D=0,clear bit
		movf	DATA2,w
		call	FindCV
		goto	ProgDirect		; write complete byte

Vbit:
		andwf	INDF,w
		btfsc	STATUS,Z
		goto	BitClear
BitSet:
		btfsc	DATA3,3			;D=0
		goto	DoAck			;D=1, ack
		goto	ExitProg
BitClear:
		btfss	DATA3,3			;D=1
		goto	DoAck			;D=0, ack
		goto	ExitProg


; -----------------------------------------------------------------------------------

FindCV:
		bcf	NOCV
		bcf	RDONLY

		xorlw	0x00			; CV513
		btfsc	STATUS,Z
		retlw	E_CV513
		xorlw	(0x00 ^ 0x08)		; CV521
		btfsc	STATUS,Z
		retlw	E_CV521
		xorlw	(0x08 ^ 0x1C)		; CV541
		btfsc	STATUS,Z
		retlw	E_CV541

		xorlw	(0x1C ^ 0x02)		; CV515
		btfsc	STATUS,Z
		retlw	E_CV515
		xorlw	(0x02 ^ 0x03)		; CV516
		btfsc	STATUS,Z
		retlw	E_CV516
		xorlw	(0x03 ^ 0x04)		; CV517
		btfsc	STATUS,Z
		retlw	E_CV517
		xorlw	(0x04 ^ 0x05)		; CV518
		btfsc	STATUS,Z
		retlw	E_CV518

		xorlw	(0x05 ^ 0x20)		; CV545
		btfsc	STATUS,Z
		retlw	E_CV545
		xorlw	(0x20 ^ 0x21)		; CV546
		btfsc	STATUS,Z
		retlw	E_CV546


		bsf	RDONLY
		xorlw	(0x21 ^ 0x06)		; CV519
		btfsc	STATUS,Z
		retlw	E_CV7
		xorlw	(0x06 ^ 0x07)		; CV520
		btfsc	STATUS,Z
		retlw	E_CV8

		bsf	NOCV			; CV not finded
		retlw	0x7F			; return last location


;---------------------------------------------------------------------------

ResetCV:
		movlw	0x01			; reset CV to default values
		movwf	EEDATA0			; address
		movlw	E_CV513
		call	SetParm
		
		movlw	0x00
		movwf	EEDATA0
		movlw	E_CV521
		call	SetParm


		movlw	0x32			; range
		movwf	EEDATA0
		movlw	E_CV515
		call	SetParm

		movlw	0x02			; speed
		movwf	EEDATA0
		movlw	E_CV516
		call	SetParm

		movlw	0x0F			; max. bright
		movwf	EEDATA0
		movlw	E_CV517
		call	SetParm

		movlw	0x0C			; start selay
		movwf	EEDATA0
		movlw	E_CV518
		call	SetParm

		movlw	0x01			; slope
		movwf	EEDATA0
		movlw	E_CV545
		call	SetParm

		movlw	0x4E			; spacing
		movwf	EEDATA0
		movlw	E_CV546
		call	SetParm

		return


LoadCV:
		movlw	CV513			; first CV to read
		movwf	FSR
		movlw	E_CV513
		movwf	EEADR0
LoadCVNxt:
		movf	EEADR0,w
		call	EE_Read
		movwf	INDF
		movlw	CV546			; last CV to read
		xorwf	FSR,w
		btfsc	STATUS,Z
		goto	LoadCVEnd
		incf	FSR,f
		incf	EEADR0,f
		goto	LoadCVNxt
LoadCVEnd:
		swapf	CV521,f
		comf	CV521,w			; top address is complemented
		andlw	0x70
		movwf	CV521			; now CV521='0AAA0000'

		swapf	CV517,f			; max. bright
		return


;---------------------------------------------------------------------------

LoadOutputs:
		movlw	EE_OUT			; read saved outputs
		call	EE_Read
		movwf	POSITION			

		movf	CV515,w			; calculate pulse for position
		btfsc	POSITION,0		; ***************
		xorlw	0xFF
		btfsc	POSITION,0		; ***************
		addlw	0x01
		addlw	d'150'
		movwf	PULSE

		movlw	0x10			; pulses for setting position
		movwf	CV516CNT
		bsf	REACHED,0		; do move to reached position
		bsf	MOVING,0

		clrw				; set lights
		btfsc	POSITION,0		
		movf	CV517,w
		movwf	SPEED1A
		btfsc	POSITION,0
		bsf	DIR_UP,OUT1A
		clrf	SPEED1B
		clrf	LIGHT_UP

		return


;----- Internal EEPROM routines ------------------------------------------------


EE_Read:
		bsf	STATUS,RP0		; w=ADR
		movwf	EEADR
		bsf	EECON1,RD
		movf	EEDATA,w
		bcf	STATUS,RP0
		return

SetParm:
		call	EE_Read			; w=ADR, EEDATA0=data. Write only changes
		xorwf	EEDATA0,w
		btfsc	STATUS,Z
		return
EE_Write:		
		movf	EEDATA0,w
		bsf	STATUS,RP0
		movwf	EEDATA
		bsf	EECON1,WREN
		bcf	INTCON,GIE
		movlw	0x55
		movwf	EECON2
		movlw	0xAA
		movwf	EECON2
		bsf	EECON1,WR
		bsf	INTCON,GIE
		bcf	EECON1,WREN
EEWrite0:
		btfsc	EECON1,WR
		goto	EEWrite0
		bcf	STATUS,RP0
		return

;---------------------------------------------------------------------------


; ----- EEPROM default values


		org	0x2100


		dw	0x01			; CV513	Primary Adress (low bits)
		dw	0x32			; CV515 Range servo (in 10us)
		dw	0x02			; CV516	Speed servo
		dw	0x0F			; CV517 Max. bright
		dw	0x0C			; CV518	Sound time (256ms)
		dw	0x0A			; CV519 Manufacturer Version
		dw	0x0D			; CV520	Manufacturer ID
		dw	0x00			; CV521 Primary Adress (high bits)
		dw	0x80			; CV541 Config
		dw	0x01			; CV545 Slope light
		dw	0x4E			; CV546	Spacing (in 256us)




		org	0x2120

		dt	"PANsound"
		dt	"F.Caņada"
		dt	(__VERDAY   >> 4)  +0x30
		dt	(__VERDAY   & 0x0F)+0x30,"/"
		dt	(__VERMONTH >> 4)  +0x30
		dt	(__VERMONTH & 0x0F)+0x30,"/"
		dt	(__VERYEAR  >> 4)  +0x30
		dt	(__VERYEAR  & 0x0F)+0x30

		org	0x217F

		dw	0x00			; last state, up

	end



