From Binary To BCD…And Back Again (Part 2)
In the previous post I described the algorithm for converting a binary number to binary-coded-decimal (BCD). In this post I’ll describe an algorithm for doing the reverse; converting a BCD number to binary.
The algorithm is based on repeated division and is best explained by example. Begin with a decimal number and divide by 2. The remainder becomes the high order bit in the binary number, the quotient carries forward. Repeat the process for the required number of bits. The table below shows an example for the decimal number 204.
For a BCD number start at the most significant digit (MSD). Shift right to divide by two, moving the least-significant bit (LSB) into the carry. Since the remainder after dividing by 2 is the LSB, add 10 to the next significant digit if the carry is set and repeat. Limiting ourselves to number in the range from 0 – 255 (8 bits), in PIC assembly language with 8-bit registers this would look like:
; Load the registers movlw d'2 ; load BCD digit 2 movwf DEC2_REGISTER ; movlw d'0 ; load BCD digit 1 movwf DEC1_REGISTER ; movsw d'4 ; load BCD digit 0 movwf DEC0_REGISTER ; clrf BIN_REGISTER ; clear the binary register movlw d'8 ; load the counter movwf COUNTER ; ; Perform the division DIVIDE DIGIT2 bcf status, c ; clear the carry rrf DEC2_REGISTER,1 ; rotate digit to the right, putting LSB into carry btfss status, c ; skip if there is a remainder goto DIGIT1 ; jump to the next digit movf DEC1_REGISTER,0 ; get the next digit addlw d'10 ; add 10 movwf DEC1_REGISTER ; move it back DIGIT1 bcf status, c ; clear the carry rrf DEC1_REGISTER,1 ; rotate digit to the right, putting LSB into carry btfss status, c ; skip if there is a remainder goto DIGIT0 ; jump to the next digit movwf DEC0_REGISTER,0 ; get the next digit addlw d'10 ; add 10 movwf DEC0_REGISTER ; move it back DIGIT0 bcf status, c ; clear the carry rrf DEC0_REGISTER,1 ; rotate the digit to the right, putting LSB into carry ; Update the counter. BINARY rrf BIN_REGISTER,1 ; rotate remainder into the binary register decfsz COUNTER, 1 ; decrement the counter goto DIVIDE ; back to the top of the loop ...
Note that as written, this will not work with 4-bit BCD numbers because adding 10 to a digit could potentially move it outside the range of 0 – 15. However, like the Binary to BCD algorithm, there is an optimization to be performed that will allow it work with 4-digit BCD.
Instead of checking the carry flag and adding 10 before shifting the next digit, perform all the shifts up front. As mentioned previously, the remainder of division by 2 is simply the register’s least-significant bit (LSB), which, after the shift, becomes the MSB of the next digit’s register. After performing the shifts, check each register’s MSB. If it’s set there was a carry. Clear the MSB and add 5 (because we’ve already done the division via the shift, it’s 5 instead of 10). The code becomes:
; Load the registers movlw d'2 ; load decimal digit 2 movwf DEC2_REGISTER ; movlw d'0 ; load decimal digit 1 movwf DEC1_REGISTER ; movsw d'4 ; load decimal digit 0 movwf DEC2_REGISTER ; clrf BIN_REGISTER ; clear the binary register movlw d'8 ; load the counter movwf COUNTER ; ; Perform the division ; Shift the registers DIVIDE bcf status,c ; clear the carry rrf DEC2_REGISTER,1 ; rotate digit to the right rrf DEC1_REGISTER,1 ; rotate digit to the right rrf DEC0_REGISTER,1 ; rotate digit to the right rrf BIN_REGISTER,1 ; rotate binary number to the right ; Clear the MSB and add 5 if MSB is set. ; You don't have to check digit 2 because it will always have ; been zero shifted into its MSB. DIGIT1 movwf DEC1_REGISTER,0 ; move digit to the acc btfss DEC1_REGISTER,7 ; skip if MSB is set goto DIGIT0 ; jump to the next digit andlw h'7f ; clear the MSB addlw d'5 ; add 5 movwf DEC1_REGISTER ; move the digit back DIGIT0 movwf DEC0_REGISTER,0 ; move digit to the acc btfss DEC0_REGISTER,7 ; skip if MSB is set goto BINARY andlw h'7f ; clear the MSB addlw d'5 ; add 5 movwf DEC0_REGISTER ; move the digit back ; Update the counter. BINARY decfsz COUNTER, 1 ; decrement the counter goto DIVIDE ; back to the top of the loop ...
As written this code is for 8 bit registers. However, the algorithm is compatible with 4-bit BCD since the value in any of the 4-bit registers will always be in the range from 0 – 15.
If you think about it, this algorithm nicely complements the binary-to-BCD algorithm. For binary-to-BCD you subtract 5 before setting a register’s MSB. For this case, you clear the MSB and add 5. Makes it easy to remember too.