PIC Microcontoller Radix Math Method

Binary to ASCII, 16 bit to 3digits (0..999) FAST

by Nikolai Golovchenko

;-------------------------------------------
;Fast binary to decimal conversion (0..999)
;
;Input: NumH:NumL
;Output Hund:Tens_Ones (packed BCD)
;
;
;
;Size: 56 instructions
;Execution time (with return): 57
;
;8-July-2000 by Nikolai Golovchenko
;23-Aug-2001
;Based on 8bit BIN2BCD of Scott Dattalo
;-------------------------------------------
bin2dec999fast
        swapf NumL, w   ;Add the upper and lower nibbles
        addwf NumL, 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 NumL, 4   ;bit 4 is a special case
         addlw 0x16 - 1 + 0x6
        skpdc
         addlw -0x06

                        ;now adjust the ten's digit
        btfsc NumL, 5   ;2^5 = 32, so add 3 to the ten's
         addlw 0x30     ;digit if bit 5 is set

        btfsc NumL, 6   ;2^6 = 64, so add 6
         addlw 0x60     ;if bit 6 is set

        btfsc NumL, 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
        clrf Hund
        rlf Hund, f     ;if there's a carry, then the input
        btfss Hund, 0   ;was greater than 99.
         addlw -0x60
        movwf Tens_Ones

        movlw 0x66      ;2^8 = 256, so add 0x56 to Tens_Ones
        btfsc NumH, 0
         movlw 0x56 + 0x66 ;add 0x66 for decimal adjust
        addwf Tens_Ones, f
        skpnc
         incf Hund, f
        clrw
        skpc
         iorlw 0x60
        skpdc
         iorlw 0x06
        subwf Tens_Ones, f

        movlw 0x66      ;2^9 = 512, so add 0x12 to Tens_Ones
        btfsc NumH, 1
         movlw 0x12 + 0x66
        addwf Tens_Ones, f
        skpnc
         incf Hund, f
        clrw
        skpc
         iorlw 0x60
        skpdc
         iorlw 0x06
        subwf Tens_Ones, f

;        btfsc NumL, 7   ;finish with hundreds
;         incf Hund, f
;        movlw 2
;        btfsc NumH, 0
;         addwf Hund, f
;        movlw 5
;        btfsc NumH, 1
;         addwf Hund, f
; Aug 23, 2001 optimization:
        rlf NumL, w             ; copy bit 7 to carry
        rlf NumH, w             ; load w with bits 9 to 7
        btfsc NumH, 1           ; add b[9]
         addlw 1                ;
        addwf Hund, f           ; add it to hundreds
return


The algorithm is based on the idea of adding up decimal weights of each
bit. Each bit has a weight (equal to 2^b, b - bit number, 0 to 15), which can
be represented and added in decimal:

bit     weight          hundreds tens  ones
------- -------         ------- ------ -------
0       1               0       0       1
1       2               0       0       2
2       4               0       0       4
3       8               0       0       8
4       16              0       1       6
5       32              0       3       2
6       64              0       6       4
7       128             1       2       8
8       256             2       5       6
9       512             5       1       2

So, if a bit is set, we add the appropriate decimal digits to our
result. But testing each bit would be very inefficient. There are
however ways to optimize this. You can notice a regularity, for
example, in the ones column. Values almost repeat the binary weights
(1,2,4,8,6,2,4,8,6,2). That means that we can separate the binary word
in repeatable strings and add these strings together, which would be
the same if we tested each bit.

First, we need to modify the table to see how to separate the bits in
strings:

bit     weight          hundreds tens  ones
------- -------         ------- ------ -------
0       1               0       0       1
1       2               0       0       2
2       4               0       0       4
3       8               0       0       8
4       16              0       1       1+5
5       32              0       2+1     2
6       64              0       4+2     4
7       128             1       2       8
8       256             2       5       1+5
9       512             4+1     1       2


So, the calculations would be:

ones = b[3:0] + b[7:4] + b[9:8] + 5*(b[4] + b[8])
tens = b[6:4] + b[6:5] + 2*b[7] + 5*b[8] + b[9]
hund = b[9:7] + b[9]

However, first two sums may exceed the 0 to 9 range, so we would have
to check for that and propagate the overflow to tens and hundreds.

Let's see what the code does...


; add b[3:0] + b[7:4] for ones
        swapf NumL, w   ;Add the upper and lower nibbles
        addwf NumL, w   ;to get the one's digit
        andlw 0x0F
; if sum is bigger than 15 (DC is set), add 16 to BCD result
        skpndc          ;Go through a binary to bcd
         addlw 0x16     ;conversion for just the one's
; if lower digit overflowed again, add 16 again
        skpndc          ;digit
         addlw 0x06
; make sure the lower digit is in 0..9 range. If not, do BCD
; correction (add 6)
        addlw 0x06
        skpdc
         addlw -0x06


; add now 5*b[4] to ones accumulator and b[4] to tens, doing the
; BCD correction at the same time
        btfsc NumL, 4   ;bit 4 is a special case
         addlw 0x16 - 1 + 0x6
        skpdc
         addlw -0x06

; here we have converted already bits 0 to 4:
; ones = b[3:0] + b[7:4] + b[9:8] + 5*b[4] + 5*b[8]
;        ---------------            -------
; tens = 2*b[6:5] + b[4] + b[6:5] + 2*b[7] + 5*b[8] + b[9]
;                   ----
; hund = b[9:7] + b[9]


; now just test bits 5 to 7 and add their decimal weights to
; tens only (bit 7 also affects hundreds). We can add maximum
; 11 to tens digit here, so it will be less than 16 and not overflow.
                        ;now adjust the ten's digit
        btfsc NumL, 5   ;2^5 = 32, so add 3 to the ten's
         addlw 0x30     ;digit if bit 5 is set

        btfsc NumL, 6   ;2^6 = 64, so add 6
         addlw 0x60     ;if bit 6 is set

        btfsc NumL, 7   ;2^7 = 128, so add 2 (the ten's
         addlw 0x20     ;digit) if bit 7 is set

; do decimal correction for tens and propagate carry to hundreds
        addlw 0x60      ;convert the ten's digit to bcd
        clrf Hund
        rlf Hund, f     ;if there's a carry, then the input
        btfss Hund, 0   ;was greater than 99.
         addlw -0x60
        movwf Tens_Ones

; Here we are done with bits 5 to 7 (except bit 7 for hundreds):
; ones = b[3:0] + b[7:4] + b[9:8] + 5*b[4] + 5*b[8]
;        ---------------            -------
; tens = 2*b[6:5] + b[4] + b[6:5] + 2*b[7] + 5*b[8] + b[9]
;        --------   ----   ------   ------
; hund = b[9:7] + b[9]


; test bit 8, and add what it contributes to ones and tens
        movlw 0x66      ;2^8 = 256, so add 0x56 to Tens_Ones
        btfsc NumH, 0
         movlw 0x56 + 0x66 ;add 0x66 for decimal adjust
        addwf Tens_Ones, f
; do decimal correction
        skpnc
         incf Hund, f
        clrw
        skpc
         iorlw 0x60
        skpdc
         iorlw 0x06
        subwf Tens_Ones, f

; Here we are done with bit 8:
; ones = b[3:0] + b[7:4] + 2*b[9] + b[8] + 5*b[4] + 5*b[8]
;        ---------------           -----   -------  ------
; tens = 2*b[6:5] + b[4] + b[6:5] + 2*b[7] + 5*b[8] + b[9]
;        --------   ----   ------   ------   ------
; hund = b[9:7] + b[9]
       

; test bit 9 (it adds 2 to ones, 1 to tens, and 5 to hundreds),
; but ignore hundreds yet
        movlw 0x66      ;2^9 = 512, so add 0x12 to Tens_Ones
        btfsc NumH, 1
         movlw 0x12 + 0x66
        addwf Tens_Ones, f
        skpnc
         incf Hund, f
        clrw
        skpc
         iorlw 0x60
        skpdc
         iorlw 0x06
        subwf Tens_Ones, f

; Now we have a ready result for ones and tens:
; ones = b[3:0] + b[7:4] + 2*b[9] + b[8] + 5*b[4] + 5*b[8]
;        ---------------   ------  -----   -------  ------
; tens = 2*b[6:5] + b[4] + b[6:5] + 2*b[7] + 5*b[8] + b[9]
;        --------   ----   ------   ------   ------  -----
; hund = b[9:7] + b[9]

; now just test bits 7, 8, and 9 and add their share to hundreds
; (without any BCD correction, because we assume input value is
; between 0 and 999, so hundreds can't be more than 9)

        btfsc NumL, 7   ;finish with hundreds
         incf Hund, f
        movlw 2
        btfsc NumH, 0
         addwf Hund, f
        movlw 5
        btfsc NumH, 1
         addwf Hund, f

That's it!

Wait, let's optimize the last few lines...

; hund = b[9:7] + b[9]

        rlf NumL, w             ; copy bit 7 to carry
        rlf NumH, w             ; load w with bits 9 to 7
        btfsc NumH, 1           ; add b[9]
         addlw 1                ;
        addwf Hund, f           ; add it to hundreds


 

 

Questions:

: