;******************************************************************************
; BOUNCINGLIGHT                                                               *
;                                                                             *
; A toy with moving lights controlled by a push button.                       *
; At rest, all lights are on.  When the input button is pressed,              *
; a light point starts bouncing.                                              *
;                                                                             *
; Bouncing is controlled by an interrupt triggered by TIMER0.                 *
;                                                                             *
; It is intended to be used with the Sorbete board.                           *
;                                                                             *
;******************************************************************************
;                                                                             *
; Pin assignments:                                                            *
;   GPIO0 - leftmost light                                                    *
;   GPIO1 - center-left light                                                 *
;   GPIO2 - center light                                                      *
;   GPIO3 - push-button (high at rest, low when pressed)                      *
;   GPIO4 - center-right light                                                *
;   GPIO5 - rightmost light                                                   *
;                                                                             *
;******************************************************************************
;                                                                             *
; Copyright (C) 2011 Sergio García-Cuevas González.                           *
;                                                                             *
; This program is free software; you can redistribute it and/or modify        *
; it under the terms of the GNU General Public License as published by        *
; the Free Software Foundation; either version 3 of the License, or           *
; (at your option) any later version.                                         *
;                                                                             *
; This program is distributed in the hope that it will be useful,             *
; but WITHOUT ANY WARRANTY; without even the implied warranty of              *
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
; GNU General Public License for more details.                                *
;                                                                             *
; You should have received a copy of the GNU General Public License           *
; along with this program.  If not, see <http://www.gnu.org/licenses/>.       *
;                                                                             *
;******************************************************************************

        list      p=12f629
        #include <p12f629.inc>

;***** CONFIGURATION
; _CPD_OFF:            off course you can copy data
; _CP_OFF:             off course you can copy code
; _BODEN_OFF:          no brown-out detection
; _MCLRE_OFF:          no reset button
; _PWRTE_ON:           wait 72 ms at power-up for supply-voltage stabilisation
; _WDT_OFF:            no watchdog timer
; _INTRC_OSC_NOCLKOUT: use internal, 4 MHz oscillator with no output
       __CONFIG  _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

;***** VARIABLE DEFINITIONS
POINT   EQU     20H
DIR     EQU     21H
#define GORIGHT DIR,0
RGHTWLL EQU     b'00000001'
LFTWLL  EQU     b'00100000'
BOUNCE  EQU     b'00000001'
NOBNC   EQU     b'00000000'
#define SKIPME  POINT,3
#define BUTTON  GPIO,3
#define HITLEFT POINT,6
#define HITRGHT STATUS,C
ALLON   EQU     b'00110111'

;***** RESET VECTOR
reset_vector
        ORG     000H            ; address 000H, reset vector
        GOTO    setup

;***** INTERRUPT VECTOR
interrupt_vector
        ORG     004H            ; address 004H, interrupt vector
        BCF     INTCON,T0IF     ; clear interrupt flag
        BTFSC   GORIGHT         ; if we have to go right
        GOTO    go_right        ; then go right
        GOTO    go_left         ; else go left
go_right
        CALL    go_right_fcn
        XORWF   DIR,F           ; update the direction
        RETFIE                  ; return from the interrupt
go_left
        CALL    go_left_fcn
        XORWF   DIR,F           ; update the direction
        RETFIE                  ; return from interrupt
go_left_fcn
        RLF     POINT,F         ; roll
        BTFSC   SKIPME          ; skip input-only pin
        RLF     POINT,F
        BTFSS   HITLEFT         ; if we didn't hit the left wall
        RETLW   NOBNC           ; then return, no bounce
        MOVLW   LFTWLL          ; else go back to the left wall
        MOVWF   POINT
        RETLW   BOUNCE          ; and return the bounce flag
go_right_fcn
        RRF     POINT,F         ; roll
        BTFSC   SKIPME          ; skip input-only pin
        RRF     POINT,F
        BTFSS   HITRGHT         ; if we didn't hit the right wall
        RETLW   NOBNC           ; then return, no bounce
        MOVLW   RGHTWLL         ; else go back to the right wall
        MOVWF   POINT
        RETLW   BOUNCE          ; and return the bounce flag

;***** RUN-TIME SETUP
setup
; we will not use the analog comparator
        BCF     CMCON,CM2       ; turn off comparator input 2
        BCF     CMCON,CM1       ; turn off comparator input 1
        BCF     CMCON,CM0       ; turn off comparator input 0
; oscillator calibration
        CALL    3FFH            ; load cal value (stored by the chip
                                ; manufacturer at instruction 3FFH)
        BSF     STATUS,RP0      ; select bank 1 for OSCCAL
        MOVWF   OSCCAL          ; update OSCCAL
        BCF     STATUS,RP0      ; go back to bank 0
; output setup
        BSF     STATUS,RP0      ; select bank 1 for TRISIO
        BCF     TRISIO,0        ; set GPIO,0 as output
        BCF     TRISIO,1        ; set GPIO,1 as output
        BCF     TRISIO,2        ; set GPIO,2 as output
        BSF     TRISIO,3        ; set GPIO,3 as input
        BCF     TRISIO,4        ; set GPIO,4 as output
        BCF     TRISIO,5        ; set GPIO,5 as output
        BCF     STATUS,RP0      ; go back to bank 0
        CLRF    GPIO            ; init GPIO
; timer setup
        CLRWDT                  ; clear WDT
        MOVLW   b'10000111'     ; GPPU    = GPIO pull-ups are disabled
                                ; INTEDG  = interrupt on rising edge of GP2/INT
                                ; TOCS    = internal clock
                                ; TOSE    = increment in low-to-high
                                ; PSA     = prescaler assigned to TIMER0
                                ; PS2:PS0 = 111 = 1 / 256
        BSF     STATUS,RP0      ; select bank 1 for OPTION_REG
        MOVWF   OPTION_REG      ; load options
        BCF     STATUS,RP0      ; go back to bank 0
        CLRWDT                  ; clear WDT
        BSF     INTCON,T0IE     ; enable interrupt handler
        BSF     INTCON,GIE      ; enable interrupts
; initialisation of variables
        MOVLW   RGHTWLL         ; start at right wall
        MOVWF   POINT
        BCF     GORIGHT         ; start going left (GORIGHT == 0)

;***** MAIN LOOP
main_loop
        BTFSC   BUTTON          ; if we pressed the button
        GOTO    all_on          ; then switch on all the lamps
        MOVF    POINT,W         ; else show the point
        MOVWF   GPIO
        GOTO    main_loop       ; and spin
all_on
        MOVLW   ALLON           ; switch on all the lamps
        MOVWF   GPIO
        GOTO    main_loop       ; and spin
        END

