;;======================================================================;;
;;			DCC ACCESORY DECODER				;;
;;======================================================================;;
;;									;;
;; Program:         DCC2SEMAF -- DCC two semaphore			;;
;; Code:            Paco Caņada						;;
;; Platform:        Microchip PIC12F629, 4 Mhz				;;
;; Date:            14.06.2005						;;
;; First release:   16.06.2005						;;
;; LastDate:        09.09.2005						;;
;;									;;
;;======================================================================;;

; Minimal external components, uses internal oscilator at 4 MHz

; This program is distributed as is but WITHOUT ANY WARRANTY
; I hope you enjoy!!
;
; Revisions:
; 14.06.2005	Start of writting code
; 26.06.2005	Added saving outputs status
; 27.06.2005	Changed max. light to 0..15. Added Direct mode programming
; 02.07.2005	Added 3 lights Renfe style semaphore and pair selection
; 04.07.2005	Changed to turn off first then light up
; 05.07.2005	Added RENFE maneouvres style and change switch programming (flashing)
; 02.09.2005	Added Lenz mode, corrected bug on CV513
; 09.09.2005	Added change mode on start pressing button

; ----- Definitions

#define		__VERNUM	D'2'
#define		__VERDAY	0x09
#define		__VERMONTH	0x09
#define		__VERYEAR	0x05


	LIST	   p=12F629	; target processor



	errorlevel -305,-302



	#include p12F629.inc





	__CONFIG  _BODEN_ON & _CP_OFF & _WDT_OFF & _MCLRE_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT 

				; Make sure that internal osc. is calibrated
				; Value has to be read before reprogramming the device.



; --- Macros

#define		DNOP		goto	$+1


; --- Constant values 

FXTAL		equ	D'4000000'

GP_TRIS         equ     0x0C			; GP2,GP3: inputs
GP_INI          equ     0x00			; all zero
OPTION_INI	equ	0x88			; Option register: no pull-up, falling GP2, no prescaler, wdt 1:1
WPU_INI		equ	0x33			; Weak pull-up enable. default, no pull-ups

INTC_INI	equ	0x90			; GIE, INTE enable, PEIE disable
PIE1_INI	equ	0x00			; no interrupts


#define		OUT1A	0			; Semaphore A
#define		OUT1B	1			; 
#define		DCCIN	GPIO,2			; DCC input pin
#define		SWITCH	GPIO,3			; switch (GPIO,3 only input)
#define		OUT2A	4			; Semaphore B
#define		OUT2B	5			; 


; --- EEPROM Section

#define		EE_INI		0x00

E_CV513		equ	EE_INI+0x00		; CV513	Primary Adress low
E_CV515		equ	EE_INI+0x02		; CV515	Max.light 0..15
E_CV516		equ	EE_INI+0x03		; CV516	
E_CV517		equ	EE_INI+0x04		; CV517	
E_CV518		equ	EE_INI+0x05		; CV518	
E_CV7		equ	EE_INI+0x06		; Manufacturer Version
E_CV8		equ	EE_INI+0x07		; Manufacturer ID
E_CV521		equ	EE_INI+0x08		; CV521	Primary Adress high
E_CV541		equ	EE_INI+0x1C		; config
E_CV545		equ	EE_INI+0x20		; CV545	Slope
E_CV546		equ	EE_INI+0x21		; CV546	Accesory flags

EE_OUT		equ	EE_INI+0x7F		; saved outputs


; ----- Variables

; --- Internal RAM Section

#define		RAMINI0		0x020		; 64 bytes

INT_W		equ	RAMINI0+0x00		; interrupt context registers
INT_STAT	equ	RAMINI0+0x01

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
CV521		equ	RAMINI0+0x11		; Primary Adress high byte

CV515		equ	RAMINI0+0x12		; max. bright
CV516		equ	RAMINI0+0x13		; 
CV517		equ	RAMINI0+0x14		; 
CV518		equ	RAMINI0+0x15		; 

CV545		equ	RAMINI0+0x16		; slope
CV546		equ	RAMINI0+0x17		; Accesory flags

FLAGS		equ	RAMINI0+0x20
EEDATA0		equ	RAMINI0+0x21		; data to write in EEPROM

TEMP		equ	RAMINI0+0x28
COUNT		equ	RAMINI0+0x29
DEBOUNCE	equ	RAMINI0+0x2A
DBC_CNT		equ	RAMINI0+0x2B

ACC_COUNT	equ	RAMINI0+0x30		; slope timer

OUTPUT		equ	RAMINI0+0x32		; output buffer
DIR_UP		equ	RAMINI0+0x33		; bright / fade
LIGHT_UP	equ	RAMINI0+0x34		; light up pending
TIMER		equ	RAMINI0+0x35		; timer 1:16

PWM1A		equ	RAMINI0+0x38		; current bright
PWM1B		equ	RAMINI0+0x39
PWM2A		equ	RAMINI0+0x3A
PWM2B		equ	RAMINI0+0x3B

SPEED1A		equ	RAMINI0+0x3C		; final bright
SPEED1B		equ	RAMINI0+0x3D
SPEED2A		equ	RAMINI0+0x3E
SPEED2B		equ	RAMINI0+0x3F


; --- Flags
						; FLAGS
#define		NEW_PACKET	FLAGS,0		; New 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		PROG_2X		FLAGS,6		; 2x prog
#define		RESET_FLG	FLAGS,7		; reset packet

KEYPRG_MSK	equ	0x10
						; CV546
#define		RENFE		CV546,0		; One RENFE semaphore
#define		MANIOBRA	CV546,1		; Two RENFE maneouvre
#define		PAIR_SEL	CV546,2		; Pair selection
#define		SAVE_OUTPUTS	CV546,3		; Save outputs to EEPROM
#define		LENZ_MODE	CV546,4		; Lenz Mode, only activate outputs works
;#define		KATODE		CV546,5		; Common Cathode semaphores


; --------------- 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

		btfsc	DCCIN								;5
		goto	Int_High_Half							;6,7
Int_Low_Half:
		movlw	d'256' - d'77'		; 77us: between 64us (one) and 90us (zero);7
		movwf	TMR0								;8
		bcf	INTCON,T0IF		; clear overflow flag for counting	;9

		bcf	INTCON,INTF							;10
		bsf	STATUS,RP0							;11
		bsf	OPTION_REG,INTEDG	; next interrupt on rising edge GP2	;12
		goto	EndInt								;13,14

; 1111111111 CCCCCCCC 0 AAAAAAAA 0 DDDDDDDD 0 EEEEEEEE 1
; 'x1111111''111 0 CCCC''CCCC 0 AAA' 'AAAAA 0 DD' DDDDDD 0 E' 'EEEEEEE 1'
;   SHIFT0     SHIFT1      SHIFT2       SHIFT3      SHIFT4      SHIFT5

; 1111111111 0 AAAAAAAA 0 DDDDDDDD 0 EEEEEEEE 1
; 'xxxxxxxx''xx111111''1111 0 AAA' 'AAAAA 0 DD' DDDDDD 0 E' 'EEEEEEE 1'
;   SHIFT0    SHIFT1     SHIFT2       SHIFT3      SHIFT4      SHIFT5


Int_High_Half:
		bsf	STATUS,C							;8
		btfsc	INTCON,T0IF		; if timer 0 overflows then is a DCC zero;9
		bcf	STATUS,C							;10
		rlf	SHIFT5,f		; receiver shift register		;11
		rlf	SHIFT4,f							;12
		rlf	SHIFT3,f							;13
		rlf	SHIFT2,f							;14
		rlf	SHIFT1,f							;15
		rlf	SHIFT0,f							;16

		movlw	0xC0			; ignore older bits			;17
		iorwf	SHIFT1,w		; 					;18
		xorlw	0xFF			;'xx111111' ? first 6 preamble bits	;19
		btfss	STATUS,Z							;20
		goto	Int_High_Check4							;21,22

Int_High_Check3:
		movlw	0xF8			; check 4 preamble and start bit	;23
		andwf	SHIFT2,w		; '1111 0 xxx'				;24
		xorlw	0xF0								;25
		btfss	STATUS,Z		; 					;26
		goto	EndHighHalf							;27,28

		btfss	SHIFT5,0		; check end bit				;29
		goto	EndHighHalf		; is zero, may be 4 byte packet		;30,31

		bsf	NEW_PACKET		; fill received packet			;31
		movf	SHIFT5,w							;32
		movwf	DATA3								;33
		movf	SHIFT4,w							;34
		movwf	DATA2								;35
		movf	SHIFT3,w							;36
		movwf	DATA1								;37
		movf	SHIFT2,w							;38
		movwf	DATA0								;39
		clrf	SHIFT1			; prevent 4 byte command decoding	;40

EndHighHalf:
		bcf	INTCON,INTF						;46	;31
		bsf	STATUS,RP0						;47	;32
		bcf	OPTION_REG,INTEDG	; next int. on falling edge GP2	;48	;33
EndInt:
		swapf	INT_STAT,w		; restore context registers	;49	;34
		movwf	STATUS							;50	;35
		swapf	INT_W,f							;51	;36
		swapf	INT_W,w							;52	;37
		retfie								;53,54	;38,39


Int_High_Check4:
		bsf	SHIFT0,7		; command 4 bytes, check 7 preamble bits;23
		incfsz	SHIFT0,w		;'x1111111'				;24
		goto	EndHighHalf							;25,26

		movlw	0xF0			; check 4 preamble and start bit	;26
		andwf	SHIFT1,w		; '1111 0 xxx'				;27
		xorlw	0xE0								;28
		btfss	STATUS,Z		; 					;29
		goto	EndHighHalf							;30,31

		bsf	NEW_PACKET		; fill received packet			;32
		bsf	DCC4BYTE		; command 4 bytes			;33
		movf	SHIFT5,w							;34
		movwf	DATA3								;35
		movf	SHIFT4,w							;36
		movwf	DATA2								;37
		movf	SHIFT3,w							;38
		movwf	DATA1								;39
		movf	SHIFT2,w							;40
		movwf	DATA0								;41
		movf	SHIFT1,w							;42
		movwf	DATA00								;43
		goto	EndHighHalf							;44,45

; ----------------------------------------------------------------------

BitPos:
		addwf	PCL,f

		retlw	0x01
		retlw	0x02
		retlw	0x04
		retlw	0x08
		retlw	0x10
		retlw	0x20
		retlw	0x40
		retlw	0x80

; ----------------------------------------------------------------------

Accessory:
		btfsc	KEY_PROG		; pressed switch?
		goto	AccessProg		; yes, program new address

		btfsc	MANIOBRA
		goto	AccessMan

		movf	CV521,w			; check high address bits
		iorlw	0x80			;'1AAAxPxx'
		btfsc	PAIR_SEL		; 
		iorlw	0x04
		xorwf	DATA2,w
		andlw	0xF4
		btfss	STATUS,Z
		goto	ExitDecode
		movf	DATA1,w			; check low address bits
		andlw	0x3F
		xorwf	CV513,w
		btfss	STATUS,Z
		goto	ExitDecode

		movf	DATA2,w			; activate outputs
		andlw	0x0B			; 'xxxxCxDD'
		btfsc	RENFE
		iorlw	0x10
		addwf	PCL,f

		goto	F1A_Clear		; normal style
		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	F1A_Set
		goto	F1B_Set
		goto	F2A_Set
		goto	F2B_Set
		goto	F3A_Set			; not used
		goto	F3B_Set			; not used
		goto	F4A_Set			; not used
		goto	F4B_Set			; not used

		goto	F1A_Clear		; RENFE style
		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	Renfe_Red
		goto	Renfe_Green
		goto	Renfe_Yellow
		goto	Renfe_Proceed
		goto	F3A_Set			; not used
		goto	F3B_Set			; not used
		goto	F4A_Set			; not used
		goto	F4B_Set			; not used

AccessMan:
		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

		movf	DATA2,w			; activate outputs
		andlw	0x0F			; 'xxxxCDDD'
		addwf	PCL,f

		goto	F1A_Clear		; RENFE maniobras
		goto	F1B_Clear
		goto	F1_Off
		goto	F1_Off
		goto	F2A_Clear
		goto	F2B_Clear
		goto	F2_Off
		goto	F2_Off

		goto	F1A_Set			; Red 1. xxxx1DDD
		goto	F1B_Set			; White 1
		goto	F1_AllOff		; Off 1
		goto	F1_All			; Red, White 1
		goto	F2A_Set			; Red 2
		goto	F2B_Set			; White 2
		goto	F2_AllOff		; Off 2
		goto	F2_All			; Red, White 2


AccessProg:
		btfss	DATA2,7			; '10AAAAAA'1AAAxPxx'? accesory operation
		goto	ExitDecode
		clrf	GPIO
		movf	DATA1,w			; get 6 low bits
		andlw	0x3F
		movwf	CV513
		movwf	EEDATA0
		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

		btfss	DATA2,2			; save pair
		bcf	PAIR_SEL
		btfsc	DATA2,2
		bsf	PAIR_SEL
		movf	CV546,w
		movwf	EEDATA0
		movlw	E_CV546
		call	SetParm

		bcf	KEY_PROG
		goto	Accessory


; ----------------------------------------------------------------------

INIT:
		clrf	GPIO
		movlw	0x07
		movwf	CMCON			; set GP2:0 to digital I/O

		bsf	STATUS,RP0		; bank 1
		movlw	GP_TRIS
		movwf	TRISIO
		call	0x3FF			; get OSCCAL value
		movwf	OSCCAL
		movlw	WPU_INI			; pull-ups
		movwf	WPU
		clrf	IOC			; interrupt on change
		clrf	VRCON			; voltage reference off
		movlw	OPTION_INI		; Option register: no pull-up, falling GP2, no prescaler, wdt 1:1
		movwf	OPTION_REG
		movlw	PIE1_INI
		movwf	PIE1
		bcf	STATUS,RP0		; bank 0
		clrf	PIR1
		movlw	0x31			; Timer 1 on, 1:8
		movwf	T1CON

		movlw	0x20			; clear RAM
		movwf	FSR
ClearRAM:
		clrf	INDF
		incf	FSR,f
		movlw	0x60
		xorwf	FSR,w
		btfss	STATUS,Z
		goto	ClearRAM

		movlw	INTC_INI
		movwf	INTCON			; enable GP2 external interrupt

		clrf	PAGEREG			; page register default

		call	LoadCV			; load CV values

		btfss   SWITCH     		; Change MODE if switch pressed
		call    ChangeMode     

		btfsc	SAVE_OUTPUTS		; load saved outputs
		call	LoadOutputs

		movlw	d'2'			; init slope counter
		movwf	ACC_COUNT

; ----------------------------------------------------------------------

MainLoop:
		btfsc	NEW_PACKET		; new packet?
		call	Decode			; yes, decode

		decfsz	DBC_CNT,f		; debounce time
		goto	Loop
		bsf	DBC_CNT,2
		bcf	STATUS,C		; check key
		btfsc	SWITCH
		bsf	STATUS,C
		rlf	DEBOUNCE,w
		movwf	DEBOUNCE
		xorlw	0xC0
		movlw	KEYPRG_MSK
		btfsc	STATUS,Z
		xorwf	FLAGS,f
Loop:
		btfsc	KEY_PROG
		goto	DoFlashProg

		btfsc	RESET_FLG		; no output on reset
		goto	MainLoop

		decfsz	ACC_COUNT,f		; slope
		goto	SpeedOn

		movf	CV545,w			; slope CV
		movwf	ACC_COUNT

		btfsc	RENFE
		goto	DoRENFE
DoLight1:
		movf	PWM1A,w			; all off?
		iorwf	PWM1B,w
		btfss	STATUS,Z
		goto	DoLight2
DoLight1A:
		btfss	LIGHT_UP,OUT1A
		goto	DoLight1B
		bcf	LIGHT_UP,OUT1A
		bsf	DIR_UP,OUT1A
		movf	CV515,w
		movwf	SPEED1A
DoLight1B:		
		btfss	LIGHT_UP,OUT1B
		goto	DoLight2
		bcf	LIGHT_UP,OUT1B
		bsf	DIR_UP,OUT1B
		movf	CV516,w
		movwf	SPEED1B
DoLight2:
		movf	PWM2A,w			; all off?
		iorwf	PWM2B,w
		btfss	STATUS,Z
		goto	DoPWM1A

DoLight2A:
		btfss	LIGHT_UP,OUT2A
		goto	DoLight2B
		bcf	LIGHT_UP,OUT2A
		bsf	DIR_UP,OUT2A
		movf	CV517,w
		movwf	SPEED2A
DoLight2B:		
		btfss	LIGHT_UP,OUT2B
		goto	DoPWM1A
		bcf	LIGHT_UP,OUT2B
		bsf	DIR_UP,OUT2B
		movf	CV518,w
		movwf	SPEED2B
		goto	DoPWM1A

DoRENFE:
		movf	PWM1A,w			; all off?
		iorwf	PWM1B,w
		iorwf	PWM2A,w
		btfss	STATUS,Z
		goto	DoPWM1A
DoRENFE1A:
		btfss	LIGHT_UP,OUT1A
		goto	DoRENFE1B
		bcf	LIGHT_UP,OUT1A
		bsf	DIR_UP,OUT1A
		movf	CV515,w
		movwf	SPEED1A
DoRENFE1B:		
		btfss	LIGHT_UP,OUT1B
		goto	DoRENFE2A
		bcf	LIGHT_UP,OUT1B
		bsf	DIR_UP,OUT1B
		movf	CV516,w
		movwf	SPEED1B
DoRENFE2A:
		btfss	LIGHT_UP,OUT2A
		goto	DoPWM1A
		bcf	LIGHT_UP,OUT2A
		bsf	DIR_UP,OUT2A
		movf	CV517,w
		movwf	SPEED2A

DoPWM1A:
		movf	PWM1A,w			; calc PWM acc/dec
		xorwf	SPEED1A,w
		btfsc	STATUS,Z
		goto	DoPWM1B
		movlw	0x01
		btfss	DIR_UP,OUT1A
		movlw	0xFF
		addwf	PWM1A,f

DoPWM1B:
		movf	PWM1B,w
		xorwf	SPEED1B,w
		btfsc	STATUS,Z
		goto	DoPWM2A
		movlw	0x01
		btfss	DIR_UP,OUT1B
		movlw	0xFF
		addwf	PWM1B,f
DoPWM2A:
		movf	PWM2A,w
		xorwf	SPEED2A,w
		btfsc	STATUS,Z
		goto	DoPWM2B
		movlw	0x01
		btfss	DIR_UP,OUT2A
		movlw	0xFF
		addwf	PWM2A,f
DoPWM2B:
		movf	PWM2B,w
		xorwf	SPEED2B,w
		btfsc	STATUS,Z
		goto	MainLoop
		movlw	0x01
		btfss	DIR_UP,OUT2B
		movlw	0xFF
		addwf	PWM2B,f
		goto	MainLoop

SpeedOn:
		clrf	OUTPUT			; do PWM of outputs

		rrf	TMR1H,w
		rrf	TMR1L,w
		movwf	TIMER
		bcf	STATUS,C
		rrf	TIMER,w
		btfsc	TMR1H,1
		iorlw	0x8F

;		movf	TMR1L,w
;		iorlw	0x0F
		movwf	TIMER
		addwf	PWM1A,w
		btfsc	STATUS,C
		bsf	OUTPUT,OUT1A
		movf	TIMER,w
		addwf	PWM1B,w
		btfsc	STATUS,C
		bsf	OUTPUT,OUT1B
		movf	TIMER,w
		addwf	PWM2A,w
		btfsc	STATUS,C
		bsf	OUTPUT,OUT2A
		movf	TIMER,w
		addwf	PWM2B,w
		btfsc	STATUS,C
		bsf	OUTPUT,OUT2B

;		movlw	(1<<OUT1A | 1<<OUT1B | 1<<OUT2A )
;		btfss	RENFE
;		movlw	(1<<OUT1A | 1<<OUT1B | 1<<OUT2A | 1<<OUT2B)
;		btfss	KATODE			; Common Anode or Common Cathode
;		movlw	0x00
;		xorwf	OUTPUT,w

		movf	OUTPUT,w
		movwf	GPIO			; output
		goto	MainLoop


DoFlashProg:
		btfss	TMR1H,7			; 0,25 sec
		goto	MainLoop

		bcf	TMR1H,7

		movlw	(1<<OUT1A)		; default

		btfsc	GPIO,OUT1A		; rotate lights
		movlw	(1<<OUT1B)
		btfsc	GPIO,OUT1B
		movlw	(1<<OUT2A)
		btfsc	GPIO,OUT2A
		movlw	(1<<OUT2B)
		btfsc	GPIO,OUT2B
		movlw	(1<<OUT1A)

		movwf	GPIO
		goto	MainLoop


ChangeMode:
		btfss	RENFE			; 00
		btfsc	MANIOBRA
		goto	ChangeModeMANIOBRA
ChangeModeRENFE:
		bsf	RENFE			; -> 01
		bcf	MANIOBRA
		goto	ShowMode
ChangeModeMANIOBRA:
		btfss	RENFE			; 01
		goto	ChangeModeREDGREEN
		bcf	RENFE			; -> 10
		bsf	MANIOBRA
		goto	ShowMode
ChangeModeREDGREEN:
		bcf	RENFE			; 10 -> 00
		bcf	MANIOBRA


ShowMode:
		movf	CV546,w			; save new mode
		movwf	EEDATA0
		movlw	E_CV546
		call	SetParm

		movf	CV546,w			; blinking times
		andlw	0x03
		addlw	0x01
		movwf	COUNT
		clrf	DBC_CNT
		clrf	TEMP
ShowLoop:
		DNOP
		DNOP
		DNOP
		DNOP
		DNOP
		movlw	0x33			; blink LEDs
		btfss	TEMP,7
		movlw	0x00
		movwf	GPIO
		decfsz	DBC_CNT,f
		goto	ShowLoop
		decfsz	TEMP,f
		goto	ShowLoop
		decfsz	COUNT,f
		goto	ShowLoop
		return


; ----------------------------------------------------------------------


Decode:
		bcf	NEW_PACKET		; prepare for next packet
		bcf	INTCON,GIE		; disable interrupts for more speed
		btfsc	DCC4BYTE
		goto	Decode4bytes

; 3 byte packets:
; 1111111111 0 AAAAAAAA 0 DDDDDDDD 0 EEEEEEEE 1
; '1111 0 AAA' 'AAAAA 0 DD' DDDDDD 0 E' 'EEEEEEE 1'
;    DATA0        DATA1       DATA2        DATA3

Decode3bytes:
		rrf	DATA0,f
		rrf	DATA1,f
		rrf	DATA2,f
		rrf	DATA3,f			; 'x1111 0 AA''AAAAAA 0 D''DDDDDDD 0''EEEEEEE'
		btfss	STATUS,C		; check end bit
		goto	ExitDecode

		rrf	DATA0,f			; 'xx1111 0 A''AAAAAAA 0''DDDDDDDD''EEEEEEEE'
		rrf	DATA1,f
		rrf	DATA2,f
		btfsc	STATUS,C		; check start bit
		goto	ExitDecode

		rrf	DATA0,f
		rrf	DATA1,f			; 'xxx1111 0''AAAAAAAA''DDDDDDDD''EEEEEEEE'
		btfsc	STATUS,C		; check start bit
		goto	ExitDecode

		movf	DATA1,w			; exclusive or check
		xorwf	DATA2,w
		xorwf	DATA3,w
		btfss	STATUS,Z		; valid packet?
		goto	ExitDecode		; no, return

; 'AAAAAAAA''DDDDDDDD''EEEEEEEE'		; 3 byte packet
;   DATA1     DATA2     DATA3

		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,GIE		; yes, don't clear reset flag
		return

; 4 byte packets:
; 1111111111 CCCCCCCC 0 AAAAAAAA 0 DDDDDDDD 0 EEEEEEEE 1
; '111 0 CCCC''CCCC 0 AAA' 'AAAAA 0 DD' DDDDDD 0 E' 'EEEEEEE 1'
;    DATA00      DATA0        DATA1       DATA2        DATA3


Decode4bytes:
		bcf	DCC4BYTE
		rrf	DATA00,f
		rrf	DATA0,f
		rrf	DATA1,f
		rrf	DATA2,f
		rrf	DATA3,f			; 'x111 0 CCC''CCCCC 0 AA''AAAAAA 0 D''DDDDDDD 0''EEEEEEE'
		btfss	STATUS,C		; check end bit
		goto	ExitDecode

		rrf	DATA00,f
		rrf	DATA0,f			; 'xx111 0 CC''CCCCCC 0 A''AAAAAAA 0''DDDDDDDD''EEEEEEEE'
		rrf	DATA1,f
		rrf	DATA2,f
		btfsc	STATUS,C		; check start bit
		goto	ExitDecode

		rrf	DATA00,f
		rrf	DATA0,f
		rrf	DATA1,f			; 'xxx111 0 C''CCCCCCC 0''AAAAAAAA''DDDDDDDD''EEEEEEEE'
		btfsc	STATUS,C		; check start bit
		goto	ExitDecode

		rrf	DATA00,f
		rrf	DATA0,f			; 'xxxx111 0''CCCCCCCC''AAAAAAAA''DDDDDDDD''EEEEEEEE'
		btfsc	STATUS,C		; check start bit
		goto	ExitDecode


		movf	DATA0,w			; exclusive or check
		xorwf	DATA1,w
		xorwf	DATA2,w
		xorwf	DATA3,w
		btfss	STATUS,Z		; valid packet?
		goto	ExitDecode		; no, return

; '0111CCAA''AAAAAAAA''DDDDDDDD''EEEEEEEE'	; Direct mode
;    DATA0    DATA1     DATA2     DATA3

		movf	DATA0,w
		andlw	0xF0
		xorlw	0x70			; '0111xxxx'?
		btfss	STATUS,Z
		goto	ExitDecode		; yes, may be service mode

		btfss	RESET_FLG		; check for SM, reset packet has to come first
		goto	ServModeError
		btfss	PROG_2X
		goto	SetSM_Flag

		bcf	PROG_2X
;		bcf	RESET_FLG
		movf	DATA2,w			; save data
		movwf	EEDATA0

		btfsc	DATA0,0			; CV513.. or CV1..
		goto	ExitProg

		movf	DATA1,w			; x0AAAAAAAA
		call	FindCV
		btfsc	NOCV
		goto	ExitProg
ProgDirect:
		btfsc	DATA0,3	
		goto	RomNxt
		btfss	DATA0,2
		goto	ExitProg		;00 not defined
RomNxt:
		btfss	DATA0,2
		goto	BitMan			;10 Bit Manipulation
		btfss	DATA0,3
		goto	EEVERI			;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

BitMan:
		call	EE_Read
		movwf	EEDATA0
		movlw	EEDATA0
		movwf	FSR
		movlw	b'00000111'
		andwf	DATA2,w
		call	BitPos
		btfss	DATA2,4			; K
		goto	Vbit			; K=0,verify bit
		btfsc	DATA2,3
		iorwf	INDF,f			; D=1,set bit
		xorlw	0xFF
		btfss	DATA2,3
		andwf	INDF,f			; D=0,clear bit
		movf	DATA1,w
		call	FindCV
		goto	ProgDirect		; write complete byte

Vbit:
		andwf	INDF,w
		btfsc	STATUS,Z
		goto	BitClear
BitSet:
		btfsc	DATA2,3			;D=0
		goto	DoAck			;D=1, ack
		goto	ExitProg
BitClear:
		btfss	DATA2,3			;D=1
		goto	DoAck			;D=0, ack
		goto	ExitProg
		


; ----------------------------------------------------------------------
		
F1A_Clear:
		btfsc	LENZ_MODE
		goto	ExitFunction
		clrf	SPEED1A
		bcf	DIR_UP,OUT1A
		goto	ExitFunction
F1B_Clear:
		btfsc	LENZ_MODE
		goto	ExitFunction
		clrf	SPEED1B
		bcf	DIR_UP,OUT1B
		goto	ExitFunction
F2A_Clear:
		btfsc	LENZ_MODE
		goto	ExitFunction
		clrf	SPEED2A
		bcf	DIR_UP,OUT2A
		goto	ExitFunction
F2B_Clear:
		btfsc	LENZ_MODE
		goto	ExitFunction
		clrf	SPEED2B
		bcf	DIR_UP,OUT2B
		goto	ExitFunction
F3A_Clear:
F3B_Clear:
F4A_Clear:
F4B_Clear:
		goto	ExitFunction


F1_All:
		btfsc	DIR_UP,OUT1A		; all two on?
		btfss	DIR_UP,OUT1B
		goto	F1_AllOn
		goto	ExitFunction		; yes, nothing to do
F1_AllOn:
		bsf	LIGHT_UP,OUT1A		; next light this
		bsf	LIGHT_UP,OUT1B		; next light this
		goto	F1_AllOff
F1_Off:
		btfsc	LENZ_MODE
		goto	ExitFunction
F1_AllOff:
		clrf	SPEED1A			; turn off
		bcf	DIR_UP,OUT1A
		clrf	SPEED1B			; turn off
		bcf	DIR_UP,OUT1B
		goto	ExitFunction

F2_All:
		btfsc	DIR_UP,OUT2A		; all two on?
		btfss	DIR_UP,OUT2B
		goto	F2_AllOn
		goto	ExitFunction		; yes, nothing to do
F2_AllOn:

		bsf	LIGHT_UP,OUT2A		; next light this
		bsf	LIGHT_UP,OUT2B		; next light this
		goto	F2_AllOff
F2_Off:
		btfsc	LENZ_MODE
		goto	ExitFunction
F2_AllOff
		clrf	SPEED2A			; turn off
		bcf	DIR_UP,OUT2A
		clrf	SPEED2B			; turn off
		bcf	DIR_UP,OUT2B
		goto	ExitFunction


F1A_Set:
		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
F1B_Set:
		clrf	SPEED1A			; turn off
		bcf	DIR_UP,OUT1A
		bcf	LIGHT_UP,OUT1A
		btfss	DIR_UP,OUT1B		; if lights nothing to do
		bsf	LIGHT_UP,OUT1B		; next light this
		goto	ExitFunction
F2A_Set:
		clrf	SPEED2B			; turn off
		bcf	DIR_UP,OUT2B
		bcf	LIGHT_UP,OUT2B
		btfss	DIR_UP,OUT2A		; if lights, nothing to do
		bsf	LIGHT_UP,OUT2A		; next light this
		goto	ExitFunction
F2B_Set:
		clrf	SPEED2A			; turn off
		bcf	DIR_UP,OUT2A
		bcf	LIGHT_UP,OUT2A
		btfss	DIR_UP,OUT2B		; if lights, nothing to do
		bsf	LIGHT_UP,OUT2B		; next light this
		goto	ExitFunction
F3A_Set:
F3B_Set:
F4A_Set:
F4B_Set:
		goto	ExitFunction


ExitFunction:
		bcf	RESET_FLG
		bsf	INTCON,GIE		; enable interrupts
		btfss	SAVE_OUTPUTS
		return
		movf	DIR_UP,w		; save output state
		iorwf	LIGHT_UP,w
		movwf	EEDATA0
		movlw	EE_OUT
		goto	SetParm

Renfe_Red:
		movlw	0xFF			; rele on
		movwf	SPEED2B
		movwf	PWM2B
		bsf	DIR_UP,OUT2B
		clrf	SPEED2A			; turn off yellow
		bcf	DIR_UP,OUT2A
		bcf	LIGHT_UP,OUT2A
		goto	F1A_Set			; turn off green, turn on red

Renfe_Green:
		clrf	SPEED2B			; rele off
		clrf	PWM2B
		bcf	DIR_UP,OUT2B
		clrf	SPEED2A			; turn off yellow
		bcf	DIR_UP,OUT2A
		bcf	LIGHT_UP,OUT2A
		goto	F1B_Set			; turn off red, turn on green

Renfe_Proceed:
		clrf	SPEED2B			; rele off
		clrf	PWM2B
		bcf	DIR_UP,OUT2B
		clrf	SPEED1A			; turn off red
		bcf	DIR_UP,OUT1A
		bcf	LIGHT_UP,OUT1A

		btfsc	DIR_UP,OUT1B		; all two on?
		btfss	DIR_UP,OUT2A
		goto	Renfe_Proceed_On	; no, turn off and light
		goto	ExitFunction		; yes, nothing to do
Renfe_Proceed_On:
		clrf	SPEED1B
		clrf	SPEED2A
		bcf	DIR_UP,OUT1B
		bcf	DIR_UP,OUT2A
		bsf	LIGHT_UP,OUT1B		; next light green
		bsf	LIGHT_UP,OUT2A		; next light yellow
		goto	ExitFunction

