;Copyright 2005-2008 Andrew Walbran ;Contact lasertag@q.geek.nz, or see http://q.geek.nz/lasertag.html ; 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 . ;Intended for PIC16F628A ;Config fuses: 11111100110100 (0x3F34) __CONFIG 0x3f34 ;Pin assignments: ; - RA2: piezo ; - RA3: reset button ; - RA5/^MCLR: tied to Vdd via resistor ; - RA7: 'life' indicator LED ; - RB5: LDR ;Register labels INDF equ 00h STATUS equ 03h FSR equ 04h TRISA equ 85h TRISB equ 86h PORTA equ 05h PORTB equ 06h EEDATA equ 9ah EEADR equ 9bh EECON1 equ 9ch CMCON equ 01fh INTCON equ 0bh ;Variables delay_count equ 20h delay_count2 equ 21h long_delay_count equ 22h beep_tone equ 23h ;Used to pass parameter to beep subroutine beep_count equ 24h ;Used internally by beep subroutine W_temp equ 25h ;Used in interrupt routine to store W STATUS_temp equ 26h ;Used in interrupt routine to store STATUS lives_left equ 27h ;Number of hits left before we die ;Constants beep_length equ 0x30 ;0x08 ;Number of cycles length for beep hit_beep_tone equ 0x30 hit_wait_time equ 0x10 ;Time to wait after being hit, before coming back to life initial_lives equ 0x0a ;Initial number of lives reset_beep_tone equ 0x10 dead_beep_tone equ 0x40 org 0000h ;Entry point goto start ;begin ISR ;Note: bank 0 must be selected when interrupt is called. Should disable interrupt (GIE) while using other banks org 0004h ;Interrupt address ;Save W and STATUS movwf W_temp ;Save W, to restore later swapf STATUS,W ;Swap nibbles of STATUS into W (why swap?) ;bcf STATUS,5 ;Change to bank 0 movwf STATUS_temp ;Save STATUS btfss INTCON,0 ;Check RBIF flag bit... goto finish ; goto finish if not set ;Wait a little for inputs to stabilise movlw 10h movwf delay_count ; to delay_count variable call delay ;Do the delay btfss PORTB,5 ;Check RB5.. goto finish ; goto finish if not high ;We've been hit movf lives_left,1 ;Copy life counter onto itself to check for 0 btfsc STATUS,2 ;If life counter is not 0, skip next line goto finish ;Life counter is 0, so do nothing bcf PORTA,7 ;Turn life indicator off movlw hit_beep_tone movwf beep_tone call beep ;Beep ;Wait movlw hit_wait_time ;Copy hit wait time... movwf long_delay_count ; to long_delay_count variable call long_delay ;Do the delay decfsz lives_left,1 ;Decrement life counter, skip next line if 0 goto finish_hit ;Still some lives left, keep going ;No lives left movlw dead_beep_tone movwf beep_tone call beep ;Beep goto finish finish_hit bsf PORTA,7 ;Turn life indicator on again finish movf PORTB,F ;Move PORTB to itself (to prevent another interrupt) bcf INTCON,0 ;Clear RBIF flag bit (may be superfluous) ;Restore W and STATUS swapf STATUS_temp,W ;Copy STATUS_temp to W movwf STATUS ;Copy W to STATUS swapf W_temp,F ;Swap W_temp swapf W_temp,W ;Swap W_temp into W retfie ;Return from interrupt ;end ISR start ;Initialisation ;Setup inputs / outputs: RA1 is output, all others are inputs bsf STATUS,5 ;Go to bank 1 movlw 0x7b ;Copy 01111011 into W... movwf TRISA ; to TRISA register movlw 0xff ;Copy 11111111... movwf TRISB ; to TRISB bcf STATUS,5 ;Go back to bank 0 movlw 0x07 ;Copy 00000111... movwf CMCON ; to CMCON (to disable comparators and allow digital input) ;;;Wait for inputs to stabilise (unnecessary, ISR will wait before checking RB5) ;Enable interrupts from RB4-7 mismatch, and globally bcf INTCON,0 ;Clear RBIF to avoid spurious interrupt (is this necessary?) bsf INTCON,3 bsf INTCON,7 ;Initialise life counter movlw initial_lives movwf lives_left bsf PORTA,7 ;Turn life indicator on (shouldn't be necessary) ;Main loop main btfss PORTA,3 ;Skip next line if reset button not pressed call reset_target clrwdt ;Clear watchdog timer (perhaps disable by configuration fuse?) goto main ;Loop ;Subroutines ;begin subroutine 'reset_target' reset_target movlw reset_beep_tone movwf beep_tone call beep ;Beep ;Reset life counter movlw initial_lives movwf lives_left bsf PORTA,7 ;Turn life LED on return ;end subroutine 'reload' ;begin subroutine 'delay' delay ;Delay loop - delay_count should be set to choose delay time ;post-condition: delay_count is 0x00 ;movlw delay_init ;Initialise second delay counter ;movwf delay_count delay_loop ;movlw 0xff ;Superfluous, registers automatically initialised to 0xFF ;movwf delay_count ;Put 0xFF in delay_count variable decfsz delay_count2,1 ;Decrement, jump past next line if 0 goto delay_loop clrwdt ;Clear watchdog timer (perhaps disable by configuration fuse?) decfsz delay_count,1 ;Decrement, jump past next line if 0 goto delay_loop return ;end subroutine 'delay' ;begin subroutine 'short_delay' short_delay ;Shorter Delay loop - delay_count should be set to choose delay time short_delay_loop movlw 0x10 ;Put 0x10... movwf delay_count2 ; in delay_count2 variable (inner loop) short_delay_loop_inner decfsz delay_count2,1 ;Decrement, jump past next line if 0 goto short_delay_loop_inner clrwdt ;Clear watchdog timer (perhaps disable by configuration fuse?) decfsz delay_count,1 ;Decrement, jump past next line if 0 goto short_delay_loop return ;end subroutine 'short_delay' ;begin subroutine 'long_delay' long_delay ;Longer delay loop - long_delay_count should be set to choose delay time clrf delay_count ;Clear delay_count, ready for calling delay long_delay_loop call delay decfsz long_delay_count,F ;Decrement, skip next line if 0 goto long_delay_loop return ;begin subroutine 'long_delay' ;begin subroutine 'beep' beep ;Initialise loop counter movlw beep_length ;Copy beep_length... movwf beep_count ; to beep count beep_loop bsf PORTA,2 ;Turn piezo output on movf beep_tone,0 movwf delay_count call short_delay bcf PORTA,2 ;Turn piezo output off again movf beep_tone,0 movwf delay_count call short_delay decfsz beep_count,1 ;Decrement, jump past next line if 0 goto beep_loop return ;end subroutine 'beep' end