# Print a number in any base without help

[Courses](https://asm-editor.specy.app/learn/courses.md) › [Z80 assembly](https://asm-editor.specy.app/learn/courses/z80.md) › Examples

Web page: https://asm-editor.specy.app/learn/courses/z80/examples/number-to-string

> The division routine run once per digit, a buffer filled backwards from its end, and the same number printed in base 16, 10 and 2.

The same program in: [M68K](https://asm-editor.specy.app/learn/courses/m68k/examples/number-to-string.md), [MIPS](https://asm-editor.specy.app/learn/courses/mips/examples/number-to-string.md), [RISC-V](https://asm-editor.specy.app/learn/courses/risc-v/examples/number-to-string.md), [x86-64](https://asm-editor.specy.app/learn/courses/x86/examples/number-to-string.md)

This program prints the number 48879 in hexadecimal, decimal, and binary. Open it in the editor,
choose **Build**, then **Run**. The console shows:

```text
BEEF
48879
1011111011101111
```

In Read two numbers and print their sum, a number output port converted a value into decimal
characters. Here the program does the conversion itself. It sends only characters to port `0x10`:
each `out (P_CHAR), a` prints the character whose code is in `a`.

```z80
P_CHAR  equ 0x10

    .org 0x8000
    ld hl, 48879        ; n = 48879
    ld c, 16            ; in hexadecimal
    call print_in_base
    ld hl, 48879
    ld c, 10            ; in decimal
    call print_in_base
    ld hl, 48879
    ld c, 2             ; in binary
    call print_in_base
    halt

; print an unsigned 16-bit number in base 2 to 36: number in hl, base in c
print_in_base:
    ld de, buffer_end
    dec de
    xor a
    ld (de), a          ; put the zero terminator at the end
digit:
    xor a               ; the remainder, which becomes the digit
    ld b, 16            ; sixteen bits of n
divide:
    add hl, hl          ; shift the next dividend bit into the C flag
    rla                 ; bring that bit into the remainder
    cp c
    jr c, no_sub
    sub c               ; the divisor goes in once
    inc l               ; set the new quotient bit to 1
no_sub:
    djnz divide
    cp 10
    jr c, is_digit
    add a, 'A' - 10     ; 10 and up become 'A' and up
    jr store
is_digit:
    add a, '0'          ; 0 to 9 become '0' to '9'
store:
    dec de
    ld (de), a          ; in front of the digits we have already
    ld a, h
    or l                ; is anything left of n?
    jr nz, digit
    ex de, hl           ; the first character of the answer
    call print
    ld a, 10
    out (P_CHAR), a
    ret

; print(p): the zero terminated string at hl
print:
    ld a, (hl)
    or a
    ret z
    out (P_CHAR), a
    inc hl
    jr print

    .org 0x9000
buffer:     .ds 18      ; sixteen binary digits, the terminator and a spare byte
buffer_end:
```

Dividing by the base leaves one digit as the remainder, and it is the **rightmost** digit. For
example, 48879 divided by 16 is 3054 with 15 left over; 15 becomes the `F` at the right end of
`BEEF`. The next division works on 3054 and finds the digit to its left. Rather than reversing the
digits afterwards, the program fills the buffer backwards. It starts with a zero terminator near
`buffer_end`, then `dec de` places each new character in front of those already stored.

The `divide` loop uses the shift and subtract method from Multiply and divide, with the remainder.
Each division takes sixteen passes because `hl` holds a 16-bit number. Think of `hl` as unread
dividend bits followed by quotient bits being built. `add hl, hl` shifts the next dividend bit out
of the top of `hl` into the C flag and leaves a zero at the bottom. `rla` shifts that bit into the
remainder in `a`. If the remainder reaches the base in `c`, `sub c` removes one base and `inc l`
changes the new bottom bit of `hl` from zero to one. Otherwise it stays zero. After sixteen passes,
`hl` holds the quotient and `a` holds the remainder. This division works here because bases 2
through 36 keep the intermediate remainder within one byte.

The remainder becomes a character. Adding `'0'` converts values 0 through 9 to `'0'` through
`'9'`. For 10 through 35, adding `'A' - 10` converts them to `'A'` through `'Z'`; the assembler
calculates that constant. The routine expects a base from 2 to 36 in `c` and does not check it.

After storing a character, `ld a, h` and `or l` check whether the quotient in `hl` is zero: the
result is zero only when both bytes are zero. If it is not, the routine divides that quotient for
the next digit. It runs the division at least once, so an input of zero prints `0`. The code then
passes the completed zero-terminated string to `print` and sends character code 10 for a newline.

The buffer has 18 bytes: the longest answer is sixteen binary digits, followed by its zero
terminator, with one spare byte. In the binary run, the first digit is at `0x9001` and the
terminator is at `0x9011`.

Try changing all three `ld hl, 48879` lines to `ld hl, 31`. Predict the three lines before you
build and run. Hexadecimal uses two digits, but decimal and binary need different lengths; the
same routine handles all three.

<details>
<summary>Check your answer</summary>

The lines are `1F`, `31`, and `11111`. In base 16, 31 divided by 16 leaves 15 (`F`) and a
quotient of 1, so the next digit (`1`) is stored in front of it.

</details>

## Instructions in this Lecture

- Z80: [`ld`](https://asm-editor.specy.app/documentation/z80/instruction/ld.md), [`call`](https://asm-editor.specy.app/documentation/z80/instruction/call.md), [`halt`](https://asm-editor.specy.app/documentation/z80/instruction/halt.md), [`dec`](https://asm-editor.specy.app/documentation/z80/instruction/dec.md), [`xor`](https://asm-editor.specy.app/documentation/z80/instruction/xor.md), [`add`](https://asm-editor.specy.app/documentation/z80/instruction/add.md), [`rla`](https://asm-editor.specy.app/documentation/z80/instruction/rla.md), [`cp`](https://asm-editor.specy.app/documentation/z80/instruction/cp.md), [`jr`](https://asm-editor.specy.app/documentation/z80/instruction/jr.md), [`sub`](https://asm-editor.specy.app/documentation/z80/instruction/sub.md), [`inc`](https://asm-editor.specy.app/documentation/z80/instruction/inc.md), [`djnz`](https://asm-editor.specy.app/documentation/z80/instruction/djnz.md), [`or`](https://asm-editor.specy.app/documentation/z80/instruction/or.md), [`ex`](https://asm-editor.specy.app/documentation/z80/instruction/ex.md), [`out`](https://asm-editor.specy.app/documentation/z80/instruction/out.md), [`ret`](https://asm-editor.specy.app/documentation/z80/instruction/ret.md)

Previous: [Read two numbers and print their sum](https://asm-editor.specy.app/learn/courses/z80/examples/sum-of-two-numbers.md)

Next: [Drawing shapes on the screen](https://asm-editor.specy.app/learn/courses/z80/examples/drawing-on-the-screen.md)
