Myke's Home Page

Book CD-ROM Home

File Copying/Harddrive Setup

Development Tools

Experiments

Projects

Useful Code Snippets and Macros

Introduction to Electronics

Introduction to Programming

Datasheets

PCBs

Links

McGraw-Hill Professional Publishing

Useful Snippets and Macros

Here are a collection of code Snippets and Macros that can be used in your applications to solve the various problems that you will encounter. Many of these "snippets" of code are so clever that I shake my head each time I see them. I want to thank everybody that has contributed these code items as they have made my life easier quite a few times.

If you have some that you think should be included, please send an email to me and I will add them to my web page as well as add them to this CD-ROM in later editions of the book.

Note that these pieces of code can be "cut and pasted" into your applications.

1. Negating the Contents of a File Register 14. Swap the Contents of Two Registers
2. Negating the Contents of "w" 15. Compare and Swap if Y < X
3. Incrementing/Decrementing "w" 16. Convert ASCII to Upper Case
4. Rotating a Byte in Place 17. Counting the number of "1"s in a Byte
5. Copy Bits from One Register to Another 18. Generating Parity for a Byte
6. Converting a Nybble to ASCII 19. Keeping a Variable within a Range
7. Converting an ASCII Byte to a Hex Nybble 20. Swapping Bit Pairs
8. Dividing by Three 21. "ANDing" Two Bits
9. Sixteen bit counter with a Constant Loop Delay 22. "ORing" Two Bits
10. Precise Delay Macro 23. "NOT" Operation on a Bit
11. Detect a Change in a Register 24. Fast Multiplication on a Byte
12. Test a byte within a range 25. Button Debounce Macro
13. Swap the Contents of "w" with a Register 26. 200,000 Cycle Delay

1. Negating the Contents of a File Register

Converting the contents of a File Register to it's 2's complement value without affecting "w" is simply accomplished by the code Snippet below. This code should not be used on any special hardware control registers.

  comf  Reg, f            ;  Invert the bits in the Register
  incf  Reg, f            ;  Add One to them to turn into 2's 
                          ;   Complement
              

2. Negating the Contents of "w"

If you have to Negate the contents of the "w" register, you could use the code above (after saving the value in "w" into a register) or you could use the code below for low-end devices (PIC16C5x, PIC12C5xx, PIC16C505). Any file register can be used for this code because its contents are never changed.

  addwf  Reg, w           ;  w = w + Reg
  subwf  Reg, w           ;  w = Reg - w
                          ;  w = Reg - ( w + Reg )
                          ;  w = -w
              

In mid range parts, the single instruction below can be used:

  sublw  0                ;  w = 0 - w
              

3. Incrementing/Decrementing "w"

Here are a couple of snippets that will allow you to increment and decrement the "w" register without affecting any file registers if you don't have "addlw"/"sublw" instructions (ie in the case of the low-end processor).

"Reg" can be any register that does not change during the execution of the three instructions. For the low-end parts, any file register can be used because there is no danger of them being updated by an interrupt handler.

To Increment:

  xorlw 0x0FF              ;  Get 1s Complement of Number
  addwf Reg, w             ;  w = Reg + (w^0x0FF)
  subwf Reg, w             ;  w = Reg + ((Reg + (w^0x0FF))^0x0FF) + 1
                           ;  w = w + 1
              

To decrement, the instructions are re-arranged:

  subwf Reg, w             ;  w = Reg + (2^0x0FF) + 1
  xorlw 0x0FF              ;  Get 1s Complement of Result
  addwf Reg, w             ;  w = w - 1
              

4. Rotating a Byte in Place

These two lines will rotate the contents of a file register without loosing data in the "Carry Flag". Rotates right and left can be implemented with this snippet. Note that the carry flag is changed.

  rlf   Register, w        ;  Load Carry with the high bit
  rlf   Register, f        ;  Shift over with high bit going low
              

5. Copy Bits from One Register to Another

Here is a fast way to save specific bits from one register into another.

  movf  Source, w
  xorwf Destination, w
  andlw B'xxxxxxxx'        ;  Replace "x" with "1" to Copy the Bit
  xorwf Destination, f     ;   Replace "x" with "0" to Leave the Bit as is
              

6. Converting a Nybble to ASCII

This is a question that comes up all the time when the contents of a byte are to be displayed/output.

The most obvious way of doing this is:

NybbletoASCII

  addwf  PCL, f            ;  Add the Contents of the Nybble to PCL/ 
  dt	 "0123456789ABCDEF"  ;  return the ASCII as a Table Offset
              

