Truncated match.
PICList
Thread
'Optimized binary to BCD Conversion'
1999\09\06@154913
by
Jason Sachs
|
I'm getting tired of searching for such a useful
PIC subroutine and finding lots of mediocre code.
-------------- CONTEST --------------
What's the fewest # of instruction cycles required to
convert a 16-bit, 24-bit, or 32-bit binary number
to packed BCD on a PIC16Cxxx?
Constraints: code, when written as a subroutine,
must fit into 256 bytes or less of program memory,
unless it's elegant enough to justify the extra
space
Entry guidelines:
* submit one .asm file that assembles under MPLAB
* claim worst-case execution time in instructions
* claim # of program memory bytes required
* credit source if you borrowed parts of someone else's code
* explain how it works
Prize: minor fame, satisfaction, etc.
(sorry if you were expecting $$!)
-------------------------------------
I've got a routine that converts 16- and 24-bit binary
to packed BCD, based on Mike Keitz's "b2bcd" posted
here last year. (msg forwarded at end)
Program memory requirements: 81 words
Execution time: 285 instr. for 16-bit
553 instr. for 24-bit
This is also extensible to higher length #'s, and
can also preserve the binary # used as an input
with a 1-instruction per bit penalty. (i.e. a 24-bit
"no-clobber" routine takes 577 instr.) A 32-bit
extension to this would take slightly less than 900
instructions.
This code shifts in bits from the binary number,
one at a time, and multiplies the resulting packed BCD
number by 2 using a decimal adjust macro. (In other words,
at the end of N loop iterations, the contents of the BCD memory
is exactly equal to the result of converting the upper N
bits of the binary input to packed BCD.)
I get about a 50% speedup by separating
the main loop into blocks of 8 bits. In other words,
let's say I've got the number 12345678. (0xBC614E)
The first 8 bits shifted in are only going to involve
one byte of this number (0xBC) and, when this is
converted to decimal, will result in, at most, the number
0x00000255. The upper two bytes are going to stay 0's,
so I don't need to involve them at all. The next byte
is always between 0 and 2, so it needs to be involved
in the bit-shifting process, but doesn't need to be
decimal-adjusted since the upper 4 bits are always 0.
Similarly for other bytes. The upper byte never needs
a full decimal adjust, at most just the lower nybble,
since it never overflows (otherwise you didn't leave
enough space for the result!).
This saves a lot of shifting around and adjusting zeros,
though it lengthens the code quite a bit. (Without this
optimization, I've got a 41-word routine that converts
24-bit binary in 849 instructions.)
(I'm curious if there are different approaches that work
at similar speeds.)
Program follows.
include "p16f874.inc"
__CONFIG _CP_OFF & _RC_OSC & _WDT_OFF & _PWRTE_ON
RADIX dec
#define ifzero btfsc STATUS, Z
#define ifnzero btfss STATUS, Z
#define ifdcarry btfsc STATUS, DC
#define ifndcarry btfss STATUS, DC
#define ifbit0 btfss
#define ifbit1 btfsc
;; bcd and bin are organized MSB first, so that
;; they can be used with MPLAB's Watch window
;; as a multibyte number.
cblock 0x20
bcd:0
bcd3
bcd2
bcd1
bcd0
bin:0
bin2
bin1
bin0
tmpi
nbit
endc
#define nbit8 0
#define nbit16 1
#define nbit24 2
org 0x0
reset
nop
goto start
start
#define num (12345678)
movlw low (num >> 16)
movwf bin2
movlw low (num >> 8)
movwf bin1
movlw low (num)
movwf bin0
#undefine num
before24
call bin24tobcd
after24
#define num (54321)
movlw low (num >> 8)
movwf bin2
movlw low (num)
movwf bin1
#undefine num
before16
call bin16tobcd
after16
goto after16
;;;;;;;;;;;;;;;;;;;;; subroutines ;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; bin[X]tobcd: convert binary to packed BCD
;;
;; Attempt at optimization by J Sachs, 9/6/99
;;
;; input: binary # in bin[2:0]
;; bin2 bin1 bin0
;; 16-bit: MSB LSB
;; 24-bit MSB B LSB
;;
;; (example: 12345678 = 0xbc614e
;; -> bin2 = 0xbc, bin1 = 0x61, bin0 = 0x4e)
;;
;; input clobbered by this routine.
;; (#define NOCLOBBER preserves input, at expense of extra
;; code execution time, 1 instruction per bit.)
;;
;; output: packed BCD # in bcd[3:0]
;; bcd3 bcd2 bcd1 bcd0
;; 16-bit: 00 0A BC DE
;; 24-bit: AB CD EF GH
;;
;; (example: 0x12345678 is encoded
;; -> bcd3 = 0x12, bcd2 = 0x34, bcd1 = 0x56, bcd0 = 0x78)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uncomment following line to preserve input
;; #define NOCLOBBER
bin24tobcd
movlw (1 << nbit24)
goto bintobcd
bin16tobcd
movlw (1 << nbit16)
bintobcd
movwf nbit
clrf bcd ;clear result to all 0.
clrf bcd2
clrf bcd1
clrf bcd0
;;;;;;; BCD adjust-prior-to-multiply-by-two routine from Mike Keitz
;;;;;;; converted to macro here. (7 instructions looks like optimum.)
;;;;;;; Also added a half-byte adjust macro. (4 instructions optimum?)
;;; BCD adjust.
bcdadj macro reg
movlw h'33'
addwf reg,f ;Add to low and high nybbles
btfsc reg,3
andlw h'f0' ;Low result >7 . OK (take the 3 out)
btfsc reg,7
andlw h'0f' ;Hi > 7 OK.
subwf reg,f ;any results <=7, subtract back.
endm
;;; BCD half-adjust (lower-nybble only)
bcdhadj macro reg
movlw 3
addwf reg,f ; Add 3.
ifbit0 reg,3 ; result yields <= 7?
; original # was 0-4! undo!
subwf reg,f ; otherwise, original # was
; 5-9, we should add 3.
endm
;; Shift in leftmost 8 bits.
;; Bytes involved: bin2, bcd0, bcd1
;; Max # is 0x0255 -> bcd1 adjustment unneeded.
movlw 8
movwf tmpi
btobcd01
bcdadj(bcd0)
#ifdef NOCLOBBER
rlf bin2,w ; preload carry if byte needs preserving.
#endif
rlf bin2,f
rlf bcd0,f
rlf bcd1,f
decfsz tmpi,f ; loop until index 0.
goto btobcd01
;; 8 bits completed.
;; Shift in next 8 bits.
;; Bytes involved: bin1, bcd0, bcd1, bcd2
;; Max # is 0x065535 -> bcd2 adjustment unneeded.
movlw 8
movwf tmpi
btobcd02
bcdadj(bcd0)
bcdadj(bcd1)
#ifdef NOCLOBBER
rlf bin1,w ; preload carry if byte needs preserving.
#endif
rlf bin1,f
rlf bcd0,f
rlf bcd1,f
rlf bcd2,f
decfsz tmpi,f ; loop until index 0.
goto btobcd02
ifbit1 nbit, nbit16
return
;; 16 bits completed.
;; Shift in next 8 bits.
;; Bytes involved: bin0, bcd0, bcd1, bcd2, bcd3
;; Max # is 0x16777215 -> bcd3 needs only a half-adjust
movlw 8
movwf tmpi
btobcd03
bcdadj(bcd0)
bcdadj(bcd1)
bcdadj(bcd2)
bcdhadj(bcd3)
#ifdef NOCLOBBER
rlf bin0,w ; preload carry if byte needs preserving.
#endif
rlf bin0,f
rlf bcd0,f
rlf bcd1,f
rlf bcd2,f
rlf bcd3,f
decfsz tmpi,f ; loop until index 0.
goto btobcd03
return
end
> {Original Message removed}
1999\09\06@173825
by
Mike Keitz
On Mon, 6 Sep 1999 15:42:10 -0400 Jason Sachs <spam_OUTjsachsTakeThisOuT
DEKARESEARCH.COM>
writes:
> I've got a routine that converts 16- and 24-bit binary
> to packed BCD, based on Mike Keitz's "b2bcd" posted
> here last year. (msg forwarded at end)
I see you've alrady found my entry. I like the BCD multiply by 2 method
especially as the numbers get larger. It is probably the best for speed.
Methods based on division may save space if you already have a division
routine in the program for some other purpose and can reuse it.
___________________________________________________________________
Get the Internet just the way you want it.
Free software, free e-mail, and free Internet access for a month!
Try Juno Web: dl.http://www.juno.com/dynoget/tagj.
More... (looser matching)
- Last day of these posts
- In 1999
, 2000 only
- Today
- New search...