18F4550 and Assembly Language Overview
In this post, I attempt to give a brief overview of the PIC18F4550 microcontroller, its instructions and their assembly language implementation. Please consult section 26.1 in the 18F4550 datasheet for a complete list and explanation of the instructions.
"A microcontroller (also MCU or µC) is a computer-on-a-chip. It is a type of microprocessor emphasizing self-sufficiency and cost-effectiveness, in contrast to a general-purpose microprocessor (the kind used in a PC). In addition to all arithmetic and logic elements of a general purpose microprocessor, the microcontroller usually also integrates additional elements such as read-only and read-write memory, and input/output interfaces."
Couldn’t have said it better myself. The PIC18F4550 microcontroller does indeed have read only and read-write memory. The former, flash ROM, can actually be erased and reprogrammed during development. However, during code execution, this memory is used as read-only. The latter includes registers (and probably other things – suggestions are welcome).
The microcontroller has 35(!) I/O pins. These pins are arranged into ports A,B,C,D and E. These pins are usable for a combination of inputs, outputs, analog-to-digital or digital-to-analog conversion and PWM. Additionally, you can use these pins for serial communication, USB communication, or to trigger interrupts.
But the sales pitch ends here. You now need to consider that you are responsible for ensuring that everything on that chip functions exactly the way you want it to. Microcontrollers do not make mistakes. People do.
It is a good idea to visualize the process of programming a PIC microcontroller:

I am confident that after setting your eyes on this visual feast, you are completely ready to take on the PIC assembly monster. But, just in case you’re not feeling completely ready, I will describe the concepts using words. The analogy is that programming a PIC is similar to having to move objects in and out of a storage space with many cubbyholes using only one hand. In such a scenario, you can do one of several things:
- Pick up an object with your hand
- Place an object in hand into a cubbyhole
- Grab an object out of a cubbyhole with your hand
- Drop the object from your hand
Similarly, a PIC consists of a single working register W, a register file with various readable/writable registers (the general term for one of these file registers is F), and various data you can read and write. The registers in the register file alter the microcontroller’s operation depending on the values stored in them. You can do one of several things:
1)Write a number to W
2)Place the number you have in W into some register in F
3)Retrieve a number from some register in F and place it into W
4)Clear the number from W
Unfortunately, this is where the cubbyhole example starts to become a bit useless. For example, you can also cheat and just move the value from one register to another without using your working register (directly from one cubbyhole to another without using your hand). And that’s 5 instructions down, 70 to go. Addition, subtraction, etc..
It is important to keep in mind that while the 18F4550 is a 16-bit device (instructions are 16 bits < l o n g >), registers consist of only 8 bits. Yes, your hand only has 8 fingers.
There are three types of instructions (18F4550 datasheet used as reference).
- Byte-oriented: These instructions involve operations on all 8 bits of a register. These include moving W (the working register) to a different register, adding W and a different register and incrementing a register. For example, a byte-oriented instruction could be used to set all the pins in PORT D to HIGH at the same time.
- Bit-oriented: There are 5 of these instructions. They perform operations on single bits within a register. This is often useful with I/O, such as when a desired output needs to be set high, low, or toggled on a single pin.
- Control: These instructions do not directly influence registers, but perform other actions to direct program flow. These include branches (conditionals), stack operations, a reset command and NOP, an instruction which does nothing.
Please do refer to the datasheet (again) and look over the instructions. Most of them are fairly simple to understand.
As an example, I will explain the Add instruction ADDWF.
The datasheet says:
ADDWF F,D,A | Add WREG (W in the above description) and F | 1 Cycle | 0010 01da ffff ffff | C, DC, Z, OV, N
The ADDWF instruction takes 3 arguments – F, D and A (though A is optional and will be ignored for now). As the description says, F is the register you would like to add to W. D determines the destination (D=0 to store in W, d=1 to store in F).
The 1 in the cycles column means that the instruction takes 1 cycle. When an instruction says 1 (2 or 3), it means that the number of cycles it takes depends on the result of the instruction (they are all conditional instructions). To borrow an explanation from Prince on the Microchip forums, you can see it the following way: The instruction itself takes 1 cycle. 2 cycles will be taken if the conditional instruction ends up skipping the following instruction. The same conditional instruction may take 3 cycles if there is a call/goto instruction following it and being skipped, since call/goto are effectively 2 instructions long. A skipped instruction is similar to executing a NOP instruction. To summarize, while the conditional instruction takes only 1 cycle, any instructions it skips still end up using cycles.
The next column gives the the machine code of the instruction in hexadecimal form. I cannot think of a use for this field for the average programmer.
The last column provides a list of the affected status bits, which compose a status register. These bits are set/cleared after the given instructions to provide more information about the result. For example the OV status bit can be checked following an addition operation to determine if an overflow has occurred.
You should now be feeling confident about starting your first assembly program. Go on, then…