2. Increments and Decrements
Incrementing a 16 Bit value is very simple:
incf Reg, f ; Increment the Low byte
btfsc STATUS, Z ; Do we have Zero (Multiple of 256)?
incf Reg + 1, f ; Increment High byte (if necessary)
If a PIC17Cxx or a PIC18Cxx is used, the "infsnz" instruction to
simplify the sixteen bit increment by one instruction:
infsnz Reg, f ; Increment "Reg's" Low Byte and Skip
incf Reg + 1, f ; High Byte Increment if Result is Not
; Equal to Zero
The Decrement of a 16 Bit value isn't quite so simple:
movf Reg, f ; Set "Z" if LOW "Reg" == 0
btfsc STATUS, Z
decf Reg + 1, f ; If Low byte is Zero, Decrement High
decf Reg, f
3. Addition/Subtraction
Addition and Subtraction of 16 Bit Variables to Constants follows the
same format with the most significant byte processed first. Addition follows the
format:
movlw HIGH 0x01234 ; Add the high byte first
addwf Reg + 1, f
movlw LOW 0x01234 ; Add the Low Byte Next
addwf Reg, f
btfsc STATUS, C ; Don't Inc high byte if carry Reset
incf Reg + 1, f
For the PIC17Cxx and PIC18Cxx, the "addwfc" instructions can be used to
simplify the operations and eliminate the need for checking the status of the carry flag.
For the addition above, the PIC17Cxx or PIC18Cxx code would be:
movlw LOW 0x01234 ; Add Low Byte First
addwf Reg, f
movlw HIGH 0x01234 ; Add High Byte Next
addwfc Reg + 1, f
The corresponding subtraction, ie:
Reg = Reg - 0x01234
Looks like:
movlw HIGH 0x01234 ; Subtract the High Byte First
subwf Reg + 1, f
movlw LOW 0x01234 ; Subtract the Low Byte Next
subwf Reg, f
btfss STATUS, C ; Don't Dec high byte if carry Set
decf Reg + 1, f
Again, the enhanced instructions in the PIC17Cxx and PIC18Cxx can
used for the sixteen bit subtraction operation. Using the "subwfb" instruction, the
sixteen bit subtraction can be simplified to:
movlw LOW 0x01234 ; Subtract the Low Byte First
bsf STATUS, C ; Don't pass any "Borrow"
subwfb Reg, f ; Reg = Reg – w - !C
movlw HIGH 0x01234
subwfb Reg + 1, f ; Reg + 1 = Reg + 1 – w - !C
The general case 16 Bit addition,
Destination = Source + 0x05678
Will look like:
movlw HIGH 0x05678 ; Add High Byte First
addwf Source + 1, w
movwf Destination + 1, f ; Store Result in Destination
movlw LOW 0x05678 ; Add Low Byte Next
addwf Source, w
movwf Destination, f ; Store Result
btfsc STATUS, C ; Is the Carry Flag Set?
incf Destination + 1, f ; Yes, Increment High Byte
If the Destination is different from both values to be added, ie:
c = a + b
the code is changed to save the sums in "w" and then store it in "c" like:
movf a + 1, w ; Add the High Bytes
addwf b + 1, w
movwf c + 1
movf a, w ; Add the Low Bytes
addwf b, w
movwf c
btfsc STATUS, C ; Increment due to Carry
incf c + 1
Subtraction is carried out in the same way, but care must be taken to ensure that the subtracting Register is kept straight (something that is less of an issue with addition). If you want to do the following statement:
c = a - b
You would use the code:
movf b + 1, w ; Get Value to be subtracted
subwf a + 1, w ; Do the High Byte
movwf c + 1
movf b, w ; Get the Value to be Subbed
subwf a, w
movwf c
btfss STATUS, C ; Look for the Carry
decf c + 1
4. Other Operations on Constants and Variables
Doing other operations (bitwise or whatever) on 16 bit values can use
the code shown above as a base.
For example, ANDing a 16 Bit Variable with 0x0A5A5 would be done like:
movlw 0x0A5 ; Get Value for ANDING
andwf Reg + 1, f ; Do the High Byte
andwf Reg, f ; Do the Low Byte
There is one difference, however and that is to do with rotating 16 Bit
values. Rotating must be carried out in such a way that the carry flag is always correct
for the shift.
To shift left:
bcf STATUS, C ; Clear the Carry Flag for new bit
rlf Reg, f ; Shift the Low Byte
rlf Reg + 1, f ; Shift High Byte with Low Carry
and to shift right:
bcf STATUS, C ; Clear Carry Flag for the New bit
rrf Reg + 1, f ; Shift down the High Byte
rrf Reg, f ; Shift Low Byte with Valid Carry
5. Comparisons with 16 Bit Variables
Comparisons involving 16 Bit Variables require that the comparison value
(or Register) is subtracted from the Register to be checked. The results of this will
then tell you what is going on with the condition. I use the same code as is shown above
and save the result in temporary values and then look at the result. The subtraction
code used for comparing a 16 Bit Variable to another 16 Bit variable is:
movf Reg2 + 1, w ; Get the High Byte of the Result
subwf Reg1 + 1, w
movwf _2 ; Store in a Temporary Register
movf Reg2, w ; Get the Low Byte
subwf Reg1, w
btfss STATUS, C ; Decrement High if Necessary
decf _2
At the end of this series of instructions, "w" contains Reg2 - Reg1 and "_2"
contains Reg2HI - Reg1HI with the borrow result of Reg2 - Reg1.
If the Variable is to be compared against an immediate value, then the
"movf" Instructions would be replaced with "movlw" and the two bytes of the immediate value.
There are six basic conditions that you can look for: Equals, Not Equals,
Greater Than, Greater Than or Equal, Less Than, Less Than or Equals. So, to discover
whether or not I have any of these conditions, I add the following code:
For Equals and not equals, the value in "w" is ORed with "_2" to see if the
Result is equal to zero.
iorwf _2, w ; Is the Result == 0?
for Equals add the lines:
btfss STATUS, Z ; Execute following Code if == 0
goto Zero_Skip ; Else, Code != 0, Skip Over
for Not Equals, append:
btfsc STATUS, Z ; Execute following if != 0
goto NotZero_Skip ; Else, Code == 0, Skip Over
If a Greater than (The 16 Bit Variable is Greater than the Comparison Value),
then the result will not be less than Zero. Actually, the same code (just with a different
Bit Skip) can be used to test.
For Greater Than:
btfsc _2, 7 ; Not Negative, 16 Bit is Greater
goto NotGreater_Skip ; Else, Skip if Not Greater than
iorwf _2, w ; Is it Equal to Zero?
btfsc STATUS, z ; No, It is Greater than
Goto NotGreater_Skip ; Else, if Zero, Not Greater than
Note that just the most significant bit of the 16 but difference is checked.
If this bit is set (= 1), then the 16 Bit Variable is less than the Comparison. If it is
reset (= 0), then it is greater than and you should check to see if the result is not equal
to zero (or else it is equal).
For Less Than:
btfss _2, 7 ; Negative, 16 Bit is Less Than
goto NotLess_Skip ; Else, Skip because Not Less Than
To check for Greater or Equal To, the last three lines of the code checking
for Greater Than are simply erased. To check for Less or Equal To, the three lines from
Not Equal to are added before the check for less than.
Here is the complete code for compare and skip on Reg1 less than or equal
to Reg2:
movf Reg2 + 1, w ; Get the High Byte of the Result
subwf Reg1 + 1, w
movwf _2 ; Store in a Temporary Register
movf Reg2, w ; Get the Low Byte
subwf Reg1, w
btfss STATUS, C ; Decrement High if Necessary
decf _2
iorwf _2, w ; Check for Equal to Zero
btfsc STATUS, Z ; If Not Zero, Jump Over
goto EqualLess_Skip ; Equals, Jump to the Code
btfsc _2, 7 ; If Number is Negative, execute
goto EqualLess_Skip ; Else, Jump Over
6. Multiplication
For both multiplication and division, repeated addition could be used,
but I find that using a scaling routine works much better and faster. These algorithms
test bits and only operate if it is appropriate.
Here is a Sixteen Bit Multiplication with a Sixteen Bit Result:
clrf Product
clrf Product + 1
movlw 16 ; Operating on 16 Bits
movwf BitCount
Loop ; Loop Here for Each Bit
rrf Multiplier + 1, f ; Shift the Multiplier down
rrf Multiplier, f ; by one
btfss STATUS, C ; If the bit is set, add
goto Skip ; the Multiplicand to the
; "Product"
movf Multiplicand + 1, w
addwf Product + 1, f
movf Multiplicand, w
addwf Product, f
btfsc STATUS, C
incf Product + 1, f
Skip ; Shift up Multiplicand and
bcf STATUS, C ; Loop Around
rlf Multiplicand, f
rlf Multiplicand + 1, f
decfsz BitCount
goto Loop
The code given below is the most efficient way of doing a sixteen bit
multiply with a 32 bit result. It is not immediately obvious, but it's very clever.
Rather than use a 32 bit add each time the shifted data is detected, it provides a
sixteen bit (with valid carry) add and then shifts the data down. This Code does not
change "Multiplicand", but does change "Multiplier". Note that in the code, I use a
thirty two bit value for "Product" (using a "Product:4" line in the "CBLOCK" variable
declare statement).
clrf Product + 2 ; "Product" will be the
clrf Product + 3 ; Result of the Operation
movlw 16 ; Operating on 16 Bits
movwf BitCount
Loop ; Loop Here for Each Bit
rrf Multiplier + 1, f ; Shift the Multiplier down
rrf Multiplier, f ; by one
btfss STATUS, C ; If the bit is set, add
goto Skip ; the Multiplicand to the
; "Product"
clrf Product + 4
movf Multiplicand + 1, w
addwf Product + 3, f
btfsc STATUS, C ; Make Sure the Carry is Passed
incf Product + 4, f ; to the Next Byte
movf Multiplicand, w
addwf Product + 2, f
btfsc STATUS, C
incfsz Product + 3, f ; Make Sure Carry is Passed with
goto $ + 2 ; the Shift
incf Product + 4, f
Skip ; Shift "Product" Down with
bcf STATUS, C
rrf Product + 4, f
rrf Product + 3, f ; the Reset Carry from the
rrf Product + 2, f ; Multiplier shift down or
rrf Product + 1, f ; the result of the sixteen
rrf Product, f ; bit addition.
decfsz BitCount
goto Loop
For the PICmicros that have built in eight by eight multipliers, the
code for sixteen bit multiplication uses the techniques taught in high school mathematics
for multiplying together to variable factors. Instead of thinking of a sixteen bit
number as just a contiguous set of sixteen bits, the number is broken up as two sets of
numbers that are eight bits in size. The PIC18CXXX sixteen bit multiplication with a
thirty two bit result is:
clrf Product + 2 ; Clear the High-Order Bits
clrf Product + 3
movf Al, w ; Do the "L" Multiplication first
mulwf Bl
movf PRODL, w ; Save result
movwf Product
movf PRODH, w
movwf Product + 1
movf Al, w ; Do the "I" Multiplication
mulwf Bh
movf PRODL, w ; Save the Most Significant Byte First
addwf Product + 1, f
movf PRODH, w
addwfc Product + 2, f ; Add to the Last Result
movf Ah, w ; Do the "O" Multiplication
mulwf Bl
movf PRODL, w ; Add the Lower Byte Next
addwf Product + 1, f
movf PRODH, w ; Add the High Byte First
addwfc Product + 2, f
btfsc STATUS, C ; Add the Carry
incf Product + 3, f
movf Ah, w ; Do the "F" Multiplication
mulwf Bh
movf PORDL, w
addwf Product + 2, f
movf PRODH, w
addwfc Product + 3, f
7. Division
The division routine provided here first finds how far the divisor can be
shifted up before comparing to the quotient. The "Count" variable in this routine is a
16 Bit variable that is used to both count the bits as well as add to the quotient.
"Temp" is an 8 Bit temporary Storage Variable. At the end of the division routine,
"Dividend" will contain the remainder of the operation.
clrf Quotient
clrf Quotient + 1
movlw 1 ; Initialize Count
movwf Count
clrf Count + 1
StartLoop ; Find How Large "Divisor" can
; be
btfsc Divisor + 1, 7 ; If at the "top", then do
goto Loop ; the Division
bcf STATUS, C ; Shift Count and Divisor Up
rlf Count, f
rlf Count + 1, f
rlf Divisor, f
rlf Divisor + 1, f
goto StartLoop
Loop ; Now, Take Away "Divisor"
; from "Dividend"
movf Divisor + 1, w ; If Divisor < Dividend then
subwf Dividend + 1, w ; Don't Take Away
movwf Temp
movf Divisor, w
subwf Dividend, w
btfss STATUS, C
decf Temp, f
btfsc Temp, 7 ; If "Temp" Negative then
goto Skip ; Divisor < Dividend
movwf Dividend ; Save the New Dividend
movf Temp, w
movwf Dividend + 1
movf Count, w ; Add Count to the Quotient
addwf Quotient + 1, f
movf Count, w
addwf Quotient + 1, f ; No Opportunity for Carry
Skip ; Shift Divisor/Count Down
bcf STATUS, C
rrf Divisor + 1, f
rrf Divisor, f
rrf Count + 1, f ; If Carry Set after Count
rrf Count, f ; Shift, Finished
btfss STATUS, C ; If Carry NOT Set, then
goto Loop ; Process next Bit
This division routine is designed to only handle positive numbers - there is
no general algorithm that handles both positive and negative numbers and passes back both
the quotient and remainder with the correct polarity efficiently.
A general form for a division routine (using the algorithm shown above)
could be the division of the core of the pseudo-code is a bit-shift analogous algorithm to
multiplication that can handle positive and negative numbers.
if (Dividend < 0) { // Change dividend to positive number
Dividend = 0 – Dividend;
dividendneg = 1; // Mark we have to change it back
} else
dividendneg = 0;
if (Divisor < 0) { // Repeat with the Divisor
Divisor = 0 – Divisor;
divisorneg = 1;
} else
divisorneg = 0;
Count = 0; // Going to Count where division starts
Quotient = 0; // Store the Quotient
while (( Divisor & 0x0400 ) != 0) {
// Find the Start of the Division
Count = Count + 1; // Increment the Number of Bits Shifted
Divisor = Divisor << 1;
}
while (Count != 0) { // Now, do the Division
if (Dividend >= Divisor) {// A subtract can take place
Quotient = Quotient + 2 ^ Count;
Dividend = Dividend – Divisor;
}
Count = Count – 1;
Divisor = Divisor >> 1;
}
if (Dividendneg == 1) // Now, change the values
if (Divisorneg == 1) {
Quotient = Quotient;
Remainder = 0 – Dividend;
} else {
Quotient = 0 – Quotient;
Remainder = 0 – Dividend;
else // The Dividend was Positive
if (Divisorneg == 1) {
Quotient = 0 – Quotient;
Remainder = Dividend;
} else {
Quotient = Quotient;
Remainder = Dividend;
}