Searching \ for '[PIC]: Brute force 16bit to BCD' in subject line. ()
Make payments with PayPal - it's fast, free and secure! Help us get a faster server
FAQ page: www.piclist.com/techref/microchip/devices.htm?key=pic
Search entire site for: 'Brute force 16bit to BCD'.

Exact match. Not showing close matches.
PICList Thread
'[PIC]: Brute force 16bit to BCD'
2004\08\28@180255 by Charles Craft

picon face
11 bytes of RAM and 183 words of program memory!

Simulator stopwatch shows 1882 instruction cycles or 1.882 msec with the 4 Mhz
internal RC oscillator I'm using on the 12F675.

I looked briefly at the two routines in the PIClist software archive and promptly got
brainache. The project I'm working on is fat on time and resources so I created
the not so efficent or small, but I hope readable, code below.

(I'm posting with the Mindspring web interface for email. If code is corrupted I'll repost
as an attachment.)


Enjoy!
chuckc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;
; Sixt2BCD - table and routines to convert a 16 bit number to BCD digits
;
; chuckc - 08/28/04
;
; This is a brute force method for converting a 16 bit value passed in 2 bytes
; into BCD digits returned in 5 bytes. There are smaller/quicker/more elegant
; chunks of code to accomplish this but the goal here was to create code that
; was readable once it cooled off. :-)
;
; There's a table of the decimal digits for each of the 16 bit positions.
; If the bit is set then the decimal digits for that bit position are added to
; the total value to be returned. Storing the BCD digits in separate bytes
; makes for easy processing on the calling side.
;
; R-M-W and PCLATH are probably two of the defining moments in PIC coding.
; PCLATH got me when I moved this code out of my main .ASM to be standalone.
; Finally worked through it so linker can stick it wherever it wants to.
;
; Possible improvements:
;
; 1. Pack the BCD digits 2 to a byte. Uses 3 bytes for return values instead
; of the current 5. Packing BCD_ERR in there would save yet another byte.
;
; 2. The BCDtable of values is X by Y values. The '0' values at the end of the
; rows take up space and don't add to the total values when processed.
; Mark end of each row instead of each being 5 values long. Tables shrinks
; from 80 to 61 values and there wouldn't be extra '0' digits to process.
;
;
;-----------------------------------------------------------------------------


       #include <TempProbe.inc>
; Contents of TempProbe.inc which is shared by .ASM files in my project
;        list      p=12f675           ; list directive to define processor
;        #include <p12f675.inc>        ; processor specific variable definitions
;        list      p=12f675           ; list directive to define processor
;        errorlevel  -302              ; suppress message 302 from list file

;        RS232XMIT        equ        0x05
; End of TempProbe.inc contents
;-----------------------------------------------------------------------------
       udata_shr
BCD_PTR                        res 1                ; Table pointer into decimal digits
BCD_10000                res 1                ; Ten thousands digit
BCD_1000                res 1                ; Thousands digit
BCD_100                        res 1                ; Hundreds digit
BCD_10                        res 1                ; Tens digit
BCD_1                        res 1                ; Ones digit
BCD_HI                        res 1                ; High byte of 16 bit value passed to routine
BCD_LO                        res 1                ; Low byte of 16 bit value passed to routine
BCD_ERR                        res 1                ; BCD_10000 >= 10
BCD_TMP                        res 1                ; Misc. temp variable
BCD_PCL                        res 1                ; Backup PCLATH to restore at end of routine


       global        BCD_10000                ; Digits returned to calling routine
       global        BCD_1000
       global        BCD_100
       global        BCD_10
       global        BCD_1
       global        BCD_HI                        ; 16 bit value passed by caller
       global        BCD_LO
       global        BCD_ERR                        ; Error flag for value greater than 99,999

       global        Sixt2BCD                ; Make routine available to others

;-----------------------------------------------------------------------------
BCD_ORG                CODE

;-----------------------------------------------------------------------------
; BCDtable - return decimal values for bit positions
;
BCDtable
       movlw        LOW BCDtbl                ; Not sure where the linker will stick us
       addwf        BCD_PTR,W                ; Lets see if we span a 256 block
       movlw        HIGH BCDtbl                ; If so we'll need to increment PCLATH
       skpnc                                        ; Did BCDtbl:low + BCD_PTR wrap?
       addlw        1                                ; Yep, let's go up one on PCLATH value (BCDtbl:hi)
       movwf        PCLATH                        ; And store it for the upcoming PCL change
       movf        BCD_PTR,W                ; Add our index into the table
       addwf        PCL,F                        ; And go get the value to return
BCDtbl
       DT        8,6,7,2,3
       DT        4,8,3,6,1
       DT        2,9,1,8,0
       DT        6,9,0,4,0
       DT        8,4,0,2,0
       DT        4,2,0,1,0
       DT        2,1,5,0,0
       DT        6,5,2,0,0
BCDtblmid
       DT        8,2,1,0,0
       DT        4,6,0,0,0
       DT        2,3,0,0,0
       DT        6,1,0,0,0
       DT        8,0,0,0,0
       DT        4,0,0,0,0
       DT        2,0,0,0,0
       DT        1,0,0,0,0
BCDtblend

       PAGE
;-----------------------------------------------------------------------------
; Sixt2BCD - convert 16 bit number in 2 bytes to BCD digits in 5 bytes
;
Sixt2BCD
       clrf        BCD_10000
       clrf        BCD_1000
       clrf        BCD_100
       clrf        BCD_10
       clrf        BCD_1
       clrf        BCD_PTR
       clrf        BCD_ERR
       movf        PCLATH,W                ; Retrieve and archive PCLATH
       movwf        BCD_PCL
       movf        BCD_HI,W                ; Grab the high byte of passed value
       movwf        BCD_TMP

BCDloop1
       btfss        BCD_TMP,7                ; Is high bit set?
       goto        BCDend2                        ; If not go get next bit or byte

       call        BCDtable                ; Retrive ones value for this bit position
       call        ADD_1                        ; Add it to the BCD Ones digit

       incf        BCD_PTR,F                ; Next position on table row is tens value
       call        BCDtable                ; Go get it
       call        ADD_10                        ; And add it to the BCD Tens digit

       incf        BCD_PTR,F                ; Next position on table row is hundreds value
       call        BCDtable                ; Go get it
       call        ADD_100                        ; And add it to the BCD Hundreds digit

       incf        BCD_PTR,F                ; Next position on table row is thousands value
       call        BCDtable                ; Go get it
       call        ADD_1000                ; And add it to the BCD Thousands digit

       incf        BCD_PTR,F                ; Last position on table row is ten thousands
       call        BCDtable                ; Go get it
       call        ADD_10000                ; And add it to the BCD Ten thousands digit
       
       incf        BCD_PTR,F                ; Move ptr to beginning of next row in the table
       goto        BCDend2a                ; Go process next bit/byte
BCDend2
       movlw        .5                                ; Bit wasn't set so skip a row in the table
       addwf        BCD_PTR,F
BCDend2a
       rlf                BCD_TMP,F                ; Rotate next bit into high position for testing
       movlw        BCDtbl                        ; If more than halfway thru table get next byte
       sublw        BCDtblmid
       subwf        BCD_PTR,W
       bz                Get_LoByte
       movlw        BCDtbl                        ; Not at midway point but are we before or after?
       sublw        BCDtblmid
       subwf        BCD_PTR,W
       bc                Chk4End
       goto        BCDloop1                ; Not to halfway point so go process the next bit

Get_LoByte
       movf        BCD_LO,W                ; Midway thru table so get low byte for processing
       movwf        BCD_TMP
       goto        BCDloop1

Chk4End
       movlw        BCDtbl                        ; Are we done with all bits (End of table values)
       sublw        BCDtblend
       subwf        BCD_PTR,W
       bz                Sixt2BCDEnd                ; If so then return
       goto        BCDloop1                ; Else go process next bit

Sixt2BCDEnd
       movf        BCD_PCL,W                ; Restore PCLATH that was archived
       movwf        PCLATH
       return

       PAGE
;-----------------------------------------------------------------------------
; ADD_* - decimal digit additions
;
ADD_1
       addwf        BCD_1,F                        ; W reg has value to add to BCD_1
       movlw        .10                                ; No digits larger than 9 allowed
       subwf        BCD_1,W                        
       bnc                ADD_END                        ; If value less than 9 we're done
       incf        BCD_10,F                ; Otherwise increment the 10's position
       movwf        BCD_1                        ; And stick the leftover in the 1's digit
       clrw                                        ; Zero out W and go value check 10's
ADD_10
       addwf        BCD_10,F                ; W reg has value to add to BCD_10
       movlw        .10                                ; No digits larger than 9 allowed
       subwf        BCD_10,W                
       bnc                ADD_END                        ; If value less than 9 we're done
       incf        BCD_100,F                ; If not then increment the 100's position
       movwf        BCD_10                        ; And stick what's left in the 10's digit
       clrw                                        ; Zero out W and go value check 100's
ADD_100
       addwf        BCD_100,F                ; W reg has value to add to BCD_100
       movlw        .10                                ; No digits larger than 9 allowed
       subwf        BCD_100,W                
       bnc                ADD_END                        ; If value less than 9 we're done
       incf        BCD_1000,F                ; If not then increment the 1000's position
       movwf        BCD_100                        ; Put leftover value in the 100's digit
       clrw                                        ; Zero out W and go value check 1000's
ADD_1000
       addwf        BCD_1000,F                ; W reg has value to add to BCD_1000
       movlw        .10                                ; No digits larger than 9 allowed
       subwf        BCD_1000,W                
       bnc                ADD_END                        ; If value less than 9 we're done
       incf        BCD_10000,F                ; If not then increment the 10,000's position
       movwf        BCD_1000                ; Put leftover value in the 1000's digit
       clrw                                        ; Zero out W and go value check 10,000's
ADD_10000
       addwf        BCD_10000,F                ; W reg has value to add to BCD_10000
       movlw        .10                                ; No digits larger than 9 allowed
       subwf        BCD_10000,W
       bnc                ADD_END                        ; If value less than 9 we're done
       incf        BCD_ERR,F                ; If not we have a problem (16 bit < 65536)
       movwf        BCD_10000                ; Stick leftover value in 10,000's digit
       clrw                                        ; Being orthogonal - no other reason :-)
ADD_END
       return

       END


_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

2004\08\29@011607 by James Newton, Host

face picon face
Thanks for sharing that, it makes a good point that the smallest, tightest,
fastest code is not always best if future maintainability is critical.

I personally KNOW that I blew out quite a few brain cells trying to
understand how the heck John Payson's binary to BCD converter works.
Irreparable brain damage.

---
James Newton: PICList webmaster/Admin
spam_OUTjamesnewtonTakeThisOuTspampiclist.com  1-619-652-0593 phone
http://www.piclist.com/member/JMN-EFP-786
PIC/PICList FAQ: http://www.piclist.com



> {Original Message removed}

2004\08\29@043507 by Russell McMahon

face
flavicon
face
> I personally KNOW that I blew out quite a few brain cells trying to
> understand how the heck John Payson's binary to BCD converter works.
> Irreparable brain damage.

Despite you KNOWing - the odds are that you actually IMPROVED your mental
ability. Brains seem to work on, as SOME other bodily functions do, on a use
it or lose it basis. A very critical part of this happens up to about 12
years old. It's a bit late to address that one now :-)

But despite prior beliefs to the contrary, brain cells do appear to grow in
later life, and mental acuity can be correlated with mental exercise. Of
course, confusing cause and effect incorrectly is a common fallacy ( post
priori fallacy).



       RM

_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

2004\08\29@123544 by olin_piclist

face picon face
part 1 1323 bytes content-type:text/plain; (decoded 7bit)

Charles Craft wrote:
> 11 bytes of RAM and 183 words of program memory!
> Simulator stopwatch shows 1882 instruction cycles

That sounded like an awful lot to me.  My knee jerk reaction was that doing
a divide by 10 and using the remainder to make each digit would be more
efficient, especially considering the shortcuts that can be taken with the
special case of dividing by a small constant.

Just for fun, I wrote an integer to BCD routine to test out this hunch.  It
is indeed significantly more efficient.  My subroutine takes 65 instruction
locations, and uses 4 temporary variables.  The existing state of the
temporary registers are saved/restored to/from a data stack.  Execution
takes 879 cycles in all 5 test cases I tried.

If the subroutine is allowed to trash the scratch registers it uses (I don't
normally do things that way) then it takes only 30 instruction locations and
executes in 847 cycles.

The disadvantage of my approach is that it's a bit harder to understand.
The special case divide by 10 routine is especially tricky, so I added a lot
of comments to try and explain what is going on.

I have attached the main source module in case anyone is interested.  It is
written assuming my PIC development environment described at
http://www.embedinc.com/pic.


part 2 8417 bytes content-type:application/octet-stream; (decode)

part 3 194 bytes content-type:text/plain; charset="us-ascii"
(decoded 7bit)

_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist


part 4 174 bytes

*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com

2004\08\29@150408 by Robert Monsen

picon face

----- Original Message -----
From: "Olin Lathrop" <.....olin_piclistKILLspamspam@spam@embedinc.com>
To: "Microcontroller discussion list - Public." <piclistspamKILLspammit.edu>
Sent: Sunday, August 29, 2004 9:35 AM
Subject: Re: [PIC]: Brute force 16bit to BCD


{Quote hidden}

I also spent time thinking about this (being retired is so boring). My first
thought was a divide by 10 routine, but that lead to another way to do this,
which is to successively subtract 10000, 1000, 100, 10...

I wrote a 16 bit -> BCD routine that does this in 565 cycles worst case. The
cycles required depends on the input number; it is smaller if the digits are
smaller. It takes up 54 bytes, and uses 2 bytes for input, 3 working bytes,
and 5 bytes for output. I didn't spend much time optimizing it, so I'm sure
a few bytes and cycles could be squeezed out without too much trouble. I
tried it with various values, but I didn't do any exhaustive testing, so
there may well be bugs. Such is ASM.

There is some trash in there (the init routine) from another project. I left
it in so its easy to cut and paste it right into MPASM and try it out.

enjoy,
 Bob Monsen

;;
*****************************************************************************************
       ;; Show 16bit -> BCD routines
       ;; Robert Monsen Aug 28, 2004
       list            p=12F675        ; list directive to define processor
       include         "p12f675.inc"   ; processor specific variable
definitions
       __CONFIG   _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF &
_PWRTE_ON & _INTRC_OSC_NOCLKOUT
       radix dec

;; DECLARE SOME CONSTANTS
******************************************************************
ADINPUT         EQU             GPIO4   ; A/D Input bit

;; MACROS GO HERE
**************************************************************************
SET16 macro a,v
       movlw   (v)&0xFF
       movwf   (a)+1
       movlw   ((v)>>8)&0xFF
       movwf   (a)
       endm

;; VARIABLES
*******************************************************************************
       cblock 0x20                     ; define some variables
       a1                              ; input numerator high digit
       a2                              ; input numerator low digit
       digit1                          ; 10,000s digit
       digit2                          ; 1,000s digit
       digit3                          ; 100s digit
       digit4                          ; 10s digit
       digit5                          ; 1s digit
       b1                              ; working registers
       b2
       r
       endc

;; PROGRAM START
*************************************************************************
       org 0x0
       call    init
       SET16   a1,64999                ;; SET WORST CASE TEST VALUE
       call    getbcd                  ;; output is digit[12345]
final
       ;; loop forever
       goto    $

getbcd
;; take input in a1,a2 and convert to bcd in digit1..digit5

       SET16   b1,10000                ;; Compute First Digit
       call    firstdigit              ;; firstdigit because it checks for
high bit set, which
       movfw   r
       movwf   digit1

       SET16   b1,1000                 ;; Compute Second Digit
       call    getdigit
       movfw   r
       movwf   digit2

       SET16   b1,100                  ;; Compute Third Digit
       call    getdigit
       movfw   r
       movwf   digit3

       SET16   b1,10                   ;; Compute Fourth and Fifth Digit
       call    getdigit
       movfw   r
       movwf   digit4
       movfw   a2
       movwf   digit5
       return
firstdigit
       ; assumes b1,b2 contains a power of 10
       ; assumes a1 contains a value where a1,a2/b1,b2 < 10
       clrf    r
firstdigit1:
       btfss   a1,7            ;; high bit is set coming in.
       goto    getdigit1       ;; use normal routine
       call    sub16           ;; subtract 'should be 10000
       incf    r,f             ;; count it
       goto    firstdigit1     ;; again.
getdigit:
       clrf    r
getdigit1:
       call    sub16           ;; here, we know a1,a2 < 32768
       btfsc   a1,7
       goto    getdigitdone
       incf    r,f
       goto    getdigit1
getdigitdone
       call    add16           ;; leave remainder in a1,a2
       return
sub16
       ; return a1,a2 - b1,b2 -> a1,a2
       movfw   b2
       subwf   a2,f
       btfss   STATUS,C
       decf    a1,f
       movfw   b1
       subwf   a1,f
       return
add16
       ; return a1,a2 + b1,b2 -> a1,a2
       movfw   b2
       addwf   a2,f
       btfsc   STATUS,C
       incf    a1,f
       movfw   b1
       addwf   a1,f
       return

init
       banksel OSCCAL                  ; Set up the clock delay. Comment
this out for the simulator
       call    0x3FF
       movwf   OSCCAL
       banksel ANSEL                   ; Program ANSEL to choose the
divider and port
       movlw   b'00111000'             ; Use dedicated RC Oscillator, and
enable GPIO0 as A/D
       movwf   ANSEL
       clrf    TRISIO                  ; Clear TRISTATE on all pins
       bsf     TRISIO, ADINPUT         ; Turn it back on for the A/D input
       banksel ADCON0                  ; Program the control register for
the A/D
       movlw   b'10001101'             ; right justified, channel 3, and on
       movwf   ADCON0
       movlw   0x7                     ; Program the control register for
the comparator;
       movwf   CMCON                   ; turn off comparator
       clrf    GPIO                    ; Now, clear the output pins
initially
       return

       end


_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

2004\08\29@151947 by Dave Tweed

face
flavicon
face
olin_piclist@embedinc.com (Olin Lathrop) wrote:
> Just for fun, I wrote an integer to BCD routine to test out this hunch.
> It is indeed significantly more efficient.  My subroutine takes 65
> instruction locations, and uses 4 temporary variables.  The existing
> state of the temporary registers are saved/restored to/from a data stack.
> Execution takes 879 cycles in all 5 test cases I tried.

Try some test cases that actually have some 8s and 9s in them.
I don't see how you can represent a BCD digit in 3 bits.

-- Dave Tweed
_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

2004\08\29@175743 by olin_piclist

face picon face
part 1 740 bytes content-type:text/plain; (decoded 7bit)

Dave Tweed wrote:
> Try some test cases that actually have some 8s and 9s in them.
> I don't see how you can represent a BCD digit in 3 bits.

Oops!  The "17th bit" from the last iteration should actually be the high
bit of the remainder, which becomes a digit value.

Fortunately the fix was as simple as changing the mask for the valid
remainder bits to not throw out the high bit.  All the statistics are
therefore still the same: 847 cycles and 30 program memory locations if
REG0 - REG3 are trashed, 879 cycles and 65 program memory locations if all
registers are restored by the subroutine.  I have attached the updated code,
which includes test cases with 8s and 9s in the result.


part 2 8631 bytes content-type:application/octet-stream; (decode)

part 3 194 bytes content-type:text/plain; charset="us-ascii"
(decoded 7bit)

_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist


part 4 174 bytes

*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com


'[PIC]: Brute force 16bit to BCD'
2004\09\07@110021 by Bob Axtell
face picon face
Even after this change, I never got this to work, Olin.Its a great idea.
Is there a later version?

--Bob

Olin Lathrop wrote:

{Quote hidden}

--
Note: Attachments must be sent to
.....attachKILLspamspam.....engineer.cotse.net, and
MAY delay replies to this message.
       520-219-2363

_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

2004\09\07@114248 by olin_piclist

face picon face
Bob Axtell wrote:
>> Oops!  The "17th bit" from the last iteration should actually be the
>> high bit of the remainder, which becomes a digit value.
>>
>> Fortunately the fix was as simple as changing the mask for the valid
>> remainder bits to not throw out the high bit.  All the statistics are
>> therefore still the same: 847 cycles and 30 program memory locations
>> if REG0 - REG3 are trashed, 879 cycles and 65 program memory
>> locations if all registers are restored by the subroutine.  I have
>> attached the updated code, which includes test cases with 8s and 9s
>> in the result.
>
> Even after this change, I never got this to work, Olin.Its a great
> idea.
> Is there a later version?

It works fine for me, including all the test cases in the source code.  In
what way could you not get it to work?  Is it a problem of building my
source code because it needs my PIC development environment?


*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

2004\09\07@154717 by Bob Axtell

face picon face
That's probably it. I use MPLAB's standard environment.It doesn't matter
now, this morning
I found out I only need 8 bits, and I'd written a fast one (31-cycles
max) sometime back.

--Bob

Olin Lathrop wrote:

{Quote hidden}

--
Note: Attachments must be sent to
EraseMEattachspam_OUTspamTakeThisOuTengineer.cotse.net, and
MAY delay replies to this message.
       520-219-2363

_______________________________________________
http://www.piclist.com
View/change your membership options at
http://mailman.mit.edu/mailman/listinfo/piclist

More... (looser matching)
- Last day of these posts
- In 2004 , 2005 only
- Today
- New search...