Experiment 1: MPLAB and Instruction Set Analysis 1: Objectives
Experiment 1: MPLAB and Instruction Set Analysis 1: Objectives
Analysis 1
Objectives
Pre-lab requirements
Before starting this experiment, you should have already acquired the MPLAB software and the related PIC
datasheets . You are encouraged to install the latest version of MPLAB.
You should know by now that most PIC instructions (logical and arithmetic) work through the working register
“W”, that is one of their operands must always be the working register “W”, the other operand might be either a
constant or a memory location. Many operations store their result in the working register; therefore we can
conclude that we need the following movement operations:
INSTRUCTIONS ARE CASE INSENSITIVE: YOU CAN WRITE IN EITHER SMALL OR CAPITAL
LETTERS
MOVLW: moves a literal (constant) to the working register (final destination). The constant is
specified by the instruction. You can directly load constants as decimal, binary, hexadecimal, octal and
ASCII. The following examples illustrate:
MOVWF: COPIES the value found in the working register into the data memory, but to which
location? The location is specified along with the instruction and according to the memory map.
Examples:
1. MOVWF 01 : COPIES the value found in W to
TMR0
2. MOVWF 05 : COPIES the value found in W to
PORTA
3. MOVWF 0C : COPIES the value found in W to
a GPR (location 0C)
4. MOVWF 32 : COPIES the value found in W to
a GPR (location 32)
5. MOVWF 52 : WRONG, out of data memory
range of the PIC 16F84a (GPR range is from 0C-
4F and 8C to CF)
Examples:
After writing the above instructions we should build the project, do so by pressing build
Notice that there are several warnings after building the file, warnings do not affect the execution of the
program but they are worth reading. This warning reads: “Found opcode in column 1”, column 1 is
reserved for labels; however, we have written instructions (opcode) instead thus the warning.
TO SOLVE THIS WARNING SIMPLY TYPE FEW BLANK SPACES BEFORE EACH INSTRUCTION OR PRESS
TAB
Notice that the default format is in hexadecimal, to change it (if you need to) simply right-click on the
row Properties and choose the new format you wish.
From the Debugger Menu choose Select Tool then MPLAB SIM
1. To begin the simulation, we will start by resetting the PIC; do so by pressing the yellow reset
button. A green arrow will appear next to the first instruction.
The green arrow means that the program counter is pointing to this instruction which has not
been executed yet.
Keep an eye on the value of the program counter (pc: initially 0), see how it changes as we
simulate the program
2. Press the “Step Into” button one at a time and check the Watch window each time an instruction
executes; keep pressing “Step Into” until you reach the NOP instruction then STOP.
Compare the results as seen in the Watch window with those expected.
Directives
Directives are not instructions. They are assembler commands that appear in the source
code but are not usually translated directly into opcodes. They are used to control the
assembler: its input, output, and data allocation. They are not converted to machine code
(.hex file) and therefore not downloaded to the PIC.
If you refer to the Appendix at the end of this experiment, you will notice that there is no end instruction
among the PIC 16 series instructions, so what is “END”?
The “END” command is a directive which tells the MPLAB IDE that we have finished our program. It has
nothing to do with neither the actual program nor the PIC.
Making your program easier to understand: the “equ” and “include” directives
As you have just noticed, it is difficult to write, read, debug or understand programs while dealing with
memory addresses as numbers. Therefore, we will learn to use new directives to facilitate program
reading.
The equate directive is used to assign labels to numeric values. They are used to DEFINE CONSTANTS or to
ASSIGN NAMES TO MEMORY ADDRESSES OR INDIVIDUAL BITS IN A REGISTER and then use the name
instead of the numeric address.
Timer0 equ 01
Intcon equ 0B
Workrg equ 0
Movlw 5 ; move the constant 5 to the working register
Movwf Timer0 ; copy the value 5 from working register to TMR0 (address 01)
Movlw 2 ; move the constant 2 to the working register
Movwf Intcon ; copy the value 2 from working register to INTCON (address 0B)
Movf Timer0, Workrg ; copy back the value 5 from TMR0 to working register
Nop ; this instruction does nothing, but it is important to write it for now
End
Notice how it is easier now to read and understand the program, you can directly know the actions
executed by the program without referring back to the memory map by simply giving each address a
name at the beginning of your program.
DIRECTIVES THEMSELVES ARE NOT CASE-SENSITIVE BUT THE LABELS YOU DEFINE ARE. SO YOU
MUST USE THE NAME AS YOU HAVE DEFINED IT SINCE IT IS CASE-SENSITIVE.
As you have already seen, the GPRs in a memory map (lower part) do not have names as the SFRs
(Upper part), so it would be difficult to use their addresses each time we want to use them. Here,
the “equate” statement proves helpful.
When simulating the above code, you need to add Num1, Num2 to the watch window, however,
since Num1 and Num2 are not SFRs but GPRs, you will not find them in the drop out menu of the
“Add SFR”, instead you will find them in the drop out menu of the “Add symbol”.
Suppose we are to write a huge program that uses all registers. It will be a tiresome task to define all
Special Function Registers (SFR) and bit names using “equate” statements. Therefore we use the include
directive. The include directive calls a file which has all the equate statements defined for you and ready to
use, its syntax is
#include “P16F84A.inc”
Movlw 5 ; move the constant 5 to the working register
Movwf TMR0 ; copy the value 5 from working register to TMR0 (address 01)
Movlw 2 ; move the constant 2 to the working register
Movwf INTCON ; copy the value 2 from working register to INTCON (address 0B)
Movf TMR0, W ; copy back the value 5 from TMR0 to working register
Nop ; this instruction does nothing, but it is important to write it for now
End
Cblock 0x35
VarX
VarY
VarZ
endc
Here, VarX has the starting address of the cblock, which is 0x35, VarY has the sequential address 0x36 and
VarZ the address of 0x37
What if I want to define variable at locations which are not sequential, that is some addresses are at 0x25
others at 0x40?
Simply use another cblock statement, you can add as many cblock statements as you need
The origin directive is used to place the instruction which exactly comes after it at the location it
specifies.
Examples:
Org 0x00
Movlw 05 ;This instruction has address 0 in program memory
Addwf TMR0 ;This instruction has address 1 in program memory
Org 0x04 ;Program memory locations 2 and 3 are empty, skip to address 4 where it contains
Addlw 08 ;this instruction
Write, build and simulate the following program in your MPLAB editor. This program is very similar to the
ones discussed above but with a change of memory locations.
#include “P16F84A.inc”
Movlw 5 ; move the constant 5 to the working register
Movwf TRISA ; copy the value 5 from working register to TRISA (address 85)
Movlw 2 ; move the constant 2 to the working register
Movwf OPTION_REG ; copy 2 from working register to OPTION_REG (address 81)
Movf TRISA, W ; copy back the value 5 from TRISA to working register
Nop ; this instruction does nothing, but it is important to write it for now
End
After simulation, you will notice that both TRISA and OPTION_REG were not filled with the values
5 and 2 respectively! But why?
Notice that the memory map is divided into two columns, each column is called a bank, here we
have two banks: bank 0 and bank 1. In order to access bank 1, we have to switch to that bank first
and same for bank 0. But how do we make the switch?
Look at the details of the STATUS register in the figure below, there are two bits RP0 and RP1,
these bits control which bank we are in:
BCF: Bit Clear File Register (makes a specified bit in a specified file register a 0)
BSF: Bit Set File Register (makes a specified bit in a specified file register a 1)
Try the program again with the following change and check the results:
#include “P16F84A.inc”
BSF STATUS, RP0
Movlw 5 ; move the constant 5 to the working register
Movwf TRISA ; copy the value 5 from working register to TRISA (address 85)
Movlw 2 ; move the constant 2 to the working register
Movwf OPTION_REG ; copy 2 from working register to OPTION_REG (address 81)
Movf TRISA, W ; copy back the value 5 from TRISA to working register
BCF STATUS, RP0
Nop ; this instruction does nothing, but it is important to write it for now
End
When using medium-range and high-end microcontrollers, it will be a hard task to check the memory map
for each register we will use. Therefore the BANKSEL directive is used instead to simplify this issue. This
directive is a command to the assembler and linker to generate bank selecting code to set the bank to the
bank containing the designated label
Example:
BANKSEL TRISA will be replaced by the assembler, which will automatically know which bank the register
is in and generate the appropriate bank selection instructions:
Bsf STATUS, RP0
Bcf STATUS, RP1
In the PIC16F877A, there are four banks; therefore you need two bits to make the switch between any of
them. An additional bit in the STATUS register is RP1, which is used to make the change between the
additional two banks.
One drawback of using BANKSEL is that it always generates two instructions even when the switch is
between bank0 and bank1 which only requires changing RP0. We could write the code above in the same
manner using Banksel
#include “P16F84A.inc”
Banksel TRISA
Movlw 5 ; move the constant 5 to the working register
Movwf TRISA ; copy the value 5 from working register to TRISA (address 85)
Movlw 2 ; move the constant 2 to the working register
Movwf OPTION_REG ; copy 2 from working register to OPTION_REG (address 81)
Movf TRISA, W ; copy back the value 5 from TRISA to working register
Banksel PORTA
Nop ; this instruction does nothing, but it is important to write it for now
End
Check the program memory window to see how BANKSEL is replaced in the above code and the difference
in between the two codes in this page.
FLAGS
The PIC 16 series has three indicator flags found in the STATUS register; they are the C, DC, and Z flags. See
the description below. Not all instructions affect the flags; some instructions affect some of the flags while
others affect all the flags. Refer to the Appendix at the end of this experiment and review which instructions
affect which flags.
The MOVLW and MOVWF do not affect any of the flags while the MOVF instruction affects the zero flag.
Copying the register to itself does make sense now because if the file has the value of zero the zero flag will
be one. Therefore the MOVF instruction is used to affect the zero flag and consequently know if a register
has the value of 0. (Suppose you are having a down counter and want to check if the result is zero or not)
13
Types of Logical and Arithmetic Instructions and Result Destination
The PIC16 series logical and arithmetic instructions are easy to understand by just reading the instruction,
for from the name you readily know what this instruction does. There are the ADD, SUB, AND, XOR, IOR
(the ordinary Inclusive OR). They only differ by their operands and the result destination. The following
table illustrates:
Many other instructions of the PIC16 series instruction set are of Type II; refer back to the Appendix at the
end of this experiment for study.
14
Starting Up with basic programs
1 include "p16f84a.inc"
2 Fib0 equ 20 ;At the end of the program the Fibonacci series numbers from 0 to 5 will
3 Fib1 equ 21 ;be stored in Fib0:Fib5
4 Fib2 equ 22
5 Fib3 equ 23
6 Fib4 equ 24
7 Fib5 equ 25
8
9 Clrw ;This instruction clears the working register, W = 0
10 clrf Fib0 ;The clrf instruction clears a file register specified, here Fib0 = 0
11 movf Fib0, w ;initializing Fib1 to the value 1 by adding 1 to Fib0 and storing it in Fib1
12 addlw 1
13 movwf Fib1
14
15 movf Fib0, W ; Fib2 = Fib1 + Fib0
16 addwf Fib1, W
17 movwf Fib2
18
19 movf Fib1, W ; Fib3 = Fib2 + Fib1
20 addwf Fib2, W
21 movwf Fib3
22
23 movf Fib2, W ; Fib4 = Fib3 + Fib2
24 addwf Fib3, W
25 movwf Fib4
26
27 movf Fib3, W ; Fib5 = Fib4 + Fib3
28 addwf Fib4, W
29 movwf Fib5
30 nop
31 end
1. Start a new MPLAB session, add the file example1.asm to your project
2. Build the project
3. Select Debugger Select Tool MPLAB SIM
4. Add the necessary variables and the working register to the watch window (remember that user
defined variables are found under the “Add Symbol” list)
15
5. Simulate the program step by step, analyze and study the function of each instruction. Stop at the
“nop” instruction
6. Study the comments and compare them to the results at each stage and after executing the
instructions
7. As you simulate your code, keep an eye on the MPLAB status bar below (the results shown in this
status bar are not related to the program, they are for demo purposes only)
The status bar below allows you to instantly check the value of the flags after each instruction is executed
In the figure above, the flags are z, DC, C
A capital letter means that the value of the flag is one; meanwhile a small letter means a value of
zero. In this case, the result is not zero; however, digit carry and a carry are present.
Now suppose we want to execute the Fibonacci series code at once - to do so, follows these steps:
1. Double click on the “nop” instruction (line 30), a red circle with a letter “B” inside is shown to the
left of the instruction. This is called a breakpoint. Breakpoints instruct the simulator to stop code
execution at this point. All instructions before the breakpoint are only executed
Run Animate
16
Program Memory Space Usage
Though we have written about 31 lines in the editor, the total number of program memory space occupied
is far less, remember that directives are not instructions and that they are not downloaded to the target
microcontroller. To get an approximate idea of how much space does the program occupy: Select View
Program Memory Symbolic tab
1 include "p16F84A.inc"
2
3 cblock 0x25
4 VarX
5 VarY
6 VarZ
7 Result
8 endc
9
10 org 0x00
11 Main ;loading the truth table
12 movlw B'01010101' ;ZYX Result
13 movwf VarX ;000 0 (bit7_VarZ, bit7_VarY, bit7_VarX)
14 movlw B'00110011' ;001 1 (bit6_VarZ, bit6_VarY, bit6_VarX)
15 movwf VarY ;010 1 .
16 movlw B'00001111' ;011 1 .
17 movwf VarZ ;100 1 .
17
18 ;101 0 .
19 ;110 0 .
20 ;111 0 (bit0_VarZ, bit0_VarY, bit0_VarX)
21 movf VarX, w
22 iorwf VarY, w
23 xorwf VarZ, w
24 movwf Result
25 nop
26 end
1. Start a new MPLAB session, add the file example2.asm to your project
2. Build the project
3. Select Debugger Select Tool MPLAB SIM
4. Add the necessary variables and the working register to the watch window (remember that user
defined variables are found under the “Add Symbol” list)
5. Simulate the program step by step, analyze and study the function of each instruction. Stop at the
“nop” instruction
6. Study the comments and compare them to the results at each stage and after executing the
instructions
18
Appendix A: Instruction Listing
19
Appendix B: MPLAB Tools
Another method to view the content of data memory is through the File Registers menu:
Observe that after code execution, these memory locations are filed
with Fibonacci series value as anticipated.
20