Page 1 of 1
Print Integer from Hex
Posted: Sun Jul 20, 2025 6:47 am
by vib77
Hello,
I hope the following is not to obvious and has not been answered 10^2 times
I have an apple II, and I want to output integer to screen from hex value in memory,
I have a routine working on my emulator (Virtual II) but not working on my real Apple II
Code: Select all
; Michael Pohoreski
; https://github.com/Michaelangel007/apple2_print_uint16
; Optimized from printm
; Thanks to qkumba for optimizations
; Thanks to Gids for nudging a zero-page version
; F8 ROM Entry Points
PRHEXZ = $FDE5
SCRN2 = $F879
; Print unsigned 16-bit integer
; A=High byte
; X=Low byte
; Also see: Applesoft LINPRT @ ED24
; ======================================================================
PrintUint16
LDY #0 ; S=F3
PHA ; S+5 = _temp+1 = $105,X S=F1
PHX ; S+4 = _temp+0 = $104,X S=F2
PHY ; S+3 = _bcd[2] = $103,X S=F0
PHY ; S+2 = _bcd[1] = $102,X S=EF
PHY ; S+1 = _bcd[0] = $101,X S=EE
Dec2BCD
LDY #16 ; 16 bits
SED ; "Double Dabble"
_Dec2BCD ; https://en.wikipedia.org/wiki/Double_dabble
TSX ; X=EE
STX _BCDbegin+1 ; *** SELF-MODIFYING
; ASL _temp+0 ; abcd efgh | ijkl mnop |
; ROL _temp+1 ; C=a bcde fghi | jklm nop0 |
; ; Bit 7654_3210 | 7654_3210 |
ASL $104,X ; _temp+0
ROL $105,X ; _temp+1
; LDX #$FD ; $00-$FD=-3 bcd[0] bcd[1] bcd[2] bcd[3]
_DoubleDabble ; Y=FD Y=FE Y=FF Y=00
JSR Dabble ; X=EF
JSR Dabble ; X=F0
JSR Dabble ; X=F1
DEY
BNE _Dec2BCD
CLD ; Y=0 = output length
BCD2Chars
LDA $100,X ; X=F1, 1F1 -> bcd[2]
JSR HexA ; print 0, 1, or 2 hex digits
DEX
_BCDbegin
CPX #00 ; *** SELF-MODIFIED X == EE ?
BNE BCD2Chars
; Safe to restore stack now since we are done with vars
TXA
ADC #$04 ; C=1 from CPX #EE
TAX
TXS
TYA ; Handle special case input = $0000 of no output
BEQ _HaveLeadingDigit
_PrintDone
RTS
Dabble
LDA $101,X ; bcd,X
ADC $101,X
STA $101,X
INX
RTS
; Converts A to high ASCII digits, prints as they become available
; @return: A will be bottom nibble in high ASCII
HexA
PHA
JSR SCRN2+2 ; LSR x4 == 0>> 4
JSR _HexNib
PLA
AND #$F
_HexNib
BNE _HaveLeadingDigit ; If have leading zero and no output yet ...
DEY ; ... then skip storing it
_HaveLeadingDigit
INY ; Y = output string length
BEQ _PrintDone
JMP PRHEXZ
is there something shorten ?
I only need 1 byte From 00 to FF to be outputted to 0 to 255 on screen
Thanks
Vincent
Re: Print Integer from Hex
Posted: Sun Jul 20, 2025 7:38 am
by BigDumbDinosaur
I have an apple II, and I want to output integer to screen from hex value in memory. I have a routine working on my emulator (Virtual II) but not working on my real Apple II.
By “not working,” what specifically is failing?
It’s likely a “Duh!” question, but I see you have 65C02 instructions in your code (PHX, PHY, etc.). Since the program runs on your Virtual II simulator (not “emulator”), it must have a virtual 65C02 for an MPU. Does your real Apple ][ also have a 65C02?
Also, I see a bunch of stackrobatics going on, but nothing to indicate where the stack pointer (SP) is pointing when you enter the routine. How do you know you aren’t wrapping SP and trashing something else on the stack?
BTW, the Wikipedia double-dabble article refers to the hardware implementation, which is not necessarily the most efficient way to do it in code. Back when I started programming (late 1960s), the term referred to a software algorithm for generating compressed (aka packed) BCD from binary. The below code snippet does the double-dabble as I learned it about 50 years ago. It’s in 65C816 assembly language, and the binary integer being converted is 64 bits in size. The integer is in FACA in little-endian format.
Code: Select all
ibinbcd rep #%00100000 ;16-bit accummulator
sep #%00010000 ;8-bit index
ldx #4 ;BCD workspace size in words -1 word
;
.init stz facc,x ;zero BCD workspace
dex ;decrement twice to...
dex ;accommodate word-sized...
bpl .init ;processing
;
lda !#64 ;set number of bits...
sta icounter ;to process
sed ;decimal arithmetic
;
.main ldx #0 ;rotation index
ldy #4 ;integer words to process
clc ;no initial carry
;
.main010 rol faca,x ;pick up bit 63
inx
inx
dey
bne .main010
;
tyx ;$00 —> .X
ldy #5 ;words in BCD result
;
.main020 lda facc,x ;BCD result × 2...
adc facc,x ;plus bit 63, if set
sta facc,x
inx
inx
dey
bne .main020
;
dec icounter ;integer fully processed?
bne .main ;no
;
cld ;binary arithmetic
rts
The !# notation in the LDA !#64 instruction tells the assembler to generate a 16-bit operand. By changing the loop counts (number of bits etc.), the above code can be adapted to the 65C02.
Re: Print Integer from Hex
Posted: Sun Jul 20, 2025 10:18 am
by BigEd
Welcome, Vincent!
As BDD says, if you want to run on a 6502, you need 6502 code, whereas 65C02 code may well misbehave.
This site (6502.org) has a reference section as well as the forum, and in particular the source code collection is here:
Source Code Repository
In there you'll find 3 links to pages on converting from hex to decimal. Probably the best for you is this one
More Hexadecimal to Decimal Conversion
where the late Andrew Jacobs offers you an 8 bit routine and also a 16 bit routine.
There's also a link to this forum thread
hex to decimal conversion
(Another resource is, or was, Codebase64 - I found this archived page
6502/6510 Maths)
Re: Print Integer from Hex
Posted: Sun Jul 20, 2025 3:29 pm
by Broti
Re: Print Integer from Hex
Posted: Sun Jul 20, 2025 4:50 pm
by BigEd
(Thanks!)
Re: Print Integer from Hex
Posted: Sun Jul 20, 2025 8:47 pm
by BigDumbDinosaur
In there you'll find 3 links to pages on converting from hex to decimal. Probably the best for you is this one
More Hexadecimal to Decimal Conversion
where the late Andrew Jacobs offers you an 8 bit routine and also a 16 bit routine.
Andrew’s routines use the “classic” software double-dabble as well. His code is better-suited to 8- and 16-bit integers than the routine I earlier posted, as his routines don’t have the inner loops and bit counter (ICOUNTER) I used. Hence his code will run faster with only a modest increase in code.
Comparison of Andrew’s routine with the one I posted illustrates the classic dichotomy of code size versus speed, but also illustrates where the 65C816 in native mode has a significant advantage over the 65C02 in arithmetic processing performance. The bit counter and inner shift and addition loops in my routine were used to keep code size from ballooning when I scaled up things to handle 64-bit integers. With the inner loops, I could not use either index register as the bit counter without constantly pushing and pulling it, at a cost of seven clock cycles per iteration—7 × 64 = 448 cycles total. As it turned out, the cost to push and pull .X in each iteration was noticeably more than that of decrementing ICOUNTER, five cycles per iteration—320 cycles total.
Offsetting the processing time expended in manipulating .X and .Y, plus the slightly-slower execution time of direct-page indexed addressing (five cycles vs. four), is the significant gain realized in the shift and addition operations, in which each ROL and ADC processes 16 bits. Even after factoring in the cost of using .X and .Y for looping as well as the extra clock cycle consumed in fetching and storing a 16-bit quantity from/to memory, outer loop processing runs about 70 percent faster than with the 65C02 over the same number of integer bits.
BTW, here is the same routine with the inner loops unrolled. It will run somewhat faster, but also is bigger. Size vs. speed...
Code: Select all
ibinbcd rep #%00100000 ;16-bit accummulator
sep #%00010000 ;8-bit index
ldx #5-1 ;BCD workspace size in words -1 word
;
.init stz facc,x ;zero BCD workspace
dex ;decrement twice to...
dex ;accommodate word-sized...
bpl .init ;processing
;
ldx #64 ;number of bits to process
sed ;decimal arithmetic
;
.main asl faca ;pick...
rol faca+2 ;up...
rol faca+4 ;bit...
rol faca+6 ;63
lda facc ;BCD result × 2...
adc facc ;plus bit 63, if set
sta facc
lda facc+2
adc facc+2
sta facc+2
lda facc+4
adc facc+4
sta facc+4
lda facc+6
adc facc+6
sta facc+6
lda facc+8
adc facc+8
sta facc+8
dex ;integer fully processed?
bne .main ;no
;
cld ;binary arithmetic
rts
The shift and addition instructions could be “condensed” by using macros instead of writing out the shifting and adding of each word, but the end result would be the same when assembled: 58 bytes of code vs. 50 for the looped version.
Incidentally, my obsession with code size in the above was from the days when my POC units only had 8 KB of ROM—the binary-to-BCD function is part of the M/L monitor. I was aiming to pack as much functionality into that ROM as possible, so every byte counted.
Re: Print Integer from Hex
Posted: Mon Jul 21, 2025 4:36 am
by barrym95838
Vincent, if you just want to output an unsigned byte as decimal, I think you can use something like this (untested, but hopefully properly down-sized from my own 16-bit routine, which has been tested):
Code: Select all
printdec: ; 32 bytes
sta temp ; print accumulator as decimal
prdec2: ;
lda #0 ; remainder (current digit)
clv ; (sets if quotient > 0)
ldy #8 ;
prdec3: ; 8-bit divide by ten {
cmp #5 ; partial rem >= radix/2?
bcc prdec4 ;
sbc #133 ; yes: update rem, set V and
sec ; C for non-zero quotient
prdec4: ;
rol temp ; quotient forms in temp
rol ; remainder forms in A
dey ;
bne prdec3 ; } continue 8-bit divide
bvc prdig ; quotient > 0?
pha ; yes: one or more digits
jsr prdec2 ; must be printed before
pla ; this one, so recurse to
prdig: ; handle them first
eor #"0" ; convert remainder to ascii
jmp putchar ; print the digit
If I got it right, it should work on every 65xx ever made, including the old ceramic ones with no ROR.

It's not a speed demon, but it's compact, has built-in leading zero suppression, and can be easily modified for bases 8, 6, 4, and 2 by placing base/2 into the CMP# instruction and 128+(base/2) into the SBC# instruction. It also uses a sneaky V-flag trick to save a few bytes, which I credit to John Brooks. It also "conveniently" zeros out Y and the byte at label "temp".
[P.S. I just checked it out on Kowalski, and it appears to be good to go, although Kowalski prefers #'0' over #"0".

]
Re: Print Integer from Hex
Posted: Tue Jul 29, 2025 1:47 pm
by jgharston
The most efficient "output hex" code is just straight-forward brute force:
PRHEX:
PHA
LSR A:LSR A:LSR A:LSR A ; get top nybble
JSR PYBYBBLE
PLA
PRNYBBLE:
AND #15
CMP #10:BCC PRDIGIT:ADC #6
PRDIGIT:
ADC "0":JMP PRCHAR
It can be optimised by playing with Decimal mode:
Code: Select all
.PrHex
PHA :\ Save A
LSR A:LSR A:LSR A:LSR A :\ Move top nybble to bottom nybble
JSR PrNybble
PLA
AND #15 :\ Mask out original bottom nybble
.PrNybble
SED :\ Switch to decimal arithmetic
CLC
ADC #&90 :\ Produce &90-&99+CC or &00-&05+CS
ADC #&40 :\ Produce &30-&39 or &41-&46
CLD :\ Switch back to binary arithmetic
JMP OSWRCH :\ Print it
See
link
Re: Print Integer from Hex
Posted: Tue Jul 29, 2025 1:54 pm
by jgharston
Ok, I've re-read the thread and am confused. The OP says printing hex, but loads of posts are printing decimal. For a simple 8-bit decimal printout, where you're not optimising with other code, I've often used this - translated from the PDP11:
Code: Select all
.PrDec
LDX #ASC"0"-1:SEC :\ Prepare for subtraction
.PrDec100
INX:SBC #100:BCS PrDec100 :\ Count how many 100s
JSR PrDecDigit :\ Print the 100s
ADC #100 :\ Balance last SUB 100
LDX #ASC"0"-1:SEC :\ Prepare for subtraction
.PrDec10
INX:SBC #10:BCS PrDec10 :\ Count how many 10s
JSR PrDecDigit :\ Print the 10s
ADC #ASC"0"+1:TAX :\ Convert back to units into X
.PrDecDigit
PHA:TXA:JSR OSWRCH :\ Print digit
PLA:RTS :\ Restore A and return
and in many cases I only need 00-99, so I trim it down to:
Code: Select all
.PrDec
LDX #ASC"0"-1:SEC :\ Prepare for subtraction
.PrDec10
INX:SBC #10:BCS PrDec10 :\ Count how many 10s
JSR PrDecDigit :\ Print the 10s
ADC #ASC"0"+1:TAX :\ Convert back to units into X
.PrDecDigit
PHA:TXA:JSR OSWRCH :\ Print digit
PLA:RTS :\ Restore A and return
Re: Print Integer from Hex
Posted: Wed Jul 30, 2025 1:38 am
by barrym95838
The OP's terminology was a bit confusing, but I just went with the last sentence:
I only need 1 byte From 00 to FF to be outputted to 0 to 255 on screen
and worked with that.

If anyone can beat 32 bytes, I will be thoroughly impressed.
Bruce Clark has his own take on the subject
here, and his code is faster but a bit larger than mine.
Re: Print Integer from Hex
Posted: Sat Aug 09, 2025 9:30 am
by BillG
Much of that bit manipulation in double dabble is making do without a decimal adjust instruction. I have posted code for a number of processors for converting binary to decimal. If there is interest, I can try to find it.
The first question is do you want smaller code size or fast execution?
Re: Print Integer from Hex
Posted: Wed Aug 13, 2025 7:09 am
by barrym95838
... The first question is do you want smaller code size or fast execution?
Yes please.

Re: Print Integer from Hex
Posted: Wed Aug 13, 2025 2:47 pm
by BigDumbDinosaur
The first question is do you want smaller code size or fast execution?
Yes please.

With the 65C816 in native mode, sometimes you can get faster execution with smaller code.
This is especially the case in loops that are doing arithmetic, as being able to process 16 bits at a time often results in fewer branch instructions being used.
Re: Print Integer from Hex
Posted: Mon Feb 09, 2026 3:44 pm
by barnacle
Having a need for an unsigned 32-bit->decimal output, I amused myself with double-dabble, which I haven't played with previously: (see
https://en.wikipedia.org/wiki/Double_dabble for the gory details)
Code: Select all
u32_hextobcd:
stz tmpq1
stz tmpq1+1
stz tmpq1+2
stz tmpq1+3
stz tmpq1+4 ; which is tmpq2 low byte
ldy #32 ; number of bits in input
u32_htb_01:
; check each nibble in the output
; if it's five or greater, add three
ldx #0
u32_htb_02:
lda tmpq1,x
and #$0f ; low nibble
cmp #$5
bcc u32_htb_03 ; if it's greater than 5
; carry is set, so just add two, not three
lda tmpq1,x
adc #2
sta tmpq1,x
u32_htb_03:
lda tmpq1,x
and #$f0
cmp #$50 ; is the high nibble > 5?
bcc u32_htb_04 ; nope
lda tmpq1,x
adc #$2f
sta tmpq1,x ; again, carry is set
u32_htb_04:
inx
cpx #5
bne u32_htb_02 ; and repeat another four times
; do the shifts
asl lba
rol lba+1
rol lba+2
rol lba+3 ; the input shifts
rol tmpq1
rol tmpq1+1
rol tmpq1+2
rol tmpq1+3
rol tmpq1+4 ; into the output
dey ; and then do the rest of the bits!
bne u32_htb_01
u32_htb_x:
rts
The input value is in
lba (four bytes) and the output appears a BCD in
tmpq1 (five bytes; I'm borrowing a byte from another variable but don't tell anyone!). In both cases the result is little-endian:
Code: Select all
$00000064 -> 00 01 00 00 00
$FFFFFFFF -> 95 72 96 94 42
$499602D2 -> 90 78 56 34 12
Obviously you'll need to sort out conversion to ASCII (just add $30 to each nibble) and leading zero suppression. Enjoy!
Neil
Re: Print Integer from Hex
Posted: Wed Feb 11, 2026 2:53 pm
by barnacle
And just to keep things tidy, here's a print routine that handles the leading zero suppression. It outputs the uint32 value in
lba as a decimal value, left aligned. En passant, it kills
lba and
tmpq[0..4].
Code: Select all
putlbadec:
jsr u32_iszero ; the easy case!
bne put_ld_00
lda #'0'
jsr putchar
bra put_ld_ex ; and we're done
put_ld_00:
jsr u32_hextobcd
ldx #5 ; start with the big numbers!
put_ld_01:
dex
lda tmpq1,x
beq put_ld_01 ; skip over all $00 bytes
; does this byte have a leading zero nibble?
and #$f0
bne put_ld_10
; it's zero, so just the low digit please
lda tmpq1,x
jsr hexcharout
dex
bmi put_ld_ex ; exit if this was the last digit
put_ld_10:
; else print the digit pair
lda tmpq1,x
jsr hex2out
dex
bpl put_ld_10
put_ld_ex:
rts
Notes:
- u32_iszero() returns Z if lba is zero
- putchar() prints the ascii character in Acc
- hexcharout() prints the hex value in the low nibble of Acc as a hex digit
- hex2out() prints Acc as a hex pair, high nibble first
- none of the output routines damage X or Y
Neil