User Tools

Site Tools


base:hexadecimal_to_decimal_conversion

Hexadecimal to Decimal Conversion

Without division or multiplication, this first routine takes an 8-bit hex number and returns a 10-bit decimal (BCD) number. It is done by the “add three” algorithm from Motorola AN-757, “Analog-to-Digital Conversion Techniques With the 6800 Microprocessor System” by Don Aldridge. Motorola apparently does not have the app. note on their website. I converted it in the 80's for a 65c02 product. Start with the input number in accumulator.

See also More Hexadecimal to Decimal Conversion. Andrew Jacobs has written equivalent routines to the ones presented here, eliminating the lookup tables in exchange for a little slower execution speed.

If you need a routine that decodes hex to plain ascii, try Another Hexadecimal to Decimal Conversion.

; A       = Hex input number (gets put into HTD_IN)
; HTD_OUT   = 1s & 10s output byte
; HTD_OUT+1 = 100s output byte

HTD:    CLD             ; (Make sure it's not in decimal mode for the
        STA HTD_IN      ;                ADCs below.)
        TAY             ; Save the input to restore later if desired.
        STZ HTD_OUT+1   ; Begin by storing 0 in the output bytes.
        STZ HTD_OUT     ; (NMOS 6502 will need LDA #0, STA ...)
        LDX #8

 htd1$: ASL HTD_IN
        ROL HTD_OUT
        ROL HTD_OUT+1

        DEX             ; The shifting will happen seven times.  After
        BEQ htd3$       ; the last shift, you don't check for digits of
                        ; 5 or more.
        LDA HTD_OUT
        AND #FH
        CMP #5
        BMI htd2$

        CLC
        LDA HTD_OUT
        ADC #3
        STA HTD_OUT

 htd2$: LDA HTD_OUT
        CMP #50H
        BMI htd1$

        CLC
        ADC #30H
        STA HTD_OUT

        BRA htd1$       ; NMOS 6502 can use JMP.

 htd3$: STY HTD_IN      ; Restore the original input.
        RTS

The above method is interesting and I used it in a product; but with a little thought I should have applied myself to years ago, I quickly saw there's a simpler and more efficient way to do the same thing:

HTD_IN:     BLKB    1
HTD_OUT:    BLKB    2       ; low byte first

TABLE:      WORD    1, 2, 4, 8, 16H, 32H, 64H, 128H
                ; (Word directive puts low byte first.)

HTD:    SED             ; Output gets added up in decimal.
        STZ HTD_OUT     ; Inititalize output word as 0.
        STZ HTD_OUT+1   ; (NMOS 6502 will need LDA#0, STA ...)

        LDX #0EH        ; $E is 14 for 2x7 bits.  (0-7 is 8 positions.)
 loop:  ASL HTD_IN      ; Look at next high bit.  If it's 0,
        BCC htd1$       ; don't add anything to the output for this bit.
        LDA HTD_OUT     ; Otherwise get the running output sum
        CLC
        ADC TABLE,X     ; and add the appropriate value for this bit
        STA HTD_OUT     ; from the table, and store the new sum.
        LDA HTD_OUT+1   ; After low byte, do high byte.
        ADC TABLE+1,X
        STA HTD_OUT+1

 htd1$: DEX             ; Go down to next bit value to loop again.
        DEX
        BPL loop        ; If still not done, go back for another loop.

        CLD
        RTS

The principle should be pretty clear. You can take it out to as many digits as you want. Here it is for 16-bit hex input to 5-digit decimal output. Since the entire input number may not fit in A, put it in HTD_IN first.

HTD_IN:		BLKB	2	; Low byte first, as is normal for 6502.
HTD_OUT:	BLKB	3   ; Low byte first, highest byte last.

                ; The table below has high byte first just to
                ; make it easier to see the number progression.
TABLE:  BYTE    0, 0H, 1H,  0, 0H, 2H,  0, 0H, 4H,  0, 0H, 8H
    	BYTE    0, 0H,16H,  0, 0H,32H,  0, 0H,64H,  0, 1H,28H
    	BYTE    0, 2H,56H,  0, 5H,12H,  0,10H,24H,  0,20H,48H
    	BYTE    0,40H,96H,  0,81H,92H,  1,63H,84H,  3,27H,68H

HTD:    SED             ; Output gets added up in decimal.

        STZ HTD_OUT     ; Inititalize output as 0.
        STZ HTD_OUT+1   ; (NMOS 6502 will need LDA#0, STA...)
        STZ HTD_OUT+2

        LDX #2DH        ; 2DH is 45 decimal, or 3x15 bits.
 loop:  ASL HTD_IN      ; (0 to 15 is 16 bit positions.)
        ROL HTD_IN+1    ; If the next highest bit was 0,
        BCC htd1$       ; then skip to the next bit after that.
        LDA HTD_OUT     ; But if the bit was 1,
        CLC             ; get ready to
        ADC TABLE+2,X   ; add the bit value in the table to the
        STA HTD_OUT     ; output sum in decimal--  first low byte,
        LDA HTD_OUT+1   ; then middle byte,
        ADC TABLE+1,X
        STA HTD_OUT+1
        LDA HTD_OUT+2   ; then high byte,
        ADC TABLE,X     ; storing each byte
        STA HTD_OUT+2   ; of the summed output in HTD_OUT.

 htd1$: DEX             ; By taking X in steps of 3, we don't have to
        DEX             ; multiply by 3 to get the right bytes from the
        DEX             ; table.
        BPL loop

        CLD
        RTS

Note by Karoshier: The above code can be easily made a little faster and shorter by splitting the data table into three separate tables, one for the low digits, one for the mid digits and one for the high digits. This way the index can be initialized to 15 instead of 45 and be decremented by 1 instead of 3, thereby saving 4 cycles per iteration and making the routine shorter by 2 bytes. If you have space constraints and you need an 8 bit version as well, you can also make the 16 bit version work for both 8 or 16 bits by changing the initial value of X according to the needs (namely 3 times 7 instead of 3 times 15) and patching the ROL opcode on the fly to a BIT instruction, which does not alter the carry.

base/hexadecimal_to_decimal_conversion.txt · Last modified: 2020-11-14 23:09 by mace