But, a much better way of doing this is:

NybbletoASCII             ;  Convert a Nybble in "w" to ASCII

  addlw  0x036            ;  Add '0' + 6 to Value
  btfsc  STATUS, DC       ;  If Digit Carry Set, then 'A' - 'F'
   addlw 7                ;   Add Difference Between '9' and 'A'
  addlw  0-6

  return                  ;  Return the ASCII of Digit in "w"
              

This method will take three instruction cycles longer than the previous code, but it requires twelve less instructions.


7. Converting an ASCII Byte to a Hex Nybble

The code below is really a rearrangement of the previous snippet. Using the aspect that the high nybble of ASCII "A" to "F" is one greater than the high nybble of "0" to "9", a value is conditionally added to make the result 0x000 to 0x00F.

ASCIItoNybble

  addlw  0x0C0            ;  If "A" to "F", Set the Carry Flag
  btfss  STATUS, C        ;  If Carry Set, then 'A' - 'F'
   addlw 7                ;   Add Difference Between '9' and 'A'
  addlw  9

  return                  ;  Return the ASCII of Digit in "w"
              

Note that ASCII characters other than "0" to "9" and "A" to "F" will result in an incorrect result.


8. Dividing by Three

As much as you try to avoid it, sometimes you have to divide. Here's an algorithm from Andy Warren for dividing a positive value by three, by knowing that divide by three can be represented by the series:

x/3 = x/2 - x/4 + x/8 - x/16 + x/32 - x/64...
              

The code to implement this is:

Div3:                     ;  Divide Contents of "w" by 3

  movwf  Dividend
  clrf   Quotient

Div3_Loop                 ;  Loop Until the Dividend == 0

  bcf    STATUS, C
  rrf    Dividend, f      ;  Dividend /2 (ie "x/2" in Series)
  movf   Dividend, w      ;   Is it Equal to Zero?
  btfsc  STATUS, Z
   goto  Div3_Done        ;  If it is, then Stop

  addwf  Quotient         ;  Add the Value to the Quotient

  rrf    Dividend, f      ;  Dividend /2 (ie "x/4" in Series)
  movf   Dividend, w
  btfsc  STATUS, Z
   goto  Div3_Done

  subwf  Quotient, f      ;  Quotient = Quotient-(Dividend / 4)

  goto   Div3_Loop

Div3_Done

  movf   Quotient, w      ;  Return the Quotient

  return
              

9. Sixteen bit counter with a Constant Loop Delay

Then Marc Heuler showed me the code:

  movlw  HIGH TimeDelay  ;  Load the Delay Values
  movwf  HiCount
  movlw  LOW TimeDelay
Dlay:
  addlw  1               ;  Increment the Counter by 1
  btfsc  STATUS, Z
   decfsz HiCount, f     ;  Decrement the High Counter
    goto Dlay
              

This loop takes five cycles to execute, regardless of whether or not "HiCount" is to be decremented (and uses one less File Register than the method above).

The actual time delay is calculated using the sixteen bit number from:

Time Dlay = (16BitDlay * 5 ins/loop * 4 clocks/ins / clock frequency) + 256
              

10. Precise Delay Macro

Here is a good, generic Delay Macro that does not change "w" or the STATUS Flag. Note that it assumes that the "DlayCount" variable is equal to zero upon code entry.

DlayMacro Macro Cycles          ;  Delay Macro for Edges
 variable i, TCycles, Value, TFlag
TCycles = Cycles
Value = 1 << 7
i = 7
TFlag = 0
 if (TCycles > 5)
 while (i >= 0)
 if ((TFlag == 0) && ((Value * 3) <= TCycles))
  bsf    DlayCount, i
TFlag = 1
TCycles = TCycles - (Value * 3)
 else
 if ((TFlag != 0) && (((Value * 3) + 1) <= TCycles))
  bsf    DlayCount, i
TCycles = TCycles - ((Value * 3) + 1)
 endif
 endif
Value = Value >> 1
i = i - 1
 endw
 if (TCycles > 3)
 Error "Delay Cycles too Large for Macro"
 endif
  decfsz  DlayCount, f
   goto   $ - 1
 endif
 while (TCycles > 1)
  goto   $ + 1
TCycles = TCycles - 2
 endw
 if (TCycles == 1)
  nop				;  Delay the Last Cycle
 endif
 endm
              

11. Detect a Change in a Register

Bob Fehrenbach has passed on a number of his snippets for inclusion in my web page and have shown up in this book. The first detects the change in a bit and saves the new data for later execution. This code can be used to detect changes in the I/O Ports, Timers, or other registers that can be updated externally to the software execution.

  movf   Reg, w                 
  andlw  Mask                   ;  Mask out unused bits
  xorwf  old, w                 ;  Compare to previous value
  btfsc  STATUS, Z              ;  If Zero set, bits are the Same
   goto  no_change
  xorwf  old                    ;  Bits are different, Store New 
                                ;   pattern in "old"
              

12. Test a byte within a range

This is an algorithm that I continually re-invent (although I don't think I've ever come up with anything as efficient as Bob Fehrenbach's routine).

  movf   Num, w
  addlw  255 - hi_lim           ;  "Num" is equal to -hi_lim
  addlw  hi_lim - lo_lim + 1    ;  "Num" is > 255 if it is above
  btfsc  STATUS, C              ;   the lo-lim
   goto  in_range               
              

13. Swap the Contents of "w" with a Register

This one has been around forever, but it never hurts to be reminded of it and to have available for easy access.

   xorwf   Reg, f               ;  w = w, Reg = Reg ^ w
   xorwf   Reg, w               ;  w = w ^ (Reg ^ w), Reg = Reg ^ w
                                ;  w = Reg, Reg = Reg ^ w
   xorwf   Reg, f               ;  w = Reg, Reg = Reg ^ w ^ Reg
                                ;  w = Reg, Reg = w
              

14. Swap the Contents of Two Registers

Using the code from the previous snippet, here's a fast way to swap the contents of two file registers:

   movf    X, w                 
   subwf   Y, w                 ;  W = Y - X
   addwf   X, f                 ;  X = X + (Y - X)
   subwf   Y, f                 ;  Y = Y - (Y - X)
              

15. Compare and Swap if Y < X

Here's a compare and swap (uses the Swap Presented above):

   movf    X, w                 
   subwf   Y, w                 ;  Is Y >= X?
   btfsc   STATUS, C            ;  If Carry Set, Yes
    goto   $ + 2                ;   Don't Swap
   addwf   X, f                 ;  Else, X = X + (Y - X)
   subwf   Y, f                 ;        Y = Y - (Y - X)
              

16. Convert ASCII to Upper Case

This is a practical application of the previous snippet. I think this subroutine demonstrates how the code works quite well.

ToUpper:
   addlw   255 - 'z'            ;  Get the High limit
   addlw   'z' - 'a' + 1        ;  Add Lower Limit to Set Carry
   btfss   STATUS, C            ;  If Carry Set, then Lower Case
    addlw  h'20'                ;   Carry NOT Set, Restore Character
   addlw   'A'                  ;  Add 'A' to restore the Character
   return
              

17. Counting the number of "1"s in a Byte

This snippet comes from Dmitry Kiryashov and the code below, is his optimization of the classic problem of counting the number of "1"s in a byte in twelve instructions/twelve cycles.

                                ;  (c) 1998 by Dmitry Kirashov

  rrf    X, w                   ;  "X" Contains Byte
  andlw  0x55                   ;  -a-c-e-g
  subwf  X, f                   ;  ABCDEFGH
                                ;  where AB=a+b, etc.
                                ;  the same trick as in example_1
  movwf  X
  andlw  0x33                   ;  --CD--GH
  addwf  X, f
  rrf    X, f                   ;  0AB00EF0
                                ;  00CD00GH
  addwf  X, f                   ;  0AB00EF0
                                ;  0CD00GH0
  rrf    X, f                   ;  0ABCD.0EFGH

  swapf  X, w
  addwf  X, w
  andlw  0x0F			;  Bit Count in "w"
              

18. Generating Parity for a Byte

The six instructions below (provided by John Payson) will calculate the "Even" Parity for a Byte. At the end of the routine, bit 0 of "X" will have the "Even" Parity bit of the original number. "Even" Parity means that if all the "1"s in the byte is summed along with Parity Bit, an even number will be produced.

  swapf  X, w
  xorwf  X, f

  rrf    X, w
  xorwf  X, f

  btfsc  X, 2
   incf  X, f
              

19. Keeping a Variable within a Range

The four instructions below will make sure that the variable "Temp" will always be in the range of Zero to "Constant".

  movlw   Constant		;  0 <= Temp <= Constant
  subwf   Temp, w
  btfsc   STATUS, C
   subwf  Temp, f
              

20. Swapping Bit Pairs

Another of Dmitry Kiryashov's routines is this sequence of instructions for swapping bit pairs in a byte in five instructions/cycles.

                                ;  (c) 1998 by Dmitry Kirashov

  movwf  X                      ;  Save the Incoming Byte in 
                                ;   a temporary register
                                ;  w = X = ABCDEFGH
  andlw  0x055                  ;  w = 0B0D0F0H
  addwf  X, f                   ;  X = ABCDEFGH + 0B0D0F0H
                                
  rrf    X, f                   ;  X = (ABCDEFGH + 0B0D0F0h) >> 1
  addwf  X, w                   ;  w = BADCFEHG
              

21. "ANDing" Two Bits

Using the code below, two bits will be "ANDed" together to find set/reset a third bit.

  bsf    Result                 ;  Assume the result is True
  btfsc  BitA                   ;  If BitA != 1 then result is False 
   btfss BitB                   ;  If BitB == 0 then result is False
    bcf  Result                 ;  Result is False, Reset the Bit
              

22. "ORing" Two Bits

"ORing" two bits together is similar to the "AND" operation, except the result is expected to be false and when either bit is set, the result is true:

  bcf    Result                 ;  Assume the result is False
  btfss  BitA                   ;  If BitA != 0 then result is True
   btfsc BitB                   ;  If BitB == 0 then result is False
    bsf  Result                 ;  Result is True, Set the Bit
              

23. "NOT" Operation on a Bit

There are two ways of implementing this operation based on where the input value is relative to the output value. If they are the same (ie the operation is to complement a specific bit), the code to be used is simply:

  movlw  1 << BitNumber         ;  Complement Specific Bit for "NOT"
  xorwf  BitRegister, f
              

If the bit is in another register, then the value stored is the complement of it:

  bcf    Result                 ;  Assume that the Input Bit is Set
  btfss  Bit                    ;   - If it is Set, then Result Correct
   bsf   Result                 ;  Input Bit Reset, Set the Result
              

24. Fast Multiplication on a Byte

Sometimes you have to do a fast multiply on a register value with a constant. in 16 Bit Operations and Macros, I go through a number of methods, but I use the following Macro for this purpose for a single byte:

FastMul Macro Register, Constant
 variable i = Constant
 clrw                           ;  Put the Product in "w"
 while (i != 0)
 if ((i & 1) != 0)
  addwf  Register, f            ;  Add the Current Contents of "Register" to "w"
 endif
 bcf     STATUS, C              ;  Shift up the Register's Contents
 rlf     Register, f
 i = i / 2                      ;  Shift down the counter value
 endw
 endm
              

25. Button Debounce Macro

This macro is designed to produce a 20 msec delay for a pulled up button to be up ("Dir" < 0) or down ("Dir" > 0). Note that the 16 bit "Dlay" variable is required with this macro.

Debounce MACRO Dir, Speed    ;  "Speed" is the PICmicro's Clock Speed
 variable DlayCount
DlayCount = 250000000 / Speed  ;  Calculate the number of Cycles in 20 msecs
DlayCount = 156250 / DlayCount
DlayCount = 0x010000 - DlayCount
                              ;   on the PICmicro MCU clock speed
 if (Direction < 0)           ;  Going Down
  btfsc  PORTA, 0
 else
  btfss  PORTA, 0             ;  Wait for Button Released
 endif
   goto  $ - 1

  movlw  LOW DlayCount        ;  Initialize Dlay for a 20 msec
  movwf  Dlay                 ;   Delay
  movlw  HIGH DlayCount
  movwf  Dlay + 1

  bcf    STATUS, Z            ;  Make Sure that Zero is Reset

  incfsz Dlay, f
   goto  $ + 2
  incf   Dlay + 1, f
 if (Direction < 0)
  btfsc  PORTA, 0
 else
  btfss  PORTA, 0             ;  Button Still Released?
 endif
   goto  $ - 11               ;  No - Loop Around Again
  btfss  STATUS, Z            ;  Zero Flag Set (20 mSecs Past?)
   goto  $ - 6

 Endm                         ;  End the Macro
              

26. 200,000 Cycle Delay

This delay code is useful for user interface situations where an easy to code delay is required. The code itself will delay for approximately 200,000 instruction cycles or 1/5 of a second for PICmicro MCU's running at 4 MHz.

  clrf   DlayCount            ;  Clear the Delay Counters
  clrf   DlayCount + 1
  decfsz DlayCount, f
   goto  $ - 1
  decfsz DlayCount + 1, f
   goto  $ - 3