This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

5 Byte number to be converted into decimal string

Hallo

it would be fine if somebody could help me on the following problem: I have to send the decimal digits of a 5 Byte Hex value, that is currently stored in an array, via the serial interface. It is no problem to get the digits of a 4 Byte number but Keil or the controller do not allow double integer!

any solution????

  • What you need is a function to divide a 40-bit value by ten. You may be able to adapt the fast_long_divide() function to be found here:

    http://www.programmersheaven.com/zone5/cat27/32144.htm

  • OK, just for fun, try this:

    #pragma ASM
    
        $REGUSE _fast_40_bit_divide( A, B, PSW, R0, R5, R7 )
    
    
        PUBLIC fast_40_bit_divide
    
    #pragma ENDASM
    
    unsigned char fast_40_bit_divide( unsigned char data * dividend, unsigned char divisor ) small
    {
        dividend = dividend;                  // Suppress unused variable warning.
        divisor = divisor;                  // Suppress unused variable warning.
    
        #pragma ASM
    
        // pointer to dividend:   R7
        // divisor in:            R5
        // uses:
        // quotient returned in:  data memory
        // remainder returned in: R7
    
    pointer         SET     R0              ;
    divisor         SET     R5              ;
                                            ;
            MOV     A,R7                    ;
            MOV     pointer,A               ;
                                            ;
    fast_40_bit_divide:                     ;
                                            ;
            MOV     A,@R0                   ;Get the MS byte of dividend and divide
            MOV     B,divisor               ; it by the divisor.
            DIV     AB                      ;
                                            ;
            MOV     @R0,A                   ;Store result of division as quotient.
                                            ;
            MOV     A,B                     ;Save the remainder (which must be in
            SWAP    A                       ; the range 0...15) shifted four bits
            MOV     B,A                     ; to the left.
                                            ;
            INC     R0                      ;
                                            ;
            MOV     loop,#04                ;load loop counter.
                                            ;
    ?fast_40_bit_divide_loop:               ;
                                            ;
            MOV     A,@pointer              ;Get the MS nibble of the
            SWAP    A                       ; dividend into the accumulator.
            ANL     A,#0x0F                 ;
            ORL     A,B                     ; Or in remainder from last iteration.
                                            ;
            MOV     B,divisor               ;Divide MS nibble by the divisor.
            DIV     AB                      ;
                                            ;
            SWAP    A                       ;Save partial result (which must be
            MOV     partial,A               ; in the range 0...15) shifted 4 bits.
                                            ;
            MOV     A,B                     ;Read remainder (which must be in
            SWAP    A                       ; the range 0...15) and shift 4 bits left.
                                            ;
            XCHD    A,@pointer              ;Get next nibble of the dividend into
                                            ; the accumulator.
            MOV     B,divisor               ;Divide by the divisor.
            DIV     AB                      ;
                                            ;
            ORL     A,partial               ;Or in the previously saved partial result.
            MOV     @pointer,A              ;Save the MSB of the quotient ready
                                            ; to be returned.
                                            ;
            MOV     A,B                     ;Save remainder
            SWAP    A                       ; shifted 4 bits left
            MOV     B,A                     ; in B.
                                            ;
            INC     pointer                 ;
    
            DJNZ    loop,?fast_40_bit_divide_loop
    
            SWAP    A                       ;The final remainder is in upper nibble
            MOV     R7,A                    ; of accumulator.
                                            ;
            RET                             ;
    
        #pragma ENDASM
    
        return( dividend );
    
    }
    
    And call like this:
        unsigned int      loop;
        unsigned char     r;
    
    //                                  MSB                      LSB
        data unsigned char     v[5] = { 0x01, 0xFF, 0xFF, 0xFF, 0xFE };
    
    //                                  == 8589934590 decimal
    
    
        loop = 14;
    
        do
        {
    
            r = fast_40_bit_divide( &v, 10 );
    
        }while( --loop != 0 );
    
    
    Variable r will be set to the successive decimal digits of v[] - starting with the least significant decimal digit.

    I have only given this a quick test. Hope it helps....

  • Minor update:

    #pragma ASM
    
        $REGUSE _fast_40_bit_divide( A, B, PSW, R0, R3, R4, R5, R6, R7 )
    
        PUBLIC fast_40_bit_divide
    
    #pragma ENDASM
    
    unsigned char fast_40_bit_divide( unsigned char data * dividend, unsigned char divisor ) small
    {
        dividend = dividend;                  // Suppress unused variable warning.
        divisor = divisor;                  // Suppress unused variable warning.
    
        #pragma ASM
    
        // pointer to dividend:   R7
        // divisor in:            R5
        // uses:                  R3, R4, R6
        // quotient returned in:  data memory
        // remainder returned in: R7
    
    pointer         SET     R0              ;
    remainder       SET     R3              ;
    partial         SET     R4              ;
    divisor         SET     R5              ;
    loop            SET     R6              ;
                                            ;
            MOV     A,R7                    ;
            MOV     pointer,A               ;
                                            ;
    fast_40_bit_divide:                     ;
                                            ;
            MOV     A,@R0                   ;Get the MS byte of dividend and divide
            MOV     B,divisor               ; it by the divisor.
            DIV     AB                      ;
                                            ;
            MOV     @R0,A                   ;Store result of division as quotient.
                                            ;
            MOV     A,B                     ;Save the remainder (which must be in
            SWAP    A                       ; the range 0...15) shifted four bits
            MOV     B,A                     ; to the left.
                                            ;
            INC     R0                      ;
                                            ;
            MOV     loop,#04                ;load loop counter.
                                            ;
    ?fast_40_bit_divide_loop:               ;
                                            ;
            MOV     A,@pointer              ;Get the MS nibble of the
            SWAP    A                       ; dividend into the accumulator.
            ANL     A,#0x0F                 ;
            ORL     A,B                     ; Or in remainder from last iteration.
                                            ;
            MOV     B,divisor               ;Divide MS nibble by the divisor.
            DIV     AB                      ;
                                            ;
            SWAP    A                       ;Save partial result (which must be
            MOV     partial,A               ; in the range 0...15) shifted 4 bits.
                                            ;
            MOV     A,B                     ;Read remainder (which must be in
            SWAP    A                       ; the range 0...15) and shift 4 bits left.
                                            ;
            XCHD    A,@pointer              ;Get next nibble of the dividend into
                                            ; the accumulator.
            MOV     B,divisor               ;Divide by the divisor.
            DIV     AB                      ;
                                            ;
            ORL     A,partial               ;Or in the previously saved partial result.
            MOV     @pointer,A              ;Save the MSB of the quotient ready
                                            ; to be returned.
                                            ;
            MOV     A,B                     ;Save remainder
            SWAP    A                       ; shifted 4 bits left
            MOV     B,A                     ; in B.
                                            ;
            INC     pointer                 ;
    
            DJNZ    loop,?fast_40_bit_divide_loop
    
            SWAP    A                       ;The final remainder is in upper nibble
            MOV     R7,A                    ; of accumulator.
                                            ;
            RET                             ;
    
        #pragma ENDASM
    
        return( dividend );
    
    }
    

  • When you are using printf, you need to use the 'l' prefix to mark a number as a long value.

    See http://www.keil.com/support/man/docs/c51/c51_printf.htm

  • "you need to use the 'l' prefix to mark a number as a long value."

    Yes, but a long is only four bytes:

    http://www.keil.com/support/man/docs/c51/c51_ap_4bytescalar.htm

    The OP is using a 5-byte (40-bit) number!

  • Sorry, I missed the 5-Byte requirement.

  • Any projected delivery date for "long long" (64 bit) support?

    (No, it's not a crazy thing to want to do with an 8-bit micro. To cite one example, IEEE 802 requires all statistics / event counters not to roll over for at least 57 minutes while counting their maximum rate. Counters that cannot achieve this duration in 32 bits must be 64 bits wide, per spec. For a Gigabit Ethernet device, that means most of the defined stats. It's not at all out of the scope of an 8051 to be embedded in a NIC card or what-have-you, and 1Gbps is a pretty routine rate these days. Not all long integers show up in astronomy programs and Bill Gate's bank accounts.)

  • "Any projected delivery date for "long long" (64 bit) support?"

    And 64 bit doubles please?

  • You may have to wait some time for 64-bit support. While you wait, the following link may be of interest:

    ftp.embedded.com/.../crenshaw98.txt

  • Thanks for the link. I use some similar C code to get by for now. Occasionally I toy with writing proper assembler subroutines to do the math, but I haven't so far.

    (One question that comes up: The DPTR can be incremented, but not decremented. So, when you're adding U64s in xdata, it would be very natural if they were little-endian. You'd just have a movx addc inc dptr loop. Keil is of course big-endian, but you can't do the same thing and "dec dptr" quite as easily. So, are there any neat 8051 assembler tricks for doing big integer math that's not all done in registers/data space? I'm not much of an assembler wizard.)

  • Of course, if you're making up your own 64-bit data type, you don't have to stick with Keil's big-endianism...

    I s'pose it depends on your application and whether 64-bit efficiency on its own is more important than the efficiency of "interfacing" to Keil's standard types?