PIC Code for Binary to Decimal Conversion
Part of
the Binary to Decimal Conversion Tutorial
|
The following decimal print routine for the 14-bit 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 call-stack 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 16-bit operand ; TEM2 contains most significant 8 bits of 16-bit operand ; the 16-bit 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 short-term scratch ; TEMPL short-term 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 no-longer 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 PIC-related material elsewhere on the web.