| PIC Instructions | ||
| Links Mentioned None |
![]() The PIC has a very small instruction set. This is a good thing because it makes the PIC easy to learn. However, the downside is that you can't do very much with one instruction. Here is a summary of the most common instructions: MOVLW Example: MOVLW 10 Side effects: none The W register is central to most programs. This instruction moves a constant into the W register. Be careful to note what radix the assembler is using by default if you don't specify a base explicitly (for example, MOVLW 0x33 specifies hex 33). You'll sometimes see numbers written with a period in front of them. This forces a decimal interpretation (for example, MOVLW .10).
MOVF, MOVWF Examples: MOVF temp,W MOVF temp,F MOVWF temp Side effects: Sets or clears Z flag (MOVF); none (MOVWF) These instructions allow you to move data between registers and the W register. The first example moves the value in register temp into W. The third example moves W into temp. The second example moves the value of temp into temp. At first, that seems pointless, but it sets the Z condition flags without disturbing anything else, so it is useful for conditional jumps. If the number moved is 0, the Z flag is set. Otherwise it is cleared. While you can use a number for a register is is more common to name them using EQU or the cblock directive. For example: temp equ 0x70
movlw 0x10
movwf temp
The cblock directive allows you to define several bytes together. For example: cblock 0x70 temp x, y endc This bit of assembler defines 3 bytes that will be at locations 0x70 (temp), 0x71 (x), and 0x72 (y). Note that if you put multiple items on the same line, you need a comma between them. When using cblock, you can also specify an increment by using a colon after each name. So for example: cblock 0x70 temp:0, temp_hi, temp_lo x, y endc This snippet creates a variable called temp at 0x70. The variable temp_hi is also at 0x70 and temp_lo is at 0x71 (x and y are at 0x72 and 0x73). Presumably, we meant for temp to hold 16 bits in this example. You might write: cblock 0x70 temp x, y arraybase:0x10 ptr endc This would put ptr 0x10 bytes after arraybase.
ADDWF, ANDWF, IORWF, SUBWF, XORWF Example: ADDWF temp,W XORWF x,F Side effect: Modifies C, DC, Z (ADDWF, SUBWF); modifies Z (ANDWF, IORWF, XORWF) These instructions do the specified operation on the W register and another register of your choice. Add and subtract work as you'd expect. The logical AND, IOR (inclusive OR) and XOR functions are the classic boolean logic primitives. The result can be placed in the W register or back in the original register depending on how you write the instruction. For example: MOVLW 1 ; put 1 in W ADDWF temp,F ; add 1 to temp, store in temp ; w still equals 1 here ADDWF x,W ; W=x+W, x is unchanged When adding, if the result won't fit in a single byte, the C flag is set. The subtraction operation always computes F-W, so be careful that you don't mean W-F. So if W contains 3 and x contains 10, performing a subtraction will result in 7. When subtracting, the C bit is a borrow flag. If the result was negative, then C will equal 0. If the C flag is 1, no borrow occurred. So computing 10-3 leaves C=1. But computing 3-10 will clear C. The result, in this case will be 0xF9 which is the two's compliment representation of -7. To compute the two's compliment of a negative number, write the magnitude as a binary number. So for 7, we have 00000111. Then invert all the bits (11111000) and add 1 (11111001). You can easily reverse the process, so if I tell you the subtraction yields 0xF9, you can write it as binary (11111001), subtract 1 (11111000) and then invert the bits (00000111) to know that it means -7. When using this scheme the topmost bit acts like a sign bit. The only problem is you can't readily tell a positive overflow from a negative number. For example, 0x70 is a positive number. Adding 0x70 to 0x70 yields 0xE0 which is correct, but is indistinguishable from a negative number. Therefore when performing signed arithmetic, you are limited to numbers from -128 to 127. There is also a DC flag which acts like the C flag, but it is set or cleared based on the carry (or borrow) condition of the low 4 bits of the operation. This can be useful when performing binary coded decimal (BCD) operations. All the instructions in this section set the Z flag if their result is zero. Otherwise, the Z flag is cleared.
ADDLW, ANDLW, IORLW, SUBLW, XORLW Example: ADDLW .30 XORLW 0x80 Side effect: Modifies C, DC, Z (ADDLW, SUBLW); modifies Z (ANDLW, IORLW, XORLW) These instruction act just like the normal add, subtract, and, inclusive or, and exclusive or instructions except they operate on a literal. So: ADDLW 2 adds 2 to W (and, of course, leaves the result in W). All the flags are modified just as they are in the register-based versions of these instructions (see above). The subtract instruction computes literal-W which is often confusing. So instead of writing: SUBLW 2 ; computes 2-W You probably meant to write: ADDLW -2 ; This is probably what you really want Adding negative 2 will get you the result you want without having to do unnecessary steps.
CLRF, CLRW Example: CLRF temp CLRW Side effect: Sets the Z flag to 1 It is a very common operation to load a zero into a register and this instruction can do that in one step. The CLRW instruction clears W, which is odd since MOVLW 0 will do this also. Unlike some processors, the PIC stores the literal inside the instruction, so a CLRW and a MOVLW 0 take the same amount of program space. The only difference is MOVLW will not change the Z flag, while CLRW will.
COMF Example: COMF temp,W Side effect: Modifies Z This instruction inverts the bits in the register specified. You can store the result in W (,W) or back to the register (,F). The Z flag will be set if the result is zero, or cleared otherwise. If you want to invert the bits in the W register, execute XORLW 0xFF which has the same effect.
INCF, DECF Example: INCF temp,W DECF x,F Side effect: Modifies Z Another common operation is to add or subtract 1 from a register. These instructions do that (INCF is +1; DECF is -1). These operations affect the Z flag (setting it if the result is zero) but do not modify the C or DC flags. As usual, you can store the result in W or back to the register. If you want to increment or decrement W try ADDLW 1 or ADDLW -1.
INCFSZ, DECFSZ Example: INCFSZ temp,W DECFSZ x,F Side effect: May modify program counter These instructions are similar to INCF and DECF. They add or subtract one from the indicated register and store the result as directed. However, these instructions don't set the Z flag. Instead, if the result is zero, they skip the next instruction. This is useful, of course, for loops. Here's an example: CLRF y MOVLW .10 ; MOVWF i ; i=10 MOVF x,W ; W=X LOOP: ADDWF y,F ; Y=Y+X DECFSZ i,F GOTO LOOP ; Leave here with the answer in y Can you guess what this loop does? Click here to find out!
NOP Example: NOP Side effect: None This instruction does nothing. It is often used in timing loops to waste time. If you are using a lot of these, you might consider other ways to waste time. For example, GOTO $+1 is one instruction that wastes 2 cycles. If you already have a return somewhere in your program, you can use it to get a single instruction to waste 4 instruction cycles. Suppose your code has a label WASTETIME that is in front of some existing RETURN instruction. Then you can execute CALL WASTETIME. This deep sixes 4 instruction cycles with one instruction.
RLF, RRF Example: RLF temp,F Side effect: Modifies C These instructions move the bits in the register left by one bit (RLF) or right (RRF). The extra bit that is shifted in comes from the C flag, and the bit that is shifted out goes to the C flag. So if you set C=1 and perform an RLF on a register that contains 0x81, the result will be 0x03 and the C flag will be set. This is often useful to manipulate bits, but there is another interesting property: shifting left multiplies a number by 2 and shifting right divides by 2! You can often combine this with adding to get easy multiplications. For example, suppose you want to compute y=10*x. Well, that is the same as y=8*x+2*x, right? So try this: BCF status,C ; C=0 RLF x,F ; X=2*X BCF status,C RLF x,W ; W=2*X (so 4*X) MOVWF temp BCF status,C RLF temp,W ; W=2*X (so 8*X) ADDWF x,W ; W=8X+2X = 10X MOVWF y There isn't an easy way to combine right shifts for division, but if you need to divide by 2, 4, 8, 16, etc. then you can simply combine shifts.
SWAPF Example: SWAPF temp,w Side effect: None This peculiar command moves the low 4 bits of the register to the top 4 bits and the top 4 bits of the register to the bottom 4 bits. You can put the result back in the register or in W. So if a register contained, say, 0xA5 and you execute SWAPF, the result will be 0x5A. This instruction can be useful when working with BCD or hex numbers. However, it is also useful when you want to move a register to or from W without affecting any flags: SWAPF temp,F SWAPF temp,W ; move temp to W with no flag changes ; If you want temp back the way it was, you need one more instruction: SWAPF temp,F
BCF, BSF Example: BCF status,Z Side effect: None These instructions clear (BCF) or set (BSF) the indicated bit in a register. The bit may have a name (like Z) or you can use a number from 0 to 7 (0 is the least significant bit and 7 is the most significant). Sometimes you can use these to save a few instructions. For example, suppose you had written: MOVLW 0x7F ANDWF temp,F You could replace this with a single BCF temp,7 instruction. Not only is this faster and takes less space, but it doesn't destroy the W register either!
GOTO Example: GOTO main Side effect: None As you'd expect, the GOTO instruction forces your program to resume execution at the label you indicate. The instruction only holds an 11 bit address, so the top bits of the new program counter come from the PCLATH register.
CALL, RETURN Example: CALL hexout RETURN Side effect: None A CALL is similar to a GOTO with one big exception: the value of the program counter is saved on an internal stack. When the program later executes a RETURN, the program will resume executing after the CALL instruction. The internal stack of a PIC16F87x, for example, is 8 levels deep. Like the GOTO, the CALL instruction takes the top part of the address from PCLATH. You can save some stack depth, code space, and execution time by watching for subroutines that call another subroutine right before they are done. For example, suppose you program contains this: suba: CALL subb CALL subc RETURN You could rewrite it more efficiently like this: suba: CALL subb GOTO subc
RETLW Example: RETLW .99 Side effect: None RETLW is exactly like RETURN except that it loads a literal value into the W register before it returns.
RETFIE Example: RETFIE Side effect: Sets GIE RETFIE is exactly like a return, but it also sets the global interrupt enable (GIE). When a hardware interrupt occurs, it clears GIE and executes what amounts to a CALL instruction. Using RETFIE allows you to enable interrupts and return to the main program all in one step. If you don't want interrupts enabled again, just execute a RETURN instead.
BTFSC, BTFSS Example: BTFSS temp,7 Side effect: May modify program counter When you want to perform a conditional jump, you need one of these instructions. They test a bit and skip the next instruction if the bit is set (BTFSS) or clear (BTFSC). Most often the next instruction is a GOTO or a CALL, but it could be any single instruction. For example, this code tests temp to see if it is zero. If it is, it loads temp with 0x80. Otherwise, temp is unchanged: MOVF temp,F ; set Z flag, no data really moved BTFSC status,Z ; skip if not zero BSF temp,7 ; since temp was 0, now it is 0x80!
CLRWDT Example: CLRWDT Side effect: None This instruction informs the watchdog timer that your program is still executing. If you aren't using the watchdog, you don't need this instruction.
SLEEP Example: SLEEP Side effect: None Putting the processor to sleep allows it to consume very little power, but you'll need an interrupt or a reset to wake it back up.
Notes! Some PIC processors have slightly different instruction sets, but if you know the above, you'll be able to handle the differences with just a little study. In addition, some assemblers for the PIC use very different mnemonics for the same instruction set. This tutorial focuses on the Microchip standard mnemonics.
|
|