Terminal (libterm)
libterm is the terminal layer that backs /dev/tty in
BDOS. It owns all terminal state — the on-screen cell grid,
cursor, palettes, scrollback ring, alternate-screen save area, and the
ANSI escape parser. Everything else (the shell, user programs, libc
printf) talks to it indirectly by writing bytes into fd 1.
It lives in two files:
Software/C/libfpgc/term/term.c(~980 LOC) — implementation.Software/C/libfpgc/include/term.h— public API.
The legacy term.c shim was deleted in Phase E; all callers now use the
term_* symbols directly.
Architecture
+----------------------+ +-------------------------+
| user program | ---> | sys_write(1, buf, len) |
+----------------------+ +-------------------------+
|
v
+-------------------------+
| BDOS VFS /dev/tty |
+-------------------------+
|
v
+-------------------------+
| libterm parser |
| (term_write) |
+-------------------------+
| |
v v
+---------------+ +-------------+
| tile renderer | | UART mirror |
| (gpu_write_ | | (uart_putc) |
| window_tile) | +-------------+
+---------------+
term_init() is called once during kernel_init (Software/C/kernel/src/init.c)
with two callbacks: a tile renderer (currently gpu_write_window_tile) and
a UART mirror (currently uart_putchar). Disabling the UART mirror at
runtime is a single call to term_set_uart_mirror(0).
ANSI escape support
libterm implements a useful subset of VT100/xterm escapes — enough for ncurses-style fullscreen apps and conventional terminal output:
CSI sequences (ESC [ ...)
| Sequence | Name | Behaviour |
|---|---|---|
[ Pn A / B / C / D |
CUU/CUD/CUF/CUB | Cursor up / down / forward / back (Pn defaults to 1) |
[ Pl ; Pc H / f |
CUP / HVP | Cursor position (1-based row;col) |
[ Pn G |
CHA | Cursor horizontal absolute |
[ Pn d |
VPA | Cursor vertical absolute |
[ Pn J |
ED | Erase display (0 = cursor→end, 1 = start→cursor, 2 = whole screen) |
[ Pn K |
EL | Erase in line (same 0/1/2 semantics) |
[ Pn ; Pn r |
DECSTBM | Set top/bottom scroll region |
[ Pn S / T |
SU / SD | Scroll up / down within the region |
[ Pn m |
SGR | Palette / attribute select (see below) |
[ s / [ u |
SCP / RCP | Save / restore cursor + palette |
[ ? 25 h / l |
DECTCEM | Show / hide cursor |
[ ? 7 h / l |
DECAWM | Auto-wrap on (default) / off. With wrap off, writing to the last column clamps the cursor there instead of advancing — useful for full-screen apps that draw to the bottom-right cell without triggering a scroll. |
[ ? 1049 h / l |
xterm alt screen | Push / pop the entire screen + cursor |
Single-byte controls
\b, \t (tab width = 4), \r, \n are honoured. Tabs round up to the
next multiple of 4 columns. \n is a true line feed (no implicit CR).
SGR palette mapping
The FPGC has a tile-palette per cell rather than separate fg/bg attributes. SGR is mapped pragmatically:
\x1b[0mresets the palette to 0.\x1b[30m..\x1b[37mselects palette indices 0..7 in the low nibble (foreground-style colors).\x1b[40m..\x1b[47mwrites 0..7 into the high nibble (background-style colors).\x1b[1m(bold) sets the0x08bit in the low nibble — useful as a "bright" toggle when combined with the above.
If you need exact control of the underlying palette, draw with
term_put_cell(x, y, tile, palette) directly inside the kernel — there
is no userland syscall for raw palette writes any more (one of the
removals in Phase E).
Public API (selected)
void term_init(int width, int height,
term_render_cell_fn render_cell,
term_uart_mirror_fn uart_mirror);
/* High-level character / string output. Goes through the parser. */
void term_putchar(char c);
void term_puts (const char *s);
void term_write (const char *buf, int len);
/* Convenience formatters used by BDOS panic / boot output. */
void term_putint (int v);
void term_puthex (unsigned int v, int prefix); /* hex, with optional "0x" prefix */
/* Direct cell access (bypasses the parser; for kernel use). */
void term_put_cell(int x, int y, unsigned char tile, unsigned char palette);
void term_get_cell(int x, int y, unsigned char *tile, unsigned char *palette);
/* Cursor + display state. */
void term_set_cursor(int x, int y);
void term_get_cursor(int *x, int *y);
void term_set_cursor_visible(int visible);
int term_get_cursor_visible(void);
void term_clear(void);
void term_clear_to_eol(void);
void term_clear_to_eos(void);
void term_get_size(int *w, int *h);
/* Scroll region + scrolling. */
void term_set_scroll_region(int top, int bot);
void term_scroll_up(int n);
void term_scroll_down(int n);
/* Alternate screen. */
void term_alt_enter(void);
void term_alt_leave(void);
int term_in_alt_screen(void);
/* Scrollback view. */
int term_scroll_view_up(void); /* returns 1 if scrolled, 0 if at top */
int term_scroll_view_down(void); /* returns 1 if scrolled, 0 if at bottom */
int term_is_scrolled_back(void);
void term_snap_to_bottom(void);
/* Repaint entire screen via render_cell callback. */
void term_repaint(void);
/* Misc. */
void term_set_palette (unsigned char palette);
unsigned char term_get_palette(void);
void term_set_uart_mirror (int enabled);
int term_get_uart_mirror (void);
/* Input / line discipline. */
void term_set_input_source(term_input_pop_fn pop);
void term_set_cooked(int cooked);
int term_get_cooked(void);
void term_set_echo(int echo);
int term_get_echo(void);
int term_read(char *buf, int max, int blocking);
TERM_WIDTH, TERM_HEIGHT, TERM_HISTORY_LINES, and TAB_WIDTH are
provided as #defines in term.h for code that wants compile-time
constants.
Raw input — /dev/tty event packets
For games and other programs that need single-key responsiveness rather
than line-buffered input, open /dev/tty with the O_RAW flag (and
typically O_NONBLOCK too):
int fd = sys_tty_open_raw(1 /* nonblocking */);
unsigned char buf[4];
while (sys_read(fd, buf, 4) == 4) {
int code = buf[0]
| (buf[1] << 8)
| (buf[2] << 16)
| (buf[3] << 24);
/* code is ASCII for printable keys, or KEY_UP / KEY_F1 / ... for
special keys; see <syscall.h> for the constants. */
}
Software/C/userBDOS/snake.c is the in-tree reference port for this API.
In cooked mode (no O_RAW), reads from /dev/tty block until a full line
has been entered and return that line as bytes — exactly what the shell
line editor and sys_read(0, ...) users expect.