diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/gencolor.py | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/tools/gencolor.py b/tools/gencolor.py new file mode 100644 index 0000000..711fa78 --- /dev/null +++ b/tools/gencolor.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# Generate src/color.asm: CGB palette data + the lookup tables the terminal +# uses to render per-glyph FOREGROUND *and* BACKGROUND color. +# +# The 7 colors are the points of a FANO PLANE (Steiner triple system S(2,3,7)): +# 7 palettes = 7 lines, and every PAIR of colors lies on exactly one line, so a +# tile that needs at most two distinct (non-black) colors always has a palette +# holding both. slot0 of every palette is black, so a black background costs no +# color id; a colored background is drawn by giving the cell's empty pixels the +# background color's slot instead of slot0. +# +# Because a tile packs two independent cells (each with fg+bg) it can need up to +# 3 non-black colors. Every color pair is covered exactly; a 3-color tile is +# covered only if that triple happens to be a line - otherwise BestPal picks the +# line covering the most of them and the odd one out is approximated. +# +# Color index: 0=black(bg/none) 1=red 2=green 3=yellow 4=blue 5=magenta 6=cyan +# 7=white. BG palette 1 is reserved for the on-screen keyboard. + +def gbc15(r, g, b): + return r | (g << 5) | (b << 10) + +COLOR_RGB = { + 1: (31, 0, 0), # red + 2: ( 0, 31, 0), # green + 3: (31, 31, 0), # yellow + 4: ( 0, 0, 31), # blue + 5: (31, 0, 31), # magenta + 6: ( 0, 31, 31), # cyan + 7: (31, 31, 31), # white +} + +# Hardware BG palette -> its 3 colors in slot1,slot2,slot3 order (slot0=black). +# The 7 triples form a Fano plane over colors 1..7. Palette 0 keeps white at +# slot1 (the OSK and plain white text draw with color-id 1). +PALETTES = { + 0: [7, 1, 6], + 2: [1, 2, 3], + 3: [1, 4, 5], + 4: [2, 4, 6], + 5: [2, 5, 7], + 6: [3, 4, 7], + 7: [3, 5, 6], +} +PAL_ORDER = [0, 2, 3, 4, 5, 6, 7] + +# --- verify Fano: every pair of 1..7 lies on exactly one line ------------------ +seen = {} +for pal, line in PALETTES.items(): + assert len(set(line)) == 3 + for i in range(3): + for j in range(i + 1, 3): + k = frozenset((line[i], line[j])) + assert k not in seen, ("pair on two lines", k) + seen[k] = pal +assert set(seen) == {frozenset((a, b)) for a in range(1, 8) + for b in range(1, 8) if a != b} + +def slot_of(pal, color): + """id (0..3) of color in palette pal. black->0; absent->1 (visible fallback).""" + if color == 0: + return 0 + line = PALETTES.get(pal, []) + return (line.index(color) + 1) if color in line else 1 + +def best_pal(mask): + """Palette covering the most colors present in the 7-bit set 'mask'.""" + if mask == 0: + return 0 + best, bestn = 0, -1 + for pal in PAL_ORDER: + n = sum(1 for c in PALETTES[pal] if mask & (1 << (c - 1))) + if n > bestn: + best, bestn = pal, n + return best + +# --- emit ---------------------------------------------------------------------- +def pal_colors(hw): + if hw == 1: # OSK: white bg, black ink (inverse) + return [(31,31,31), (0,0,0), (0,0,0), (0,0,0)] + return [(0,0,0)] + [COLOR_RGB[c] for c in PALETTES[hw]] + +L = [] +L.append("; auto-generated by tools/gencolor.py - Fano-plane color tables") +L.append("; (foreground + background). DO NOT EDIT.") +L.append('SECTION "color", ROM0') +L.append("") +L.append("; 8 CGB BG palettes, 4 colors each (c0=black). Loaded at BCPS $80.") +L.append("PalData::") +for hw in range(8): + words = [] + for (r, g, b) in pal_colors(hw): + v = gbc15(r, g, b) + words.append("$%02X,$%02X" % (v & 0xFF, (v >> 8) & 0xFF)) + L.append(" db " + ",".join(words) + " ; palette %d" % hw) +L.append("") +L.append("; BitTab[color] = bit for that color in the 'colors present' set (black=0)") +L.append("BitTab::") +L.append(" db " + ",".join("$%02X" % (0 if c == 0 else (1 << (c - 1))) for c in range(8))) +L.append("") +L.append("; BestPal[set] = palette (0..7) covering the most colors in a 7-bit set") +L.append("BestPal::") +for base in range(0, 128, 16): + L.append(" db " + ",".join("$%02X" % best_pal(base + j) for j in range(16)) + + " ; %d..%d" % (base, base + 15)) +L.append("") +L.append("; SlotOf[palette*8 + color] = that color's slot/id (0..3) in the palette") +L.append("SlotOf::") +for pal in range(8): + L.append(" db " + ",".join("$%02X" % slot_of(pal, c) for c in range(8)) + + " ; palette %d" % pal) +open("/home/user/dev/gbos/src/color.asm", "w").write("\n".join(L) + "\n") +print("wrote src/color.asm") |
