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
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"
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
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
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