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 ]
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'
From http://www.myke.com/basic.htm
Here's a compare and swapmovf 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
code to find the minimum or maximum value of 2 or values.
Code to force a variable to the maximum value if it exceeds that value.
Also known as ``saturating arithmetic''. Very important in audio filters.
"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
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
; 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.
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: