

             
	list      p=12F508            ; list directive to define processor
	#include <p12F508.inc>        ; processor specific variable definitions



	__CONFIG   _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC
                ; GP3/MCLR is I/O pin
                ; Code protect off
                ; Watchdog timer off
                


	cblock 0x07
                delay.loop.in
                delay.loop.out
                switch.timer
                hold.timer
                led.state
                mode
                index
	index.base
                mask
               	endc

; Name bits
pb.switch	equ	3               ; port bit for switch
pb.led1	equ	1               ; port bit for LED1


multiplex       equ             7               ; multiplex flag bit


pson            equ             1<<multiplex
psoff           equ             0
led1on          equ             1<<pb.led1
led1off         equ             0


; Define data table macro's
ledstep       MACRO           hold, pwrsave, led1, 
              retlw           hold
              retlw           pwrsave; 	 led1 
              ENDM

restart         MACRO
            	retlw           0
                ENDM

	ORG             0x000         ; coding begins here
               	movwf           OSCCAL        ; update register with factory cal value 
	goto	start         ; goto to start of code


;**********************************************************************************

             

                radix           dec
select.mode     movfw           mode
                addwf           PCL,F
                retlw           mode.1        ; return start address of mode.1 sequence 
      
	;**********************************************************************
	; IMPORTANT: Make sure you set this correctly
max.mode	retlw	1	; set this value to the number of modes
      	;**********************************************************************


                ; all LEDs constant on, power-save on

mode.1          ledstep         50, psoff, led1on ;

                restart
;mode.2          ledstep         50, psoff, led1off ;

            ;    restart


;**********************************************************************************
                radix hex              
    
	; Initialise PIC after a power-on or wake-from-sleep

start	movlw	(1<<pb.switch)     ; set inputs on GPIO port
	tris	GPIO	; options T0CS = Int Osc, Enable weak pull-ups, enable wake-up on GPIO change
	movlw	~(1<<NOT_GPWU | 1<<T0CS ) ;
	option
	clrwdt

                btfsc           STATUS, GPWUF   ; test wake-on change flag bit
                goto            wakeup          ; If flag bit set, this is a Wake-up and not POR
                
                ; initialise variables after a power-on reset only
	clrf	GPIO	; start up with outputs off
                clrf            mode       
	clrf	delay.loop.in	; initialise delay loop counter variable
	clrf	switch.timer	; initialise switch timer variable
                clrf            led.state
                movlw           1<<pb.led1      ; initialise multiplex drive mask
                movwf           mask
                movlw           mode.1          ; initialise mode table index
                movwf           index
                goto            reload          ; jump straight to reload to setup start of new mode
                                                ; from there it drops back to main           
		
;--------------------------------------------------------------------------------- 
;	Main code loop
;
wakeup	movlw           .100            ; setup a 2S switch down timer
                movwf           switch.timer
	movf	GPIO,W
	bcf	STATUS, GPWUF
	btfsc           GPIO, pb.switch ; skip next if switch is not pressed
                sleep
               
                
waking.up       call            delay           ; wait a short time
                btfsc           GPIO, pb.switch ; is switch still down?
                goto            wakeup          ; no, then go back to sleep

                decfsz          switch.timer,F  ; yes, then decrement timer and skip next if timed out
                goto            waking.up       ; if not timed out, wait a bit longer

	bsf	switch.timer,1	; force bit 7 high in switch timer.
			; this is detected in the switch code and ensures we
			; don't select the next mode each to the switch is pressed
			; to power the light on.

main            call            delay           ; run 20mS delay

                btfss           GPIO, pb.switch ; is switch pressed?
                goto            switch.down     ; yes, run switch down function

                ;------------------------------
                ; this code only runs when the switch is NOT pressed.
                ; if this is the first time through after the switch was pressed
                ; switch.timer will contain a non-zero value. First we test the value
                ; then we set it back to 0, if the value was greater than the debounce/noise
                ; filter we run the switch code, if not we do nothing.
                ;
                ; The maximum value in this timer is normally <128 (bit 7 clear) so we set bit 7
                ; when returning from sleep to tell this code to ignore the switch press that brings 
                ; the PIC out of standby, otherwise it would also select the next mode.

switch.release  btfsc	switch.timer,1	; if this bit set we've just woken from power-down
	clrf	switch.timer	; so clear the timer so we don't treat it as a switch press
	movlw           -.3             ; is the value in switch.timer >2
                addwf           switch.timer,W
                clrf            switch.timer    ; always reset the timer to 0
                skpc                            ; timer value greater than filer so run switch.pressed function
                goto            lights          ; timer value less than filter so just run light function

                ;------------------------------
                ; This code will run once after the switch is released following a valid press

                incf            mode,F          ; increment state
                call            max.mode        ; compare it to highest mode
                subwf           mode,W          ;
                skpnc                           ; skip next if state less than highest mode
                clrf            mode            ; otherwise reset back to state 0

                call            select.mode     ; lookup start address of table data for next mode
                movwf           index	; save start address to index
	movwf	index.base	; and also to index.base 
                goto            reload          ; jump straight to reload to setup start of new mode
                ; LED function select


                ;------------------------------
                ; While switch is down, increment switch.timer
                ; If it reaches 100 (20mS x 100 = 2S) then turn off and sleep
                ; otherwise continue to run the normal lights function code.
switch.down     btfsc	switch.timer,1	; if we wake from sleep, this bit is set
	goto	lights	; so we don't select the next mode each time it wakes.
	incf            switch.timer,F  
                movlw           -.100
                addwf           switch.timer,W
                skpc
                goto            lights

                ; Switch held for 2S so turn off
                clrf            GPIO
	clrf	led.state
	btfss	GPIO, pb.switch
	goto	$-1
	call	delay
	movf	GPIO,W
	bcf	STATUS, GPWUF
                sleep

;--------------------------------------------------------
                ; standard inner/outer counter loop delay
	; 4Mhz clock / 

delay	movlw	.26	; gives delay of ~20.2mS
	movwf	delay.loop.out

                ; inner loop
delay.loop      decfsz          delay.loop.in,F ; loop for 255 iterations
                goto            $-1
                
                ; multiplex the LED drive to improve battery life
                ; multiplex bit in the LED data determines whether only the masked led.state LED
                ; is riven or the led.state is output directly to GPIO.
                ; For light effects where only single LED is on, or where you want the brightness over power saving
	; there is no need to multiplex the LED.  To reduce battery load use multiplex drive
                rrf             mask,F
                skpnc
                bsf             mask,2
                movfw           mask
                andwf           led.state,W
                btfss           led.state, multiplex            ; if multiplex flag bit clear
                movfw           led.state                       ; don't multiplex the LED drive
                movwf           GPIO               

                ; outer loop
                decfsz          delay.loop.out,F
                goto            delay.loop      
	retlw	0x00

                ;------------------------------
                ; Indexed table lookup function

lights          decfsz          hold.timer,F	; decrement the led timer
                goto            main	; loop until it reaches zero

reload          call            lookup	; lookup next led timer value
                movwf           hold.timer	; and save
	iorlw	0x00	; test if it was 0?
	skpnz		; if not skip next
	goto	reset.index	; if it was goto reset.index

                call            lookup	; lookup next led data 
                movwf           led.state	; and save
                goto            main	; otherwise we keep going with the next data in the sequence
                
reset.index     movfw           index.base	; load the base.index value into W
                movwf           index	; and put in the working index
                goto            reload	; then reload with the first line of data in the sequence

lookup          movfw           index	; take the absolute address held in index and put in W
                incf            index,F	; then index = index+1 ready for next line of data
                movwf           PCL	; and put W into PCL.  This will force program execution
			; to start from the address held in index (which will be a
			; retlw 0x00 instruction in the sequence data table)



                if ($>0xFE)
                error "Code found in upper 256 program memory words, see notes at top of this .ASM file"
                endif
 
                
	end

