Binary coded decimal
Original here
No idea if this sort of thing has been posted before, but I wrote this for displaying scores in 2048
and figured it might be useful to somebody.

One method for display numbers that take more than 16 bits is to convert it to Binary Coded Decimal
first, and display the result. BCD works by using four bits to store each decimal (base 10) digit of
a number. The following code can convert a number to BCD, and display it. It's currently written to
convert a 24 bit number to a 10 digit BCD number, but can be modified to support anything really. It
is memory ineffecient, because it uses one byte for each digit rather than storing two digits per
byte. This is useful though because it makes the display routine simpler.

Conversion to BCD

This routine converts a little endian value to a little endian BCD value


;Converts a 24 bit unsigned int pointed to by HL to BCD
;Convert to BCD with double dabble method, see
;Handles up to a 10 digit number
;Input: HL - pointer to number
;Output: bcdScratch - stores BCD number.
.var NUM_DIGITS, bcdScratch
.var NUM_SRC_BYTES, bcdSource
    ld de,bcdSource
    ld bc,NUM_SRC_BYTES
    ld ix,bcdSource

    xor a
    ld hl,bcdScratch
    ld b,NUM_DIGITS
    ld (hl),a
    inc hl
    djnz _zeroScratch

    ld b,NUM_SRC_BYTES * 8
        ;Do increment
        ld c,NUM_DIGITS
        ld hl,bcdScratch
        ;Iterate through each BCD digit.
        ;If digit > 4, add 3
            ld a,(hl)
            cp 5
            jr c,{@}
            add a,3
            ld (hl),a
            inc hl
            dec c
            jr nz,_bcdIncLp

        ;Shift SRC bits
        sla (ix)
        .for _off, 1, NUM_SRC_BYTES - 1
            rl (ix + _off)

        ld c,NUM_DIGITS
        ld hl,bcdScratch
            ld a,(hl)
            bit 4,a
            jr z,{@}
            and %1111 ;Mask out high bits, since we only want the lower 4 bits for the digit
            scf       ;Set carry if bit 4 set
            ld (hl),a
            inc hl
            dec c
            jr nz,_bcdShiftLp
        djnz _bcdConvLp

Displaying BCD

Displaying BCD is quite simple, since each digit is stored within its own byte. You can simply add
the char code for '0' to the value to get the text char you want. This routine displays an unsigned
BCD value, without leading zeroes


;Displays the BCD value at HL
;1 byte per digit
    ld de,NUM_DIGITS - 1
    add hl,de ;Go to end

    ;Skip leading zeroes, except if the value IS zero
    ld b,NUM_DIGITS - 1
    ld a,(hl)
    or a
    jr nz,{@}
    dec hl
    djnz _skipLeadingZeroes
    inc b ;B = num digits to display
    ld a,(hl)
    add a,'0'
    push hl
    push bc
    ;Replace this with anything that takes a char code in 'A' and displays it
    pop bc
    pop hl
    dec hl
    djnz _dispBCDDigits

In case anyone was wondering, no, this is not a heavily optimized routine. I tried to make it at least somewhat clear what the code was doing rather than hyper-optimize it.
This is fantastic! It is extremely helpful for displaying sprite data as well. Smile Great resource!
I have a routine that converts a number in E:HL into a string in decimal. It zero-pads it to a fixed size, 7 digits I think. It writes the string to IX. I believe that, despite the subfunction names divf and div, it doesn't actually do true division, but instead subtracts repeatedly, until the selected digit is zero. It should be fairly fast, at least faster than any routine using a general-purpose divide. At a glance, I can't tell if it's any faster than your algorithm. At any rate, this is highly dependent on the exact value being converted; it's considerably faster for a number containing lots of zero than for a number containing lots of nines.


; Converts a 24-bit number into a string.
; Created in Mimas, from PLET source code.
; Inputs:
;  - E:HL: 24-bit unsigned number.
;    Well, I think it's EHL. High byte might be a different register.
;  - IX: Ptr to location to write string. Will be 8 bytes long: 7 digits and 1 null.
; Output:
;  - String written to IX, null-terminated.
;    If the input number was greater than 9999999, the first digit will be A-G.
;    Saves on having to do another loop iteration for the case that you have a number greater than 9999999.
;    It's easy enough to properly support 8-digit numbers by adding another iteration.
;    (And removing the A-G code.)
; Destroys:
;  - AF
;  - BC
;  - DE
;  - HL
;  - IX
   ld   a, e
   ld   c, 0F0h
   ld   de, 0BDC0h
   call   .divf
   ld   c, 0FEh
   ld   de, 7960h
   call   .divf
; INC  C
   ld   c, 0FFh
   ld   de, 0D8F0h
   call   .divf
; LD   C,$FF
   ld   de, 0FC18h
   call   .div
   ld   de, 0FF9Ch
   call   .div
   ld   e, 0F6h
   call   .div
   ld   e, d
   ld   b, 2Fh
   inc   b
   add   hl, de
   jr   c, @B
   sbc   hl, de
   ld   (ix + 0), b
   inc   ix
   ld   (ix + 0), 0

   ld   b, 2Fh
   inc   b
   add   hl, de
   adc   a, c
   jr   c, @B
   sbc   hl, de
   sbc   a, c
   ld   c, a
   ld   a, b
   cp   3Ah
   jr   c, @F
   add   a, 7
   ld   b, a
   ld   a, c
   ld   (ix + 0), b
   inc   ix
   ld   (ix + 0), 0
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are GMT - 5 Hours
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum