In this unit we will look at the assembly language programming requirements for a microcontroller. This will be followed by a detailed look at the instruction set of the PIC microcontroller with examples. Some fundamental programming requirements will be discussed along the way.
A microcontroller is designed to control hardware. To this end it has a number of digital I/O ports; it may also have ADC and DAC devices so that it can interact with analogue signals. And finally, it will contain a number of peripheral devices such as timer modules, serial I/O, an ADC unit and perhaps other specialised peripheral modules such as a CAN controller or serial peripheral controller.
The instruction set for a microcontroller will therefore be designed to allow efficient control of both its internal devices and the surrounding infrastructure (controlled by its ports).
The main requirements for the instruction set are:
The instruction sets for microcontrollers vary greatly. Some offer a very large number of instructions (eg. the Renesas H83048 and Freescale MC68HC08), others have a very small set of instructions (eg. the Microchip PIC range of microcontrollers).
For information on the Renesas H83048, it is a member of the H8/300H series and can be found following the links from the Renesas homepage:- "http://eu.renesas.com/" then click the 'MPU & MCU' link, then click the 'H8 Family' link on the pins vs clock frequency graph and finally click the H8/300H series link to gain access to the associated documentation and application notes. It can be seen in the associated Programming & Software Manual that it has 64 instructions associated with it.
For information on the Freescale MC68HC08 series, follow the links from the Freescale homepage:- "http://www.freescale.com/" then click the 8-bit microcontroller link, then in the left hand menu select the HC08G Family option from within the HC08 expandable menu to open up the general purpose devices within this family. Open any Product Info Data sheet and within Chapter 6 Central Processing Unit (CPU) notes you will find the Instruction set summary and also the Opcode Map (which is a 16 x 16 grid of Instructions for this device).
For information on a commonly used PIC, say the PIC16F84, follow the links from the Microchip homepage:- "http://www.microchip.com/" then click on the 8-bit PIC microcontrollers, then click on the PIC16 MCU under the product family tree. Scroll down through the list of devices and select the datasheet for the PIC16F84. Follw the links through to the datasheet and in the pdf document go to the Instruction Set Summary. Here you will find the actual Instruction set descriptions and for this device you will notice it utilises a set of 35 Instructions.
The instruction set is grouped into three basic categories:-
BYTE-ORIENTED FILE REGISTER OPERATIONS
Mnemonic |
Operand |
Description |
Field | Description | |
|---|---|---|---|---|---|
BYTE-ORIENTED FILE REGISTER OPERATIONS |
f | Register file address (0x00 to 0x7F) | |||
| ADDWF | f, d | Add W and f | W | Working register (accumulator) | |
| ANDWF | f, d | AND W with f | b | Bit address within an 8-bit file register | |
| CLRF | f | Clear f | k | Literal field, constant data or label | |
| CLRW | - | Clear W | x | Don't care location (= 0 or 1) | |
| COMF | f, d | Complement f | d | Destination | |
| DECF | f, d | Decrement f | d = 0: stores result in W, | ||
| DECFSZ | f, d | Decrement f, Skip if 0 | d = 1: stores result in file register f. | ||
| INCF | f, d | Increment f | Default is d = 1 | ||
| INCFSZ | f, d | Increment f, Skip if 0 | |||
| IORWF | f, d | Inclusive OR W with f | |||
| MOVF | f, d | Move f | |||
| MOVWF | f | Move W to f | |||
| NOP | - | No Operation | |||
| RLF | f, d | Rotate Left f through Carry | |||
| RRF | f, d | Rotate Right f through Carry | |||
| SUBWF | f, d | Subtract W from f | |||
| SWAPF | f, d | Swap nibbles in f | |||
| XORWF | f, d | Exclusive OR W with f | |||
BIT-ORIENTED FILE REGISTER OPERATIONS
Mnemonic |
Operand |
Description |
|---|---|---|
BIT-ORIENTED FILE REGISTER OPERATIONS |
||
| BCF | f, b | Bit Clear f |
| BSF | f, b | Bit Set f |
| BTFSC | f, b | Bit Test f, Skip if Clear |
| BTFSS | f, b | Bit Test f, Skip if Set |
LITERAL AND CONTROL OPERATIONS
Mnemonic |
Operand |
Description |
|---|---|---|
LITERAL AND CONTROL OPERATIONS |
||
| ADDLW | k | Add literal and W |
| ANDLW | k | AND literal with W |
| CALL | k | Call subroutine |
| CLRWDT | - | Clear Watchdog Timer |
| GOTO | k | Go to address |
| IORLW | k | Inclusive OR literal with W |
| MOVLW | k | Move literal to W |
| RETFIE | - | Return from interrupt |
| RETLW | k | Return with literal in W |
| RETURN | - | Return from Subroutine |
| SLEEP | - | Go into standby mode |
| SUBLW | k | Subtract W from literal |
| XORLW | k | Exclusive OR literal with W |
All instructions are executed within one single instruction cycle, unless a conditional test is true or the program counter is changed as a result of an instruction. In which case, the execution takes two instruction cycles with the second cycle executed as a NOP. One instruction cycle consists of four oscillator periods. (Thus, for an oscillator frequency of 4 MHz, the normal instruction execution time is 1 µs).
We will consider the PIC microcontroller instruction set in some detail and will then compare it with other microcontrollers such as the MC68HC05 and MC68HC08 later on.
[Down to PIC Instruction Set, section 3.7]
These are much the same as for any computer. The microcontroller software development will be based around a programming environment which will normally include:
The object code file cannot be executed on the target microcontroller. It needs to be passed through a program called a Linker.
The job of the Linker is to take the object file produced by the Assembler and link it with standard library modules (that have already been pre-assembled into object files) to produce a complete working executable file that can be loaded into the memory of the microcontroller.
Sometimes the Assembler and Linker are combined into one program so that assembling a source program automatically links it as well. This is the case with the PIC Assembler.
Figure 1, below, shows the different stages in the production of a program. Note that the only stage that requires a lot of user input is the initial production of the user source code file. The rest of the program production involves using the Assembler and Linker tools.

