Embedded Controller and Product Design

Microchip design partner logo
Brooks Technology specializes in designing with Microchip Products.  Visit the Microchip Technology web site for more information by clicking here.


All microcontroller instruction sets can be confusing and mis- leading.  Macros can be used to assign names, that look just like a native instruction, but actually represent a longer instruction sequence.  Or you can make a PIC18F series ASM instruction look like that of another processor, if that's what you prefer.

Macros can eliminate confusion when it comes to testing a condition and deciding whether to jump to a new place in your program.  Most micros have test and skip instructions that are used in conjunction with jump instructions to alter program flow.  But, because you end up skipping the jump instruction on a given instruction, the logic of the operation is inverted and a source of programming errors.  This is easily handled by a macro, as shown in examples to the right.

As time permits I will expand this area with useful tips and hints and various code snippets.



PIC® is a registered trademark of Microchip Technology, Inc.


Using Macros

If you are programming in Microchip assembly you'll no doubt find it challenging and frustrating at times, especially with large programs.   Most programmers use C of course, but when you need super fast code, or when you must know exactly your controller is doing, then you must use ASM.  Using clever macros can take away a lot of the pain!  When I create a macro I emulate the PIC® MCU assembly style to eliminate confusion.  For example, the instruction: movlw 0x45 moves the value 0x45, the literal, to the W register.  The literal, or "l" is the source, and the "w" register is the destination.  The following macro combines two instructions into one convenient line.

movlf   macro   literal,file    ; source to destination
        movlw   literal
        movwf   file

usage example:
        movlf   0x45,temp       ; move 0x45 into temp

One must remember this is actually two 16 bit instructions, you haven't magically created a new machine instruction.  So, with 18F ASM it takes 32 bits of instruction to move an 8 bit value to a register!  Don't think about it!  Most of these devices have sufficient memory!  Now for several of my favorite macros.  To test the result of a math operation the "bit test" instructions are often used.  The bit test instructions will do a skip operation when a condition is true.  example:

test:   btfss   status,c        ; if the carry bit is set skip the following instruction
        goto    cy_was_not_set  ; so if the condition we test for is false, then jump!

We are branching when the condition we are testing for is the opposite.  Just a little confusing.  Here's how to make it more understandable with a macro:

jc      macro   address         ; if cy set then jump (jc=jump if carry set)
        btfsc   status,c
        goto    address

usage example:
        jc      address         ; makes a lot more sense, eliminates logic errors.

Of course, it's easy to do the opposite:

jnc     macro   address         ; jump / goto address if carry not set
        btfss   status,c
        goto    address

Expanding on the macros to include more test/branch/jump operations is easy:

jnz     macro   address
        btfss   status,z
        goto    address

The 18F PIC® MCU series are 8 bit machines but you can easily do 16 bit operations with appropriate instruction sequences.  So, let's put some 16 bit operations into macros to make them easier to use.  Here is a 16 bit increment macro that uses little endian format:

cblock  0x00                    ; you'll need to have allocated two bytes
        your_varL               ; to form your 16 bit var of course

macro   movlf16 literal,fileL   ; this macro is used to set a 16 bit var to a literal value
        movlw   low literal     ; move low 8 bits of value "literal" to w
        movwf   fileL           ; and place in the ls portion of our 16 bit var
        movlw   high literal    ; now handle the upper 8 bits
        movwf   fileL+1

usage:  movlf16 299,your_varL   ; your_varL = 299

Note:  we can also create the above macro using other previously defined macros, the result is the same:

macro   movlf16 literal,fileL
        movlf   low literal,fileL
        movlf   high literal,fileL+1

Here is a 16 bit increment macro:

macro   incf16  fileL           ; call this macro and point to the ls var, which is first in mem
        incf    fileL           ; increment the low 8 bits
        btfsc   status,c        ; if no carry, skip
        incf    fileL+1         ; cy was set, so increment high 8 bits
        movf    file+1,w        ; these two instructions are not needed, but are added to
        iorwf   fileL,w         ; allow easy result testing as shown later

And here is a 16 bit decrement macro:

macro   decf16  fileL
        decf    fileL          ; decrement the low 8 bits
        btfss   status,c       ; if no cy skip
        decf    fileL+1        ; cy (borrow) was set, so decrement high 8 bits
        movf    fileL,w        ; low 8 bits to w
        iorwf   fileL+1,w      ; or with high 8 bits for subsequent test operations

usage example: (typical "for" loop, all done with macros)

        movlf16 1500,your_varL ; your_varL = 1500

loop1:  ..
        .. your loop code

        decf16  your_varL      ; decrease the loop count by 1
        jnz     loop1          ; repeat until loop count=0

More coming soon!