Display semantics¶
Reference: what a SAA5050 does with the control codes in a row, beyond the colour
and the mosaic bits viewdata/ansi.py already walks. Measured against Beebium’s
Saa5050 (~/Code/beebium/src/core/include/beebium/Saa5050.hpp), the in-repo
oracle, and marked verified against it or inferred where the source does
not settle a point or where a modern HTML render departs from the chip.
Control codes, and when each takes effect¶
A control code occupies a character cell, which shows as a space (or as held graphics, below). The distinction that matters is whether the code changes the display at its own cell or only after it.
Code |
Meaning |
Effect |
|---|---|---|
|
alpha colour (fg) |
set-after |
|
flash |
set-after |
|
steady |
set-after |
|
normal height |
set-after |
|
double height |
set-after |
|
graphics colour (fg) |
set-after |
|
conceal |
set-after |
|
contiguous graphics |
set-after |
|
separated graphics |
set-after |
|
black background |
set-at |
|
new background |
set-at |
|
hold graphics |
set-at |
|
release graphics |
set-after |
Verified. In Saa5050::byte, a control code is processed before the cell’s
output is written, so a code that changes the background (0x1C, 0x1D) colours
its own cell; a foreground or attribute code writes a space in the prevailing
state and applies from the next cell. 0x1E shows the held mosaic in its own
cell; 0x1F still shows it and releases from the next.
Every attribute resets at the start of each row: white on black, alpha,
steady, contiguous, no hold, no conceal (Saa5050::start_of_line). A row is read
left to right and nothing carries from the row above.
Double height¶
0x0D makes the row twice as tall: this character row shows the top halves of
its glyphs, and the row below shows the bottom halves.
Verified (set_raster, process_control_code 0x0D/0x0C, byte): a
0x0D sets a raster shift so each source font row is drawn on two scanlines, and
sets a flag that persists to the next character row, which is displayed at a
raster offset of 20 — its lower half. The flag then clears, so double height
spans exactly two physical rows. On the lower row the chip reads that row’s own
memory and shows its glyphs’ bottom halves; a page that wants a clean
double-height line therefore leaves the row below it blank, or repeats the text.
A 0x0C (or 0x0D) that changes the height blanks its own cell.
Inferred (render departure). A browser cannot show a font’s lower half from a
raster offset. The HTML renderer instead draws the whole glyph at the row it
appears on, scaled 2× from the top (transform: scaleY(2)), and leaves the same
columns on the row below blank so the descending half has room. The result reads
the same; the mechanism is not the chip’s.
Hold graphics¶
Between 0x1E (hold) and 0x1F (release), a control cell shows the last
mosaic drawn on the row instead of a space, in the graphics charset in force
when it was held.
Verified (byte, capture_cell, m_last_graphics_data/m_last_graphics_char):
the last displayed graphics character (a code with bit 5 set, in a graphics
charset, not concealed) is remembered; while hold is on, a control code’s cell
repeats it rather than blanking. An alpha colour code clears the remembered
mosaic (so the held cell after it is blank). Hold is cleared at the start of each
row.
Conceal¶
0x18 hides every following character on the row until a colour code or the row
ends.
Verified (process_control_code 0x18; the colour cases clear m_conceal):
concealed characters are output as blank. Any alpha or graphics colour code
(0x00–0x07, 0x11–0x17) reveals the rest of the row. Conceal is cleared at
the start of each row.
Flash¶
0x08 flashes the following characters; 0x09 stops it.
Verified (0x08/0x09, m_frame_flash_visible, vsync): flashing text is
shown or hidden by a frame counter that cycles roughly once a second (hidden for
16 of every 64 fields, shown for the other 48). A static render shows a flashing
run steady; an HTML render animates it and honours prefers-reduced-motion.
Separated graphics¶
0x1A selects separated mosaics, 0x19 contiguous. A separated mosaic is the
same 2×3 block pattern with a gap on the left and bottom of each block.
Verified (get_graphics_row): separated graphics blank the left column of
each half (columns 0 and 3 of the six) and the bottom row of each block (font
rows 2, 6 and 9), leaving the blocks unjoined. Contiguous mosaics fill the cell.
The mosaic bit layout is bit0=top-left, bit1=top-right, bit2=middle-left,
bit3=middle-right, bit4=bottom-left, bit6=bottom-right — bit5 is
skipped, being the bit that distinguishes the alphanumeric range 0x40–0x5F
from the mosaics. This is the same layout Bedstead’s Private Use mosaics use (see
the spike report).
What viewdata/ansi.py does not yet implement¶
The current walk tracks foreground colour, background (as set-after, not set-at),
the graphics-on flag and contiguous mosaics. It does not implement: double height
(0x0D), hold graphics (0x1E/0x1F), conceal (0x18), flash (0x08), the
separated charset (0x1A, drawn as contiguous), or set-at backgrounds. These are
the semantics the display walk gains when it moves to viewdata/display.py.