1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
#!/usr/bin/env python3
# Generate a 4x8 font as RGBDS asm. Glyphs are up to 3px wide (in a 4px cell,
# 1px right gap) and use the full 8px height with a real baseline at row 5:
# caps/ascenders rows 1..5 x-height (lowercase) rows 2..5
# descenders reach rows 6..7 row 0 = top gap / line spacing
# Output: 96 glyphs (ASCII 32..127), 8 bytes each; each byte = one 4-px row,
# bit3 = leftmost pixel of the cell, bits 3..1 = the glyph, bit0 = gap.
G = {}
def d(ch, top, rows): # rows: list of 3-char strings ('#'=on)
G[ch] = (top, rows)
# ---- uppercase (top=1, 5 tall) ----
CAPS = {
'A':["##.","#.#","###","#.#","#.#"], 'B':["##.","#.#","##.","#.#","##."],
'C':[".##","#..","#..","#..",".##"], 'D':["##.","#.#","#.#","#.#","##."],
'E':["###","#..","##.","#..","###"], 'F':["###","#..","##.","#..","#.."],
'G':[".##","#..","#.#","#.#",".##"], 'H':["#.#","#.#","###","#.#","#.#"],
'I':["###",".#.",".#.",".#.","###"], 'J':["..#","..#","..#","#.#",".#."],
'K':["#.#","#.#","##.","#.#","#.#"], 'L':["#..","#..","#..","#..","###"],
'M':["#.#","###","###","#.#","#.#"], 'N':["#.#","###","###","###","#.#"],
'O':[".#.","#.#","#.#","#.#",".#."], 'P':["##.","#.#","##.","#..","#.."],
'Q':[".#.","#.#","#.#",".#.","..#"], 'R':["##.","#.#","##.","#.#","#.#"],
'S':[".##","#..",".#.","..#","##."], 'T':["###",".#.",".#.",".#.",".#."],
'U':["#.#","#.#","#.#","#.#",".##"], 'V':["#.#","#.#","#.#",".#.",".#."],
'W':["#.#","#.#","###","###","#.#"], 'X':["#.#","#.#",".#.","#.#","#.#"],
'Y':["#.#","#.#",".#.",".#.",".#."], 'Z':["###","..#",".#.","#..","###"],
}
for c,r in CAPS.items(): d(c,1,r)
# ---- digits (top=1, 5 tall) ----
DIG = {
'0':[".#.","#.#","#.#","#.#",".#."], '1':[".#.","##.",".#.",".#.","###"],
'2':["##.","..#",".#.","#..","###"], '3':["##.","..#",".#.","..#","##."],
'4':["#.#","#.#","###","..#","..#"], '5':["###","#..","##.","..#","##."],
'6':[".##","#..","##.","#.#",".#."], '7':["###","..#",".#.",".#.",".#."],
'8':[".#.","#.#",".#.","#.#",".#."], '9':[".#.","#.#",".##","..#","##."],
}
for c,r in DIG.items(): d(c,1,r)
# ---- lowercase: x-height rows 2..5, baseline row 5 ----
# plain x-height (top=2, 4 tall)
XH = {
'a':["##.",".##","#.#",".##"], 'c':[".##","#..","#..",".##"],
'e':[".#.","#.#","##.",".##"], 'm':["#.#","###","###","#.#"],
'n':["##.","#.#","#.#","#.#"], 'o':[".#.","#.#","#.#",".#."],
'r':["#.#","##.","#..","#.."], 's':[".##","##.",".##","##."],
'u':["#.#","#.#","#.#",".##"], 'v':["#.#","#.#","#.#",".#."],
'w':["#.#","###","###","#.#"], 'x':["#.#",".#.",".#.","#.#"],
'z':["###","..#",".#.","###"],
}
for c,r in XH.items(): d(c,2,r)
# ascenders (top=1, 5 tall)
ASC = {
'b':["#..","#..","##.","#.#","##."], 'd':["..#","..#",".##","#.#",".##"],
'f':[".##",".#.","###",".#.",".#."], 'h':["#..","#..","##.","#.#","#.#"],
'k':["#..","#.#","##.","#.#","#.#"], 'l':["##.",".#.",".#.",".#.",".##"],
't':[".#.","###",".#.",".#.",".##"], 'i':[".#.","...",".#.",".#.",".#."],
}
for c,r in ASC.items(): d(c,1,r)
# descenders (top=2, body 4 + descender -> rows 2..7)
DESC = {
'g':[".##","#.#",".##","..#","##."], 'p':["##.","#.#","##.","#..","#.."],
'q':[".##","#.#",".##","..#","..#"], 'y':["#.#","#.#",".##","..#","##."],
'j':["..#","...","..#","..#","##."],
}
for c,r in DESC.items(): d(c,2,r)
# ---- punctuation ----
P = {
'.':(4,[".#."]), ',':(4,[".#.","#.."]), ':':(2,[".#.","...",".#."]),
';':(2,[".#.","...",".#.","#.."]), '!':(1,[".#.",".#.",".#.","...",".#."]),
'?':(1,["##.","..#",".#.","...",".#."]), '-':(3,["###"]),
'+':(2,[".#.","###",".#."]), '=':(2,["###","...","###"]),
'*':(1,["#.#",".#.","###",".#.","#.#"]), '/':(1,["..#","..#",".#.","#..","#.."]),
'\\':(1,["#..","#..",".#.","..#","..#"]), '(':(1,["..#",".#.",".#.",".#.","..#"]),
')':(1,["#..",".#.",".#.",".#.","#.."]), '[':(1,["##.","#..","#..","#..","##."]),
']':(1,[".##","..#","..#","..#",".##"]), '<':(1,["..#",".#.","#..",".#.","..#"]),
'>':(1,["#..",".#.","..#",".#.","#.."]), '#':(1,["#.#","###","#.#","###","#.#"]),
'$':(1,[".##","#.#",".#.","#.#","##."]), '%':(1,["#.#","..#",".#.","#..","#.#"]),
'&':(1,[".#.","#.#",".#.","#.#",".##"]), "'":(1,[".#.",".#."]),
'"':(1,["#.#","#.#"]), '@':(1,[".#.","#.#","###","#..",".##"]),
'_':(7,["###"]), '~':(3,[".##","##."]), '^':(1,[".#.","#.#"]),
'|':(1,[".#.",".#.",".#.",".#.",".#."]), '`':(1,["#..",".#."]),
'{':(1,["..#",".#.","##.",".#.","..#"]), '}':(1,["#..",".#.",".##",".#.","#.."]),
' ':(0,[]),
}
for c,(t,r) in P.items(): d(c,t,r)
def bits(s):
v=0
for i,px in enumerate(s[:3]):
if px=='#': v |= (1 << (3-i))
return v
def glyph_bytes(top, rows):
out=[0]*8
for k,s in enumerate(rows):
r = top + k
if 0 <= r < 8: out[r] = bits(s)
return out
lines=["; auto-generated by tools/genfont.py - 4x8 mixed-case font, 96 glyphs",
"SECTION \"font\", ROM0", "FontData::"]
missing=[]
for code in range(32,128):
ch = chr(code)
if ch in G:
top,rows = G[ch]
else:
top,rows = 1,["###","#.#","#.#","#.#","###"] # box for undefined
if code!=127: missing.append(ch)
b = glyph_bytes(top,rows)
label = ch if ch not in '\\' else '\\\\'
lines.append(" db " + ",".join("$%02X"%x for x in b) + " ; '%s'"%(label if 32<code<127 else ('SP' if code==32 else 'DEL')))
open("/home/user/dev/gbos/src/font.asm","w").write("\n".join(lines)+"\n")
print("wrote src/font.asm; missing glyphs:", "".join(missing) or "(none)")
|