00000000: 2b2b 7036 092e 452c 453b 3b78 6f2c 4523 ++p6..E,E;;xo,E#
00000010: 2323 2323 2323 2323 2323 2323 2323 2329 ###############)
00000020: 2929 2929 2929 2929 307e 7620 2122 2425 ))))))))0~v !"$%
00000030: 2627 282a 2d2f 3132 3334 3537 3839 3a3c &'(*-/12345789:<
00000040: 3d3e 3f40 4142 4344 7172 7374 7577 797a =>?@ABCDqrstuwyz
00000050: 7b7c 7d {|}
Try it online!
Printable characters aren't too useful in z80, but we can write this:
dec hl
dec hl
ld (hl),b
ld (hl),9
ld l,'E'
inc l
ld b,l
dec sp ; $0009 starts here
dec sp
ld a,b
ld l,a
inc l
ld b,l
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
inc hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
jr nc, $+2+'~'
halt
db " !\"$%&'(*-/12345789:<=>?@ABCDqrstuwyz{|}"
The idea is to print characters from 'F'
to 'n'
, and put all the remaining characters in the code (the "garbage" string at the end).
The preamble writes 09
(tab) to $fffe
, so that we can point SP there to jump to $0009
after putchar. The ld (hl), b
achieves nothing; I just get rid of 'p'
there.
L
is initialized to 'E'+1
, and counts up until (HL+17) << 9
overflows (sets the carry), which is when L
is 111 = 'o'
, the first character we don't want to print.
If the carry is not set, we jump over a halt instruction and the garbage string, into thousands of NOPs, the PC eventually running into putchar = $8000
and returning to $0009
.
See this answer for some more info on how this code prints characters in the first place.
0x20
to0x7e
, which are defined as the "printable ASCII characters". Technically tabs and newlines are actually control characters. \$\endgroup\$