From: John Payson via Scott Dattalo
see
http://www.dattalo.com/technical/software/pic/bcd.txt
for notes on how this works. Plan on a headache. <GRIN>
[ed: quick guess at speed is that about 200 instructions will be executed
and 50 bytes + 7 registers used]
;Takes hex number in NumH:NumL Returns decimal in ;TenK:Thou:Hund:Tens:Ones ;written by John Payson ;input ;=A3*163 + A2*162 + A1*161 + A0*160 ;=A3*4096 + A2*256 + A1*16 + A0 NumH EQU AD3M ;A3*16+A2 NumL EQU AD3L ;A1*16+A0 ;share variables ;=B4*104 + B3*103 + B2*102 + B1*101 + B0*100 ;=B4*10000 + B3*1000 + B2*100 + B1*10 + B0 TenK EQU LOOPER ;B4 Thou EQU D2 ;B3 Hund EQU D1 ;B2 Tens EQU R2 ;B1 Ones EQU R1 ;B0 swapf NumH,w ;w = A2*16+A3 andlw 0x0F ;w = A3 *** PERSONALLY, I'D REPLACE THESE 2 addlw 0xF0 ;w = A3-16 *** LINES WITH "IORLW b'11110000B' " -AW movwf Thou ;B3 = A3-16 addwf Thou,f ;B3 = 2*(A3-16) = 2A3 - 32 addlw .226 ;w = A3-16 - 30 = A3-46 movwf Hund ;B2 = A3-46 addlw .50 ;w = A3-46 + 50 = A3+4 movwf Ones ;B0 = A3+4 movf NumH,w ;w = A3*16+A2 andlw 0x0F ;w = A2 addwf Hund,f ;B2 = A3-46 + A2 = A3+A2-46 addwf Hund,f ;B2 = A3+A2-46 + A2 = A3+2A2-46 addwf Ones,f ;B0 = A3+4 + A2 = A3+A2+4 addlw .233 ;w = A2 - 23 movwf Tens ;B1 = A2-23 addwf Tens,f ;B1 = 2*(A2-23) addwf Tens,f ;B1 = 3*(A2-23) = 3A2-69 (Doh! thanks NG) swapf NumL,w ;w = A0*16+A1 andlw 0x0F ;w = A1 addwf Tens,f ;B1 = 3A2-69 + A1 = 3A2+A1-69 range -69...-9 addwf Ones,f ;B0 = A3+A2+4 + A1 = A3+A2+A1+4 and Carry = 0 (thanks NG) rlf Tens,f ;B1 = 2*(3A2+A1-69) + C = 6A2+2A1-138 and Carry is now 1 as tens register had to be negitive rlf Ones,f ;B0 = 2*(A3+A2+A1+4) + C = 2A3+2A2+2A1+9 (+9 not +8 due to the carry from prev line, Thanks NG) comf Ones,f ;B0 = ~(2A3+2A2+2A1+9) = -2A3-2A2-2A1-10 (ones complement plus 1 is twos complement. Thanks SD) ;;Nikolai Golovchenko [golovchenko at MAIL.RU] says: comf can be regarded like: ;; comf Ones, f ;; incf Ones, f ;; decf Ones, f ;;First two instructions make up negation. So, ;;Ones = -1 * Ones - 1 ;; = - 2 * (A3 + A2 + A1) - 9 - 1 ;; = - 2 * (A3 + A2 + A1) - 10 rlf Ones,f ;B0 = 2*(-2A3-2A2-2A1-10) = -4A3-4A2-4A1-20 movf NumL,w ;w = A1*16+A0 andlw 0x0F ;w = A0 addwf Ones,f ;B0 = -4A3-4A2-4A1-20 + A0 = A0-4(A3+A2+A1)-20 range -215...-5 Carry=0 rlf Thou,f ;B3 = 2*(2A3 - 32) = 4A3 - 64 movlw 0x07 ;w = 7 movwf TenK ;B4 = 7 ;B0 = A0-4(A3+A2+A1)-20 ;-5...-200 ;B1 = 6A2+2A1-138 ;-18...-138 ;B2 = A3+2A2-46 ;-1...-46 ;B3 = 4A3-64 ;-4...-64 ;B4 = 7 ;7 ; At this point, the original number is ; equal to TenK*10000+Thou*1000+Hund*100+Tens*10+Ones ; if those entities are regarded as two's compliment ; binary. To be precise, all of them are negative ; except TenK. Now the number needs to be normal- ; ized, but this can all be done with simple byte ; arithmetic. movlw .10 ;w = 10 Lb1: ;do addwf Ones,f ; B0 += 10 decf Tens,f ; B1 -= 1 btfss 3,0 ;skip no carry goto Lb1 ; while B0 < 0 ;jmp carry Lb2: ;do addwf Tens,f ; B1 += 10 decf Hund,f ; B2 -= 1 btfss 3,0 goto Lb2 ; while B1 < 0 Lb3: ;do addwf Hund,f ; B2 += 10 decf Thou,f ; B3 -= 1 btfss 3,0 goto Lb3 ; while B2 < 0 Lb4: ;do addwf Thou,f ; B3 += 10 decf TenK,f ; B4 -= 1 btfss 3,0 goto Lb4 ; while B3 < 0 retlw 0
Archive:
Comments:
btfss 3,0
by btfss STATUS, C, ACCESS
addwf Ones,f ; B0 += 10
decf Tens,f ; B1 -= 1
btfss 3,0
decf Tens,f ; B1 -= 1
addwf Ones,f ; B0 += 10
btfss STATUS, C, ACCESS
See also:
If you have a 4 digit hexadecimal number, it may be written as N = a_3*16^3 + a_2*16^2 + a_1*16 + a_0 where a_i, i=0,1,2,3 are the four hexadecimal digits. If you wish to convert this to decimal, then you need to express N as N = b_4*10^4 + b_3*10^3 + b_2*10^2 + b_1*10 + b_0 Where b_j, j=0,1,2,3,4 are the five decimal digits. The reason there are 5 digits in the decimal representation is because the maximum four digit hexadecimal number (0xffff) requires 5 decimal digits (65535). Now the goal is to find a set of equations that allow the b_j's to be expressed in terms of the a_i's. There are infinitely many ways to do this. Here are two of probably the simplest expressions. First, expand the 16^i's and then collect all coefficients of the 10^j's: N = a_3*4096 + a_2*256 + a_1*16 + a_0 = a_3*4*10^3 + a_2*2*10^2 + (a_3*9 + a_2*5 + a_1)*10 + 6*(a_3 + a_2 + a_1) + a_0 This gives us five equations: b_0 = 6*(a_3 + a_2 + a_1) + a_0 b_1 = a_3*9 + a_2*5 + a_1 b_2 = 2*a_2 b_3 = 4*a_3 b_4 = 0 Which as John says, must be "normalized". Normalization in this context means we need to reduce the b_j's such that 0 <= b_j <= 9 In other words we need to find: c_0 = b_0 mod 10 b_1 = (b_1 + (b_0 - c_0)/10) c_1 = b_1 mod 10 b_2 = (b_2 + (b_1 - c_1)/10) c_2 = b_2 mod 10 b_3 = (b_3 + (b_2 - c_2)/10) c_3 = b_3 mod 10 c_4 = (b_4 + (b_3 - c_3)/10) mod 10 = (b_2 - c_2)/10 Division by 10 can be done quite efficiently (as was shown in another thread several months ago). However, it does require a significant amount of code space compared to say repeated subtractions. Unfortunately, there can be very many subtractions that are required. For example, b_1 could be as large as 15*16, or 240. 10 would have to be subtracted 24 times if you wish to compute 240 mod 10. I presume John realized this inefficiency and thus sought to express N so that repeated subtractions could be used and that the total number of subtractions are minimized. This leads to the next way that N can be expressed: N = a_3*(4100 - 4) + a_2*(260 - 4) + a_1*(20-4) + a_0 = 4*a_3*10^3 + (a_3 + 2*a_2)*10^2 + (6*a_2 + 2*a_1)*10 + a_0 - 4*(a_3 + a_2 + a_1) This gives five new equations for the b_j's. b_0 = a_0 - 4*(a_3 + a_2 + a_1) b_1 = 6*a_2 + 2*a_1 b_2 = a_3 + 2*a_2 b_3 = 4*a_3 b_4 = 0 However, these equations are still not conducive to the repeated subtraction algorithm, at least the way John has done it. In other words, it is possible to pre-condition each of the b_j's so that they are less than zero. Then the repeated subtractions can simultaneously perform the "mod 10" and "/10" operations shown above. Consider the equation b_0 for example, b_0 = a_0 - 4*(a_3 + a_2 + a_1) Since each a_i must satisfy: 0 <= a_i <= 15, then b_0 ranges: -60 <= b_0 <= 15 We can make b_0 negative by subtracting any number greater than 15. A logical choice is 20. This is because if we subtract 20 from b_0, we can add 2 to b_1 to keep the net result the same. The reason we add "2" can be seen: b_1*10 + b_0 = b_1*10 + b_0 + 20 - 20 = (b_1 + 2)*10 + b_0 - 20 Carrying this concept out for the rest of the b_i's we have. b_0 = a_0 - 4*(a_3 + a_2 + a_1) - 20 b_1 = 6*a_2 + 2*a_1 + 2 - 140 = 6*a_2 + 2*a_1 - 138 b_2 = a_3 + 2*a_2 + 14 - 60 = a_3 + 2*a_2 - 46 b_3 = 4*a_3 + 6 - 70 = 4*a_3 - 64 b_4 = 0 + 7 = 7 And if you look at John's code closely, you will see this is how he has expressed the b_j's.
Code:
I reworked the second part of the code (normalizing B0..B4) for a PIC18. Normalization is done by division and modulo operations which are done by mul. The modified conversion runs in constant time and it is about 74 cycles faster than John Payson's normalization routine (average based on all conversions from 0..ffff). It takes about 15-20 more lines of code. There are no additional requirements to the original code (no temporary vars or so). It works for pic18 family featuring the "mul" opcode. Thanks to all contributors and the smart explanation of the original code. movlw 0x07 ;w = 7 movwf TenK ;B4 = 7 ; Now the number needs to be normal- ; ized, but this can all be done with simple byte ; arithmetic. ;**** use original code up to here **** ;**** modified normalization start here **** comf Ones, w ;w = -(B0+1) ; range (w) 5..199 mullw h'CD' movf PRODH, w mullw h'20' ;PRODH = -(B0+1) / 10 incf PRODH, w subwf Tens ;B1 = B1 - (-(B0+1)/10 + 1) ; range -18..-138 -(1..20) = -19..-158 mullw d'10' movf PRODL, w addwf Ones ;B0 = B0 + 10*(-(B0+1)/10 + 1) = B0 mod 10 comf Tens, w ;w = -(B1+1) ; range (w) 18..157 mullw h'CD' movf PRODH, w mullw h'20' ;PRODH = -(B1+1) / 10 incf PRODH, w subwf Hund ;B2 = B2 - (-(B1+1)/10 + 1) ; range -1..-46 -(2..16) = -3..-62 mullw d'10' movf PRODL, w addwf Tens ;B1 = B1 + 10*(-(B1+1)/10 + 1) = B1 mod 10 comf Hund, w ;w = -(B2+1) ; range (w) 2..61 mullw h'1A' ;PRODH = -(B2+1) / 10 incf PRODH, w subwf Thou ;B3 = B3 - (-(B2+1)/10 + 1) ; range -4..-64 -(1..7) = -5..-71 mullw d'10' movf PRODL, w addwf Hund ;B2 = B2 + 10*(-(B2+1)/10 + 1) = B2 mod 10 comf Thou, w ;w = -(B3+1) ; range (w) 4..70 mullw h'CD' movf PRODH, w mullw h'20' ;PRODH = -(B3+1) / 10 incf PRODH, w subwf TenK ;B4 = B4 - (-(B3+1)/10 + 1) mullw d'10' movf PRODL, w addwf Thou ;B3 = B3 + 10*(-(B3+1)/10 + 1) = B3 mod 10 return