A program called a Loader takes your executable .hex file from the hard disk and loads it onto the target microcontroller.
The original source file will contain instructions about where to place the .hex file in the target microcontroller memory. Typically, this instruction will be: ORG 0000H This instruction ORGanises the target program to be loaded to the target microcontroller memory address 0000 hex.
ie. at the address 0000 0000 0000 0000 binary.
Other key addresses may also be needed, such as the interrupt vector addresses for any interrupt service routines (isr) that the program may use.
Generally, the Loader program is integrated into the development environment so that it is performed by the programmer tools.
For example, in the MPLAB PIC development environment, the PICSTART menu will deal with loading the target program into the memory of the EPROM Programmer system which will then program the target microcontroller which has been placed into the zero insertion socket of the programmer system.
If the Assembler and Linker report that they have successfully assembled and linked the program it might be assumed that the program will then run correctly. This is not necessarily the case.
All the Assembler will do is ensure that the instructions you have used in your program use the correct syntax (ie. they conform to the spelling and format required by the assembler). This does not mean that the program will run correctly since it is possible to create a program that is syntactically correct but is nonsense when it comes to the sequence of instructions.
The next step is to execute the program and see if it does what we want it to do, and if not, modify it so that it does. This process is known as debugging.
Debugging can take up more time than the actual writing of the original program source code. There are a number of ways in which a program can be debugged

Use an EPROM Emulator. This is a system that contains RAM. It is connected to the target system by removing any EPROM memory from the target system and placing the header attached to the cable from the EPROM Emulator to the EPROM socket of the target system. In this way the RAM memory of the EPROM Emulator is used in place of actual EPROM memory. The EPROM Emulator is connected to the PC and the executable program is placed into the RAM or the Emulator. It runs in this RAM, but the target system sees the program in its EPROM address space. With this arrangement, the PC can access the EPROM Emulator RAM and can thus control the program execution.
Figure 3 shows an EPROM Emulator setup. The software is transferred from the file on the PC to the EPROM Emulator. The Header cable from the EPROM Emulator connects to the EPROM chip on the target board so that the target microcontroller on the Target board can access the program in the EPROM Emulator RAM as if it were in its own EPROM address space.

Develop the program on the development system (PC) with Assembler and Linker, then download the program directly to a target system to run on that system. This method is not very productive since there is no control of the program execution on the target system.
The appropriate method for this course is to use a simulator on the PC to develop programs, then download the executable files to the PIC trainer board.
The PIC used is a PIC16F84, which contains 1024 words of program memory, 68 bytes of Data RAM and 64 bytes of Data EEPROM. Thus the executable file is downloaded from the PC into the Enhanced Flash Program memory of the PIC16F84. The PIC trainer contains hardware to program the PIC16F84, whilst the program used to download the executable file provides the necessary control of the program memory.
The development of a microcomputer-based application thus follows the paths shown in the following figure 4.

Software design is usually based on a specification. The sequence of HW design, SW design, Assembly, Simulation and Debug may be repeated a number of times until the system meets the required specification.
The turquoise boxes represent areas which require the most user activity; the most intensive activities are HW design, SW design, Simulation, and Debugging.
This section will look at the way in which an assembly language program is structured and will introduce some of the assembly language instructions of the PIC microcontroller.
The source code for a microcontroller can be written in assembly language or in a high level language such as C (providing a suitable compiler is available). For this part of the course we will use assembly language programming.
A typical assembly language program will consist of:
Some assembler directives:
The EQU directive associates a name with a physical value such as an address in memory.
Syntax:
name EQU physical_value
| TMR0 | EQU | 1 | ; Timer Zero (RTCC-Real Time Clock Counter) |
| PCL | EQU | 2 | ; the program counter |
| STATUS | EQU | 3 | ; the status register |
| CARRY | EQU | 0 | ; the carry bit ('bit 0' in the status register) |
| DCARRY | EQU | 1 | ; the digit carry bit ('bit 1' in the status register) |
| W | EQU | 0 | ; result destination to W work register |
| F | EQU | 1 | ; result destination to F (file) register |
| Z | EQU | 2 | ; the zero bit ('bit 2' in the status register) |
| TMR0 | EQU | 1 | ; Timer Zero (RTCC-Real Time Clock Counter) |
| PCL | EQU | 2 | ; the program counter |
| STATUS | EQU | 3 | ; the status register |
| CARRY | EQU | 0 | ; the carry bit ('bit 0' in the status register) |
| DCARRY | EQU | 1 | ; the digit carry bit ('bit 1' in the status register) |
| W | EQU | 0 | ; result destination to W work register |
| F | EQU | 1 | ; result destination to F (file) register |
| Z | EQU | 2 | ; the zero bit ('bit 2' in the status register) |
Where TMR0, PCL and STATUS are three Special Function Registers (static RAM) at address's 01h, 02h & 03h respectfully in a PIC.
After the above equate directives, the names can be used instead of register
addresses within the PIC microcontroller,
ie. meaningful names can be used instead of the numerical values.
eg. CLRF TMR0 will clear all the bits in the Timer Zero counter register (which has the physical address of 1, or 0001 hex).
The instruction CLRF 1 would do the same, but the use of the label 'TMR0' makes the instruction more readable.
Some of the equates refer to individual bits within a particular register.
eg. CARRY EQU 0 refers to the first bit (Bit zero) in the status register.
bit 7 |
bit 6 |
bit 5 |
bit 4 |
bit 3 |
bit 2 |
bit 1 |
bit 0 |
|---|---|---|---|---|---|---|---|
Carry bit |
The name CARRY only has meaning when referring to the status register.
eg. BCF STATUS, CARRY will clear the carry bit (bit 0) in the status register.
A program will typically also include equate directives for the I/O registers.
| porta | EQU | 5 | ; port A is at the address 5, or 00 0000 0000 0101 |
| PA0 | EQU | 0 | ; these labels simply provide names for each bit |
| PA1 | EQU | 1 | ; of port A |
| PA2 | EQU | 2 | |
| PA3 | EQU | 3 | |
| PA4 | EQU | 4 | |
| PA5 | EQU | 5 | |
| PA6 | EQU | 6 | |
| PA7 | EQU | 7 |
| portb | EQU | 6 | ; port B is at the address 6, or 00 0000 0000 0110 |
| PB0 | EQU | 0 | ; these labels simply provide names for each bit |
| PB1 | EQU | 1 | ; of port B |
| PB2 | EQU | 2 | |
| PB3 | EQU | 3 | |
| PB4 | EQU | 4 | |
| PB5 | EQU | 5 | |
| PB6 | EQU | 6 | |
| PB7 | EQU | 7 |
More of these register labels can be declared for other PIC registers.
We can also declare memory equates such as
| ORG | 00h | ; This defines the start address of a program | |
| GOTO | init | ; This directs to the program start | |
| init | |||
| ; Your program goes in here | |||
| END | ; Defines the end of the program so the assembler knows where to stop |
So a complete program structure would look like this:
| TMR0 | EQU | 1 | ; the timer counter |
| PCL | EQU | 2 | ; the program counter |
| STATUS | EQU | 3 | ; the status register |
| CARRY | EQU | 0 | ; the carry bit (in the status register) |
| DCARRY | EQU | 1 | ; the digit carry bit (in the status register) |
| W | EQU | 0 | ; result destination to W work register |
| F | EQU | 1 | ; result destination to F (file) register |
| Z | EQU | 2 | ; the zero bit (in the status register). |
| port a | EQU | 5 | ; port A is at the address 5, or 00 00000000 0101 (14 bit word) |
| PA0 | EQU | 0 | ; these labels simply provide names for each bit of port A |
| PA1 | EQU | 1 | |
| PA2 | EQU | 2 | |
| PA3 | EQU | 3 | |
| PA4 | EQU | 4 | |
| PA5 | EQU | 5 | |
| PA6 | EQU | 6 | |
| PA7 | EQU | 7 | |
| port b | EQU | 6 | ; port Bis at the address 6, or 00 00000000 0110 (14 bit word) |
| PA0 | EQU | 0 | ; these labels simply provide names for each bit of port B |
| PA1 | EQU | 1 | |
| PA2 | EQU | 2 | |
| PA3 | EQU | 3 | |
| PA4 | EQU | 4 | |
| PA5 | EQU | 5 | |
| PA6 | EQU | 6 | |
| PA7 | EQU | 7 | |
| ORG | 00h | ; this defines the start address of a program | |
| GOTO | init | ; This directs to the program start | |
| init | |||
| ; *** subroutines start here *** | |||
| ; *** main program starts here *** | |||
| END | ; Defines the end of the program so the assembler knows where to stop | ||
To avoid having to enter all these equate directives each time a new program
is started, it is convenient to place them in a separate file and include
them into the new program using the assembler command include
eg. include "PIC16F84.h"
In this example, the register equate directives have been placed in a file called PIC16F84.h so that they can be included into the program thus:
| include | "PIC16F84.h" | ||
| ORG | 00h | ; This defines the start address of a program | |
| GOTO | init | ; This directs to the program start | |
| ; *** Subroutines start here *** | |||
| ; *** main program starts here *** | |||
| init | |||
| ; Your program goes in here | |||
| END | ; Defines the end of the program so the assembler knows where to stop | ||
All microprocessors (and microcontrollers) need a reset vector. This is a special memory location (in ROM memory space) that is used to contain the first memory location of a program. Without it the microcontroller would not know where to start program execution.
For most of The PIC devices nowadys, the RESET vector is at 0000h and the interrupt vector is at 0004h. Earlier PIC devices had the following memory locations for the reset vectors (figure 5):
You can see now how the assembler directive command ORG is able to direct the PIC microcontroller to the start of the user program. The start address of the program is placed into the reset vector locations. Thus for a PIC16C54:
1FFh init where init is the physical start address of the user program.
Earlier PIC devices often had there own reset vector and it used to have its reset vector at the top of the available ROM, which was at 1FFh, 3FFh, or 7FFh, depending upon the amount of ROM memory available for the particular PIC type. All the current PIC devices now use a standardized vector position at the bottom of the memory map (0000h in the ROM area).
We will now look at some of the instructions used by the PIC microcontroller. The PIC instruction set falls into three groups:
Most of these instructions execute in a single clock cycle. The duration of a single clock cycle depends on the frequency of the clock oscillator which is usually between 4MHz and 40MHz, depending upon the PIC device, with some of the PIC18 series now capable of running at 48 and even 64 MHz.
To keep electromagnetic emission levels low, most general purpose applications will use a 4MHz clock and the execution time is therefore 1µS per instruction.
Each memory cycle is made up of four states, so the duration of a clock cycle (and hence the instruction execution time) is four times the oscillator period. For example,
From Figure 3.2, on page 15 of the Microchip PIC16C54 data sheet, you can see the relationship between the oscillator clock and the fetch and execute cycles of the PIC (Fig 5a).
A complete breakdown of all the PIC16F84 instructions is to be found in the PIC16F84 Data Sheet.
This section looks at the instruction set for the PIC, giving examples of how the instructions are used.
One of the principal requirements of a microcontroller is to control the external environment via the digital I/O ports. Indeed, the first microcontrollers (Intel 8048/8031, Motorola 6801) were basically generic microprocessors with integrated digital I/O and a timer module.
Controlling digital I/O often involves turning individual bits on and off, taking care not to affect any other I/O bits. Traditionally this has been achieved by using ANDing and ORing mask operations, but most modern microcontrollers now provide special instructions to selectively set or reset individual bits of a selected port.
The PIC has two instructions, one to turn on a selected bit of a port, the other to turn it off.
| BSF | f, b | used to turn on (set) bit b of file register f. eg. BSF portb,5 will set bit 5 of portB |
| BCF | f, b | used to turn off (clear) bit b of file register f. eg. BCF portb,5 will reset bit 5 of portB. |
Note that there is no instruction for turning on or off a group of bits. To do this it is necessary to set or reset each bit in turn or use a mask.
For example, assuming that all bits of portB start at zero, the effect of three such operators in sequence is shown below:
| 7654 3210 port bits | ||
|---|---|---|
| BSF | portb,0 | 0000 0001 |
| BSF | portb,1 | 0000 0011 |
| BCF | portb,0 | 0000 0010 |
The Go to Instruction (GOTO)
All microcontrollers need an instruction to jump out of the program sequence.
The instruction to do this on the PIC is the GOTO instruction.
| GOTO | K |
causes the program to jump to the label or address k. |
For example:
| GOTO | 0050 | causes the program to go to program memory address 0050 hex |
GOTO instructions cause the program to branch to another part of the program. They are often used to cause the program to loop back to repeat an earlier section of code.
For example, the following program code will turn on and off bit 5 of port A in an endless loop:
| agn | BSF | porta, 5 | ; turn on bit 5 of portA |
| BCF | porta, 5 | ; turn off bit 5 of portA | |
| GOTO | agn | ;go back and do it again |
Bit Test and Skip if Set/Clear (BTFSS and BTFSC)
A typical PIC application may involve testing an input bit and performing some action when the bit changes state. For example the input could be the state of a door switch and the action could be to turn on a relay which then switches on a motor.
The PIC provides two instructions, Bit Test and Skip if Set (BTFSS) and Bit Test and Skip if Clear (BTFSC).
| BTFSS | f, b | Test bit b in file register f and skip the next instruction if it is set (ie.1) |
| BTFSC | f, b | Test bit b in file register f and skip the next instruction if it is clear (ie.0) |
Figure 6 shows a typical interfacing example and is explained below.
| agn | BSF | portb, 0 | ;set bit 0 of portB (the LED will turn off) |
| BCF | portb, 0 | ;clear bit 0 of portB (the LED will turn on) | |
| BTFSC | porta, 2 | ;test bit 2 of portA and skip the next instruction if S1closed | |
| GOTO | agn | ;branch back to label agn | |
| BCF | portb,1 | ;clear bit 1 of portB and so energise the relay |
This program will cause bit 0 of portB to pulse on and off while input bit 2 of portA is a 1, ie. while the switch S1 is open. As soon as bit 2 of portA becomes a 0 (when the switch is closed), the program will clear bit 1 of portB and so energise the relay.
Delay Routine
In the program above the LED will be turned on and off so rapidly that the flashing action will not be noticed - the LED will appear to be lit at half power.
The time to execute an instruction is 1 clock cycle except for the BTFSS/BTFSC instructions which have an additional clock cycle if a skip is involved. Therefore, the number clock cycles for a traverse around the loop of the program is: 1 + 1 + 1 + 1 = 4 clock cycles Assuming a 4MHz crystal for the PIC clock, the clock cycle is 1µs (oscillator frequency /4). The time to complete the program loop once is 4µs. The LED will be off for 1µs and on for 3µs and the flash rate will be 250kHz. Clearly, we will not be able to see the LED flashing.
A delay between turning the LED on and off, and another between turning it off and on again will allow the LED flashing to be observed. We will look at how to produce a delay later, but for now assume that a delay routine has been produced as a sub-program. This can be invoked using the instruction CALL delay, where delay is the name of the sub-program.
Our program now becomes:
| agn | BSF | portb, 0 | ;set bit 0 of portB (the LED will turn off) |
| CALL | delay | ;call the delay sub-program to introduce a delay | |
| BCF | portb, 0 | ;clear bit 0 of portB (the LED will turn on) | |
| CALL | delay | ; call the delay sub-program to introduce another delay | |
| BTFSC | porta, 2 | ;test bit 2 of portA and skip the next instruction if S1closed | |
| GOTO | agn | ;branch back to label agn | |
| BCF | portb,1 | ;clear bit 1 of portB and so energise the relay |
The delay sub-program will simply take up processor time to ensure the LED remains on or off for a reasonable amount of time.
Produce a fragment of assembly program to turn on the relay (connected to bit 1 of portB - RB1) when the switch SA2 (at bit 2 of portA) is operated TWICE.
off1 BTFSC porta,2 ;loop around off1 while S1 is open, skip goto when S1 closed
GOTO off1
on1 BTFSS porta,2 ;loop around on1 while S1 is closed, skip goto when S1 open
GOTO on1
off2 BTFSC porta,2 ; loop around off2 while S1 is open, skip goto when S1 closed
GOTO off2
on2 BTFSS porta,2 ; loop around on2 while S1 is closed, skip goto when S1 open
GOTO on2
BCF portb,1 ;Operate relay as two switch operations have now been detected
The W register (working register) is particularly important as it is used to obtain literal data values and pass them to other registers in the file register.
NOTE: A literal is simply a data value (not an address).
Two instructions used to do this are:
| MOVLW | n | move (copy) the value n into the W register. |
| MOVWF | f | move (copy) the contents of the W register into register f |
Examples
| MOVLW | 36h | will copy the literal 36 hex (0011 0110) into the W register. |
| MOVWF | portb |
will copy the contents of the W register into portB (assuming that portB has previously been set up to be an output port). |
So now we have a way to place literal data into a selected register. For example:
| MOVLW | 12h |
| MOVWF | OPTION |
moves the literal 12 hex (0001 0010 in binary) into the OPTION register via the W register.
| Addr | Name | 7654 3210 port bits |
|---|---|---|
| 81h | OPTION | 0001 0010 |
The ports of the PIC (and indeed of most microcontrollers) can be configured as input or output ports. In fact the individual port bits can be configured as input or output bits independently.
Figure 7 illustrates how the PIC portA can be configured for input or output operation. Port A of the PIC16F84 has five bit wide bi-directional port (using bits A0 to A4 inclusive). TRISA is the register to set up the direction of the bits in PORT A. A '1' indicates an input bit. A '0' indicates an output bit.

The TRISA register is located at address 85h in the file register map and is used to configure portA for input or output by placing a 1 or a 0 into the bits of the TRISA register.
Placing the bit pattern 0000 1101 into the TRISA register would configure portA as illustrated in figure 8 and described below.
Out of the 5 available bits, bit4 and bit1 are configured as outputs, whilst bits 3, 2, and 0 are inputs. (remember:- Bit pattern 0000 1101 is equivalent to 0D in hex.)
The PIC instructions to do this would be:
| config | BSF | STATUS, 5 | ;set bit 5 of STATUS register to access bank 1 |
| MOVELW | 0Dh | ;copy literal 0D hex to W register | |
| MOVWF | TRISA | ;copy contents of W register into TRISA | |
| BCF | STATUS, 5 | ;clear bit 5 of STATUS register (back to bank 0) |
This code needs some explanation.
The default register file is called page 0. The TRISA and TRISB registers are located in the second bank of register files known as bank/page 1. Figure 9 below illustrates the arrangement of the two banks of memory register files. Note that in register bank 0 address 05h accesses porta, while the configuration register TRISA is in register file bank 1 at address 85h.

To gain access to register bank 1 we need to set bit 5 of the STATUS register to 1 (high). Bit 5 is the register file page select bit. This is most easily done by using the bit set instruction: BSF STATUS,5 To gain access to the default register file bank 0, we simply clear bit 5 in the status register with: BCF STATUS,5 You will notice that some registers (such as the STATUS register) are duplicated in both register banks.
The most commonly used bits in the status register are illustrated below.
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
|---|---|---|---|---|---|---|---|---|
IRP |
RP0 | TO | Z | DC | C | STATUS register |
Where: -
Try producing a segment of PIC assembly program to configure portA as an input port and portB as an output port.
config BSF STATUS,5 ;set bit 5 in status register to access memory bank/page 1
MOVLW 00H ;load all zeros into the working register. (0000 0000).
MOVWF TRISB ;move 'all zeros' into TRISB to configure PORTB as all output.
MOVLW FFH ;load all ones into the working register. (1111 1111).
MOVWF TRISA ;move 'all ones' into TRISA to configure PORTA as all input.
BCF STATUS,5 ;clear bit 5 in STATUS to access page 0 again.
The TRISB register is located at 86h in the file register map.
The following PIC instructions are commonly used:
| CLRW | CleaR the W register. ie. set all its bits to 0. |
| CLRF f | CleaR the File register f to zero. |
This last instruction is useful for setting all the bits in a particular register to zero.
Examples
CLRF OPTION would set all bits of the OPTION register to zero.
CLRF portb would clear all bits of portB (assuming that it has already been configured as an output).
Note that a CLRW or a CLRF instruction will set the Z bit in the STATUS register.
The logical AND and OR operators can be used to clear or set bits according to the bit pattern specified as a literal. The literal is known as a mask.
Logical AND-ing is used to clear selected bits in a byte to zero.
| ANDLW | n | The value n is logically AND-ed to the contents of the W register and the result placed into the W register. |
For example, ANDLW 7Bh would logically AND the contents of W with the literal 7BH. So if W contained 9Dh, for example, the bit patterns would be :
| W n Result |
1001 1101 0111 1011 0001 1001 |
contents of W is 9Dh in this example the literal value 7Bh (the mask) bits 7 and 2 have been cleared by the AND operation |
Which bits are cleared in the W register with the following instructions?
| MOVLW | DBh |
| ANDLW | ACh |
W is loaded with DBh 1101 1011 Literal byte is ACh 1010 1100 Result 1000 1000 as the final value in W AND-ing Literal byte (1010 1100) with W (1101 1011) would leave bits 3 and 7 set:- (Bits 0, 1, 4 and 6 have been cleared.)
Logical OR-ing is used to set selected bits in a byte to 1.
| IORLW | n | Logically inclusive OR n with the contents of W. |
For example, IORLW 7Bh would logically OR the contents of W with literal 7Bh (0111 1011). So if W contained 99h (1001 1001), for example, the bit patterns would be :
| W n Result |
1001 1001 0111 1011 1111 1011 |
contents of W is 99 in this example the literal value 7Bh (the mask) bits 1,5 and 6 have been set by the OR operation. |
Which bits are set in the W register with the following instructions?
| MOVLW | 85h |
| IORW | 22h |
W is loaded with 85h 1000 0101 Literal byte is 22h 0010 0010 Result 1010 0111 as the final value in W OR-ing Literal byte (0010 0010) with W (1000 0101) would leave bits 0, 2 and 7 set and also set bits 1 and 5.
This method of clearing and setting bits is sometimes known as masking. The literal used in both the ANDLW and the IORLW instructions is the mask byte.
We could use this idea to set or clear multiple bits of an output port. For example:
| MOVLW | 85h | ; output 1000 0101 to the working register |
| MOVWF | portb | ; output contents of w to portB |
| IORLW | 22h | ; sets bits 1 and 5 of the w register (OR 22h with w) |
| MOVWF | portb | ; move the modified contents of w to portB |
| ANDLW | FEh | ; clears bit 0 of the w register (AND FEh with w) |
| MOVWF | portb | ; move the new contents of w to portB |
Many of the PIC instructions can place their result into either the W register (as in previous examples) or into one of the selected file registers. The destination is determined by the value of d (destination select).
For example, MOVF f, d would copy the contents of the file register f into the destination d
Example
| MOVLW | FCh | |
| MOVWF | 35h | ; move literal FC hex into location 35h |
| INCF | 35h, 1 | ; increment contents of memory location 35h |
| MOVF | 35h, 0 | ; move contents of 35h into W register |
The second instruction moves the contents of W register (the FCh literal) into the memory location 35 hex. The operand 35h is a free memory location in register page 0 (see the register file memory map we looked at earlier; it's also on page 12 of the PIC16C84 data sheet). The third instruction increments (adds one to) the contents of memory location 35h and stores it in 35h again. The final instruction moves (copies) the contents of memory location 35 into the W register (overwriting the value FC placed there by the first instruction). Note that in the last instruction, bit d=0 indicating the destination as the W register.
It is good programming practice to use an equate to give the d bit a more meaningful name.
| W | EQU | 0 |
| F | EQU | 1 |
So that the use of this type of instructions used in the above example becomes more readable:
| MOVLW | FCh | |
| MOVWF | 35h | move literal FC hex into location 35h |
| INCF | 35h, F | ; increment contents of memory location 35h and store it there |
| MOVF | 35h, W | ; move contents of 35 hex into W register |
| ADDWF | f, d |
| IORWF | f, d |
| ANDWF | f, d |
| XORWF | f, d |
| ADDWF | f, d |
| SUBWF | f, d |
| COMF | f, d |
| INCF | f, d |
| DECF | f, d |
| RRF | f, d |
| RLF | f, d |
| SWAPF | f, d |
Look up the instructions listed above in the Microchip PIC16F84 data sheet.
There are two instructions that can be used to create loops that execute for a particular number of times.
| DECFSZ | f,d | ;decrement register f and skip the next instruction if zero |
| INCFSZ | f,d | ;increment register f and skip the next instruction if zero |
Example
| next | DECFSZ | count |
| GOTO | next | |
| BSF | portb,1 |
The first instruction decrements the contents of memory location count. If it is not equal to zero, the GOTO instruction will be executed. This, of course, causes the program to loop back to the DECFSZ instruction to decrement count once more.
This will continue until the DECFSZ instruction decrements count to zero, at which point the DECFSZ instruction will skip the GOTO instruction and execute the next instruction (in this case setting bit 1 of portB).
The two instructions DECFSZ and GOTO take up processor cycles and thus constitute a "delay".
The DECFSZ instruction can be used to keep a count of some event taking place, for example we may wish to keep a count of the number of times an input changes from 1 to 0.
The INCFSZ instruction behaves in much the same way, but adds one each time it is executed.
A sub-program (or subroutine as it is sometimes called) is a piece of program that can be called from anywhere from within a program by using the instruction CALL 'name_of_sub_program' We have already seen this idea before with the delay sub-program. CALL delay The program instructions comprising the delay sub-program are:
| delay | MOVLW | FFh |
| MOVWF | count,F | |
| next | DECFSZ | count |
| GOTO | next | |
| RETURN |
The sub-program needs to start with a label (delay) and end with the instruction RETURN.
When the CALL instruction calls sub-program, the program branches off to
the group of instructions following the label delay.
On completing the sub-program (ie. on reaching the instruction RETURN) the
program branches back to the next instruction in the main program following
the CALL instruction.
In order to know where to return from a sub-program the PIC microcontroller must know where to branch back to in the main program. This is achieved by storing the next instruction address after the CALL instruction in a special area of memory called the STACK.
The RETURN instruction will look at the stack and obtain the return address which it will load into the PIC's program counter (which keeps track of where the next instruction is to be executed in program memory).
The next diagram illustrates the STACK mechanism used in the PIC 16Cxx series.
Figure 10 shows a stack in use. The CALL instruction (ignore "Interrupt" in the diagram for the moment) places the return address onto the stack and the RETURN instruction gets it back off the STACK and places it in the Program Counter register (PC). These actions all take place without any further action being taken by the programmer.

The complete program containing both the main program sequence and the sub-program looks like this:
| agn | BSF | portb, 0 | ;set bit 0 of portB (the LED will turn off) |
| CALL | delay | ;call the delay sub-program to introduce a delay | |
| BCF | portb, 0 | ;clear bit 0 of portB (the LED will turn on) | |
| CALL | delay | ;call the delay sub-program to introduce another delay | |
| BTFSC | porta, 2 | ;test bit 2 of portA and skip the next instruction if S1 closed | |
| GOTO | agn | ;branch back to label agn | |
| BSF | portb,1 | ;clear bit 1 of portB and so energise the relay |
| delay | MOVLW | FFh |
| MOVWF | count, F | |
| next | DECFSZ | count |
| GOTO | next | |
| RETURN |
Note: The delay produced by this simple sub-program is not very long and something a little more complex would be used in practice.
When looking at the diagram for the stack we saw that the stack was also used for interrupts. What is an interrupt?
There are situations where we wish to take some kind of action whenever a particular event occurs, but in order not to waste time, we do not want to keep looking for the event to happen. A very good example is when you use the keyboard of your computer. The PC only looks at the keyboard to discover which key has been pressed after it happens - pressing a key generates an interrupt.
When an interrupt occurs, the processor is automatically directed to a particular part of memory called an interrupt vector where it finds the address of the part of program to be executed in response to the interrupt service routine (isr).
The arrangement is illustrated below.
In the case of the keyboard interrupt, and as shown in figure 11, we have:
The advantage of using an interrupt is that the interrupt source (eg. the pressing of a key) can occur at any time. The processor just completes the current instruction that it is executing, then performs the isr, and returns to execute the next instruction as if nothing had happened. This means that when writing the main program we do not have to worry about when the interrupt will occur since it is taken care of by the interrupt mechanism.
An important aspect of interrupts is that they are serviced straight away rather than when the processor happens to notice (as would be the case if the processor kept polling the source waiting for the event to occur).
Most microcontrollers are required to work in an embedded real time environment. Therefore they are provided with interrupt capability; usually an input pin that can be connected to whatever is to cause the interrupt. All the programmer has to do is:
Look at the PIC16F84 data sheets and discover:
Now click on the link to go to the Interactive Self Assessment
The practical sessions involve using the Microchip PIC MPLAB programming environment to enter, assemble and run a number of assembly language programs. They will also help you to become familiar with such things as single stepping through the program, setting breakpoints, making use of the simulator and using a stimulus file. Happy programming!
There are links to Practicals 0 to 5 from both here and the overview page or the drop-down menu/
1. Introduction to the MPLAB Development Environment
4. Setting breakpoints and using the Trace feature
When you have completed the practical work, try the programming exercise.
Updated 20.06.07 ML
Powered by Google
Site Map