Renfe_Yellow:
		clrf	SPEED2B			; rele off
		clrf	PWM2B
		bcf	DIR_UP,OUT2B
		clrf	SPEED1A			; turn off all
		clrf	SPEED1B			; 
		bcf	DIR_UP,OUT1A
		bcf	DIR_UP,OUT1B
		bcf	LIGHT_UP,OUT1A
		bcf	LIGHT_UP,OUT1B
		btfss	DIR_UP,OUT2A		; if lights, nothing to do
		bsf	LIGHT_UP,OUT2A		; next light yellow
		goto	ExitFunction

		
; ----------------------------------------------------------------------

Broadcast:
		movf	DATA2,w			; reset packet?
		btfss	STATUS,Z
		goto	ExitDecode
		bcf	PROG_2X
		bsf	RESET_FLG
						; reset decoder
		clrf	GPIO

		bsf	INTCON,GIE		; enable interrupts
		return


ExitDecode:
		bcf	RESET_FLG
		bsf	INTCON,GIE		; 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

		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,GIE		; enable interrupts
		return
		
; -----------------------------------------------------------------------------------

AckPulse:
		movlw	b'00110011'		; all lights on
		movwf	GPIO
		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	GPIO			; all light off
		return

; -----------------------------------------------------------------------------------

LoadCV:
		movlw	E_CV513			; address low
		call	EE_Read
		movwf	CV513			; set value for comparison
		movlw	E_CV521			; address high
		call	EE_Read
		movwf	CV521
		swapf	CV521,f
		comf	CV521,w			; top address is complemented
		andlw	0x70
		movwf	CV521			; now CV521='0AAA0000'

		movlw	E_CV515			; 
		call	EE_Read
		iorlw	0xF0			; 'xxxxABCD'
		movwf	CV515			; '1111ABCD'
		swapf	CV515,f			; 'ABCD1111'

		movlw	E_CV516			; 
		call	EE_Read
		iorlw	0xF0			; 'xxxxABCD'
		movwf	CV516			; '1111ABCD'
		swapf	CV516,f			; 'ABCD1111'

		movlw	E_CV517			; 
		call	EE_Read
		iorlw	0xF0			; 'xxxxABCD'
		movwf	CV517			; '1111ABCD'
		swapf	CV517,f			; 'ABCD1111'

		movlw	E_CV518			; 
		call	EE_Read
		iorlw	0xF0			; 'xxxxABCD'
		movwf	CV518			; '1111ABCD'
		swapf	CV518,f			; 'ABCD1111'

		movlw	E_CV545			; slope
		call	EE_Read
		movwf	CV545
		movlw	E_CV546			; accesory flags
		call	EE_Read
		movwf	CV546

		return


LoadOutputs:
		movlw	EE_OUT			; read saved outputs
		call	EE_Read
		movwf	DIR_UP			
		movf	CV515,w
		btfss	DIR_UP,OUT1A
		clrw
		movwf	SPEED1A
		movf	CV516,w
		btfss	DIR_UP,OUT1B
		clrw
		movwf	SPEED1B
		movf	CV517,w
		btfss	DIR_UP,OUT2A
		clrw
		movwf	SPEED2A
		movf	CV518,w
		btfss	DIR_UP,OUT2B
		clrw
		movwf	SPEED2B
		clrf	LIGHT_UP
		return
		

; -----------------------------------------------------------------------------------

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
		movlw	E_CV513
		call	SetParm

		movlw	0x0F			; max. light
		movwf	EEDATA0
		movlw	E_CV515
		call	SetParm
		movlw	E_CV516
		call	SetParm
		movlw	E_CV517
		call	SetParm
		movlw	E_CV518
		call	SetParm

		movlw	0x00
		movwf	EEDATA0
		movlw	E_CV521
		call	SetParm
	
		movlw	0x80
		movwf	EEDATA0
		movlw	E_CV541
		call	SetParm


		movlw	0x14			; slope
		movwf	EEDATA0
		movlw	E_CV545
		call	SetParm

		movlw	0x18			; not save outputs, Lenz Mode
		movwf	EEDATA0
		movlw	E_CV546
		call	SetParm

		return


;---------------------------------------------------------------------------

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	Adress low
		dw	0xFF			; 
		dw	0x0F			; CV515 Max. bright
		dw	0x0F			; CV516
		dw	0x0F			; CV517
		dw	0x0F			; CV518
		dw	0x14			; CV519 Manufacturer Version
		dw	0x0D			; CV520	Manufacturer ID
		dw	0x00			; CV521 Address high
		dw	0xFF			; 
		dw	0xFF			;
		dw	0xFF			;
		dw	0xFF			;
		dw	0xFF			;
		dw	0xFF			; 
		dw	0xFF			;
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0x80			; CV541 Config
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0xFF			; 
		dw	0x14			; CV545 Slope
		dw	0x18			; CV546 Accesory flags

		org	0x2130

		dt	" 2semaf "
		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	(1<<OUT1A | 1<<OUT2A)		; default output

	end


