PIC Code for Binary to Decimal Conversion
Part of
the Binary to Decimal Conversion Tutorial

The following decimal print routine for the 14bit family of PIC microcontrollers is lifted from production code I developed. The production code comes from a commercial product, and I have verified that no substantial changes have been made in the process of extracting it from that context; nonetheless, the usual disclaimers apply!
This code is presented as inline code! In practice, most programmers will want this wrapped up as a callable function, but callstack space on the PIC is a scarce resource, so in many cases, other wrappers will be necessary.
; preconditions ; TEM0 contains least significant 8 bits of 16bit operand ; TEM2 contains most significant 8 bits of 16bit operand ; the 16bit operand is positive (if it was negative, it ; already been negated and the sign printed) ; resource usage ; TEM0 holds low nibble, is converted to low decimal digit ; TEM1 holds second nibble, is converted to second digit ; TEM2 holds third nibble, converted to third digit ; TEM3 holds 4th nibble, converted to 4th digit ; TEMPH shortterm scratch ; TEMPL shortterm scratch ; ; calls PUTDIG, to prints each decimal digit ; when called with an ingeger from 0 to 9 in W ; PUTDIG may not use TEM0, TEM1, TEM2, TEM3 ; ; all internal labels have the prefix PUTDEC ; copyright 2002, Douglas W. Jones ; this code may be incorporated into any product so long ; this notice is preserved in the source code and so long ; as any copyright notice on the final software or firmware ; product gives credit to the author. ; warranty ; This is freeware, you get what you pay for. The author ; is pretty sure this code works, but can't offer anything ; better than that. He who transcribes this code for ; incorporation into some larger program must take full ; responsibility for any errors. SWAPF TEM0,W ; get high nibble of low byte MOVWF TEM1 ; into position SWAPF TEM2,W ; get high nibble of high byte MOVWF TEM3 ; into position MOVLW 0x0F ; setup to mask nibbles ANDWF TEM0,F ; in each nibble, use only low 4 bits ANDWF TEM1,F ANDWF TEM2,F ANDWF TEM3,F ; TEM3 ... TEM0 hold 4 nibbles of number, TEM0 holds LSB ; TEM0 = (TEM3 + TEM2 + TEM1)*6 + TEM0 ; TEM0 <=( 8 + 15 + 15)*6 + 15 = 243 MOVF TEM3,W ADDWF TEM2,W ADDWF TEM1,W ; note, C reset because TEM1+TEM2+TEM3 <= 38 MOVWF TEMPL ; TEMPL = W = (TEM3+TEM2+TEM1) RLF TEMPL,F ; TEMPL =(TEM3+TEM2+TEM1)*2 ADDWF TEMPL,F ; TEMPL =(TEM3+TEM2+TEM1)*3(C reset,TEMPL<=114) RLF TEMPL,W ; W =(TEM3+TEM2+TEM1)*6(C reset, W <= 228) ADDWF TEM0,F ; TEM0 done; TEM0 <= 243, C is still reset ; note TEMPH and TEMPL are nolonger needed ; TEMPH = TEM0 div 10 is carry into next digit ; TEM0 = TEM0 mod 10 is the least significant digit ; approximate TEM0/10 by TEM0*.000110011 (assume C already reset) MOVF TEM0,W MOVWF TEMPH ; TEMPH = TEM0*1. RRF TEMPH,F ; TEMPH = TEM0*0.1 (assumes C initially zero) ADDWF TEMPH,F ; TEMPH = TEM0*1.1 (result high bit in C) RRF TEMPH,F ; TEMPH = TEM0*0.11 (high bit recovered) SWAPF TEMPH,W ; W = TEM0*0.000011 ANDLW 0x0F ; (discard high bits) ADDWF TEMPH,F ; TEMPH = TEM0*0.110011 (result high bit in C) RRF TEMPH,F ; TEMPH = TEM0*0.0110011 (high bit recovered) RRF TEMPH,F ; TEMPH = TEM0*0.00110011 (bit of junk) RRF TEMPH,F ; TEMPH = TEM0*0.000110011 (2 bits of junk) MOVLW 0x3F ; (discard junk bits) ANDWF TEMPH,F ; TEM0 = TEM0  10*TEMPH, the remainder approximation RLF TEMPH,W ; high bits of TEMPH are zero, so this clears C ANDLW 0xFE ; (clear any random carry in) MOVWF TEMPL ; TEMPL = W = TEMPH*2 RLF TEMPL,F ; TEMPL = TEMPH*4 RLF TEMPL,F ; TEMPL = TEMPL*8 ADDWF TEMPL,W ; W = TEMPH*8 + TEMPH*2 SUBWF TEM0,F ; TEM0 = TEM0  10*TEMPH ; TEMPH may be off by 1 and TEM0 off by 10 MOVLW D'10' ADDWF TEM0,W ; W = TEM0  10 (sets C if TEM0 >= 10) BTFSC STATUS,C MOVWF TEM0 ; if W >= 0, TEM0 = TEM0  10 BTFSC STATUS,C INCF TEMPH,F ; if W >= 0, TEMPH++ ; TEM0 is now the least significant decimal digit! ; TEM1 = TEMPH + TEM3*9 + TEM2*5 + TEM1 ; TEM1 <= 24 + 8*9 + 15*5 + 15 = 186 (carry never set!) MOVF TEMPH,W ADDWF TEM3,W ADDWF TEM2,W ADDWF TEM1,F ; TEM1 = TEMPH + TEM3*1 + TEM2*1 + TEM1(C reset) RLF TEM3,W ADDWF TEM2,W ; W = TEM3*2 + TEM2*1 MOVWF TEMPL RLF TEMPL,F ; TEMPL = TEM3*4 + TEM2*2 RLF TEMPL,W ; W = TEM3*8 + TEM2*4 ADDWF TEM1,F ; TEM1 = TEMPH + TEM3*9 + TEM2*5 + TEM1 BTFSC STATUS,Z GOTO PUTDEC1 ; if rest of number is zero, print just 1 digit ; TEMPH = TEM1 div 10 is carry into next digit ; TEM1 = TEM1 mod 10 is the next to the least significant digit ; approximate TEM1/10 by TEM1*.000110011 MOVF TEM1,W MOVWF TEMPH ; TEMPH = TEM1*1. RRF TEMPH,F ; TEMPH = TEM1*0.1 ADDWF TEMPH,F ; TEMPH = TEM1*1.1 (high bit is in carry) RRF TEMPH,F ; TEMPH = TEM1*0.11 SWAPF TEMPH,W ; W = TEM1*0.000011 ANDLW 0x0F ; (discard high bits) ADDWF TEMPH,F ; TEMPH = TEM1*0.110011 RRF TEMPH,F ; TEMPH = TEM1*0.0110011 RRF TEMPH,F ; TEMPH = TEM1*0.00110011 RRF TEMPH,F ; TEMPH = TEM1*0.000110011 MOVLW 0x3F ; (discard high bits) ANDWF TEMPH,F ; TEM1 = TEM1  10*TEMPH, the remainder approximation RLF TEMPH,W ANDLW 0xFE ; (clear any random carry in) MOVWF TEMPL ; TEMPL = W = TEMPH*2 RLF TEMPL,F ; TEMPL = TEMPH*4 RLF TEMPL,F ; TEMPL = TEMPL*8 ADDWF TEMPL,W ; W = TEMPH*8 + TEMPH*2 SUBWF TEM1,F ; TEM1 = TEM1  10*TEMPH ; TEMPH may be off by 1 and TEM1 off by 10 MOVLW D'10' ADDWF TEM1,W ; W = TEM1  10 BTFSC STATUS,C MOVWF TEM1 ; if W >= 0, TEM1 = TEM1  10 BTFSC STATUS,C INCF TEMPH,F ; if W >= 0, TEMPH++ ; TEM1 is the next to the least significant decimal digit! ; TEM2 = TEMPH + TEM2*2 ; TEM2 <= 18 + 15*2 = 48 (no carry, and top 2 bits unused too) MOVF TEMPH,W ADDWF TEM2,W ADDWF TEM2,F ; TEM2 = TEMPH + TEM2*2 (never carry out!) MOVF TEM2,W ; check if need to print high digits IORWF TEM3,W ; TEM3 must be rolled into test! BTFSC STATUS,Z GOTO PUTDEC2 ; if rest is zero, print just 2 digits ; TEMPH = TEM2 div 10 is carry into high two digits ; TEM2 = TEM2 mod 10 is the middle digit ; get exact TEM2/10 by TEM2*.0001101 (good because TEM2 < 68) MOVF TEM2,W ; W = TEM2*1.0 MOVWF TEMPH ; TEMPH = TEM2*1.0 RRF TEMPH,F ; TEMPH = TEM2*0.1 RRF TEMPH,F ; TEMPH = TEM2*0.01 BCF TEMPH,7 ; (discard high bit; next to high already clear) ADDWF TEMPH,F ; TEMPH = TEM2*1.01 RRF TEMPH,F ; TEMPH = TEM2*0.101 ADDWF TEMPH,F ; TEMPH = TEM2*1.101 (does not set carry!) SWAPF TEMPH,W ; W = TEM2*0.0001101 ANDLW 0x0F ; (discard high bits) MOVWF TEMPH ; W = TEMPH = TEM2*0.0001101 = TEM2/10 ; TEM2 = TEM2  10*TEMPH, the remainder RLF TEMPH,W MOVWF TEMPL ; TEMPL = W = TEMPH*2 RLF TEMPL,F ; TEMPL = TEMPH*4 RLF TEMPL,F ; TEMPL = TEMPL*8 ADDWF TEMPL,W ; W = TEMPH*8 + TEMPH*2 SUBWF TEM2,F ; TEM2 = TEM2  10*TEMPH ; TEM2 is the middle decimal digit! ; TEM3 = TEMPH + TEM3*4 ; TEM3 <= 8 + 8*4 = 40 (no carry, and top 2 bits unused too) BCF STATUS,C RLF TEM3,F ; TEM3 = TEM3*2 RLF TEM3,W ; TEM3 = TEM3*4 ADDWF TEMPH,W ; W = TEMPH + TEM3*4 (no carry!) BTFSC STATUS,Z GOTO PUTDEC3 ; if rest is zero, just print bottom 3 digits ; TEMPH = W div 10 is the high digit ; TEM3 = W mod 10 is the least significant digit ; get exact W/10 by W*.0001101 (good because W < 68) MOVWF TEM3 ; TEM3 = W*1.0 MOVWF TEMPH ; TEMPH = TEM3*1.0 RRF TEMPH,F ; TEMPH = TEM3*0.1 (carry in is zero!) RRF TEMPH,F ; TEMPH = TEM3*0.01 BCF TEMPH,7 ; (discard high bit; next to high already clear) ADDWF TEMPH,F ; TEMPH = TEM3*1.01 (carry out is zero!) RRF TEMPH,F ; TEMPH = TEM3*0.101 ADDWF TEMPH,F ; TEMPH = TEM3*1.101 (does not set carry!) SWAPF TEMPH,W ; W = TEM3*0.0001101 ANDLW 0x0F ; (discard high bits) MOVWF TEMPH ; TEMPH = TEM3*0.0001101 ; TEM3 = TEM3  10*TEMPH, the remainder RLF TEMPH,W ; W = TEMPH*2 MOVWF TEMPL ; TEMPL = TEMPH*2 RLF TEMPL,F ; TEMPL = TEMPH*4 RLF TEMPL,F ; TEMPL = TEMPL*8 ADDWF TEMPL,W ; W = TEMPH*8 + TEMPH*2 SUBWF TEM3,F ; TEM3 = TEM3  10*TEMPH ; TEM3 is the next to most significant decimal digit! ; TEMPH is the most significant decimal digit! ; conversion to decimal is done! now output digits MOVF TEMPH,W BTFSS STATUS,Z ; don't print it if leading digit is zero CALL PUTDIG ; output char MOVF TEM3,W CALL PUTDIG ; output char PUTDEC3: MOVF TEM2,W CALL PUTDIG ; output char PUTDEC2: MOVF TEM1,W CALL PUTDIG ; output char PUTDEC1: MOVF TEM0,W CALL PUTDIG ; output char ; postconditions ; the number has been printed by a series of calls to PUTDIG ; TEM0, TEM1, TEM2, TEM3, TEMPH and TEMPL have been used ; along with any memory locations used by PUTDIG.
I have more PICrelated material elsewhere on the web.