;------------------------------------------------------------------------------------------------------ ; Simon.asm program ; Written by Andrew D. Vassallo (email@example.com) ; copyright 2000, 2001 ; ; This code may not be used for any commercial purposes. If reproduced in any form, the original ; author's credits must be included. Users are free to distribute this code in any form, as long as ; it is done so free of charge. Modifications are welcome, as long as the original author's credits ; remain intact in the header of this file. ;------------------------------------------------------------------------------------------------------ ; Program Abstract ; ; This is based on an old handheld electronic game "Simon." Through 4 pushbutton switches, ; the user attempts to mirror an increasingly difficult sequence of blinking LEDs and speaker ; tones. Upon successful game completion (of 63 moves), the game sounds a cycling series of tones. ; Upon failed input at any point in the game, the correct move will be displayed, the error tone ; will sound, and then the maximum move number reached is sounded out on the speaker: first, low ; tones will sound the "tens" and then a higher tone will sound the "ones." So, for 12 moves reached, ; one single low tone will sound followed by two higher tones. ; ; Each move is stored in memory and matched against 4 user input switches. ; The "moves" generated by the program are obtained from the TMR0 register at the time of ; the last user input, preventing the user from intentionally pushing a switch at a specific ; time to generate a predictable LED turn-on. ; ; Each "move" is recorded in EEPROM data memory, 1 "move" per memory register (since location 0x00 isn't ; used, we really only have 63 moves possible). Upon ; mismatch of a switch input from the user and a recorded (expected) "move," the correct LED ; will turn on while a buzzer sounds the error tone. Also, if the user takes more time ; than allowed (~5 seconds), the timeout will indicate failed match. Note that if the user waits ; 4.9 seconds to press the button, then he has another 4 seconds to release it, as TMR_Overflow is ; reset. This gives a total of 10 seconds time to think between moves if the user is really careful. ; ; (Note: It is possible to fit 4 "moves" per register (shifted in) to maximize the storage capacity at 252 ; moves, but at this point, 63 moves are enough. If anyone wants to try to increase this to 126 moves (swap ; the moves into the storage registers) or more (up to 252 moves by shifting the 2 LED bits), go right ; ahead. I wrote the core of this in a day, and added some options later, so there's probably some room for ; improvement. On the other hand, if you can remember more than 63 moves in sequence, you probably ; shouldn't be playing this game :) ; ; Note the use of TMR0 interrupts to generate the different tones. This effectively allows continuous ; asynchronous control of switch input polling and speaker oscillation. ; I tried to comment this as completely as possible to help out those new to PIC programming. If ; there's something you don't understand, drop me an e-mail and I'll try to help. ; ;------------------------------------------------------------------------------------------------------ ; Options: ; A reset button (SW5) is provided for the user to restart a new game (pulls MCLR down to Vss for reset). ; This button may be omitted if a power On/Off switch is installed. ; ; A switch (SW6) is provided for choosing high or low difficulty (fast or slow game play). ; ; Holding down the Red button (SW1) during power-up will initiate a "demonstration mode," where the program ; creates the move sequence and displays it without accepting user input. ; ; Holding down the Yellow button (SW2) during power-up will initiate a "reverse mode," where the sequence ; of moves is displayed in reverse order (the latest move is displayed first). ; ; Holding down the Green button (SW3) during power-up will initiate a "silent mode," where the tones are ; not sounded along with the LED illumination. ; ; Holding down the Blue button (SW4) during power-up will initate a "double mode," where two moves are ; generated each time around. ;------------------------------------------------------------------------------------------------------ list p=16F84 ; list directive to define processor #include <p16F84.inc> ; processor specific variable definitions __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _RC_OSC ;------ All timing in this program is set for RC operation using a 4.7K and 18 pF RC circuit. This really ;------ doesn't operate properly at the calculated frequency, so I had to change the timing for the delay ;------ loops and buzzer tones through experimentation. Each Tcy is approximately 1.8us rather than 1.0us ;------ (at 4MHz). If desired, a 4MHz crystal could be used with a TMR0 prescale of 1:2 and probably achieve ;------ the same overall game speed. Maybe some of the Delay subroutine timing would have to be fixed, though, ;------ as it counts based on incrementing W, not via TMR0. ;***** VARIABLE DEFINITIONS CBLOCK 0x0C w_temp status_temp ; for context saving during interrupt routines rnd_hold ; register to hold temporary value of rnd number TMR_Div ; divisor for TMR0 overflow - every 64 overflows equals ONE TMR_Overflow increment TMR_Overflow ; counts how many times TMR0 overflows Delay_Count ; counter for delay loop LED_Number ; holds which LED should be active User_Number ; holds which button was pressed by user Move_Number ; register to hold address of EEPROM current move Recall_Addr ; current move number - call address for EEPROM recall routine speed_value ; holds the current tone delay cycle Tone ; register to hold flags to determine which tone to sound: ; (if all clear, do not sound any tones) ; bit0: 1=sound Red_Tone ; 0=don't sound Red_Tone ; bit1: 1=sound Yellow_Tone ; 0=don't sound Yellow_Tone ; bit2: 1=sound Green_Tone ; 0=don't sound Green_Tone ; bit3: 1=sound Blue_Tone ; 0=don't sound Blue_Tone ; bit4: 1=sound Error_Tone ; 0=don't sound Error_Tone Tone_Count ; register to hold the number of TMR0 overflows desired for proper tone flags ; register to hold generic flags ; bit0: 1=tone output wave currently high ; 0=tone output wave currently low ; bit1: 1=demo mode enabled ; 0=demo mode off - normal program operation ; bit2: 1=reverse mode enabled - newest move displayed first ; 0=normal program operation - newest move displayed last ; bit3: 1=difficult mode - two moves per round ; 0=easy mode - normal operation ; bit4: 1=second time through loop for difficult mode ; 0=first time through loop - ok to repeat for second move in this round ; bit5: 1=silent mode enabled ; 0=not silent mode - OK to output tones Hold_Number ; register to hold the random number seed for next randomized number (used for demo mode) ENDC ; 15 RAM registers allocated ;------ Initialize literal values Timeout EQU 0xC8 ; timeout delay limit (Timeout * TMR0 overflows*64 = ~5 seconds) easy_speed EQU 0x14 ; starting speed value - 20 loops is a nice starting speed ; increasing this will slow the game play down by increasing the buzzer ON time, and vice versa ; Using this value, it will take 26 moves to get down to the base_speed. difficult_speed EQU 0x0D ; using this value, it will take 12 moves to get to the base speed base_speed EQU 0x07 ; upper limit for speed, be careful (0x07 is not very fast, but fast enough) Error_Tone EQU 0x08 ; 8 loops @ 256*1.8us = 135 Hz Red_Tone EQU 0x05 ; 5 loops @ 256*1.8us = 2.30ms per high and low bit (217Hz) Yellow_Tone EQU 0x04 ; 4 loops @ 256*1.8us = 1.84ms per high and low bit (271Hz) Green_Tone EQU 0x03 ; 3 loops @ 256*1.8us = 1.38ms per high and low bit (361Hz) Blue_Tone EQU 0x02 ; 2 loops @ 256*1.8us = .920ms per high and low bit (542Hz) OPTIONVAL EQU 0x98 ; PORTB P/U off, TMR0 internal, RB0 falling edge, WDT prescale (1:1 for TMR0) INTCONVAL EQU 0xA8 ; GIE & T0IE enabled, RB0 int. disabled, RBIE (port change) enabled for Tone generation only TRISAVAL EQU 0xFF ; direction: PORTA all input ;-- PORTA<0:3> are Red, Yellow, Green, Blue pushbuttons, respectively TRISBVAL EQU 0x00 ; direction: PORTB all output ;-- RB1 is connected to speaker output. ;-- PORTB<2:5> are Red, Yellow, Green, Blue LEDs, respectively ;-- Note that RB0 is reserved in case RB0 interrupt is desired. ;********************************************************************** ORG 0x000 ; processor reset vector goto Start ; go to beginning of program ;------ Interrupts below ORG 0x004 ; interrupt vector location movwf w_temp ; save off W and STATUS registers swapf STATUS, 0 movwf status_temp clrf STATUS ; select Bank0 btfsc flags, 5 ; if silent mode... clrf Tone ; ...then prevent any tones from sounding btfsc INTCON, T0IF goto Timer_Int btfsc INTCON, RBIF ; PORTB change flag (only set to generate tone) goto Generate_Tone movf INTCON, 0 andlw 0xF8 ; clear all flags and ignore the interrupt if unknown movwf INTCON goto Reset_Interrupts Timer_Int ;------ Note the use of TMR_Div - this achieves a dual rate for TMR0 overflow. The straight 1:1 rate is used for ;------ the tone generation frequency count, and the 1:64 rate is used for the game speed. bcf INTCON, T0IF ; clear TMR0 overflow-interrupt flag decfsz TMR_Div goto Continue_Tone ; always find out if a tone is sounding incf TMR_Overflow ; this increments once every 64 TMR0 overflows movlw 0x40 ; reset TMR_Div to 64 cycles through movwf TMR_Div goto Continue_Tone ; always check tone sound if TMR0 overflows Generate_Tone ;------ Find out which Tone should be sounded, then begin the tone on the NEXT TMR0 interrupt btfsc Tone, 0 movlw Red_Tone btfsc Tone, 1 movlw Yellow_Tone btfsc Tone, 2 movlw Green_Tone btfsc Tone, 3 movlw Blue_Tone btfsc Tone, 4 movlw Error_Tone movwf Tone_Count goto Reset_Interrupts Continue_Tone movf Tone, 1 btfss STATUS, Z ; if Tone register is all zeros, do not sound or continue any tones decfsz Tone_Count goto Reset_Interrupts goto Switch_Wave Switch_Wave btfss flags, 0 ; if we're currently outputting a high wave, switch to low goto Set_High bcf PORTB, 1 bcf flags, 0 ; we're outputting a low wave now goto Generate_Tone ; reset Tone_Count for next time Set_High bsf PORTB, 1 ; otherwise set the wave high bsf flags, 0 ; we're outputting a high wave now goto Generate_Tone Reset_Interrupts bcf INTCON, RBIF ; always keep this flag cleared swapf status_temp, 0 ; restore all registers movwf STATUS swapf w_temp, 1 swapf w_temp, 0 retfie ;------ Done with interrupts. Start bsf STATUS, RP0 ; select bank 1 movlw OPTIONVAL movwf OPTION_REG ; set options in PIC movlw TRISAVAL movwf TRISA ; set port A direction bits movlw TRISBVAL ; set PORTB for all output movwf TRISB bcf STATUS, RP0 ; select bank 0 clrf Move_Number clrf Tone clrf flags movlw 0x3C movwf PORTB ; set all LEDs to turn on for power-up indication btfss PORTA, 0 ; Red button normally pulled high - if low, then enable demo mode bsf flags, 1 btfss PORTA, 1 ; Yellow button normally pulled high - if low, then enable reverse mode bsf flags, 2 btfss PORTA, 2 ; Green button normally pulled high - if low, then enable silent mode bsf flags, 5 btfss PORTA, 3 ; Blue button normally pulled high - if low, then enable double mode bsf flags, 3 movlw base_speed ; base speed - absolute upper speed limit movwf speed_value movlw easy_speed ; default is easy speed btfss PORTA, 4 movlw difficult_speed ; difficult switch selected (normally pulled high) addwf speed_value ; add the constant to the base speed ;------ How speed_value works: ; LED will light and correct tone will sound *when generated by the program*. (When user input generates the tone, it ; will shut off when the button is released, or if the timeout is exceeded) A flag is set to initiate an interrupt ; to begin the tone sounding. After TMR_Overflow increments enough to equal the speed_value, the sound will shut off. ; TMR_Overflow will increment once per (256 TMR0 counts * 64 TMR_Div) instructions @ ~1.8us each. So, the total time for ; each TMR_Overflow is ~0.0295 seconds. With a speed_value of 0x10, for example, this will be 16*.0295=0.47 seconds per ; move. Note that this will keep getting faster until the base_speed is reached. ;------ ;------ Delay for ~4 seconds (3 + 1 for Main delay) for user to get set up clrw clrf Delay_Count ; set for 256 loops (~.45 seconds total) call Delay clrw clrf Delay_Count ; set for 256 loops call Delay clrw clrf Delay_Count ; set for 256 loops call Delay clrw clrf Delay_Count ; set for 256 loops call Delay clrw clrf Delay_Count ; set for 256 loops call Delay clrw clrf Delay_Count ; set for 256 loops call Delay clrw clrf Delay_Count ; set for 256 loops call Delay movlw INTCONVAL movwf INTCON ; set interrupts movf TMR0, 0 movwf Hold_Number ; used for demo mode clrf PORTB ; turn off all LEDs after power-up sequence Main ;------ Delay for ~1 second between sequences clrw clrf Delay_Count ; set for 256 loops call Delay clrw clrf Delay_Count ; set for 256 loops call Delay rrf Move_Number, 0 ; decrement once every 2 moves to increase game speed btfss STATUS, C goto No_Reduction movf speed_value, 0 sublw base_speed btfss STATUS, C ; if speed_value >= base speed, speed_value-- every 2 moves decf speed_value No_Reduction ;------ This section reads the current value of TMR0 (unpredictable because of user input on switches) and loads ;------ it into LED_Number to be randomized. If demo mode is enabled, don't use TMR0 each time, only use the first ;------ one as a seed, then recycle each randomized value as a seed for the next move. ;------ Once the move is randomized, it's stored in EEPROM memory, then the recall address is set (to the first move ;------ if normal operation, or to the last move if reverse mode is enabled) and each move is recalled and displayed ;------ at a speed determined by the speed_value. movf TMR0, 0 ; use unknown state of TMR0 as seed for random number btfsc flags, 1 ; if demo mode, load random number movf Hold_Number, 0 movwf LED_Number call Randomize ; returns LED number to illuminate next incf Move_Number ; create next move - do not use location 0x00 (start at 0x01) call Store_Number ; add this number to stored values clrf Recall_Addr incf Recall_Addr ; begin at 0x01 for move recall for normal operation mode movf Move_Number, 0 btfsc flags, 2 ; if reverse mode, begin recall address at maximum move number movwf Recall_Addr ; start recalling moves at the current max. move number ; This register is decreased to zero to provide the move sequence ; while the Move_Number register holds the current latest move number sublw 0x40 btfsc STATUS, Z ; 64 moves max. (really 63 since we started at 0x01) goto Game_Finished ; if we get to this point, game is over btfsc flags, 3 ; if we're in difficult mode, then check flag bit 4 btfsc flags, 4 ; if this is the first time through (bit=clear)... goto Recall_Loop ; if bit3 clear or if bit4 set, skip the second move bsf flags, 4 goto Main ; ...then set flag and create another move for difficult mode Recall_Loop bcf flags, 4 ; as default, clear flag movlw 0xAA ; set for 170 loops to add a small break between moves movwf Delay_Count clrw call Delay call Recall_Number ; read the current Recall_Addr and return the move into LED_Number movlw LED_Number ; treat as literal to load address pointer into FSR movwf FSR ; load FSR with LED_Number address pointer call Switch_LED ; determines which LED to illuminate, does so and selects tone call SoundTone ; begin sounding correct tone btfsc flags, 2 ; if reverse mode is set, skip to proper display method goto Reverse_Method movf Move_Number, 0 subwf Recall_Addr, 0 btfsc STATUS, C goto Test_Mode ; if equal or greater, we're done recalling moves incf Recall_Addr ; otherwise display next move goto Recall_Loop Reverse_Method decfsz Recall_Addr ; output next move (note that 0x01 is the lowest move) goto Recall_Loop Test_Mode btfsc flags, 1 ; if demo mode, skip user input... goto Main ; ...and loop directly back to generate the next move ;------ This next section of the program recalls each move and waits for user input before comparing them. After ;------ each move is recalled, we wait for user input by polling the switches. Once one is pressed, the corresponding ;------ tone is sounded as long as the button is depressed. Once released (if within the timeout period), the ;------ user input move is compared with the expected (recalled) move. If they match, the next move is recalled ;------ until all moves are finished. Then we loop back again to generate the next move. Once we hit 63 moves, ;------ we're done. clrf Recall_Addr incf Recall_Addr ; begin at 0x01 for move recall for normal operation mode movf Move_Number, 0 btfsc flags, 2 ; if reverse mode, start at maximum move number movwf Recall_Addr ; reset address register to top position GetUserInput call Recall_Number ; get current "move" to match with user input movlw 0x40 movwf TMR_Div ; reset divisor and overflow registers clrf TMR_Overflow ; holds total timeout counts for user input call WaitForButton ; waits for user input, W register holds button pressed on return movwf User_Number ; dump off which button was pressed movlw 0x0C movwf Delay_Count clrw call Delay ; debounce switch to 20ms movlw User_Number ; treat as literal to load address pointer into FSR movwf FSR ; load FSR with LED_Number address pointer call Switch_LED ; indicate which LED the user pressed ;------ Wait for button to be released - user can't cheat by holding down the button due to timeout. ;------ We will use User_Number to determine which button to wait for, rather than check ALL buttons, since ;------ the user could press another button and, due to the bounce, effectively "release" a button, thus throwing ;------ off the timing. movlw 0x40 movwf TMR_Div clrf TMR_Overflow bsf INTCON, RBIF ; use flag to begin generating tone call WaitForRelease ; if timeout occurs, Failed_Input will be called from WaitForRelease clrf PORTB ; turn off LEDs (and buzzer if still on) clrf Tone ; reset the tone register to stop generating tone movlw 0x0C movwf Delay_Count clrw call Delay ; debounce switch to 20ms movf LED_Number, 0 ; compare LED and User numbers subwf User_Number, 0 btfss STATUS, Z goto Failed_Input ; if current move and User inputs don't match, abort btfsc flags, 2 ; if reverse mode is set, skip to proper display method goto Reverse_Compare movf Move_Number, 0 subwf Recall_Addr, 0 btfsc STATUS, C goto Main ; if equal or greater, we're done recalling moves incf Recall_Addr ; otherwise display next move goto GetUserInput Reverse_Compare decfsz Recall_Addr ; output next move (note that 0x01 is the lowest move) goto GetUserInput goto Main ; continuous loop - find the next number in the cycle ;------------------------------------------- Win/Loss Endgame routines listed below -------------------------------- Failed_Input ;------ Turn on LED that was SUPPOSED to be entered and sound error tone. clrf PORTB ; reset LEDs and buzzer in preparation for Error indication movlw LED_Number ; treat as literal to load address pointer into FSR movwf FSR ; load FSR with LED_Number address pointer call Switch_LED ; will sound correct tone with LED in addition to the error_tone movlw 0x40 ; long tone delay movwf speed_value clrf Tone bsf Tone, 4 ; error tone to be sounded call SoundTone clrw clrf Delay_Count call Delay clrw clrf Delay_Count ; set for 256 loops call Delay ; wait for 1 second before sounding completed move count ;------ Divide Move_Number by 10 to identify to the user the maximum move number achieved. Store multiplier in Recall_Addr ;------ and remainder in Move_Number. Sound out total count with 10s as long low tones and 1s as short high tones. ;------ Also, light up Red LED for 10s and Yellow LED for 1s. ;------ We're not doing anything fancy here - we're just using successive subtraction for the division since it ;------ would be such a small number. Alternately, some real division bit-shifting code could be substituted. clrf Recall_Addr Div_By_10 movlw 0x0A ; successive subtraction by 10 subwf Move_Number, 0 btfss STATUS, C ; if carry=0, we overflowed, so begin sounding tones goto Sound_Tens incf Recall_Addr movwf Move_Number ; move remainder into Move_Number register for next subtraction goto Div_By_10 Sound_Tens movf Recall_Addr, 1 ; move file to itself to test for zero btfsc STATUS, Z goto Sound_Ones ; if Recall_Addr = 0, skip to Ones movlw 0x20 ; longer time duration for 10s movwf speed_value clrf Tone bsf PORTB, 2 ; turn Red LED on bsf Tone, 0 ; Red tone counts number of 10s call SoundTone ; shuts off buzzer and LED clrw clrf Delay_Count ; set for 256 loops call Delay ; wait for 0.5 seconds between tones decfsz Recall_Addr goto Sound_Tens Sound_Ones movf Move_Number, 1 btfsc STATUS, Z sleep ; if no Ones, then end game movlw 0x10 ; shorter tone duration for 1s movwf speed_value clrf Tone bsf PORTB, 3 ; turn Yellow LED on bsf Tone, 3 ; Blue tone counts number of 1s call SoundTone ; shuts off buzzer and LED clrw clrf Delay_Count ; set for 256 loops call Delay ; wait for 0.5 seconds between tones decfsz Move_Number goto Sound_Ones sleep ; power-down to save battery after game over Game_Finished ;------ Cycle speaker tones to indicate game won. movlw 0x07 movwf speed_value ; preset quick cycle time for tones in this routine movlw 0x05 movwf Delay_Count ; just re-use Delay_Count as a loop counter clrf Tone GF_Loop bsf Tone, 0 call SoundTone bsf Tone, 1 call SoundTone bsf Tone, 2 call SoundTone bsf Tone, 3 call SoundTone decfsz Delay_Count ; 4 times through this routine goto GF_Loop sleep ;------------------------------------------- Subroutines listed below ---------------------------------------------- Store_Number ;------ Stores value of LED_Number into EEPROM address "Move_Number" movf Move_Number, 0 movwf EEADR movf LED_Number, 0 movwf EEDATA bsf STATUS, RP0 ; select Bank1 for EECON access bcf INTCON, GIE ; disable interrupts temporarily bsf EECON1, WREN ; enable EEPROM write movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1, WR ; write values to EE bsf INTCON, GIE ; reenable interrupts bcf EECON1, WREN ; disable EEPROM write Waitforflag btfss EECON1, EEIF ; poll flag bit for write cycle completion goto Waitforflag bcf EECON1, EEIF ; reset flag - write cycle complete bcf STATUS, RP0 ; leave Bank0 active by default return Recall_Number ;------ Recalls the value located at Recall_Addr from EEPROM into LED_Number movf Recall_Addr, 0 movwf EEADR bsf STATUS, RP0 ; select Bank1 for EECON access bsf EECON1, RD ; enable read bcf STATUS, RP0 ; select Bank0 for EEDATA access movf EEDATA, 0 movwf LED_Number return Randomize ;Rnew = Rold * 221 + 53 ;221 = 256 - 32 - 4 + 1 ;256 can be eliminated ;so we need to calculate Rnew = Rold * (1 - 32 - 4) + 53 using ;truncating arithmetic ;or Rnew = Rold * (-32 - 3) + 53 clrc rlf LED_Number, 1 swapf LED_Number, 0 andlw 0xE0 rrf LED_Number, 1 addwf LED_Number, 0 addwf LED_Number, 0 addwf LED_Number, 0 sublw 0x35 movwf LED_Number movwf Hold_Number ; used for demo mode only ;Then divide by 64 to get a result from 0-3 (4 values): ;shift LED_Number right 6 times rlf LED_Number, 1 clrf rnd_hold rlf rnd_hold, 1 rlf LED_Number, 1 rlf rnd_hold, 1 movf rnd_hold, 0 movwf LED_Number return Switch_LED ;------ Find out which LED should be turned on, then do it and select appropriate tone ;------ Indirect addressing is used due to 2 different sources to switch (user or program) ;------ Leave LED on when finished - must clear PORTB after this routine to turn off LEDs ;------ (This is done so when button is pressed by user input, the LED remains on) btfsc INDF, 1 ; test bit 1 goto GT_2 ; number is either 2 or 3 btfsc INDF, 0 ; test bit 0 goto Yellow bsf PORTB, 2 ; must be zero (Red) bsf Tone, 0 ; set flag to turn on Low tone return Yellow bsf PORTB, 3 ; number is 1 (Yellow) bsf Tone, 1 return GT_2 btfsc INDF, 0 goto Blue bsf PORTB, 4 ; must be 2 (Green) bsf Tone, 2 return Blue bsf PORTB, 5 ; number is 3 (Blue) bsf Tone, 3 return WaitForButton ;------ Continually poll PORTA switches to determine which was pressed (grounded). Rather than using ;------ PORTB pin interrupt on change, polling is just as quick and I don't have to worry about reading ;------ other PORTB pins causing trouble or disabling/enabling interrupts at the proper times, etc. ;------ If TMR_Overflow equals allowed delay time (Timeout register), then abort due to timeout. btfss PORTA, 0 ; normally pulled-up retlw 0x00 ; Red button depressed (RA0) btfss PORTA, 1 retlw 0x01 ; Yellow (RA1) btfss PORTA, 2 retlw 0x02 ; Green (RA2) btfss PORTA, 3 retlw 0x03 ; Blue (RA3) movf TMR_Overflow, 0 sublw Timeout btfsc STATUS, Z goto Failed_Input goto WaitForButton WaitForRelease ;------ Continually poll PORTA switches to determine if User_Number pin was released. ;------ If TMR_Overflow equals allowed delay time (Timeout register), then abort due to timeout. btfsc User_Number, 1 ; test bit 1 goto Pin2_3 ; number is either 2 or 3 btfsc User_Number, 0 ; test bit 0 goto Pin1Loop Pin0Loop btfsc PORTA, 0 ; when pin is pulled up again, return return movf TMR_Overflow, 0 sublw Timeout btfsc STATUS, Z goto Failed_Input ; exceeded timeout allowance goto Pin0Loop Pin1Loop btfsc PORTA, 1 return movf TMR_Overflow, 0 sublw Timeout btfsc STATUS, Z goto Failed_Input goto Pin1Loop Pin2_3 btfsc User_Number, 0 goto Pin3Loop Pin2Loop btfsc PORTA, 2 return movf TMR_Overflow, 0 sublw Timeout btfsc STATUS, Z goto Failed_Input goto Pin2Loop Pin3Loop btfsc PORTA, 3 return movf TMR_Overflow, 0 sublw Timeout btfsc STATUS, Z goto Failed_Input goto Pin3Loop SoundTone ;---Set flag to start interrupt moving which starts tone sounding, clear flag inside interrupt. ;---At every TMR0 overflow, we will check to see if tone is still sounding and also to see if we have ;---to change the wave bit from high to low. The "Tone" register bits being set will determine if ;---the PORTB, 1 output continues, since the flag is cleared after the first time through. movlw 0x40 movwf TMR_Div ; reset divisor and overflow registers clrf TMR_Overflow bsf INTCON, RBIF ; use flag to begin generating tone SoundToneLoop movf speed_value, 0 subwf TMR_Overflow, 0 btfss STATUS, Z goto SoundToneLoop clrf PORTB ; turn off LEDs (and buzzer if still on) clrf Tone ; reset the tone register to stop generating tone return Delay ;------ Note that W register must be cleared before calling this routine to obtain the proper delay. ;------ Delay_Count is predefined with the multiplier (Delay_Count*256 loops*4Tcy) gives total delay. ;------ Delay is approximately .45 seconds with Delay_Count=0xFF. ;------ Note that if W register is preloaded with higher values, the total delay time can be fine tuned. ;------ Alternately, another constant can be defined and preloaded prior to entering this routine, then ;------ copying it to W and proceding normally. Another inner loop would need to be defined, with the ;------ constant value being held outside the inner loop. addlw 0x01 btfss STATUS, Z goto Delay decfsz Delay_Count goto Delay return END ; directive 'end of program'
|file: /Techref/piclist/simon/simon.asm, 28KB, , updated: 2001/2/27 15:54, local time: 2019/12/13 01:29,
|©2019 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?|
<A HREF="http://www.piclist.com/techref/piclist/simon/simon.asm"> piclist simon simon</A>
|Did you find what you needed?|
PICList 2019 contributors:
o List host: MIT, Site host massmind.org, Top posters @20191213 RussellMc, David Van Horn, Bob Blick, Neil, AB Pearce - UKRI STFC, Jason White, James Cameron, Dwayne Reid, Manu Abraham, David C Brown,
* Page Editors: James Newton, David Cary, and YOU!
* Roman Black of Black Robotics donates from sales of Linistep stepper controller kits.
* Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters.
* Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated!
* Contributors: Richard Seriani, Sr.
Welcome to www.piclist.com!