piclist 2001\04\06\102145a >
Thread: Rounding to closest 10's multiple, code enclosed :)
www.piclist.com/techref/microchip/devices.htm?key=pic
BY : Kübek Tonyemail (remove spam text)

Hi,
as I promised I've hammered out some rounding code and
will attach the rounding to closest 10s multiple below.
The rounding to closest 1's multiple will follow
in 5'min as an separate posting.

It's coded for 24 bit input/output trough fsr0,
uses some 18Cxx specific instructions ( wouldn't be
to hard to convert to other pic's ).
It's not fully omptimized, I'll leave that as an exerzice
to the user ( there are some places that can be improved )
The rounding takes about 190'ish instructions worst case.
As far as I can tell there really are no faster options
to round an 24 bit value. But please prove me wrong :)

And without the input from Bob,Roman and Scott it sure would
have been a rougher ride. Thanks.

Use at will.

; ***********************************************************************
; ROUND_TENS_FSR0_24bit - Rounds 24 bit unsigned value value pointed to by
fsr0
; according to (tens)granularity in w
; FSR0 must point to MSB of input, output will overwrite input at exit
; Granularity can be 0x05, 0x02, 0x01
; 0x05 round to nearest decimal '50', 0x02 round to nearest decimal '20'
etc.
; In other words low nibble of w decides the granularity for the tens digit
( as 'bcd' )
; Other granularities are untested and may/may not nork.
;
; Executes in about 190 cycles worst case
; About 180 instructions are used. I.e mostly unrolled code
;
; Algorithm:
; * Adds half granularity to input. ( i.e. for granularity 0x05 ( 50 ) 25 is
; * 'Exctract' each bit's addition to the ones and tens digit
;   separately. After the actual 'extraction' they are normalized ( to 0-9
range ).
; * If tens digit is larger then granularity, granulairty is subtracted from
it
;   until next subtraction generates negative result.
; * Multiply tens digit by 10 ( i.e. to 'real' value )
; * Subtract the result from the input and overwrite with output.
;
;
; Ram usage ( all in access bank ):
; ARG2_24       1 byte 'tens'
; ARG2_24+1     1 byte 'ones'
; Temp          1 byte 'granularity'/10 ( i.e. input in w )
; Temp+1        1 byte for calculation of 'real' granularity/2
;
ROUND_TENS_FSR0_24bit
GLOBAL  ROUND_TENS_FSR0_24bit
; save granularity
MOVWF   Temp,A
; set pointer to lsb of input
MOVF    POSTINC0,W,A
MOVF    PREINC0,W,A     ; pointer now to lsb and lsb in w
; add half of granularity(real) to input
;, make a copy of granularity
MOVFF   Temp,Temp+1
; calculate 'real' granularity/2 ( i.e. input granularity*5 )
; multiply by 4
BCF     STATUS,C        ; clear carry
RLCF    Temp+1,F,A      ; multiply with 2
RLCF    Temp+1,F,A      ; multiply with 4
; add 'pseudo' granularity ( input )
MOVF    Temp,W,A
ADDWF   Temp+1,W,A      ; and add them, w = input granularity*5 ( or
rather 'real' granularity/2 )
ADDWF   POSTDEC0,F,A    ; add to lsb and set pointer to middle byte
MOVF    INDF0,W,A       ; get middle byte
BTFSC   STATUS,C
ADDLW   D'1'            ; propagate the carry
MOVWF   POSTDEC0,A      ; save it and set pointer to msb
BTFSC   STATUS,C
INCF    INDF0,F,A       ; propagate the carry and leave pointer to
msb

; clear ones and tens bytes
CLRF    ARG2_24+1,A     ; temp storage for the ones
CLRF    ARG2_24,A       ; temp storage for the tens

; * calcualte top byte byte low nibble
MOVF    INDF0,W,A       ;
ANDLW   b'00001111'     ; mask out lowest nibble
BTFSC   STATUS,Z
GOTO    ROUND_TENS_FSR0_NIB2

BTFSC   INDF0,0,A       ; lowest bit set in low nibble ?
MOVWF   ARG2_24+1,A     ; start value for the ones

; calculate tens byte, bit 'tens-values' from bit 0 : 3,7,4,8
MOVLW   0x00
BTFSC   INDF0,0,A       ; test bit
BTFSC   INDF0,1,A       ; test bit
BTFSC   INDF0,2,A       ; test bit
BTFSC   INDF0,3,A       ; test bit
; and move the result to the tens byte
MOVWF   ARG2_24,A       ; tens byte

ROUND_TENS_FSR0_NIB2

; calcualte top byte high nibble
SWAPF   INDF0,W,A       ;
ANDLW   b'00001111'     ; mask out lowest nibble

BTFSC   INDF0,4,A       ; lowest bit set in high nibble ?
; calculate tens byte, bit 'tens-values' from bit 0 : 7,5,0,0 ( i.e.
skip test of top two bits )
MOVLW   0x00
BTFSC   INDF0,4,A       ; test bit
BTFSC   POSTINC0,5,A    ; test bit ***AND** increment pointer to
middle byte of input
; and add result to the tens byte

; * calcualte middle byte low nibble
MOVF    INDF0,W,A       ;
ANDLW   b'00001111'     ; mask out lowest nibble
BTFSC   STATUS,Z
GOTO    ROUND_TENS_FSR0_NIB3

BTFSC   INDF0,0,A       ; lowest bit set in low nibble ?
; calculate tens byte, bit 'tens-values' from bit 0 : 5,1,2,4
MOVLW   0x00
BTFSC   INDF0,0,A       ; test bit
BTFSC   INDF0,1,A       ; test bit
BTFSC   INDF0,2,A       ; test bit
BTFSC   INDF0,3,A       ; test bit
; and add result to the tens byte

ROUND_TENS_FSR0_NIB3

; calcualte middle byte high nibble
SWAPF   INDF0,W,A       ;
ANDLW   b'00001111'     ; mask out lowest nibble
BTFSC   INDF0,4,A       ; lowest bit set in high nibble ?
; calculate tens byte, bit 'tens-values' from bit 0 : 9,9,8,6
MOVLW   0x00
BTFSC   INDF0,4,A       ; test bit
BTFSC   INDF0,5,A       ; test bit
BTFSC   INDF0,6,A       ; test bit
BTFSC   POSTINC0,7,A    ; test bit *AND* increment pointer to lowest
byte of input
; and add result to the tens byte

; * calculate lowest nibble of lowet byte
; note has no effect on the tens byte
MOVF    INDF0,W,A       ;
ANDLW   b'00001111'     ; mask out lowest nibble
; calculate top nibble 'ones' byte
SWAPF   INDF0,W,A       ;
ANDLW   b'00001111'     ; mask out lowest (prev. highest) nibble
; if zero skip and continue with normalization
BTFSC   STATUS,Z
GOTO    ROUND_TENS_FSR0_NORM

BTFSC   INDF0,4,A       ; lowest bit set in top nibble ?
; calculate tens byte, bit 'tens-values' from bit 4 : 1,3,6,2
MOVLW   0x00
BTFSC   INDF0,4,A       ; test bit
BTFSC   INDF0,5,A       ; test bit
BTFSC   INDF0,6,A       ; test bit
BTFSC   INDF0,7,A       ; test bit, leave pointer to lsb of input
; and add it to the tens byte
ADDWF   ARG2_24,F,A     ; save tens byte

; pointer fsr0 points to lsb of input here
ROUND_TENS_FSR0_NORM

; *** ones and tens updated now we need to add the overflow from the
ones
; to the tens ( i.e. ones and tens are NOT limited to 0-9 yet )
; Maximum value for the ones: 109
; Maximum value for the tens: 90  ( i.e. 90*10 = 900 )

; ** Normalize tens and ones digits **
; we start by subtracting 30 from the ones until negative, then add
ten until positive again
; after which we now it's in 0-9 range
; subtract 30 from ones
MOVLW   D'30'
ROUND_TENS_FSR0_SUB30
INCF    ARG2_24,F,A     ; +10
INCF    ARG2_24,F,A     ; +20
INCF    ARG2_24,F,A     ; +30
; subtract 30 from ones
SUBWF   ARG2_24+1,F,A
SKPNC   ; overflow, i.e. negative then add 10 until positive again
GOTO    ROUND_TENS_FSR0_SUB30
; add 10 until positive again
MOVLW   D'10'
; remove ten from tens
DECF    ARG2_24,F,A     ; -10
SKPC    ; positive ?
GOTO    ROUND_TENS_FSR0_ADD10   ; nope still negative
; now ones are positive and below 10, i.e. we have successfully
subtracted
; the ones digit for the 24 bit input
; now we 'adjust' the tens digit to be inthe same range 0-9
MOVLW   D'30'   ;i.e. 300
ROUND_TENS_FSR0_SUB300
; subtract 30 (300) from tens
SUBWF   ARG2_24,F,A
SKPNC   ; overflow, i.e. negative then add 10 until positive again
GOTO    ROUND_TENS_FSR0_SUB300
; add 10(100) until positive again
MOVLW   D'10'
; add 10(100) to tens until postive
SKPC    ; positive ?
GOTO    ROUND_TENS_FSR0_ADD100  ; nope still negative
; now tens are also positive and below 10, i.e. we have successfully
subtracted
; the tens digit for the 24 bit input

; ** calculate granularity diff in the tens digit
; subtract granularity from the tens byte
MOVF    ARG2_24,W,A     ; start value
ROUND_TENS_FSR0_GRAN_CHK
MOVWF   ARG2_24,A       ; save start value ( or concecutive values )
MOVF    Temp,W,A        ; get granularity
; subtract it from the tens
SUBWF   ARG2_24,W,A
; check if negative or positive
; if positive/zero save result and try again
BTFSS   STATUS,N
GOTO    ROUND_TENS_FSR0_GRAN_CHK
; now we need to add the tens and ones, first 'multiply' the tens
; by 10
BCF     STATUS,C
RLCF    ARG2_24,F,A     ;tens = tens *2
MOVFF   ARG2_24,Temp    ; overwrite the granularity ( we don't need
it anymore ) = tens *2
RLCF    ARG2_24,F,A     ; tens*2 = tens*2*2
RLCF    ARG2_24,W,A     ; tens*2*2 = tens*2*2*2 = tens*8
; add temp(tens*2) and tens*8 to w
ADDWF   Temp,W,A        ; w = tens*10
; then we it with the ones
; w = tens*10 + ones
; now we subtract this from the input
SUBWF   POSTDEC0,F,A    ; subtract and set pointer to middle byte
SKPC
DECF    INDF0,F,A       ; propagate the borrow
MOVF    POSTDEC0,W      ; set pointer to msb
SKPC
DECF    INDF0,F,A       ; propagate the borrow and leave pointer to
msb of input

RETURN

Tony Kübek, Flintab AB
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
E-mail: tony.kubekflintab.com
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestmitvma.mit.edu

<200104061411.QAA21049@mn10.swip.net> quoted-printable