SIMON
Analysing the PIC16F628 program

P1   P2   P3   P4   P5

THE PROGRAM
To understand how ANY program works, you have to try to get into the mind of the programmer.
This is very difficult as he doesn't leave much behind, other than the lines of code for the program.
It's easy to see why some instructions have been included but the missing link is the overall structure of the program.
It's easy to see what the instructions are doing in the program, but the question is, why has they been included?
SIMON is a typical example.
How does it work? How are the random values created? How are they stored. How does the program keep track of the player repeating the sequence, how does it store the highest score in EEPROM, and lots of other things.
Each function or "operation" is a "building-block" and once you know how they are created, you can use them for other programs.
That's how your skills build up.
The complete program is on the next page.
In this discussion we will analyse some of the sub-routines.

The first question you may be asking is: "How are the random numbers created?"
The microcontroller has one or more "timers" or "counters" consisting of an 8-bit file or register, that can be connected to the main oscillator in the chip and incremented at the same rate as the micro executes the program.
The timer we have used for the random-number generation is TIMER 0. It is constantly being "clocked" and when the program gets to the sub-routine called: MakeStep it calls the MakeRandom sub-routine:

MakeStep:
	call	MakeRandom	;ascertain random number

The MakeRandom sub-routine looks at timer 0, removes all bits except the two lowest and puts the result into file 34h:

MakeRandom:
	movf	tmr0,w
	andlw	0x03	;mask all but bit0 and bit1
	movwf	random	;move random number into file 34h
	return

The next mystery is: "How are the random numbers stored?"
Firstly you have to understand some of the features of the Simon program:
The game creates up to a maximum of 52 "steps."
Each step consists of a number: 0 = red, 1 = green, 2 = yellow, 3 = orange.
Each result requires 2 bits and the program stores four steps in a file. Bits 0&1 = step 1,
bits 2&3 = step 2, bits 4&5 = step 3, bit 5&7 = step 4.
The first memory location is 3Ah and the last location is 46h. Thirteen locations x 4 = 52 steps.
Each file holding four steps is referred to as a "line." The program has a pointer that looks down the files, from 3Ah to 46h, called the "line pointer."
The 2-bits for each step is called a "column."
The column-pointer is shifted across each file and looks at the 1st, 2nd, 3rd and 4th step.
The sub-routine that places a new value into a file is: MakeStep.
The program also has two files that keeps track of the number of steps.
When power is turned on, all the files are cleared and the "line-file," line_w is loaded with the beginning of memory for the steps (3Ah) and the column file (column_w) will be zero.
MakeStep sub-routine gets the random number and moves the value in line_w to the File Select Register (FSR) so that when the INDirect File (INDF) is accessed, the random number will be placed in the file identified by INDF.
To find out if the random number is to be placed in column 1, 2, 3 or 4, column_w is XOR'ed with 0, 1 or 2. If it is none of these values, the program assumes it is 3. The two bits are added to the file via the IOR instruction. This is the Inclusive OR instruction in which any "1" in either file is placed into the file in its correct location. If column_w is "1," the two bits of the random number are shifted left two times so they can be put into location "2&3" via INDF.
Column_w file is then incremented and bit 2 is tested to see if the file has reached 04. If so, it is zero'ed and line_w is incremented.

MakeStep:
	call	MakeRandom	;get random number
	movf	line_w,w		;get current line
	movwf	fsr		;pointer to next available memory
	bcf	status,c		;initialise carry
	movf	column_w,w	;get current column
	xorlw	0		;is column file = 0?
	BTFSC   STATUS,Z		;zero flag will be set if column file is zero.
	GOTO	GCol0		;column = 0 - don't shift the random number
	xorlw	0x01		;is column file = 1?
	BTFSC   STATUS,Z
	GOTO	GCol1		;column = 1 - shift random 2 times
	XORLW   0x03
	BTFSC   STATUS,Z
	GOTO	GCol2		;column = 2 - shift random 4 times
	rlf	random,f		;column = 3 - shift random 6 times
	rlf	random,f
GCol2:	rlf	random,f
	rlf	random,f
GCol1:	rlf	random,f
	rlf	random,f
GCol0:	movf	indf,w		;file looked at by FSR is put into W
	iorwf	random,w		;merge new random number into table
	movwf	indf		;restore in sequence table
	incf	column_w,f	;update column
	btfss	column_w,2	;have 4 steps been put into the column file?
	return			;no - return
	clrf	column_w		;yes - clear the column counter file. 
	incf	line_w,f		;increment the line file for the next 4 steps
	return

"How does the program read the steps?"
The sub-routine to read the steps is called "ShowSequence."
Another sub-routine allows the creation of up to a maximum of 52 steps which the player has to repeat.
These steps are placed in 13 files - from 3A to 46h - with four steps in each file.
The ShowSequence starts at file 3A by loading 3A into FSR and reading INDF. The answer will be the value stored in file 3A. The first two bits indicate the first step, etc up to bits 6,7 for step 4 of the sequence.
The value in file 3A is transferred to steptemp to work on it.
ShowSequence will not be accessed unless at least one step has been created.
ShowSequence knows the number of steps in the highest file by referring to column_w pointer. The sub-routine assumes all lower files have 4 steps - even though some steps will be "00."
The column-pointer read-file is zeroed and the first line in memory is loaded into FSR.
The value in the first memory location is placed into file 47h (steptemp) to work on it. It may have 1, 2, 3 or 4 steps if the program has just started. The sub-routine gets the number of steps from column_r pointer. If column_r pointer is zero, only one step is in the sequence. File 47h is put into W and bits 2-7 are masked to get only bit 0 and 1. This value is moved to a temporary file called RANDOM and one of the LEDs is displayed on the screen, along with a tone.
The column-read file is then incremented and checked to see if more than 4 passes of the sub-routine have been made and if not it is compared to column_w pointer to see if less than 4 steps are in the file.
When the correct number of steps have been displayed, the micro exits the sub-routine.
Obviously the program would be much simpler if 52 separate files were used. The micro could simply advance down the files and display the particular LED. But that would not demonstrate the skill of the programmer!

ShowSequence:
	clrf	column_r		;zero the read-column pointer
	movlw	LINE_OFFSET	;load 3A into line-pointer 	
	movwf	line_r		;   to start at file 3A	
Ss1:	movf	line_r,w		;this instruction is needed when looping
	movwf	fsr		;put 3A into File Select Register
	movf	indf,w		;put value in file looked-at by FSR into W
	movwf	steptemp		;move value into file 47h to work on it
	movf	column_r,w	;get current column (will be 0, 1, 2 or 3)
	xorlw	0		;is column-pointer = 0?
	BTFSC   	STATUS,Z		;yes, for the first pass of the routine
	GOTO	SCol0		;column-pointer = 0 - don't shift
	xorlw	0x01		;yes, for the second pass of the routine
	BTFSC   	STATUS,Z
	GOTO	SCol1		;column-pointer = 1 - shift random 2 times
	XORLW   	0x03
	BTFSC   	STATUS,Z
	GOTO	SCol2		;column-pointer = 2 - shift random 4 times
	rrf	steptemp,f		;column-pointer will be 3 - shift random 6 times
	rrf	steptemp,f
SCol2:	rrf	steptemp,f
	rrf	steptemp,f
SCol1:	rrf	steptemp,f
	rrf	steptemp,f
SCol0:	movf	steptemp,w	;get current memory byte of table
	andlw	0x03		;mask bits 2-7
	movwf	random		;move the step-value to a file called RANDOM
	call	ShowStep		;display LED according to stored value
	call	Delay150		;wait a little after player
	call	Delay150		;has pressed a key
	call	Delay150
	incf	column_r,f		;1st pass=0, 2nd pass=1, 3rd pass=2, 4th pass=3
	btfsc	column_r,2	;5th pass = 0000 0100 -  bit 2 detected
	goto	Colov1		;	
Ss2:	movf	column_r,w	;no
	xorwf	column_w,w	;column pointers the same? We are detecting 
	btfss	status,z		;  if only one, two or three steps are in the file.
	goto	Ss1		;no - at least another step to show
	movf	line_r,w		;yes
	xorwf	line_w,w		;reached the end of sequence?
	btfsc	status,z		;no - jump next instruction
	return			;yes - sequence complete!
	goto	Ss1		;at least another step to show
Colov1:	clrf	column_r		;zero the pointer as the job is done
	incf	line_r,f		;increment the line pointer
	goto	Ss2	

"How does the program show the steps?"
A 'step" consists of showing the LED then producing a corresponding tone. The program does this via the ShowSep sub-routine.
The value for the LED is contained in file: RANDOM.
This file is XOR'ed with 0, 1 or 2 (and if not one of these values, it assumes the value is 3) to get the value for the LED.
This is done in sub-routine ShowLED and after outputting the value of the LED, the micro returns with a beep value in W so the tone can be produced.

ShowStep:
	call	ShowLed		;show random number on led
	movwf	note_tone		;ShowLed returns with beep value in W
	movlw	LENGTH_SEMIBREVE
	movwf	note_length
	movlw	0x10		;easy level
	btfss	porta,LEVEL	;get level of difficulty
	movlw	0x01		;hard level	
	movwf	note_tempo	;speed of beep
	call	SoundPlay		;play button beep
	clrf	portb		;turn all leds off	
	return

ShowLed:
	movf	random,w		;get random number
	xorlw	0	
	BTFSC   	STATUS,Z
	goto	ShowRed		;random=0
	xorlw	0x01
	BTFSC   	STATUS,Z
	GOTO	ShowGreen	;random=1
	xorlw	0x03
	BTFSC   	STATUS,Z
	GOTO	ShowYellow	;random=2
	goto	ShowOrange	;random=3

ShowRed:
	bsf	portb,LED_RED	;turn on red led
	retlw	KEY_RED_SOUND	;tone frequency

ShowGreen:
	bsf	portb,LED_GREEN	;turn on green led
	retlw	KEY_GREEN_SOUND	;tone frequency

ShowYellow:
	bsf	portb,LED_YELLOW	;turn on yellow led
	retlw	KEY_YELLOW_SOUND	;tone frequency

ShowOrange:
	bsf	portb,LED_ORANGE	;turn on orange led
	retlw	KEY_ORANGE_SOUND	;tone frequency


"How does the program recognise the player?"
The player needs to repeat the sequence of flashes and beeps. This is handled by "GetUserSequence" sub-routine.
The sub-routine gets the first step and by Indirectly addressing memory and placing the value of the first file into steptemp shifting it to the right (if necessary) and ANDing it with 03 to get the two lowest bits. These are placed in a file called RANDOM.

GetUserSequence:
	clrf	column_r		;begin at start of step sequence
	movlw	LINE_OFFSET		
	movwf	line_r	
Us1:	movf	line_r,w
	movwf	fsr
	movf	indf,w
	movwf	steptemp
	movf	column_r,w	;get current column
	xorlw	0	
	BTFSC   	STATUS,Z			
	GOTO	UCol0		;column=0 - don't shift
	xorlw	0x01
	BTFSC   	STATUS,Z
	GOTO	UCol1		;column=1 - shift random 2 times
	xorlw	0x03
	BTFSC   	STATUS,Z
	GOTO	UCol2		;column=2 - shift random 4 times
UCol3:	rrf	steptemp,f		;column=3 - shift random 6 times
	rrf	steptemp,f
UCol2:	rrf	steptemp,f
	rrf	steptemp,f
UCol1:	rrf	steptemp,f
	rrf	steptemp,f
UCol0:	movf	steptemp,w	;get current memory byte of table
	andlw	0x03		;mask bits 2-7
	movwf	random

	call	Keypad		;get player's input - returned in W
				;W=0 - no key pressed (timeout)
				;W=1 - player has pressed a key

The Keypad sub-routine is then called. Keypad sub-routine firstly clears the "key" file and loads three files to create a delay loop of 5 seconds looking for a button-press. If the sub-routine
times-out before a key is pressed, W will be zero.
When a key is pressed, the appropriate LED is illuminated and the a tone is produced. The LED is kept illuminated while the key is pressed.
When the key is released, the output port is cleared and the sub-routine returns.

Keypad:
	clrf	key		;initialise key
	movlw	0x07		;assume easy level - 5 sec timeout
	btfss	porta,LEVEL	;get level of difficulty
	movlw	0x03		;hard level - 2 sec timeout
	movwf	timerc
Kp2:	movlw	0xff
	movwf	timerb
Kp1:	movlw	0xff
	movwf	timera

Kwait:	btfsc	portb,KEY_RED
	goto	KeyRed
	btfsc	portb,KEY_GREEN
	goto	KeyGreen
	btfsc	portb,KEY_YELLOW
	goto	KeyYellow
	btfsc	portb,KEY_ORANGE
	goto	KeyOrange
	decfsz	timera,f
	goto	Kwait
	decfsz	timerb,f
	goto	Kp1
	decfsz	timerc,f
	goto	Kp2
	retlw	0		;player hasn't pressed key!
	
KeyRed:
	bsf	portb,LED_RED	;turn on red led
	clrf	key		;key=0
	movlw	KEY_RED_SOUND	;tone frequency
	goto	Beep1

KeyGreen:
	bsf	portb,LED_GREEN	;turn on green led
	bsf	key,0		;key=1
	movlw	KEY_GREEN_SOUND	;tone frequency
	goto	Beep1

KeyYellow:
	bsf	portb,LED_YELLOW	;turn on yellow led
	bsf	key,1		;key=2
	movlw	KEY_YELLOW_SOUND	;tone frequency
	goto 	Beep1

KeyOrange:
	bsf	portb,LED_ORANGE	;turn on orange led
	bsf	key,0		;key=3
	bsf	key,1
	movlw	KEY_ORANGE_SOUND	;tone frequency
Beep1:	movwf	note_tone		;save tone
	movlw	LENGTH_SEMIBREVE
	movwf	note_length
	movlw	0x03	
	movwf	note_tempo	;speed of beep
Beep2:	call	SoundPlay		;play button beep
	movf	portb,w		;test for a key press
	andlw	0x0f		;mask keys
	xorlw	0x00		;are any keys being pressed?
	btfss	status,z		;no - skip next instruction
	goto	Beep2		;yes - keep beeping!
	clrf	portb		;turn off all leds
	retlw	1		;return to calling function

After calling Keypad, the sub-routine compares the key with the step. If the key is incorrect, the micro exits the sub-routine. If the key is correct, the sub-routine gets the next step and waits for the key to be pressed.

       
    	call	Keypad		;get player's input - returned in W
				;W=0 - no key pressed (timeout)
				;W=1 - player has pressed a key
	xorlw	0		;did player press a key?
	btfsc	status,z		;yes 
	retlw	0		;no - exit function 
	movf	key,w		;compare pressed key with step
	xorwf	random,w		;is it the same?		
	btfss	status,z		;yes - jump next instruction
	retlw	0		;no - incorrect, exit function
	incf	column_r,f		;update column
	btfsc	column_r,2	;overflow?
	goto	Colov2		;yes 	
Us2:	movf	column_r,w	;no
	xorwf	column_w,w	;column pointers the same?
	btfss	status,z			
	goto	Us1		;no - at least another step to show
	movf	line_r,w		;yes
	xorwf	line_w,w		;reached the end of sequence?
	btfsc	status,z		;no - jump next instruction
	retlw	1		;yes - sequence complete!
	goto	Us1		;at least another step to show	

Colov2:	clrf	column_r
	incf	line_r,f
	goto	Us2

There are other routines in the program however, at this stage, you only need to follow the ones we have discussed.
If you are new to programming or just want to modify some of the program, go to Start Here with PIC16F628. It has the instruction-set and templates for the micro.

P3   P5