
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;											;
;	Reaction version 1.0					;
;											;
;	By Brad Slattery 2009					;
;	www.bradsprojects.com					;
;	brad@bradsprojects.com					;
;											;
;	'Designing electronic projects,			;
;	to spread the name of Jesus'			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




	LIST p=16f628a 				;	tell assembler what chip we are using (if you are using the 16f628a, then
	include "P16f628a.inc"		;	make sure you change this line and the previous line to read p16f628a
	__config h'3f18'			;	sets the configuration settings - internal oscillator, watchdog timer OFF
								;	Power Up Timer DISABLED, Master Clear DISABLED, Brown Out Detect DISABLED,
								;	Low Voltage Programming DISABLED, Data EE Read Protect Disabled,
								;	Code Protect OFF. (this will remain the same for all tutorials)


PC equ h'02'					;	The program counter will be refered to as PC - The program counter is
								;	a little counter within the microcontroller to let itself know what line number
								;	it is upto when running a program. We can make the microcontroller jump to a certain
								;	line number by changing the value stored in PC. (we will do this in a later tutorial)

	cblock h'20'				;	Start defining variables
	delay_1						;	used in the delay routine
	delay_2						;	used in the delay routine
	random_counter				;	used to create our 'random' number
	random_number				;	used to save our 'random' number
	reaction_timer				;	used to determine how fast you pressed the button
	reaction_loop_hold_1		;	used to slow down how fast our reaction variable counts
	reaction_loop_hold_2		;	used to slow down how fast our reaction variable counts
	false_start_counter			;	used to hold our LED in the false start indication mode
	animation_loop_counter		;	used to determine how long we display our animation for.
	endc						;	end of defining variables



	org h'0000'					;	start from 0000 hex


	movlw h'07'					;	This will turn the comparators OFF.	
	movwf CMCON 				;	(we just want to use the ports as digital ports)


	bsf STATUS, RP0 			;	select bank 1 (to enable us to change the Input / Output status of our ports)
	movlw b'00000000' 			;	set PORTB all outputs (A '0' means output, A '1' means input. We can set each
	movwf TRISB					;	We can set each bit individualy. Each port having 8-bits or 8 pins.
	movlw b'00100000' 			;	set PORTA all outputs except for bit 5. Bit 5 is an input ONLY pin.
	movwf TRISA 				;	It cannot be set to an output!
	bcf STATUS, RP0 			;	select bank 0




setup							;	Get everything setup ready for the main program

	clrf PORTA					;	ensures LED is off (both red and green)
	movlw b'11111111'			;	ensures seven segment display is off (remember it's a common ANODE)
	movwf PORTB

	movlw d'150'				;	Let's start the game with a medium delay before displaying GREEN
	movwf random_number			;	(255 would be max delay)
	clrf random_counter			;	ensure that we start counting from the start (00)
	clrf reaction_loop_hold_1	;	ensure that these two variables give us the maximum delay possible
	clrf reaction_loop_hold_2	;	which is counting down from 000 (remember if we decrement 000 it then goes to 255)
								;	we then have to wait for 000 to come back around before the decfsz inctruction will skip


begin							;	This is where our main program starts.
	call display_red			;	call the display red routine
	call display_green			;	call the display green routine 
	goto begin					;	and now go and do it all again (it will run in a continuos loop)


display_red						;	this subroutine takes care of displaying the RED LED.
	bcf PORTA, 1				;	turn on the RED LED and
	bsf PORTA, 0				;	turn off the GREEN LED.
red_loop						;	loop back here if we are not yet ready to display the GREEN LED.
	movlw d'50'					;	set a delay to slow down the process of changing from red to Green	
	call delay					;	(this delay is quite quick to ensure that it still recognises a button press)
	btfsc PORTA, 5				;	have we pressed the button?
	goto false_start			;	if yes then we shouldn't have! so goto the false start routine to flash the LED
	decfsz random_number, f		;	if we haven't pressed it then decrement whatever was in random_number by 1
	goto red_loop				;	if we have not reached zero then just keep displaying red BUT
	return						;	if we have counted all the way to zero, then we have finished displaying RED and now
								;	it is time to display the GREEN LED, so lets return shall we!


false_start								;	we come here if the button was pressed BEFORE the GREEN LED had illuminated.
	movlw d'10'							;	We want to repeat this false start LED flash 10 times
	movwf false_start_counter			;
false_start_loop						;	we keep flashing the LED until our 10 flahses are completed.
	bcf PORTA, 0						;	turn off the RED LED
	movlw d'100'						;	setup a delay for a reasonably fast flash rate
	call delay							;	and now run that delay
	bsf PORTA, 0						;	turn on the RED LED
	movlw d'100'						;	setup a delay for a reasonably fast flash rate
	call delay							;	and now run that delay
		decfsz false_start_counter, f	;	have we finished with our LED flashes yet?
	goto false_start_loop				;	if not then go up and flash it another time
	goto begin							;	but if we have, go back to the beginning of the game


display_green							;	This routine takes care of displaying the Green LED and then checking our reaction
	clrf reaction_timer					;	ensure that we start the reaction timer from the start. (dont want to have a handicap)
	bcf PORTA, 0						;	turn off the RED LED
	bsf PORTA, 1						;	turn on the GREEN LED
delay_loop_green						;	We come back here if we are not yet done with displaying the GREEN LED
	movlw d'80'							;	setup a delay in our green loop	(the higher the number here means that
	movwf delay_1						;	it is easier to get a good reaction time) try making it 255 and see how
	movwf delay_2						;	easy it is to get a reaction time of zero!
green_loop								;	come back here if we are not yet finished with the green loop
	decf random_counter, f				;	this is used to generate our 'random' number
	btfsc PORTA, 5						;	have we pressed the button yet?
	goto result_loop					;	if we have then go straight to the result routine BUT
	decfsz delay_1, f					;	if we haven't pressed it then continue in our green loop animation
		goto green_loop					;	until we finally reach 0 in both delay_1 and delay_2
	decfsz delay_2, f					;	when we do finally reach zero then we can increment the reaction timer by 1.
		goto green_loop					;	can you see that the more times that we continue in this loop then the slower
										;	the reaction timer increments and therefor makes it easier for us to get a goog score?
	incf reaction_timer, f				;	okay now we have finished a loop we can increment the reaction_timer by 1.
	movf reaction_timer, w				;	and now move that into w so then we can perform a subtraction instruction
	sublw d'09'							;	we will get some answer here but we aren't concerned with the number, but rather whether
	btfsc STATUS, C						;	the 09 is greater than what was in w (which was the number in reaction_timer)
	goto delay_loop_green				;	if we have not yet reached the max count of 9 then go back and do it again
	movlw d'09'							;	if we have reached 09 then we need to copy 09 back into reaction_timer
	movwf reaction_timer				;	this will then go onto performing the result_loop routine (below)


result_loop
	call save_random_number				;	now we need to save whatever random number we landed on
	call animation						;	before we display the score, we want to display a nice little animation!
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'00'							;	then check to see if the number equals 00,
	btfsc STATUS, Z						;	if it does then we have landed on zero (very quick indeed!)
	goto number_0						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'01'							;	then check to see if the number equals 01,
	btfsc STATUS, Z						;	if it does then we have landed on one
	goto number_1						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'02'							;	then check to see if the number equals 02,
	btfsc STATUS, Z						;	if it does then we have landed on two
	goto number_2						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'03'							;	then check to see if the number equals 03,
	btfsc STATUS, Z						;	if it does then we have landed on three
	goto number_3						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'04'							;	then check to see if the number equals 04,
	btfsc STATUS, Z						;	if it does then we have landed on four
	goto number_4						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'05'							;	then check to see if the number equals 05,
	btfsc STATUS, Z						;	if it does then we have landed on five
	goto number_5						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'06'							;	then check to see if the number equals 06,
	btfsc STATUS, Z						;	if it does then we have landed on six
	goto number_6						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'07'							;	then check to see if the number equals 07,
	btfsc STATUS, Z						;	if it does then we have landed on seven
	goto number_7						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'08'							;	then check to see if the number equals 08,
	btfsc STATUS, Z						;	if it does then we have landed on eight
	goto number_8						;	so display this on the seven segment display OTHERWISE:
	movf reaction_timer, w				;	copy our number in reaction_timer into w.
	xorlw d'09'							;	then check to see if the number equals 09,
	btfsc STATUS, Z						;	if it does then we have landed on nine
	goto number_9						;	so display this on the seven segment display


number_0								;	this will display the decimal number 0 on the display
	movlw b'11000000'					;	remember the seven segment display is connected to PORTB
	movwf PORTB							;	we send out combinations of 1's and 0's to turn on certain
	return								;	LED's to give us our decimal digit!
number_1								;	this will display the decimal number 1 on the display
	movlw b'11110011'					;
	movwf PORTB							;
	return								;
number_2								;	this will display the decimal number 2 on the display
	movlw b'10100100'					;
	movwf PORTB							;
	return								;
number_3								;	this will display the decimal number 3 on the display
	movlw b'10100001'					;
	movwf PORTB							;
	return								;
number_4								;	this will display the decimal number 4 on the display
	movlw b'10010011'					;
	movwf PORTB							;
	return								;
number_5								;	this will display the decimal number 5 on the display
	movlw b'10001001'					;
	movwf PORTB							;
	return								;
number_6								;	this will display the decimal number 6 on the display
	movlw b'10001000'					;
	movwf PORTB							;
	return								;
number_7								;	this will display the decimal number 7 on the display
	movlw b'11100011'					;
	movwf PORTB							;
	return								;
number_8								;	this will display the decimal number 8 on the display
	movlw b'10000000'					;
	movwf PORTB							;
	return								;
number_9								;	this will display the decimal number 9 on the display
	movlw b'10000001'					;
	movwf PORTB							;
	return								;


animation								;	This routine gives you a nice animation (to keep it interesting...)
	bsf PORTA, 0						;	turn on the RED LED (now both red and green will be on = ORANGE)
	movlw b'11111110'					;	start out with ONE segment on
	movwf PORTB							;	the seven segment display
	movlw d'07'							;	this animation will have 07 'frames'
	movwf animation_loop_counter		;
animation_loop							;	come back here if we have not yet finished with the animation
	movlw d'100'						;	slow down the animation just a bit so we can see it happening.
	call delay							;	and call that delay
	rlf PORTB, f						;	now make a different LED segment turn on.
	decfsz animation_loop_counter, f	;	have we completed out animation yet?
	goto animation_loop					;	if not then keep doing it
	return								;	but if we have, then just return to the main program


save_random_number						;	This takes care of storing our random number for use in the next round...
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'250'						;	is greater than decimal 250.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_a							;	goto load_a so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'229'						;	is greater than decimal 229.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_b							;	goto load_b so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'203'						;	is greater than decimal 203.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_c							;	goto load_c so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'177'						;	is greater than decimal 177.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_d							;	goto load_d so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'151'						;	is greater than decimal 151.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_e							;	goto load_e so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'125'						;	is greater than decimal 125.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_f							;	goto load_f so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'99'							;	is greater than decimal 99.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_g							;	goto load_g so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'73'							;	is greater than decimal 73.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_h							;	goto load_h so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'47'							;	is greater than decimal 47.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_i							;	goto load_i so that we can copy a certain 'random number'.
	movf random_counter, w				;	copy the random_counter to w and then check to see if it
	sublw d'21'							;	is greater than decimal 21.
	btfss STATUS, C						;	if not then check the next number. But if it is greater, then
	goto load_j							;	goto load_j so that we can copy a certain 'random number'.


load_a									;	these next load routines take care of copy a random number for use next time round.
	movlw d'255'						;
	movwf random_number					;
	return								;
load_b									;
	movlw d'154'						;
	movwf random_number					;
	return								;
load_c									;
	movlw d'89'							;
	movwf random_number					;
	return								;
load_d									;
	movlw d'124'						;
	movwf random_number					;
	return								;
load_e									;
	movlw d'175'						;
	movwf random_number					;
	return								;
load_f									;
	movlw d'101'						;
	movwf random_number					;
	return								;
load_g									;
	movlw d'50'							;
	movwf random_number					;	
	return								;
load_h									;
	movlw d'137'						;
	movwf random_number					;
	return								;
load_i									;
	movlw d'167'						;
	movwf random_number					;
	return								;
load_j									;
	movlw d'70'							;
	movwf random_number					;
	return								;




delay							;	here is a nice and simple delay routine
	movwf delay_1				;	copy the number in the w register to delay_1 and delay_2
	movwf delay_2				;	Now the rest of the routine will focus on counting down to zero.
delay_loop						;	We come back to this label when we have not yet reached zero.
	decfsz delay_1, f			;	decrement whatever is in delay_1 by 1 and store the answer back in delay_1
		goto delay_loop			;	if the answer is not zero, then go back to the delay_loop label. but if the
	decfsz delay_2, f			;	answer is zero then decrement delay_2 by one and store the answer in delay_2
		goto delay_loop			;	if the answer is not zero, then go back to delay_loop label. but if the answer
	return						;	is zero, then we have completed our delay and now we can return to our main program!



	end							;	We always need to have end at the end, even if we don't want the program
								;	to actually end, it still must be here!
