On Wed, 5 Jul 2000, Nikolai Golovchenko wrote: > Hi Miguel, > > I tried to adapt John Payson's BIN2BCD routine and come up with the > following equations: > > b_0 = a_0 - 4(a_2 + a_1) - 20 > b_1 = 6a_2 + 2a_1 - 48 > b_2 = 2a_2 - 15 I had done this once too. (20 minutes of digging) Okay I found it (goes back to Oct '97). There are two parts, the code and the theory. However, before going off and using this code, be sure to check the more efficient routine at the end (of this rather long message). --------------------- list p=16C64,t=ON,c=132,n=80,st=off radix dec include "P16C64.INC" cblock 0x20 ones,tens,hund hex x endc ORG 0 ;Reset Vector GOTO Main ORG 4 ;Interrupt Vector Main BCF STATUS,RP0 ;Point to BANK 0 MOVLW 0 MOVWF hex xxx MOVF hex,W NOP CALL hex2dec NOP INCF hex,F GOTO xxx ;---------------------------------------- hex2dec SWAPF hex,W ANDLW 0x0f MOVWF tens ADDWF tens,F RLF tens,W SUBLW -20 MOVWF ones MOVF hex,W ANDLW 0x0f ADDWF ones,F MOVLW -38 ADDWF tens,F MOVLW 4 MOVWF hund MOVLW 10 lb1 ADDWF ones,F DECF tens,F SKPC goto lb1 lb2 ADDWF tens,F DECF hund,F SKPC goto lb2 RETURN END ------------------------------------- 8-bit hexadecimal to decimal conversion: --------------------------------------- Suppose you're given N, an 8-bit number. It's easy to express this in hexdecimal (base 16) in terms of the upper and lower nibbles: N = a_1*16 + a_0 where a_1 is the upper nibble, a_0 is the lower nibble, and 16 is with respect to base 10. N can also be expressed similarly in terms of three decimal digits: N = b_2*100 + b_1*10 + b_0 Our goal is to find an algorithm that can express the b_i's in terms of a_j's. There are several ways to go about doing this, but here's a straight-forward brute force approach: N = b_2*100 + b_1*10 + b_0 = a_1*16 + a_0 = a_1*10 + a_0 + a_1*6 So we could try to begin by saying: b_0 = a_0 + a_1*6 b_1 = a_1 b_2 = 0 However, we also know that the decimal digits are between 0 and 9. It's fairly obvious that for most numbers the b_0 digit is greater than 9. So the first attempt doesn't quite work, unless we keep track of the overflow. In other words, if b_0 is greater than 9 then we just carry the excess over to b_1. Mathematically: b_0 = (a_0 + a_1*6) mod 10 b_1 = (a_1 + [a_0 + a_1*6 - b_0]/10) mod 10 b_2 = ((a_1 + [a_0 + a_1*6 - b_0]/10) - b_1)/10 An example: N = 0x7f ==> a_1=7, a_0=15 a_0 + a_1*6 = 15 + 7*6 = 57 b_0 = 57 mod 10 = 7 <========================== b_1 = (a_1 + [a_0 + a_1*6 - b_0]/10) mod 10 = ( 7 + [ 15 + 7 *6 - 7]/10) mod 10 = ( 7 + [ 57 - 7]/10) mod 10 = 12 mod 10 = 2 <========================== b_2 = ((a_1 + [a_0 + a_1*6 - b_0]/10) - b_1)/10 = ( 12 - 2 )/10 = 1 <========================== So all of that mess with the /10 and mod 10 takes care of the overflow. I only illustrate it for completeness, because the program deals with the overflow in a slightly different way. The three equations for the decimal digits are biased so that they're guaranteed to be negative. Then the "overflow" is handled by repeatedly adding 10 to the equation until it becomes positive. So re-starting the derivation: N = b_2*100 + b_1*10 + b_0 = a_1*16 + a_0 = a_1*10 + a_0 + a_1*6 = a_1*20 + a_0 - a_1*4 And the three equations: b_0 = a_0 - a_1*4 b_1 = a_1*2 b_2 = 0 To ensure that the b_0 equation is always negative for any a_0 and a_1 we need to subtract at least 16 from it. However it makes more sense to subtract 20, because: N = b_2*100 + b_1*10 + b_0 = a_1*20 + a_0 - a_1*4 + 20 - 20 = (a_1*20 + 20) + (a_0 - a_1*4 - 20) And the three equations: b_0 = a_0 - a_1*4 -20 b_1 = a_1*2 + 2 b_2 = 0 Similarly, to ensure the tens' equation is negative we need to subtract at least 33 from it. Rounding up to the next constant divisible by 10, or 40, we get: N = b_2*100 + b_1*10 + b_0 = (a_1*20 + 20) + (a_0 - a_1*4 - 20) = (a_1*2+2)*10 + (a_0-a_1*4-20) + 400-400 =4*100 + (a_1*2+2-40)*10 + (a_0-a_1*4-20) And finally we arrive at the desired equations: b_0 = a_0 - a_1*4 - 20 b_1 = a_1*2 + 2 - 40 b_2 = 4 Here's some (untested) C code to implement the algorithm: void hex2dec(unsigned char hex) { unsigned char a_1,a_0; a_0 = hex & 0xf; a_1 = hex >> 4; b_0 = a_0 - a_1*4 - 20; b_1 = a_1*2 + 2 - 40; b_2 = 4; /* Now normalize the results */ do { b_0 += 10; b_1--; } while(b_0 & 0x80) /* continue to loop while b_0 is negative */ do { b_1 += 10; b_2--; } while(b_1 & 0x80) /* continue to loop while b_1 is negative */ } > > Check out www.piclist.com for Scott's excellent explanations of this > technique. > > cblock 0x20 > NumL, NumH > Ones, Tens, Hund > endc > > > clrf NumL > clrf NumH > again > call bin2dec999 > incf NumL > skpnz > incf NumH > goto again > > > ;Binary to decimal conversion (0..999) > ; > ;Input: NumH:NumL > ;Output Hund:Tens:Ones > ; > ;If Input > 999 Output will roll over, e.g. > ;for input=5678 output=678. > ; > ; > ;Size: 34 instructions > ;Execution time (max) including return: > ;22+5*9-1+5*6-1+4*3-1+2 = 108 cycles > ; > ;5-July-2000 by Nikolai Golovchenko > > bin2dec999 > movf NumH, w > addlw 241 > addwf NumH, w > movwf Hund ;b_2 = 2a_2 - 15 > > addwf Hund, w > addwf Hund, w > addlw 253 > movwf Tens > swapf NumL, w > andlw 0x0F > addwf Tens, f > addwf Tens, f ;b_1 = 6a_2 + 2a_1 - 48 > > addwf NumH, w > sublw 251 > movwf Ones > addwf Ones, f > addwf Ones, f > addwf Ones, f > movf NumL, w > andlw 0x0F > addwf Ones, f ;b_0 = a_0 - 4(a_2 + a_1) - 20 > > movlw 10 > bin2dec999a ;9 cycles max > addwf Ones, f > decf Tens, f > skpc > goto bin2dec999a > > bin2dec999b ;6 cycles max > addwf Tens, f > decf Hund, f > skpc > goto bin2dec999b > > bin2dec999c ;3 cycles max > addwf Hund, f > skpc > goto bin2dec999c > > return > > You probably can convert it to C instruction by instruction, so that > efficiency won't be lost... > > Hope it helps. > > Nikolai > If you want to convert just a byte, then here is something I posted last Christmas Eve (which is a repost from something from March of 99 [which was a repost from May '98 {making this the 4'th time it's been posted}]) that would work for you: --------------------------------- It's based on binary comparisons. It takes advantage of this little trick to quickly ascertain the ones' digit: If you look at the ones' digit for 2^N you see this pattern: n = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 ... 2^n % 10 = 1, 2, 4, 8, 6, 2, 4, 8, 6, 2, 4, 8 ... 2^n in hex = 0, 2, 4, 8,10,20,40,80, ... If it wasn't for the annoying 6's, you could simply sum the nibbles to get and get the ones' digit (after a relatively simple binary to BCD conversion). For example, the ones' digit for 2^5 = 0x20 is 2 (because 0x20 = 32 decimal). This sum-of-digits trick works even if the binary number is not a perfect power of 2. For example, consider 0xef: 0xef = 239 base 10, the one's digit is '9' 0xe + 0xf = 0x1d A binary to bcd conversion of 0x1d yields 0x29 (because 1d hex is 29 decimal). And the one's digit is '9', just like the one's digit of 0xef. Now, this trick only works if bit 4 is not set. (notice my contrived exampled has every bit set except for that one). It's simple enough to test if bit 4, (and bit 8 for 16-bit conversions) and to subtract 1 and then add 6 (or simply add 5) if it is. The second observation is that the sum of all of the tens' digits of 2^n (for n<8) is less than 16, and thus can fit into one nibble. This simplifies having to deal with overflow until after all of the digits have been added together. (What I'm saying is simply that if you look at the eight 2^n numbers 1,2,4,8,0x10,0x20,0x40,0x80 and sum all of the upper nibbles together: 0x10 + 0x20 + 0x40 + 0x80 you get 0xf0 which doesn't cause a carry.) The third observation is that the BCD result is greater than 200 only if the most significant bit is set. Note, that this is necessary but not sufficient condition. e.g. 0x80 has the msb set, but is only 128, but 200 is 0xc8. ;******************************************************** ;binary_to_bcd - 8-bits ; ;Input ; bin - 8-bit binary number ;Outputs ; hundreds - the hundreds digit of the BCD conversion ; tens_and_ones - the tens and ones digits of the BCD conversion binary_to_bcd: CLRF hundreds SWAPF bin,W ;Add the upper and lower nibbles ADDWF bin,W ;to get the one's digit ANDLW 0x0f SKPNDC ;Go through a binary to bcd ADDLW 0x16 ;conversion for just the one's SKPNDC ;digit ADDLW 0x06 ADDLW 0x06 SKPDC ADDLW -0x06 BTFSC bin,4 ;Bit 4 is a special case ADDLW 0x16 - 1 + 0x6 SKPDC ADDLW -0x06 ;Now adjust the ten's digit BTFSC bin,5 ;2^5 = 32, so add 3 to the ten's ADDLW 0x30 ;digit if bit 5 is set BTFSC bin,6 ;2^6 = 64, so add 6 ADDLW 0x60 ;if bit 6 is set BTFSC bin,7 ;2^7 = 128, so add 2 (the ten's ADDLW 0x20 ;digit) if bit 7 is set ADDLW 0x60 ;Convert the ten's digit to BCD RLF hundreds,F ;If there's a carry, then the input BTFSS hundreds,0 ;was greater than 99. ADDLW -0x60 MOVWF tens_and_ones BTFSC bin,7 ;If msb is set then the hundred's INCF hundreds,F ;digit is a '2' 28 cycles. A quick session through gpsim shows that it works for all cases. Scott -- http://www.piclist.com hint: The list server can filter out subtopics (like ads or off topics) for you. See http://www.piclist.com/#topics