Yes ideally you should avoid needing this in the first place, but every so often we'll run into the need to show a number that wasn't BCD originally. Especially those that use C since it doesn't have native BCD support.
EDIT: aaaaand turns out I missed an obvious one (BCD can operate directly on memory, and while memory accesses are slow it's still nothing compared to all the rotations that were going on). Putting here the optimized routine now. The other optimizations mentioned are still relevant though.
EDIT 2: fixed what should have been an obvious bug (・_・) I swear I had tested this earlier, must have broken when I did the optimization.
Code: Select all
;****************************************************************************
; IntToAscii, UintToAscii
; Converts a 32-bit integer into a nul-terminated ASCII string.
; IntToAscii is signed, UintToAscii is unsigned.
;----------------------------------------------------------------------------
; input d7.l ... 32-bit integer
; input a6.l ... Pointer to buffer
;----------------------------------------------------------------------------
; breaks: d5-d7, a4-a6
;----------------------------------------------------------------------------
; notes: 12 bytes are needed in the buffer.
;****************************************************************************
IntToAscii:
tst.l d7 ; Is the value negative?
bpl.s UintToAscii
move.b #'-', (a6)+
neg.l d7
;----------------------------------------------------------------------------
UintToAscii:
movem.l d0-d3, -(sp) ; Save registers
moveq #0, d6 ; The ugly part: converting from
move.l d6, -(sp) ; binary into BCD
move.w d6, -(sp)
lea 1(sp), a5 ; The way this algorithm works is
lea @Table(pc), a4 ; that we have BCD values 1, 2, 4...
moveq #32-1, d6 ; every power of 2. Then we scan
@Loop: ; through each bit and sum up the
lsr.l #1, d7 ; corresponding BCD values.
bcc.s @Zero
and.b #$00, ccr ; Definitely faster than doing many
addq.w #5, a4 ; 32-bit divisions (worse than it
addq.w #5, a5 ; sounds since 68000 can only give
abcd.b -(a4), -(a5) ; 16-bit quotients, uuuugh). Still
abcd.b -(a4), -(a5) ; could be optimized further by
abcd.b -(a4), -(a5) ; handling several bits at a time
abcd.b -(a4), -(a5) ; (and the fact that not all digits
abcd.b -(a4), -(a5) ; need to be added early on).
@Zero:
addq.w #5, a4
dbf d6, @Loop
move.b 1(sp), d3
move.l 2(sp), d2
addq.w #6, sp
tst.b d3 ; Convert the first two digits
beq.s @No1stTwo ; (if any, that is)
move.b d3, d7
lsr.b #4, d7
beq.s @No1stOne
add.b #'0', d7
move.b d7, (a6)+
@No1stOne:
and.b #$0F, d3
add.b #'0', d3
move.b d3, (a6)+
moveq #7-1, d6
rol.l #4, d2
bra.s @NoZeroSkip
@No1stTwo:
moveq #7-1, d6 ; Skip leading zeroes
@ZeroSkip:
rol.l #4, d2
move.b d2, d5
and.b #$0F, d5
bne.s @NoZeroSkip
dbf d6, @ZeroSkip
rol.l #4, d2
@NoZeroSkip:
addq.w #1, d6 ; Convert remaining digits
@DigitLoop:
move.b d2, d5
and.b #$0F, d5
add.b #'0', d5
move.b d5, (a6)+
rol.l #4, d2
dbf d6, @DigitLoop
clr.b (a6) ; End the string here
movem.l (sp)+, d0-d3 ; Restore registers
rts ; End of subroutine
;----------------------------------------------------------------------------
@Table:
dc.b $00,$00,$00,$00,$01
dc.b $00,$00,$00,$00,$02
dc.b $00,$00,$00,$00,$04
dc.b $00,$00,$00,$00,$08
dc.b $00,$00,$00,$00,$16
dc.b $00,$00,$00,$00,$32
dc.b $00,$00,$00,$00,$64
dc.b $00,$00,$00,$01,$28
dc.b $00,$00,$00,$02,$56
dc.b $00,$00,$00,$05,$12
dc.b $00,$00,$00,$10,$24
dc.b $00,$00,$00,$20,$48
dc.b $00,$00,$00,$40,$96
dc.b $00,$00,$00,$81,$92
dc.b $00,$00,$01,$63,$84
dc.b $00,$00,$03,$27,$68
dc.b $00,$00,$06,$55,$36
dc.b $00,$00,$13,$10,$72
dc.b $00,$00,$26,$21,$44
dc.b $00,$00,$52,$42,$88
dc.b $00,$01,$04,$85,$76
dc.b $00,$02,$09,$71,$52
dc.b $00,$04,$19,$43,$04
dc.b $00,$08,$38,$86,$08
dc.b $00,$16,$77,$72,$16
dc.b $00,$33,$55,$44,$32
dc.b $00,$67,$10,$88,$64
dc.b $01,$34,$21,$77,$28
dc.b $02,$68,$43,$54,$56
dc.b $05,$36,$87,$09,$12
dc.b $10,$73,$74,$18,$24
dc.b $21,$47,$48,$36,$48