please dont rip this site

Piclist Simon Simon.asm

;------------------------------------------------------------------------------------------------------
; Simon.asm program
; Written by Andrew D. Vassallo (snurple@hotmail.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 16:54, local time: 2024/4/20 01:51,
TOP NEW HELP FIND: 
18.226.177.223:LOG IN

 ©2024 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?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://www.sxlist.com/techref/piclist/simon/simon.asm"> piclist simon simon</A>

Did you find what you needed?

 

Welcome to sxlist.com!


Site supported by
sales, advertizing,
& kind contributors
just like you!

Please don't rip/copy
(here's why

Copies of the site on CD
are available at minimal cost.
 

Welcome to www.sxlist.com!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .