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
|