Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
dksmall
PostPosted: Jun 14, 2007 - 06:17 PM
Raving lunatic


Joined: Apr 16, 2001
Posts: 3522
Location: Phoenix, Arizona

Nothing like a nice jolt when I saw who the OP was! Shocked Then I realized it's an old thread. Razz
 
 View user's profile Send private message  
Reply with quote Back to top
klini
PostPosted: Dec 19, 2007 - 05:27 AM
Newbie


Joined: Jun 25, 2007
Posts: 7


Wouldn't it be nice to not deal with the rounding error? If we were to add a compensation amount before multiplying by 26, we then have no error:

Code:
.def temp1 = r16
.def temp2 = r17
.def A = r18

// Input: A
// Output: r1
DivBy10:

   // calculate the amount needed to add to A to compensate for the rounding error to come
   // compensation = (A + 3) / 64
   mov temp2, A
   ldi temp1, 3
   add temp2,temp1
   // "divide by 64" shortcut
   swap temp2
   andi temp2, 15
   lsr temp2
   lsr temp2
   
   // now use 'A - compensation'
   mov temp1, A
   sub temp1, temp2

   // Multiply by magic number
   ldi temp2, 26
   mul temp1, temp2

   ret


I've checked it out on my computer and seems like this works good, and doesn't eat many more cycles than the original.
 
 View user's profile Send private message  
Reply with quote Back to top
Nephazz
PostPosted: Feb 24, 2009 - 08:47 PM
Hangaround


Joined: Dec 07, 2008
Posts: 347
Location: germany

Quote:


Code:

; the input number must be in register "input"
; value/10 is in register "result"
; register temp1 and R1:R0 are clobbered
div10:
   ldi temp1, 205
   mul temp1, input
   lsr R1
   lsr R1
   lsr R1
   mov result, R1
   ret



This one rocks! Saved my day, today Smile

_________________
Jan Kießling
74HC Overview
Flowchart CAD
schematic CAD
 
 View user's profile Send private message  
Reply with quote Back to top
seveprim
PostPosted: Mar 06, 2009 - 03:54 AM
Rookie


Joined: Jan 18, 2005
Posts: 38


i suppose one could allocate a huge look up table ln FLASH on the larger AVR range ... practically instant result which i have done many many years ago... (code lost to time)
 
 View user's profile Send private message  
Reply with quote Back to top
vbunch
PostPosted: Dec 21, 2009 - 08:04 PM
Wannabe


Joined: Sep 29, 2001
Posts: 53
Location: Superior, Colorado USA

Thanks to all for alively discussion. I need help on a 16 bit divide:
I have been using the DIV16u straight from apnote 1011 (AVR200) since 1998, and found a possible error.

When you have $7080 (28,800) divided by $3841 (14,401), the correct answer is: $0001 and remainder $FFF6 (1.99986).
However, the Atmel DIV16u returns $0001 remainder $383F, quite an error.

When you have $7080 (28,800) divided by $3840 (14,400), the correct answer is: $0002 and remainder $0000 (2.00000), and the Atmel code returns the correct same answer.

Please help, I need a fix fast, or am I wrong?

Vern Bunch, AVR consultant and AVR designer
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
peret
PostPosted: Jan 05, 2010 - 06:41 PM
Raving lunatic


Joined: May 26, 2004
Posts: 2538
Location: Las Vegas, Nevada

vbunch wrote:
When you have $7080 (28,800) divided by $3841 (14,401), the correct answer is: $0001 and remainder $FFF6 (1.99986).
However, the Atmel DIV16u returns $0001 remainder $383F, quite an error.

Dude, WTF?

Let's do this manually, by long division.

$3841 goes into $7080 once.
Code:
(hex assumed)
      ____1___
3841 ) 7080
       3841 -
       ----
       383F

$3841 won't go into $383F, so the quotient is 1 and the remainder is $383F. The Atmel routine returns the correct answer.

The number $FFF6 might possibly occur in some floating point representation, but can never happen in unsigned integer math, since it is larger than the dividend.
 
 View user's profile Send private message  
Reply with quote Back to top
peret
PostPosted: Jan 05, 2010 - 07:02 PM
Raving lunatic


Joined: May 26, 2004
Posts: 2538
Location: Las Vegas, Nevada

Oh, I see where you got it from.

If you want to generate the fractional bits, take the remainder, multiply by 16 (shift left 4) and divide this by the divisor. Repeat until you have enough "(hexa)decimal" places.
Code:

      ____1FF...
3841 ) 7080
       3841 -
       ----
       383F <<4
       3841 * F -
       ----
        3821 <<4
        3841 * F -
         ----
         3641
          ... etc


Again, this is just confirming that the original division routine got it right. You didn't carry out enough steps to format the answer the way you expected.

Note that since the first remainder is guaranteed to be less than the divisor, each successive step is guaranteed to produce a quotient less than 16, ie a single hex digit. You have to manually concatenate these single digits to the number of places you want.
 
 View user's profile Send private message  
Reply with quote Back to top
vbunch
PostPosted: Jan 08, 2010 - 05:53 PM
Wannabe


Joined: Sep 29, 2001
Posts: 53
Location: Superior, Colorado USA

Thank you my friends for your correct responses. It is my fault that I mentioned "remainder" when I expected a fully usable result, without having to divide again, and one that had 16 bit accuracy. So the Atmel division routine is "correct" but unusable without more computation. Since there was no mention that the result is unusable without further division, I niavely skipped the true math definition of "remainder" and went right to "answer", I should not have done that.

I have since written an assembly language routine that gives me a correct (and directly usable) 16 bit division in one run, and it is less time (156 cycles) then the published 16 bit divide routine. It took 3 weeks at 104 hours a week, and I'm way beyond tired. Very Happy
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
clawson
PostPosted: Jan 08, 2010 - 06:01 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62281
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

I have since written an assembly language routine that gives me a correct (and directly usable) 16 bit division in one run, and it is less time (156 cycles) then the published 16 bit divide routine. It took 3 weeks at 104 hours a week, and I'm way beyond tired

I guess it's too late to say that you could have "borrowed" the source for a / routine from an open source C compiler?

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
peret
PostPosted: Jan 08, 2010 - 06:09 PM
Raving lunatic


Joined: May 26, 2004
Posts: 2538
Location: Las Vegas, Nevada

Quote:
I guess it's too late to say that you could have "borrowed" the source for a / routine from an open source C compiler?

Actually I suspect in this case he could not, because the answer he wanted was in a fixed point format. He got simple integer division from the appnote. The compiler would have given him full float, which would challenge me to adapt to a fixed format in assembler (and I consider myself rather good at it).

As a matter of interest, if we did look at the source for a / routine in an open source C compiler - let's say, and why not, the GCC compiler - would we be looking at assembler, or C?
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jan 08, 2010 - 06:16 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62281
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

would we be looking at assembler, or C?

Assembler. Here's a snippet from gcc/config/avr/libgcc.s:
Code:
/*******************************************************
       Division 16 / 16 => (result + remainder)
*******************************************************/
#define   r_remL   r26   /* remainder Low */
#define   r_remH   r27   /* remainder High */

/* return: remainder */
#define   r_arg1L   r24   /* dividend Low */
#define   r_arg1H   r25   /* dividend High */

/* return: quotient */
#define   r_arg2L   r22   /* divisor Low */
#define   r_arg2H   r23   /* divisor High */
   
#define   r_cnt   r21   /* loop count */

#if defined (L_udivmodhi4)
   .global   __udivmodhi4
   .func   __udivmodhi4
__udivmodhi4:
   sub   r_remL,r_remL
   sub   r_remH,r_remH   ; clear remainder and carry
   ldi   r_cnt,17   ; init loop counter
   rjmp   __udivmodhi4_ep   ; jump to entry point
__udivmodhi4_loop:
        rol   r_remL      ; shift dividend into remainder
   rol   r_remH
        cp   r_remL,r_arg2L   ; compare remainder & divisor
   cpc   r_remH,r_arg2H
        brcs   __udivmodhi4_ep   ; remainder < divisor
        sub   r_remL,r_arg2L   ; restore remainder
        sbc   r_remH,r_arg2H
__udivmodhi4_ep:
        rol   r_arg1L      ; shift dividend (with CARRY)
        rol   r_arg1H
        dec   r_cnt      ; decrement loop counter
        brne   __udivmodhi4_loop
   com   r_arg1L
   com   r_arg1H
; div/mod results to return registers, as for the div() function
   mov_l   r_arg2L, r_arg1L   ; quotient
   mov_h   r_arg2H, r_arg1H
   mov_l   r_arg1L, r_remL      ; remainder
   mov_h   r_arg1H, r_remH
   ret
   .endfunc
#endif /* defined (L_udivmodhi4) */

#if defined (L_divmodhi4)
   .global   __divmodhi4
   .func   __divmodhi4
__divmodhi4:
   .global   _div
_div:
        bst     r_arg1H,7   ; store sign of dividend
        mov     __tmp_reg__,r_arg1H
        eor     __tmp_reg__,r_arg2H   ; r0.7 is sign of result
   rcall   __divmodhi4_neg1 ; dividend negative : negate
   sbrc   r_arg2H,7
   rcall   __divmodhi4_neg2 ; divisor negative : negate
   rcall   __udivmodhi4   ; do the unsigned div/mod
   rcall   __divmodhi4_neg1 ; correct remainder sign
   tst   __tmp_reg__
   brpl   __divmodhi4_exit
__divmodhi4_neg2:
   com   r_arg2H
   neg   r_arg2L      ; correct divisor/result sign
   sbci   r_arg2H,0xff
__divmodhi4_exit:
   ret
__divmodhi4_neg1:
   brtc   __divmodhi4_exit
   com   r_arg1H
   neg   r_arg1L      ; correct dividend/remainder sign
   sbci   r_arg1H,0xff
   ret
   .endfunc
#endif /* defined (L_divmodhi4) */

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
MBedder
PostPosted: Jan 15, 2010 - 03:50 PM
Raving lunatic


Joined: Nov 02, 2009
Posts: 3239
Location: Zelenograd, Russia

Just for fun, I have castrated Very Happy the OP's div_10 code even further:

Code:
;----------------------------------------
; 8-bit division by 10 routine.
;
; Input: R0 = dividend.
; Output: R1 = result = dividend/10.
;
; Registers R0 and R16 are clobbered.
; 5 words/10 clocks including RET.
;
div10:
   ldi   r16,205
   mul   r0,r16
   ldi   r16,32
   mul   r1,r16
   ret
;----------------------------------------
 
 View user's profile Send private message  
Reply with quote Back to top
sparky23882
PostPosted: Apr 30, 2010 - 11:37 AM
Hangaround


Joined: Jul 18, 2007
Posts: 154
Location: Germany

The OP's div_10 code was buggy at values like 19, 29, 39, ... or at 21, 31, 41, ... depending on whether the correction (INC) was done or not. Here's a fixed version, with remainder calculation (remainder lines marked, strip them if not needed, saves 5 cycles):
Code:

// input: A, output/value: R1, output/remainder: A
// total cycles: 13
PUSH A      //2 Me (for remainder)
INC A      //1 Me
CPI A, 0   //1 Me
BRNE x      //1,2 Me
DEC A      //1 Me
x:         //0 Me
LDI B, 51   //1 RetroDan
MUL A, B   //2 RetroDan
LSR R1      //1 RetroDan
POP A      //2 Me (for remainder)
SUB A, R1   //1 Me (for remainder)

Hope this is useful for someone out there.

My approach:

I made a list with values, RetroDan and RetroDan with INC. It looked somehow like this:
8 - 0 0
9 - 0 1
10 - 0 1
11 - 1 1

So one was too low, the other too high around exact matches (i.e. dividable by 10 with no remainder). So I took the one without INC (saves one cycle), INCreased the value before giving it to the RetroDan stuff, so all results were accurate - but "255" which becomes 0.

A quick check for the overflow after increasing (i.e. value=0), decrease it again (make it 255 again, which gives the correct result) and done.

P.S: I know it's an old thread, but I think it's an important one that's read by a lot of people for finding a good divide-by-10 routine.

_________________
Markus
 
 View user's profile Send private message  
Reply with quote Back to top
danni
PostPosted: May 03, 2010 - 10:49 AM
Raving lunatic


Joined: Sep 05, 2001
Posts: 2496


But I would ask at first, whats the purpose to do a division by 10?

Mostly it was the decimal output conversion.
The fastest way to do so, was the subtraction method, since no divison nor multiplication was needed.
A small example for 16 bit ASCII output can be seen on:

http://www.avrfreaks.net/index.php?name ... 721#453721


Peter
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: May 03, 2010 - 08:20 PM
10k+ Postman


Joined: Nov 17, 2004
Posts: 13828
Location: Vancouver, BC

Quote:
The fastest way to do so, was the subtraction method, since no divison nor multiplication was needed.
But repeated subtraction that that routine uses is division. And the multiplies when done with the hardware multiply are very fast.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits