Converting between binary and decimal in assembly
One of the interesting challenges in working in assembly, versus C, is converting between BCD and binary. We all know that processors work in binary and users work in C, right? The problem is, even thought some processors include BCD math capabilities, things like multiplication, division, and all the system timers, work in binary.
So, one of the long standing problems, is how do we efficiently convert between base 2 and base 10. Well, a couple of years ago, I was faced with this problem, and I came up with a fairly efficient conversion algorithm that works well in both directions.
The routines are based on the same basic principal as a multiply routine. For example, let’s convert an 8-bit binary number to BCD.
8-bit binary value 10110101
First, we build up a constant table for each bit location;
0 0 1
0 0 2
0 0 4
0 0 8
0 1 6
0 3 2
0 6 4
1 2 8
Then we create a set of BCD output variables and clear them
BCD100 BCD10 BCD1
Next, we shift through the binary value, and for every 1, we add the corresponding value from the table, using BCD addition;
8-bit binary value 10110101
0 0 1
0 0 4
0 1 6
0 3 2
+ 1 2 8
—————————————
1 8 1
To go in the other direction, we just use a binary array of constants, and add using binary addition
BCD value 181
Binary constants
00000001 1
00000010 2
00000100 4
00001000 8
00001010 10
00010100 20
00101000 40
01010000 80
01100100 100
11001000 200
And the conversion looks like this;
BCD value 181
01100100 100
01010000 80
+ 00000001 1
———————————-
10110101
Of course, this technique can be expanded to any number of bits; all you need to do is expand the constant table to include all the bit values. Attached to the end of this blog are listings for both binary to decimal and decimal to binary for the PIC16FXXX family of parts;
;************************************************************************
; Converts a 5 digit BCD to 16-bit number in H_byte:L_byte
; Limitation: 5-digit bcd must be less than 2^17 (= 65536) in order
; to fit into the 16-bit result. Else result is remainder.
;
; R2H R2L R1H R1L R0H R0L
; - dig5 dig4 dig3 dig2 dig1
BCD5BIN16:
clrf H_byte
movf R2, W
andlw 0×0F
movwf L_byte
call mpy10a ; result = 10a+b
swapf R1, W
call mpy10b ; result = 10[10a+b]
movf R1, W
call mpy10b ; result = 10[10[10a+b]+c]
swapf R0, W
call mpy10b ; result = 10[10[10[10a+b]+c]+d]
movf R0, W
andlw 0×0F
addwf L_byte, F
btfsc STATUS, C
incf H_byte, F ; result = 10[10[10[10a+b]+c]+d]+e
return ; BCD to binary conversion done
mpy10b:
andlw 0×0F
addwf L_byte, F
btfsc STATUS, C
incf H_byte, F
mpy10a:
bcf STATUS, C ; multiply by 2
rlf L_byte, W
movwf L_temp
rlf H_byte, W ; (H_temp,L_H_temp) = 2*N
movwf H_temp
bcf STATUS, C ; multiply by 2
rlf L_byte, F
rlf H_byte, F
bcf STATUS, C ; multiply by 2
rlf L_byte, F
rlf H_byte, F
bcf STATUS, C ; multiply by 2
rlf L_byte, F
rlf H_byte, F ; (H_byte,L_byte) = 8*N
movf L_temp, W
addwf L_byte, F
btfsc STATUS, C
incf H_byte, F
movf H_temp, W
addwf H_byte, F
return ; (H_byte,L_byte) = 10*N
;*********************************************************************
;———————————————————————
; Bin16BCD
; Converts 16-bit binary number (H_byte:L_byte) into a 5 digit
; BCD number in 5 nibbles of registers R2, R1, R0 as shown.
;
; R2H R2L R1H R1L R0H R0L
; - dig5 dig4 dig3 dig2 dig1
;———————————————————————
Bin16BCD:
bcf STATUS,C ; clear the carry bit in STATUS
movlw .16
movwf temp+1
clrf R0
clrf R1
clrf R2
b16b_loop16:
rlf L_byte, F
rlf H_byte, F
rlf R0, F
rlf R1, F
rlf R2, F
decfsz temp+1, F
goto adjDEC
return
adjDEC:
movlw R0
movwf FSR
call adjBCD
movlw R1
movwf FSR
call adjBCD
movlw R2
movwf FSR
call adjBCD
goto b16b_loop16
adjBCD:
movlw 0×03
addwf INDF,W
movwf temp
btfsc temp,3 ; test if result > 7
movwf INDF
movlw 0×30
addwf INDF,W
movwf temp
btfsc temp,7 ; test if result > 7
movwf INDF ; save as MSD
return