====== 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.