;Programmable Lights
;Author: Kirill Yelizarov

	LIST		P=PIC12C509, R=DEC
	INCLUDE		

	__CONFIG _IntRC_OSC & _WDT_OFF & _CP_OFF & _MCLRE_OFF
                       
;                      ------------ D A T A ------------

MPC	        equ	0x07 	;Macro program counter
KeyFlags	equ	0x08	;Keyboard flags
Keys		equ	0x09	;Keyboard keys
Command         equ	0x0a 	;Current command
Value		equ	0x0b	;Local data
Program		equ	0x0c	;Current program number
TimeDelay	equ	0x0d	;Time delay changed with Inc or Dec buttons
TimeCount	equ	0x0e	;Current time count
RepeatCount	equ	0x0f	;Counter for a loop

;----- KeyBoard bits -----
ResetKey	equ	1	;Switch to program #1
TestKey		equ	4	;Switch to program #0 (called Test, all lamps are turned on)
LineIn		equ	3	;Line in for all buttons
DecKey		equ	5   ;Decrease delay between lights flow
IncKey		equ	0	;Increase delay between lights flow
ProgKey		equ	2	;Switch to next program

;                ---------- Lights macro language -------------
#define	Light	 	retlw	0x00+	;Set new light
#define	MarkLabel	retlw	0x20+   ;Mark a label #(1...31)
#define	ReturnToLabel	retlw   0x40+	;Return to label #(1...31)
#define	SetRepeatValue	retlw	0x80+	;Set repeat value (1...31)
#define	JumpToProgram	retlw	0xa0+	;Jump to program #(1...31)
#define	StartOfProgram	retlw	0xc0+	;Start of program #(1...31)
#define	RestartProgram	retlw	0xe0+	;Return to start of program #(1...31)

Mark		equ	0x20    
ReturnTo	equ     0x40	
RepeatValue	equ	0x80
JumpTo		equ	0xa0	
StartOf		equ	0xc0
Restart		equ	0xe0

Button		equ	0x00
Line		equ	0x01

MAX_PROGRAM	equ	12
Test		equ	0		;Test program

;                      ------------ M A C R O ----------
;This macro is used to search pressed button
;total button de bounce test is 2*16 ms = 32 ms with one extra test on the 16th ms

TestButton	macro	source,line,butt,key,flag,out
		local	reset
		local	test

;source - type of signal (Button - synchronized by the lights, Line - not syncronized)
;line - input test GPIO pin
;butt - button output GPIO pin
;key - depression memory location
;flag - key flag memory location
;out - label to return from macro

		bcf	GPIO,butt	;drive butt pin low
		nop			;wait
		btfsc	GPIO,line	;look what happens on line pin
		goto	reset		;if high go and reset all for this button
		bsf	GPIO,butt	;drive butt pin high
		
		IF	source==Button	;extra test for syncronized buttons
		nop			;wait
		btfss	GPIO,line	;test pin for high value
		goto	reset		;if pin is still low then it's a line in signal
		ENDIF
		
		IF	source==Line
		nop			;wait
		btfsc	GPIO,line	;test line in if signal is still low
		goto	reset		;else a polling signal was read
		ENDIF

		btfsc	key,butt	;test key depression for butt pin
		goto	test		;yes there was a depression on last test, go to test
		bsf	key,butt	;no then set key depression flag
		goto	out		;and wait for the next test
reset:
		bsf	GPIO,butt	;drive butt pin high
		bcf	key,butt	;clear depression key
		bcf	flag,butt	;clear key flag
		goto	out		;test next
test:
		btfsc	flag,butt	;test key flag
		goto	out		;if set then test next (to prevent multiple run of the
					; same function while the button is pressed)
		bsf	flag,butt	;set key flag
					;function code will be merged here
		endm


;                      ------------ C O D E ------------

	org	0x00

	goto	Start			;Skip subroutine and table

;This multifunction subroutine can fetch next, or previous, or current command from the table

DecPC				;Get previous command
	decf	MPC,F
	movlw	low Table
	xorwf	MPC,W
	btfss	STATUS,Z
	goto	ReadPC
	movlw	low (Start-1)
	movwf	MPC
	goto	ReadPC
