# PICMicrocontroller Comparison MathMethods

Sections:

related pages:

Archive:

[FIXME: lots of redundant stuff here.] [FIXME: Should I break this into several pages ? How ?]

http://www.forth.com/Content/Handbook/FPH22.html has a little illustration trying to explain signed, unsigned, and ``circular'' interpretation of a number. Comparing one time with another time should almost always use ``circular'' interpretation to avoid problems at rollover ... FIXME: circular comparison code ? Is failure to use the circular comparisons the cause of the "fatal subtraction error" mentioned at http://www.emeagwali.com/interviews/capstone_press/ ?

## Comparing values in a PIC (THEORY)

How can the PIC do comparison ? There's no ``compare'' instruction !

The fundamental instruction for most comparisons is

```    subwf Y,w
```

. After that instruction executes, the state of the flag bits in the STATUS register reflects whether Y was bigger, smaller, or identical to the (original) value in w.

(In your own code, you will somehow get one of the values you want to compare into the w register, and replace the letter ``Y'' with the name of the register containing the other value).

The subwf instruction, all by itself, compares 2 unsigned 8 bit values. Later we'll see how to compare 16 bit values and signed values.

As a side effect, it also performs subtraction. I'll call the value that you load into w before the subwf ``w''; I'll call the value the PIC puts into w after the subwf ``wnew''.

If you're already familiar with how "addwf" sets the carry register, you might reflect on the fact that

```    subwf Y,w
```

gives exactly the same results (in wnew, Carry, and Zero) as the final add in this sequence:

```    ; w(intermediate) := -w (two's complement)
xorlw 0xff
; wnew = Y + w(intermediate) = Y-w
```

There's only 3 possible results:

• if w < Y: Status,Z = 0. Status,C = 1. wnew = Y-w.
• if w == Y: Status,Z = 1. Status,C = 1. wnew = 0.
• if Y < w: Status,Z = 0. Status,C = 0. wnew = Y-w + 0x100.

You can combine those possibilites in pairs to get these other commonly used operators:

• if Y <= w: Status,Z = Status,C. (i.e., either both are 1, or both are 0).
• if w != Y: Status,Z = 0. Status,C = (could be either).
• if w <= Y: Status,Z = (could be either). Status,C = 1.

Hotshot PIC assembly-language programmers try to force their comparisons to be (w <= Y) or (Y < w), because then they only need to check Status,C -- they can ignore Status,Z.

-- with help from Robin Abbott - robin.abbott@dial.pipex.com

If you're used to programming in assembly language on a non-PIC chip, check this out:

Comparisons to 0 -- if( Y <= 0 ) or if( 0 < Y ), where Y is a signed (8 or 16 bit) number -- are the fastest comparisons on the PIC. They are even faster than comparing ``if( w == Y )'' or even ``if( w == 0 )''.

## 8 bit compares

So how do you actually use this nifty instruction ?

I'm going to use ``RAMx'' and ``RAMy'' to indicate values in a RAM register, ``K'' to indicate some fixed constant value (often defined using the ``EQU'' assembler directive). (If you want to compare 2 constant values, use the assembler directive ``#if ... #else ... #endif''. The grid below *should* cover all other combinations. ).

Q: Hey ! you left out all the ``greater than'' operators !

A: Whoops. Well, if you need ``A > B'', you can always use ``B < A''.

Most of these are from Tony Nixon.

I'm going to go on the assumption that you want to execute a chunk of code only if the conditional is *true*. (This is what ``if()'' means in C and other decent high-level languages. Don't let those BASIC programmers confuse you.)

Put that chunk of code (or a CALL to it) immediately after these blocks of code, and follow that chunk with some unique label. Then replace the word "Endif" with the name of that label.

If that chunk of code is *exactly* one instruction long (say ``CALL''), and you're very clever, you can optimize this code even more.

( swstmt.htm describes how to code ``if - then - else - endif'' blocks).

Q: Is there a *reason* Tony Nixon uses ``addlw'' and ``addwf'' rather than ``sublw'' and ``subwf'' ? All PICs support subwf, right ? And the ones that don't support sublw don't support ``addlw'' either, right ?

If you want to use a ``12 bit core PIC'' such as the 12C509, note that it does not have the SUBLW, ADDLW instructions. See the PIC Microcontroller Instruction Set Quick Reference and Core Comparison Matrix . If you're using one of those chips, then you can use MOVLW then SUBWF (or ADDWF) ...

```;if RAMx <= K
;Tony Nixon
movf RAMx,w
addlw 255 - K           ; eg if RAMx > 5 ... addlw d'250'
skpnc
goto Endif

;if K <= RAMx
;Tony Nixon
movf RAMx,w
addlw 255 - K + 1       ; eg if RAMx < 5 ... addlw d'251'
skpc
goto Endif

;if RAMx < K
; Tony Nixon
movf RAMx,w
addlw 255 - K + 1       ; eg if RAMx >= 5 ... addlw d'251'
skpnc
goto Endif

;if K < RAMx
; Tony Nixon
movf RAMx,w
addlw 255 - K           ; eg if RAMx <= 5 ... addlw d'250'
skpc
goto Endif
```

```;if RAMx <= RAMy
; Scott Dattalo
movf RAMx,w
subwf RAMy,w
skpc
goto Endif
```
```;if RAMy < RAMx
; Scott Dattalo
movf RAMx,w
subwf RAMy,w
skpnc
goto Endif
```
```;if RAMx < RAMy /* obsolete */
; unknown -- perhaps James Newton ?
movf RAMx, w
subwf RAMy, w
skpz	;for the case that RAMx=RAMy where C will be 1
skpc
goto Endif

;if RAMx <= RAMy
; better, but only works on 18cxxx chips !
; Scott Dattalo
movf  RAMx,w  ;wreg = RAMx
subwf RAMy,w  ;wreg = RAMy - RAMx
bn    Endif ;Branch if negative only available on 18cxxx
; (You could use the bnc [branch if no carry] to achieve the same effect.
;  The negative bit has a clearer meaning in this context).
;The N bit will be cleared if RAMx == RAMy or
;RAMy > RAMx, and will be set if RAMy < RAMx
;18cxxx 3*16 = 48 bits of program memory
;16cxxx 5*14 = 70 bits of program memory

;if RAMy < RAMx
; better, but only works on 18cxxx chips !
; Scott Dattalo
movf  RAMx,w    ;wreg = RAMx
subwf RAMy,w    ;wreg = RAMy - RAMx
bnn   Endif     ;branch if not negative
;The N bit will be cleared if RAMx == RAMy or
;RAMy > RAMx, and will be set if RAMx < RAMy
; Again, you could use the bc (branch on carry) instruction too.

```

Regulus Berdin says: For readability, one could write a jump if equal macro:

```JIFEQ   MACRO   register,literal,address
movlw   literal
xorwf   register,w
skpnz
ENDM

switch:
JIFEQ   data,'A',Process_A
JIFEQ   data,'B',Process_B
JIFEQ   data,'C',Process_C
.
.
.

process_A:
...
return

process_B:
...
return

process_C:
...
return
.
.
.

```

## 16 bit compares

16 bit compares. Here I use

• X = XH:XL the Hi and Lo bytes of the RAM variable X,
• Y = YH:YL the Hi and Lo bytes of the RAM variable Y, and
• K = KH:KL the Hi and Lo bytes of some fixed constant value K.

You must change the ``then:'' and ``endif:'' labels to some unique name. [FIXME: Or should I change these to macro locals ? ]

