PICmicro® assembly code

Addressing modes

Addressing modes describe how assemblers resolve and use instructions and data. The mid-range PIC family generally supports two main addressing modes: immediate (for literal or constant operations), and direct (for register or data operations).

Immediate mode provides the data as part of the instruction. For example movlw 4 moves the data '4' into the W register.

Direct mode operations point to the register that contains the data. For example, addwf 25h tells the microcontroller to add the contents of W to the contents of the register at address 25h in the microcontroller's data memory.

A third, indirect, addressing mode is available through a hardware register operation. In this mode, the instruction acts on a register that contains a pointer to another register containing the data. However, unlike many other microcontrollers, indirect addressing in the PIC is not considered as a separate addressing mode since it can only be done by using a direct mode operation on one specific hardware register.

Source and destination order

Although assembly code is similar for all microprocessors, there is one big difference between Microchip's format (and others, too) and that used by Intel in their desktop microprocessors. Microchip assembly code follows a 'from-to' order, like this:

addwf 25h

Think of this instruction as a short form version of 'add W to file register 25h'. It adds the number in W to the number in file register 25h, and leaves the result in file register 25h. The order of the 'wf' part of the instruction implies the order of the addition.

Intel format reverses the source and destination so that they are in a 'to-from' order. For demonstration purposes, this would be the equivalent (and, totally imaginary) Intel format instruction:

addfw 25h

The data in W and register 25h are still added together, as they were in the Microchip instruction. But now, instead of meaning 'add W to file register 25h', think of the instruction as meaning 'add to file register 25h from W'. It's a backward reference (at least from the Microchip point of view).

Of course, whichever way that you learn assembly code first is the one you'll consider to be the right way—the other way just seems wrong! This is the stuff of 'religious wars' between programmers who grew up on different microprocessor architectures. But, the to-from order that the PIC uses is best, of course!

What is assembly code?

Assembly code is a human-readable form of the machine code instructions that control microcontrollers and microprocessors.

In reality, assembly code programs usually contain a hybrid of microcontroller instructions as well as commands to control the assembler. The microcontroller instructions in an assembly code program are in the form of short acronyms called mnemonics. Statements that control the assembler are called directives. Comments are also an important part of the program code and are used by programmers to describe the function of their code to other humans readers.

Assembly code programs are created written in a text editor, and are known as source code. The completed source code text file is read by a program called an assembler which compiles the source code into machine code (also called hex code or object code) for programming into the microcontroller. Whew! Got that? Here is the simplified version: source code gets made into machine code by the assembler. We'll look at an example program shortly.

How do assembly and other languages compare?

Programming in assembly code is considered low-level programming because it is done at the level of the microprocessor's logic and hardware capabilities. Low-level programming languages require the programmer to be intimately familiar with the computer hardware, and to create their own program subroutines to accomplish higher-level functions such as writing characters to an LCD, or reading keyboard input. The primary advantages of using assembly code are that programs can be easily modified to work with new or customized hardware, and that assembly code programs typically run very fast, making very efficient use of limited resources or processing power.

Assembly code is not without its drawbacks, however. The primary disadvantage of assembly code is increased code complexity, making it more difficult and time consuming for programmers to create, edit and debug assembly code programs than those written in a high-level language. Because assembly code is more complex, it becomes more difficult to write large programs in assembly code. That's why most desktop computer programs are written in higher-level languages like C++ or Java—it's easier to do, and desktop programmers don't need to concern themselves with the intimate details of the computer hardware (that's what drivers do, and drivers are often written in assembly code).

Why use assembly code?

Despite the challenges, assembly code has some big advantages in microcontroller circuits such as the ones on this site. Assembly code is ideal for controlling hardware, because its instructions relate directly to the hardware. Circuits such as the UFO are designed primarily to do one simple task, like flash LEDs. The UFO hardware is simple, and its task is single-function and simple, making programming the UFO in assembly code a reasonably straightforward exercise.

What does assembly code look like?

Although the assembly code mnemonics are unique to each family of microcontrollers, all assembly code shares some common characteristics. Assembly code programs are line-based, with each line of code divided into distinct fields, or columns. These columns are:

Label		instruction	data		;comments

Using actual assembly code instructions, a section of program code might look like this:

Count		movlw		4		;Load W with 4 and
		addwf		25h		;add to counter register
		goto		Count		;Repeat forever

Let's examine each line in detail.

Count is a label—a name for a section of code. All labels must begin at the left-hand margin of the text file.

movlw is an instruction mnemonic representing the operation that moves a literal into W. W is an internal register in the processing unit of the PIC which, in this case, becomes loaded with the data, or literal, '4'. Some instructions require data, others operate without data.

Comments in Microchip code follow a semi-colon, and are important in describing the code an maintaining its readability. Comments usually appear after instructions and data, but can be located anywhere in the code as long as they follow a semi-colon.

Notice that the second line does not contain a label. Labels and comments are optional.

addwf is an instruction that adds the contents of the W register to the contents of a memory register, in this case the RAM address 25h. Whatever number was in register 25h will be higher by 4 after this instruction executes.

The goto on the third is a control instruction. Its data 'Count' is interpreted by the assembler at build time and converted into the numeric address of the Count label. In other words, the program loops back to Count and repeats these same instructions forever.

Notice that the data field can represent a 'real' or immediate number, or a direct reference to a register that holds a number, or a relative reference to label.

All of the fields, or columns, in assembly code are commonly separated by tab characters.

Of course, there's more to an assembly code program than this simple code sample shows. Assembly code programs generally start with a program header that includes directives to set up the processor and programming environment, and continues with one or more subroutines and perhaps a data array.

What happens to the assembly code file?

Once the assembly source code for the microcontroller is written, it is 'assembled' (or built, compiled, or made) into machine code by an assembler program running on a desktop computer. As the assembler compiles the program, it checks for syntax errors, and resolves all of the references before turning the program into machine code. Some assemblers use a slightly different syntax for their references or directives. The examples on this site are designed for the free MPLAB IDE (Integrated Development Environment), which includes a source file editor, assembler, simulator and downloader. After the program is assembled, it is time to download it into the target microcontroller.

What is downloading?

Downloading is the process of transferring the compiled machine code into the microcontroller for operation or testing. This is generally accomplished by tethering your desktop computer to a stand-alone programmer, or to hardware that allows your microcontroller to be programmed in-circuit. Some downloaders also have in-circuit emulation and debugging facilities to assist in troubleshooting the program or hardware circuits.