We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
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 ); }
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 );
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?