```; signed and unsigned 16 bit comparison routine:
; by David Cary 2001-03-30
; returns the correct flags (Z and C)
; to indicate the X=Y, Y<X, or X<Y.
; Does not modify X or Y.
compare_signed16: ; 7
; uses a "temp" register.
movf Yhi,w
xorlw 0x80
movwf temp
movf Xhi,w
xorlw 0x80
subwf temp,w	; subtract Y-X
goto Are_they_equal
compare_unsigned_16: ; 7
movf Xhi,w
subwf Yhi,w ; subtract Y-X
Are_they_equal:
; Are they equal ?
skpz
goto results16
; yes, they are equal -- compare lo
movf Xlo,w
subwf Ylo,w	; subtract Y-X
results16:
; if X=Y then now Z=1.
; if Y<X then now C=0.
; if X<=Y then now C=1.
return

```

After calling the above routine (or cutting-and-pasting a copy of it), you can use the result flags (Z and C) just like the 8 bit compares . For example,

```
;if( X <= Y )
call compare_unsigned_16
skpc
goto endif
; then:
;...
endif:

;if( X <= K ) (signed)
movlw Khi
movwf Yhi
movlw Klo
movwf Ylo
call compare_signed_16
skpc
goto endif
; then:
;...
endif:

```

I need signed 16bit compare and branch macros for PIC16F877 working on pseudo 16bit registers ACCaLO,ACCaHI like some Microchip application notes. There are heaps of 16bit subtract/add routines but none set any useful status bits that can be used to branch on the 8 possibilities (signed & unsigned)

I have made a crude version of these using the status code boolean algebra found in a Motorola 68000 data book and it appears to work ok. I was rather hoping that someone (microchip)would have a thoroughly tested and more elegant version of this. Maths routines are not my speciality & surely this should be readily available. Doesn't anyone out there compare 2 signed numbers then branch if one is greater than the other?

Any help would be greatly appreciated. If anyone can improve on my code then they are most welcome to have it.

Just an idea. How about modifying the signed format to a biased format and compare biased values as unsigned numbers?

By a biased format I mean inverted sign bit. That would shift the least signed value to 0, zero to middle, and highest signed value to highest unsigned value. For example,

```-128 ^ 128 = 0
-127 ^ 128 = 1
-127 ^ 128 = 2
...
-1   ^ 128 = 127
0    ^ 128 = 128
1    ^ 128 = 129
2    ^ 128 = 130
...
127  ^ 128 = 255
```

Then just use unsigned routines for comparison. To restore the input values, invert sign bits again.

Most of these are based on code from Tony Nixon. These only do *unsigned* comparisons.

```
;********** 16 BIT
;if( X <= K )
movfw XH
sublw KH ;not available on 12 bit core
skpc
goto endif
skpz
goto then
movfw XL
sublw KL
skpc
goto endif
then:
...
endif:

;if( K <= Y )
movlw KH
subwf YH,w ; *is* available on 12 bit core
skpc
goto endif
skpz
goto then
movlw KL
subwf YL,w
skpc
goto endif
then:
...
endif:

;if(K < X)
movlw KH
subwf XH,w ; *is* available on 12 bit core
skpc
goto endif
skpz
goto then
movf XL,w
sublw KL ;not available on 12 bit core
skpnc
goto endif
then:
...
endif:

;if(Y < K)
movf YH,w
sublw KH ;not available on 12 bit core
skpc
goto endif
skpz
goto then
movlw KL
subwf YL,w
skpnc
goto endif
then:
...
endif:

;if( X <= Y ) /* obsolete */
movfw XH
subwf YH,w ; *is* available on 12 bit core
skpc
goto endif
skpz
goto then
movfw XL
subwf YL,w ; Use ,w rather then ,f to preserve Y.
skpc
goto endif
; X and Y are unchanged.
then:
...
endif:

```

Scott Dattalo (on 2001-04-10) said ``the classic 6 instruction subtract ... I'm not the original author. I think Bob also got accused of writing that code too. But in reality it predates both of us by years.''

Here is the 6 instruction subtract, (from math/sub/16bb.htm ) and a couple of compare routines based on it. This works even on a 12 bit core [right ?].

```;----------------------------
; 16-bit Subtraction-with-Borrow
;       SourceH:SourceL = Number to be subtracted
;       DestH:DestL = Number to be subtracted FROM
;Out    DestH:DestL = Result = dest-source
;       Carry = NOT( Borrow result)
; by  Rudy Wieser (2000-02-17)
movfw    SourceL
subwf   DestL,f
movfw    SourceH
skpc
incfsz  SourceH,W
subwf   DestH,f           ;dest = dest - source, WITH VALID CARRY
;(although the Z flag is not valid).
;----------------------------

; if( y < x )
; by unknown
; Y and X are unchanged.
movfw xl
subwf yl,w
movfw xh
skpc ; c=0 indicates a borrow we need to propagate.
incfsz xh,w ; handle xh=0xff correctly, unlike ``incf xh,w''.
subwf yh,w
skpnc
goto endif
; then:
; /* y is less than x */
...
endif:

; if( x <= y )
; by unknown
; Y and X are unchanged.
movfw xl
subwf yl,w
movfw xh
skpc ; c=0 indicates a borrow we need to propagate.
incfsz xh,w ; handle xh=0xff correctly, unlike ``incf xh,w''.
subwf yh,w
skpc
goto endif
; then:
; /* x is less than or equal to y */
...
endif:

```

Scott Dattalo says:

Similar sequences exist for the other cases.
```;if( x < y )
; originally by Scott Dattalo (2001 ?)
movfw y_lo
subwf x_lo,w
movfw y_hi
subwfc x_hi,w ; only works on 18cxxx chips !
bc endif
; /* x is less than y */
...
endif:

;if( y <= x )
; originally by Scott Dattalo (2001 ?)
movfw y_lo
subwf x_lo,w
movfw y_hi
subwfc x_hi,w ; only works on 18cxxx chips !
bnc endif
; /* y is less than or equal to x */
...
endif:

;if( x <= y )
; originally by Scott Dattalo (2001 ?)
movfw y_lo
subwf x_lo,w
movfw y_hi
subwfc x_hi,w ; only works on 18cxxx chips !
bn endif
; /* x is less than or equal to y */
...
endif:

```

Antonio L Benci [Nino.Benci at SPME.MONASH.EDU.AU or possibly spme.monash.edu] http://www.physics.monash.edu.au/~ninob on 2001-04-10 11:53:45 PM wrote:

I wrote this code about 6 years ago...
```;*******************************************************************
; A not too optimised 16 bit compare routine for 16 absolute values,
; ie 0 -> 65536.
; Compare WORD to COMP (a word value).
; If WORD = COMP return with 00
; If WORD > COMP return with 01
; If WORD < COMP return with 80 ;*******************************************************************
include "p16c5x.inc" ; include file for processor type
hword equ 0x10 ; storage for high byte of WORD
lword equ hword+1 ; storage for low byte of WORD
hcomp equ 0x12 ; storage for high byte of COMP
lcomp equ hcomp+1 ; storage for low byte of COMP
COMP:
movfw hcomp ; get high byte of comp value
subwf hword,0 ; subtract values
btfsc status,z ; first check if result is 0
goto COMPL ; if zero compare low bytes
btfsc status,c ; else test carry bit
retlw 0x01 ; if WORD > COMP, return with 01h
retlw   0x80            ; if WORD < COMP, return with 80h
COMPL:
movfw lcomp ; get low byte of comp value
subwf lword,0 ; subtract values
btfsc status,z ; first check if result is 0
retlw 0x00 ; if result is 0, return with 00
btfsc status,c ; if c set then WORD > COMP
retlw   0x01            ; if WORD > COMP, return with 01h
retlw   0x80            ; if WORD < COMP, return with 80h end

```

Alan "the Rocket Scientist" says:

```;This macro is for a 16 bit absolute value subtraction.
;The smaller of the two numbers will be subtracted from
;the larger. A bit will be set if the first of the two
;numbers is larger. The bit will be cleared if the second
;of the two numbers is larger. The results of the subtraction
;will be placed in the first set of registers (fr0,fr1) if the
;first number is the higher, The results will be placed in both
;register if the second number is higher.

subabs macro fr0,fr1,fr2,fr3,fr4,bit

local above,label
local abovelo,label
local below,label
local belowlo,label
local equal,label
local out,label

movf     fr2,0
subwf   fr0,0
btfsc _Z
goto equal
above:
movf fr2,0
subwf fr0,0
btfss _C
goto below

movf fr3,0
subwf fr1,1
btfsc _C
goto abovelo
decf fr0,1
abovelo:
movf fr2,0
subwf fr0,1
bcf fr4,bit
goto out
below:
movf fr1,0
subwf fr3,1
btfsc _C
goto belowlo
decf fr2,1
belowlo:
movf fr0,0
subwf fr2,1
movf fr3,0
movwf fr1
movf fr2,0
movwf fr0
bsf fr4,bit
goto out
equal:
movf fr1,0
subwf fr3,0
btfss _C
goto above
goto below
out:
endm
```

Here is the line of code I actually use

```       subabs     cmdhi,cmdlo,poshi,poslo,PORTB,2
```

I use this to find the difference between the commanded position of the actuator and current position. The calculated error is then used to find a value from a look up table that is output to a motor controller IC via the PWM output (CCP1). The direction of rotation input on the IC is output from PORTB. I have reused this macro in several different designs.

Here is another one that I have used much more extensively. It will compare the 16 bit value stored in fr, fr-1 to a 16 bit literal number and then jump to an address is the value in the registers is higher than the literal.

```cjalwd macro fr1,L1,L2,addr
movf fr1,W
sublw L1
btfss _C

movlw L1
subwf fr1,W
btfss _C
goto \$+5

movf fr1-1,W
sublw L2
btfss _C
endm
```

Here is an example of this macro that uses the 16 bit value stored in temp_hi,temp_lo, note that the lower byte is not in the line of code but is used anyway. Care must be taken to insure the lower byte is one address space lower in memory than the higher byte.

```
cjalwd       temp_hi,0x01,0xA9,store_temp
```

If the value in temp_hi,temp_lo is greater than 0x10A9 then the program will jump to the address location labeled store_temp.

Questions:

Footnotes:

2001-06-23:DAV: Deleted a bunch of redundant if(RAMx < RAMy) sequences.

2001-06-17:DAV: updated the above code to use the mnemonics

```skpc ; also known as ``btfss status,carry''
skpnc ; also known as ``btfsc status,carry''
```

. All good assemblers http://piclist.com/techref/microchip/languages.htm (in particular, GPASM http://gpasm.sourceforge.net/ and MPASM which comes with MPLAB http://www.microchip.com/0/tools/picmicro/devenv/mplabi/ ) support these ``skip if carry'' and ``skip if no carry'' mnemonics.

David A Cary of Motorguide Pinpoint shares this code:

```

;mnemonics:
;Set the Z flag: Z = (0==Y);
tstf Y ; also known as movf Y,f on 16xxx processors
tstf Y ; apparently known as comf Y,f ; comf Y,f; on 17Cxxx processors

;skip the next instruction (typically a ``goto'') if Z is set:
skpz ; ``btfss status,z'', or ``btfss alusta,z'' on 17cxxx processors
skpnz ; ``btfsc status,z'', or ``btfsc alusta,z'' on 17cxxx processors

;carry ... ``not borrow''
skpc ; also known as ``btfss alusta,carry'' on 17Cxxx processors
skpnc ; also known as ``btfsc alusta,carry'' on 17cxxx processors

```

(is there a better page for the above mnemonics?)

There is some excellent code for MIN() and MAX() at /techref/microchip/condrepl.htm .

• custom designed mnemonics ``Special Instruction Mnemonics'' inspired by Olin's 1 line macros

Since these are all 1 assembly instruction, it's OK to use them right after one of the ``skip'' instructions.

summarized from: Thread: instructions not in the instruction set ! http://piclist.com/techref/postbot.asp?by=time&id=piclist/2001/06/09/225915a

```
(from Olin)
SKPWGT   skip if w greater than
SKPWLE   skip if w less or equal
SKPWEQ   skip if w equal
SKPWNE   skip if w not equal

(from Roman Black)
INCW
DECW

(from Dwayne Reid)
ve got a few more that I find useful:

12 bit core only:

tstw            MACRO           ;test w, valid Z; C & DC unchanged
xorlw       0
endm

comw            MACRO           ;complement W, valid Z; C & DC
unchanged
xorlw       0xFF
endm

12 bit and 14 bit core parts:

decw            MACRO                   ;decrement w, valid Z, C
& DC trashed
endm                                ;C & DC =1 if w>0 after
decrement

negw            MACRO                   ;negate w
sublw       0
endm                                ;valid z; C & DC trashed

```

David A Cary Says: " http://massmind.org/techref/microchip/seepicsrc/psbpix/if.htm " +

Code:

• Martin Viteznik shares this code:
```Routine comparing two unsigned 16-bit numbers:
<code>
;Written by Martin Viteznik, jsem () einstein ddot cz
;this routine compares two unsigned 16-bit values,
;X and Y. The H-byte is hbyte(X|y) etc. :-)

movf hbyteX,0
subwf hbyteY,0
btfss STATUS,C	;X>Y..zc,X<Y..zC,X=Y..ZC
goto Xisgreater	;result: X>Y
btfss STATUS,Z
goto Yisgreater	;result: Y>X
movf lbyteX,0
subwf lbyteY,0
btfss STATUS,C	;X>Y..zc,X<Y..zC,X=Y..ZC
goto Xisgreater ;result: X>Y
btfss STATUS,Z
goto Yisgreater	;result: Y>X
goto XequY	;result: X=Y
</code>
```
• Check out the code for MIN() and MAX() at /techref/microchip/condrepl.htm .

See:

Questions:

Hi
I have problem in comparing 8 bit operands when the first operand is negative (in 2's complement). I have the doubt that the carry is to be complemented when the sign of the 2 operands are opposite. In the example below the latter 2 cases fail.
clrc
clrz
movlw 1
sublw 2 ;Carry=1
nop

clrc
clrz
movlw 2
sublw 2 ;Carry=1
nop

clrc
clrz
movlw 3
sublw 2 ;Carry=0
nop

clrc
clrz
movlw 4
sublw 2 ;Carry=0
nop

clrc
clrz
movlw -3
sublw -2 ;Carry=1
nop

clrc
clrz
movlw -2
sublw -2 ;Carry=1
nop

clrc
clrz
movlw -1
sublw -2 ;Carry=0
nop

clrc
clrz
movlw 0
sublw -2 ;Carry=1
nop

clrc
clrz
movlw 1
sublw -2 ;Carry=1
nop
+

 file: /Techref/microchip/compcon.htm, 27KB, , updated: 2013/9/28 10:37, local time: 2017/12/16 03:46, owner: DAV-MP-E62a, TOP NEW HELP FIND:  54.163.61.66:LOG IN

 ©2017 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?Please DO link to this page! Digg it! / MAKE! /  PIC Microcontroller Comparison Math Methods

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.

Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
 Did you find what you needed? "No. I'm looking for: " "No. Take me to the search page." "No. Take me to the top so I can drill down by catagory" "No. I'm willing to pay for help, please refer me to a qualified consultant" "No. But I'm interested. me at when this page is expanded."

 PICList 2017 contributors: o List host: MIT, Site host massmind.org, Top posters @20171216 RussellMc, Van Horn, David, Sean Breheny, James Cameron, alan.b.pearce, IVP, Neil, Bob Blick, David C Brown, John Gardner, * Page Editors: James Newton, David Cary, and YOU! * Roman Black of Black Robotics donates from sales of Linistep stepper controller kits. * Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters. * Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated! * Contributors: Richard Seriani, Sr.

.