Analysing the PIC16F628 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
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
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
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.
Quick Links
Legal Stuff
Social Media