IncPC                  		;Get next command
	incf	MPC,F
	movlw	low Start
	xorwf	MPC,W
	btfss	STATUS,Z
	goto	ReadPC
	movlw	low (Table+1)
	movwf	MPC
ReadPC                           ;Read current command
	movf	MPC,W
	call	Table
	movwf	Value
	retlw	0

;                ------------------- P R O G R A M ---------------------
Table     				;Start of program table
	movwf	PCL 

;***********************************************
;*            Christmas Lights                 *
;*                  1997                       *
;*        Author: Kirill Yelizarov             *
;***********************************************

	StartOfProgram Test
	Light	b'11111'
	RestartProgram Test

	StartOfProgram 1

	Light	b'11111'
	Light	b'11111'
	Light	b'11111'
	Light	b'01111'
	Light	b'01111'
	Light	b'01111'
	Light	b'00111'
	Light	b'00111'
	Light	b'00111'
	Light	b'00011'
	Light	b'00011'
	Light	b'00011'
	Light	b'00001'
	Light	b'00001'
	Light	b'00001'
                        
	SetRepeatValue 10
	MarkLabel 1
	StartOfProgram 2
	Light	b'10000'
	Light	b'01000'
	Light	b'00100'
	Light	b'00010'
	Light	b'00001'
	RestartProgram 2
	ReturnToLabel 1
                       
	SetRepeatValue 10
	MarkLabel 2
	StartOfProgram 3
	Light	b'10001'
	Light	b'11000'
	Light	b'01100'
	Light	b'00110'
	Light	b'00011'
	RestartProgram 3
	ReturnToLabel 2

	SetRepeatValue 10
	MarkLabel 3
	StartOfProgram 4
	Light	b'10011'
	Light	b'11001'
	Light	b'11100'
	Light	b'01110'
	Light	b'00111'
	RestartProgram 4
	ReturnToLabel 3
 
  	SetRepeatValue 10
	MarkLabel 4
	StartOfProgram 5
	Light	b'10111'
	Light	b'11011'
	Light	b'11101'
	Light	b'11110'
	Light	b'01111'
	RestartProgram 5
	ReturnToLabel 4

	SetRepeatValue 10
	MarkLabel 5
	Light	b'00111'
	Light	b'10011'
	Light	b'11001'
	Light	b'11100'
	Light	b'01110'
	ReturnToLabel 5
                       
	SetRepeatValue 10
	MarkLabel 6
	Light	b'00110'
	Light	b'00011'
	Light	b'10001'
	Light	b'11000'
	Light	b'01100'
	ReturnToLabel 6     

	SetRepeatValue 10
	MarkLabel 7
	Light	b'00100'
	Light	b'00010'
	Light	b'00001'
	Light	b'10000'
	Light	b'01000'
	ReturnToLabel 7   

	Light	b'00100'
	Light	b'00010'
	Light	b'00001'

 	StartOfProgram 6
	Light	b'10000'
	Light	b'10000'
	Light	b'10000'
	Light	b'11000'
	Light	b'11000'
	Light	b'11000'
	Light	b'11100'
	Light	b'11100'
	Light	b'11100'
	Light	b'11110'
	Light	b'11110'
	Light	b'11110'
	Light	b'11111'
	Light	b'11111'
	Light	b'11111'
	Light	b'11110'
	Light	b'11110'
	Light	b'11110'
	Light	b'11100'
	Light	b'11100'
	Light	b'11100'
	Light	b'11000'
	Light	b'11000'
	Light	b'11000'
	Light	b'10000'
	Light	b'10000'
	Light	b'10000'
	RestartProgram 6 

	SetRepeatValue 10
	MarkLabel 8
	StartOfProgram 7
	Light	b'00001'
	Light	b'00010'
	Light	b'00100'
	Light	b'01000'
	Light	b'10000'
	RestartProgram 7
	ReturnToLabel 8 

	SetRepeatValue 10
	MarkLabel 9
	StartOfProgram 8
	Light	b'10001'
	Light	b'00011'
	Light	b'00110'
	Light	b'01100'
	Light	b'11000'
	RestartProgram 8
	ReturnToLabel 9

	SetRepeatValue 10
	MarkLabel 10
	StartOfProgram 9
	Light	b'11001'
	Light	b'10011'
	Light	b'00111'
	Light	b'01110'
	Light	b'11100'
	RestartProgram 9
	ReturnToLabel 10

	SetRepeatValue 10
	MarkLabel 11
	StartOfProgram 10
	Light	b'11101'
	Light	b'11011'
	Light	b'10111'
	Light	b'01111'
	Light	b'11110'
	RestartProgram 10
	ReturnToLabel 11  

	SetRepeatValue 10
	MarkLabel 12
	Light	b'11100'
	Light	b'11001'
	Light	b'10011'
	Light	b'00111'
	Light	b'01110'
	ReturnToLabel 12

	SetRepeatValue 10
	MarkLabel 13
	Light	b'01100'
	Light	b'11000'
	Light	b'10001'
	Light	b'00011'
	Light	b'00110'
	ReturnToLabel 13

	SetRepeatValue 10
	MarkLabel 14
	Light	b'00100'
	Light	b'01000'
	Light	b'10000'
	Light	b'00001'
	Light	b'00010'
	ReturnToLabel 14

	Light	b'00100'
	Light	b'01000'
	Light	b'10000'
                       
	StartOfProgram 11
	Light	b'00001'
	Light	b'00001'
	Light	b'00001'
	Light	b'00011'
	Light	b'00011'
	Light	b'00011'
	Light	b'00111'
	Light	b'00111'
	Light	b'00111'
	Light	b'01111'
	Light	b'01111'
	Light	b'01111'
	RestartProgram 1
	Light	b'11111'
	Light	b'11111'
	Light	b'11111'
	Light	b'01111'
	Light	b'01111'
	Light	b'01111'
	Light	b'00111'
	Light	b'00111'
	Light	b'00111'
	Light	b'00011'
	Light	b'00011'
	Light	b'00011'
	Light	b'00001'
	Light	b'00001'
	Light	b'00001'
	RestartProgram 11

	StartOfProgram 12
	Light	b'10001'
	Light	b'00000'
	Light	b'01010'
	Light	b'00000'
	Light	b'00100'
	Light	b'00000'
	Light	b'01010'
	Light	b'00000'
	RestartProgram 12

;                 ------------ M A I N -------------
Start:

	IF	Start>0x100
	ERROR	"Lights Message: Program Table too large."
	ENDIF

	clrf	Keys		;Reset key depression
	clrf	KeyFlags	;Reset keyboard flags
	clrf	TMR0		;clear TMR0
	movlw	b'10000101'	;Enable weak pull-up on GP3 and set prescaler to 1:64
;	movlw	b'10000000'     ;*****For MPLAB debug
	option

	clrf	GPIO
	comf	GPIO,F		;Turn OFF the lights
	movlw	b'00001000'	;Set GP3 (LineIn) as input and the rest are outputs
	tris	GPIO

	movlw	low (Start-1)	;Set program counter to the end of Table
	movwf	MPC

	movlw	0x10
;	movlw	0x01		;*****For MPLAB debug
	movwf	TimeCount	;Each time TMR0 overflows this value will decrement
	movwf	TimeDelay	;Set time dalay between two Light commands to 16*16 ms = 256 ms
				;may be changed with Inc and Dec keys
	movlw	0x01
	movwf	Program		;Set program #1

SetProgram:
	call	IncPC		;Get next command
	movf	Value,W
	andlw	b'11100000'
	xorlw	StartOf         ;If it's a StartOfProgram then continue
	btfss   STATUS,Z	;else go and get another command
	goto	SetProgram
	movf	Value,W
	andlw	b'00011111'
	xorwf	Program,W	;Be shure the right program found
	btfss	STATUS,Z	;else go and get another command
	goto	SetProgram

MainLoop:
	call	IncPC		;Get next command
	movf	Value,W
	andlw	b'11100000'	;Test Light Command
	btfsc	STATUS,Z
	goto	SetLights
	movwf	Command         ;Save Command

	xorlw	ReturnTo         ;Test ReturnToLabel command
	btfss   STATUS,Z
	goto	TestRestartProgram ;else analyze next command
	movf	Value,W
	andlw	b'00011111'
	movwf	Command		;Save label in Command because it's contents are no longer needed
	decfsz	RepeatCount,F     ;decrease loop counter set by 
	goto	FindLabel
	goto	MainLoop
FindLabel:
	call	DecPC		;Search backward for a desired label
	movf	Value,W
	andlw	b'1110000'
	xorlw	Mark
	btfss	STATUS,Z
	goto	FindLabel
	movlw	b'00011111'
	andwf	Value,W
	xorwf	Command,W
	btfss	STATUS,Z
	goto	FindLabel
	goto	MainLoop

TestRestartProgram:		;Look if it's a RestartProgram command
	movf	Command,W
	xorlw	Restart
	btfss   STATUS,Z
	goto	TestRepeatValue
	movlw	b'00011111'
	andwf	Value,W
	xorwf	Program,W
	btfss	STATUS,Z
	goto	MainLoop
	goto	SetProgram

TestRepeatValue:
	movf	Command,W
	xorlw	RepeatValue
	btfss   STATUS,Z
	goto	TestJumpTo
	movlw	b'00011111'
	andwf	Value,W
	movwf	RepeatCount
	goto	MainLoop

TestJumpTo:
	movf	Command,W
	xorlw	JumpTo
	btfss   STATUS,Z
	goto	MainLoop
	movlw	b'00011111'
	andwf	Value,W
	movwf	Program
	goto	SetProgram

SetLights:
	movlw	b'00110111'
	btfsc	Value,4
	bsf	Value,5
	bcf	Value,4
	btfsc	Value,3
	bsf	Value,4
	andwf	Value,F             ;cut everything but the lights

Wait1:	
	btfss	TMR0,7	;wait till seventh bit rise, ***** comment it when in MPLAB debug
	goto	Wait1			;***** Comment it when in MPLAB debug

	movlw	b'11111111'
	movwf	GPIO

	TestButton	Button,LineIn,ProgKey,Keys,KeyFlags,TestInc
NextProgram:
	incf	Program,F		
	movlw	MAX_PROGRAM+1
	xorwf	Program,W
	btfss	STATUS,Z
	goto	SetProgram
	movlw	0x01
	movwf	Program
	goto	SetProgram

TestInc:
	TestButton	Button,LineIn,IncKey,Keys,KeyFlags,TestDec
	movlw	0x01
	movwf	TimeCount	;Set TimeCount to one to make delay change fast
	incfsz	TimeDelay,F
	goto	TestDec
	movlw	0xff
	movwf	TimeDelay

TestDec:
	TestButton	Button,LineIn,DecKey,Keys,KeyFlags,TestLine
	movlw	0x01
	movwf	TimeCount	;Set TimeCount to one to make delay change fast
	decfsz	TimeDelay,F
	goto	TestLine
	movlw	0x01
	movwf	TimeDelay

TestLine:
	TestButton	Line,LineIn,LineIn,Keys,KeyFlags,TestTest
	goto	NextProgram

TestTest:
	TestButton	Button,LineIn,TestKey,Keys,KeyFlags,TestReset
	clrf	Program
	goto	SetProgram

TestReset:
	TestButton	Button,LineIn,ResetKey,Keys,KeyFlags,RestoreLights
	goto	Start
	
RestoreLights:
	movf	Value,W
	movwf	GPIO			;output Lights to GPIO
Wait0:
	btfsc	TMR0,7	;wait till the seventh bit reset, *****Comment it when in MPLAB debug
	goto	Wait0			;*****Comment it when in MPLAB debug
	decfsz	TimeCount,F		
	goto	Wait1		        ;if zero not reached then make another key check
	movf	TimeDelay,W		;reset TimeCount with TimeDelay value
	movwf	TimeCount
	goto	MainLoop

	org	0x3ff
	movlw	b'01110000'	;set OSCCAL
	end