PIC Microcontoller Comparison Math Methods

Compare values

Robin Abbott - robin.abbott@dial.pipex.com says

I find it easier to show the state of the flags in a table, consider:
movfw X
subwf Y,w

;This subtracts X from Y and leaves the result in W.
;Now look at flags for the compare operations:

X<Y      C=1, Z=0
X<=Y     C=1, Z=Don't Care
X==Y     C=1, Z=1
X>=Y     C=0, Z=Don't Care
X>Y      C=0, Z=0
X!=Y     C=?, Z=0

Hope this helps! (Mind you it would have been easier if Microchip had simply put the logic in Hardware to "correct" the carry flag after a subtraction !

Tony Nixon says

Just a few code snippets I discovered in the last two weeks while writing some new software.
;if RAMx > Y

movf RAMx,w
addlw 255 - Y           ; eg if RAMx > 5 ... addlw d'250'
btfsc status,carry
goto True

;if RAMx < Y

movf RAMx,w
addlw 255 - Y + 1       ; eg if RAMx < 5 ... addlw d'251'
btfss status,carry
goto True

;if RAMx >= Y

movf RAMx,w
addlw 255 - Y + 1       ; eg if RAMx >= 5 ... addlw d'251'
btfsc status,carry
goto True

;if RAMx <= Y

movf RAMx,w
addlw 255 - Y           ; eg if RAMx <= 5 ... addlw d'250'
btfss status,carry
goto True

Just following on from the previous snippets, in an effort to make comparisons easier to understand, here are some more. The best part is, the Z flag is ignored.

;if RAMx > RAMy

movf RAMy,w
sublw 0xFF
addwf RAMx,w
btfsc status,carry
goto true

Scott Dattalo says:

on the 18cxxx chips this can be shortened to:
  movf  RAMx,w  ;wreg = RAMx
  subwf RAMy,w  ;wreg = RAMy - RAMx
  bn    true    ;Branch if negative
                ;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

(or you could use the bnc [branch if no carry] to achieve the same effect. The negative bit has a clearer meaning in this context).

btw, Tony's example can be shortened to:

  movf  RAMx,w
  subwf RAMy,w
  skpc
   goto true
but there are still 4*14 = 52 bits. however this works on the 12bit core, so only 4*12 = 48 bits of program memory are required there.
;if RAMx < RAMy

movf RAMx,w
sublw 0xFF
addwf RAMy,w
btfsc status,carry
goto true

;if RAMx >= RAMy

movf RAMx,w
sublw 0xFF
addwf RAMy,w
btfss status,carry
goto true

Scott Dattalo says:

on the 18cxxx chips this can be shortened to:
  movf  RAMy,w    ;wreg = RAMy
  subwf RAMx,w    ;wreg = RAMx - RAMy
  bnn   true      ;branch if not negative
                  ;The N bit will be cleared if RAMx == RAMy or
                  ;RAMx > RAMy, and will be set if RAMy < RAMx

Again, you could use the bc (branch on carry) instruction too. Also, Tony's sequence can be reduced by an instruction.

;if RAMx <= RAMy

movf RAMy,w
sublw 0xFF
addwf RAMx,w
btfss status,carry
goto true

;********** 16 BIT
;RAM > X
movf RAMH,w
sublw XH
btfss status,c
goto true
btfss status,z
goto false
movf RAML,w
sublw XL
btfss status,c
goto true

;RAM < X
movlw XH
subwf RAMH,w
btfss status,c
goto true
btfss status,z
goto false
movlw XL
subwf RAML,w
btfss status,c
goto true

;RAM >= X
movf RAMH,w
sublw XH
btfss status,c
goto true
btfss status,z
goto false
movlw XL
subwf RAML,w
btfsc status,c
goto true

;RAM <= X
movlw XH
subwf RAMH,w
btfss status,c
goto true
btfss status,z
goto false
movf RAML,w
sublw XL
btfsc status,c
goto true

Scott Dattalo says:

for the 16-bit sequences it'd be much quicker to:
;XH:XL  YH:YL

  movf    yl,w
  subwf   xl,w
  movf    yh,w
  skpz
   incf   yh,w	;propagate the carry from the low byte subtraction.
    subwf xh,w
  skpnc
   goto   x_is_greater_than_or_equal_to_y

Similar sequences exist for the other cases. for the 18cxxx

char compare(unsigned int x, unsigned int y)
{

 if(x<y)
   {
     x is less than y
   }
 else
   {
     x is greater than or equal to y
   }
}


  movf    y_hi,w
  cpfslt  x_hi
   bra    x_is_ge_y
  movf    y_lo,w
  cpfslt  x_lo
   bra    x_is_ge_y
x_is_lt_y
  ...


x_is_ge_y
  ...

But this can be made faster by using subtracts:

  movf   y_lo,w
  subwf  x_lo,w
  movf   y_hi,w
  subwfc x_hi,w
  bnc    x_is_lt_y
;or
  bc 	 x_is_ge_y
;or
  bn     x_is_gt_y

Also: