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:
	mov	W, X
	mov	W, 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

	mov	W, RAMx
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;addlw 255 - Y           ; eg if RAMx > 5 ... addlw d'250'
	mov	Hack, W
	mov	W, #255 - Y	; eg if RAMx > 5 ... addlw d'250'
	add	W, Hack
	snb	status.carry
	jmp	True

;if RAMx < Y

	mov	W, RAMx
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;addlw 255 - Y + 1       ; eg if RAMx < 5 ... addlw d'251'
	mov	Hack, W
	mov	W, #255 - Y + 1	; eg if RAMx < 5 ... addlw d'251'
	add	W, Hack
	sb	status.carry
	jmp	True

;if RAMx >= Y

	mov	W, RAMx
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;addlw 255 - Y + 1       ; eg if RAMx >= 5 ... addlw d'251'
	mov	Hack, W
	mov	W, #255 - Y + 1	; eg if RAMx >= 5 ... addlw d'251'
	add	W, Hack
	snb	status.carry
	jmp	True

;if RAMx <= Y

	mov	W, RAMx
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;addlw 255 - Y           ; eg if RAMx <= 5 ... addlw d'250'
	mov	Hack, W
	mov	W, #255 - Y	; eg if RAMx <= 5 ... addlw d'250'
	add	W, Hack
	sb	status.carry
	jmp	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

	mov	W, RAMy
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw 0xFF
	add	W, RAMx
	snb	status.carry
	jmp	true

Scott Dattalo says:

on the 18cxxx chips this can be shortened to:
	mov	W, RAMx	;wreg = RAMx
	mov	W, 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:

	mov	W, RAMx
	mov	W, RAMy-w
	sb	C
	jmp	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

	mov	W, RAMx
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw 0xFF
	add	W, RAMy
	snb	status.carry
	jmp	true

;if RAMx >= RAMy

	mov	W, RAMx
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw 0xFF
	add	W, RAMy
	sb	status.carry
	jmp	true

Scott Dattalo says:

on the 18cxxx chips this can be shortened to:
	mov	W, RAMy	;wreg = RAMy
	mov	W, 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

	mov	W, RAMy
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw 0xFF
	add	W, RAMx
	sb	status.carry
	jmp	true

;********** 16 BIT
;RAM > X
	mov	W, RAMH
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw XH
	sb	C
	jmp	true
	sb	Z
	jmp	false
	mov	W, RAML
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw XL
	sb	C
	jmp	true

;RAM < X
	mov	W, #XH
	mov	W, RAMH-w
	sb	C
	jmp	true
	sb	Z
	jmp	false
	mov	W, #XL
	mov	W, RAML-w
	sb	C
	jmp	true

;RAM >= X
	mov	W, RAMH
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw XH
	sb	C
	jmp	true
	sb	Z
	jmp	false
	mov	W, #XL
	mov	W, RAML-w
	snb	C
	jmp	true

;RAM <= X
	mov	W, #XH
	mov	W, RAMH-w
	sb	C
	jmp	true
	sb	Z
	jmp	false
	mov	W, RAML
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
sublw XL
	snb	C
	jmp	true

Scott Dattalo says:

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

	mov	W, yl
	mov	W, xl-w
	mov	W, yh
	sb	Z
	mov	W, ++yh	;propagate the carry from the low byte subtraction.
	mov	W, xh-w
	snb	C
	jmp	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
   }
}


	mov	W, y_hi
  cpfslt  x_hi
   bra    x_is_ge_y
	mov	W, y_lo
  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:

	mov	W, y_lo
	mov	W, x_lo-w
	mov	W, y_hi
  subwfc x_hi,w
;*** WARNING: MPASM macro BNC is not supported yet. Replace manually.
  bnc    x_is_lt_y
;or
;*** WARNING: MPASM macro BC is not supported yet. Replace manually.
  bc 	 x_is_ge_y
;or
  bn     x_is_gt_y

Also: