PIC Microcontoller Program Flow Method: Conditionally replace one value with another

contents:

See also:

[FIXME: rather than having many copies of each routine, one for each register that you need to adjust, point FSR at the register and use a single routine that uses FSR fsr.htm ]

Single Value Replacement

If w='a' then replace it with 'x', if it is something else, leave it alone.

Nikolai Golovchenko says It can be done like this:

        xorlw 'a'       ;compare to 'a', w' = w ^ 'a'
        btfss STATUS, Z
         movlw 'x'^'a'  ;if not equal, w = 'x' ^ 'a'
        xorlw 'a'       ;if w was equal to 'a', restore it
                        ;if   w  was  unequal  to 'a',
                        ; w = 'x' ^ 'a' ^ 'a'= 'x'


Conditional Swap

From http://www.myke.com/basic.htm

Here's a compare and swap
   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)

This can be used to, for example, Convert ASCII to Upper Case

Min and Max

code to find the minimum or maximum value of 2 or values.

limit

Code to force a variable to the maximum value if it exceeds that value.

Also known as ``saturating arithmetic''. Very important in audio filters.

8 bit saturating arithmetic

"clipping" or "limiting": If the value _x is "too big", clip it to the maximum value. If the value _x is "too small", clip it to the minimum value. Otherwise leave _x alone.

min_limit equ 5
max_limit equ H'F1'

_limit_x:
; clip _x to make sure it doesn't exceed the limits.
; unsigned_max8:
; _x_new := max( _x_initial, min_limit )
   ; from Anders Jansson
    movlw min_limit
    subwf _x, W
   skpc ; btfss STATUS, C
        subwf _x, F
; unsigned_min8:
; _x_new := min( _x_initial, max_limit )
   ; from Anders Jansson
   movlw max_limit
   subwf _x, W
   skpnc ; btfsc STATUS, C
       subwf _x, F
; now we can be sure that (min_limit <= x) and (x <= max_limit).
   return

More routines:

; WARNING: untested code. Does this really work ?
; Change register R0 to maximum of (R0, limit), where limit is passed in w.
; Works when both registers are 8 bit unsigned.
; (What about when both are signed ?)
; results: R0new = max(R0initial, w);
; w is unchanged.
; by David Cary
unsigned_max8:
	subwf R0,f
	btfsc R0,7
	clrf R0
	addwf R0
	return

; R0new = min(R0initial, w);
; w is unchanged
unsigned_min8:
	subwf R0,f
	btfss R0,7
	clrf R0
	addwf R0
	return
; Example: Force R0 to stay in the range 8..0x19
saturate8:
    movlw 8
    call unsigned_max8
    movlw 0x19
    call unsigned_min8


; Change signed 8 bit register R0 to maximum of (R0, 0).
signed_max8:
    btfsc R0,7 ; skip if positive
    clrf R0
    return

16-bit saturating arithmetic

This code adds a signed 8 bit value "reading" to a signed 16 bit running sum "total". (useful for the "I" part of PID control). If the result overflows 16 bits, it properly saturates to max_int or min_int.

    ; code by jspaarg 2005-05-25
    ; as posted to http://forum.microchip.com/tm.asp?m=108810&mpage=2
    ; minor changes by David Cary

    ; warning: untested code.

    ...

    reading res 1
    total_L res 1
    temp res 1
    total_H res 1

    ...

accumulate_reading:
    ; sign-extend reading into temp
    clrf temp
    btfss reading, 7
    decf temp,f
    ; now "temp" holds 0xFF if reading was negative, 0 if reading was positive

    ; semi-normal 16 bit sum
    ; http://techref.massmind.org/techref/microchip/math/add/index.htm
    movf reading,w
    addwf total_L
    skpnc
    incf temp,f
    ; now temp = 0 for no change, +1 to increment, or 0xFF to decrement.
    movf temp,w
    addwf total_H


    ; if you are sure that total_H can never overflow,
    ; then we are already done here.
 
    ; Check for overflow
    ; If you sure that you can never exceed limits, then you don't need to check.

    ; (special shortcut that only works because we are adding 8 bits to 16 bits --
    ; -- won't work for adding 16 bits to 16 bits)

    ; only 2 ways to overflow:
    ; (a) total_H was already the positive number 0x7F, and we incremented it
    ; with a positive number (temp = +1), resulting in 0x80 and C=0. --> need to saturate to total = 0x7FFF.
    ; However, if total_H was already the negative number 0x80, and we "added" reading=0,
    ; we get the same result: 0x80 and C=0; we *don't* want to saturate.
    ; (b) total_H was already the negative number 0x80, and we "decremented" it
    ; with a negative number (temp = 0xFF), resulting in 0x7F and C=1. --> need to saturate to total = 0x8000 (or total = 0x8001 would probably be OK).
    ; However, if total_H was already the positive number 0x7F (total = 0x7F01), and we "added" reading = -1,
    ; we get the same result: 0x7F and C=1; we *don't* want to saturate.

    ; Check for overflow.
    ; If you have an overflow, 
    ; force the total to the appropriate value 
    ; (0x7FFF for max pos, 0x8000 for max neg).

    ; An overflow happened if
    ; (a) temp now equals +1, and the result total_H = 0x80, or
    ; (b) temp now equals -1 (0xFF), and the result total_H = 0x7F.

; check_for_underflow:
    ; if ( (total_H == 0x7F) and (temp == -1) ) then ForceNeg;
    movlw 0x7F
    xorwf total_H,W
    skpz
        goto check_for_overflow
    movlw H'FF'
    xorwf temp,W
    skpz
        return
; ForceNeg:
    movlw 0x80
    movwf total_H
    movlw 0x01
    movwf total_L
    return

check_for_overflow:
    ; if ( (total_H == 0x80) and (temp == +1) ) then ForcePos;
    movlw 0x80
    xorwf total_H,W
    skpz
        return
    movlw 1
    xorwf temp,W
    skpz
        return
; ForcePos:
    movlw 0x7f
    movwf total_H
    movlw 0xff
    movwf total_L
    return




Absolute Value

; WARNING: untested code. Does this really work ?
; Take absolute value of signed 8 bit register R0:
; R0 = abs(R0);
abs_8:
        btfsc R0,7
        decf R0
        btfsc R0,7
        comf R0
        return
; Bug: when R0 is the "wierd" value, 0x80, -128,
; this function returns +127, (the most positive
; value that can be represented as a signed
; 8 bit value) not +128 (since that cannot
; be represented as a signed 8 bit value).

; warning: untested
; Obsolete ?
; from Peter Peres 2001-04-14
; Fmax = max(Fmax, INDF); // update running maximum so far
    movf INDF,w
    subwf Fmax,w ;; Fmax - INDF < 0 ? C = 0
    movf INDF,w 
    btfss STATUS,C 
    movwf Fmax 
; warning: untested 
; Obsolete ? 
; from Peter Peres 2001-04-14 
; Fmin = min(Fmin, INDF)
; // update running maximum so far
 movf Fmin,w
 subwf INDF,w ;
; INDF - Fmin < 0 ? C = 0
 movf INDF,w
 btfss STATUS,C
 movwf Fmin 

; warning: untested 
; from Andy Warren 2001-04-14 
; Fmax = max(Fmax, INDF)
; // update running maximum so far
 MOVF Fmax 	; W = INDF - Fmax.
 SUBWF INDF,W 	; C = ( Fmax <= INDF ).
 SKPNC 		;IF Fmax <= INDF,
 ADDWF Fmax,f 	; then Fmax := (Fmax + INDF - Fmax) = INDF. 

; warning: untested 
; from Andy Warren 2001-04-14 
; Fmin = min(Fmin, INDF)
; // update running maximum so far
 MOVF Fmin,W 	; W = INDF - Fmin.
 SUBWF INDF,W 	; C = ( Fmax <= INDF ). 
 SKPC 		;IF INDF < Fmin,
 ADDWF Fmin,f 	; then Fmin := (Fmin + INDF - Fmin) = INDF. 

16 bit signed integer min and 16 bit signed integer max

Keywords: (if anyone looks for this code using a search engine) compare comparing 16-bit signed integer integers DS1820 DS18B20 DALLAS thermometer

I wrote this code after spending several hours on the net looking for a readymade example. I wanted to build a thermometer based on the Dallas DS1820, and have a minimum-maximum reading. This requires a signed-16-bit (2's complement) subtraction. Finally I got this (not so optimized):

;Compare 16-bit signed integer (2's complement) to minimum & maximum.
;First byte is LSB (as in the DS1820 thermometer), second byte is MSB

;compare to MINIMUM:
	btfsc	zeroMinMaxFlag
	    goto	setMin

	movf	temperature,w
	subwf	min,w		;subtract LSB (low byte)
	movf	temperature+1,w
	btfss	STATUS,C
	    addlw	1
	subwf	min+1,w		;subtract MSB (hi byte)
	andlw	b'10000000'	;test result's sign bit
	btfss	STATUS,Z
	    goto	skipSetMin
setMin
	movf	temperature,w
	movwf	min
	movf	temperature+1,w
	movwf	min+1
skipSetMin

;--
;compare to MAXIMUM:

	btfsc	zeroMinMaxFlag
	goto	setMax

	movf	temperature,w
	subwf	max,w		;subtract LSB (low byte)
	movf	temperature+1,w
	btfss	STATUS,C
	    addlw	1
	subwf	max+1,w		;subtract MSB (hi byte)
	andlw	b'10000000'	;test result's sign bit
	btfsc	STATUS,Z
	    goto	skipSetMax
setMax
	movf	temperature,w
	movwf	max
	movf	temperature+1,w
	movwf	max+1
skipSetMax

James Newton replies: Thank you!

Thank you, whoever you are, this looks useful. (Did you mean to post this anonymously, or do you want us to name you in the credits?) -- DavidCary

See also: PIC Microcontroller Comparison Math Methods

[syntax check this page: http://validator.w3.org/check?uri=http://massmind.org/techref/microchip/condrepl.htm ]

Questions:

Comments: