miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
Loading...
Searching...
No Matches
TTY and VT Terminal Stack in miniOS

miniOS includes a layered terminal stack: a VT/ANSI parser with two rendering backends (VGA text mode and GOP linear framebuffer), a POSIX-like line discipline, and a VFS character device node at /dev/tty.

Architecture

Userspace (read/write/ioctl on /dev/tty)
|
VFS layer (VFS_DEVICE_TTY dispatch)
|
TTY line discipline (tty.c)
/ \
tty_read() tty_write()
[input buf] [vt_write_byte()]
| |
keyboard ISR VT core (vt.c)
tty_keyboard_input() [UTF-8 decoder + escape parser]
|
┌───────────┴───────────┐
GOP backend VGA backend
(pixel blit, 32bpp) (0xFFFF8000000B8000)
1280×800 / 8×16 font 80×25, CP437, attr byte
160×50 character grid

When the kernel is compiled with -DCONSOLE_GOP (default) and GRUB provides a Multiboot2 framebuffer tag, gop_init_from_mb2() in main.c maps the physical framebuffer and calls vt_init_gop(). All subsequent output goes through the pixel backend. Without a Multiboot2 framebuffer the VGA text-mode backend is used as a fallback.

VT Core

vt_init() initialises the ANSI escape parser and the active renderer. All output passes through vt_write_byte(c) or vt_write(buf, len).

UTF-8 Decoding

The VT core contains a full UTF-8 state machine in vt_handle_normal_char(). Multi-byte sequences (2, 3, or 4 bytes) are accumulated and decoded to a Unicode codepoint. On an invalid continuation byte the partial sequence is flushed as individual Latin-1 bytes before retrying.

In GOP mode the codepoint is looked up in gop_cp_map[] (a sorted 557-entry table, binary-searchable) derived from the LatArCyrHeb-16 PSF2 font. In VGA mode codepoints in the range U+0080–U+00FF are mapped to their nearest CP437 equivalent via a lookup table.

The keyboard driver emits Latin-1 characters (e.g. ä, ö, ü, ß) as 2-byte UTF-8 before calling tty_keyboard_input(). Backspace in canonical mode strips the entire UTF-8 character (all trailing continuation bytes plus the lead byte).

Font (GOP mode)

GOP mode uses the LatArCyrHeb-16 PSF2 font (8×16 pixels, 512 glyphs). The font header is auto-generated by scripts/gen_font.py from the system font at /usr/share/kbd/consolefonts/LatArCyrHeb-16.psfu.gz and written to include/miniOS/drivers/gop_font.h. Run make to regenerate if the font source changes.

Key codepoints: ASCII U+0020–U+007E identity-mapped to glyphs 32–126; Latin-1 (ä=U+00E4, ö=U+00F6, ü=U+00FC) present; em dash (=U+2014) at glyph 489.

Blinking Cursor (GOP mode)

A 2-scanline underline cursor is drawn over the current cell in the GOP framebuffer. It blinks at 5 Hz (100 ms on / 100 ms off):

  • vt_cursor_tick() is called from the LAPIC timer ISR at 100 Hz, on the BSP only (cpu_id == 0).
  • Every 10 ticks the cursor state toggles and gop_draw_cursor() or gop_erase_cursor() is called under spinlock_irqsave.
  • gop_erase_cursor() restores the cell by redrawing the glyph from the backing store (gop_cell_glyph[][] + gop_cell_attr[][]), avoiding stale pixel artifacts after scroll or clear operations.
  • vt_update_cursor_hw() erases and redraws the cursor after every character write so the cursor tracks the insertion point correctly.

In VGA mode the hardware cursor is driven via VGA CRT registers (no blink logic in software).

Backing Store (GOP mode)

A static 160×50 array of uint16_t gop_cell_glyph[][] and uint8_t gop_cell_attr[][] mirrors every cell on screen. All write, clear, and scroll operations update both the pixel framebuffer and the backing store atomically under vt_fb_lock. This is required so gop_erase_cursor() can restore cell contents correctly.

Thread Safety

vt_fb_lock (a spinlock_t initialised with SPINLOCK_INIT) protects all framebuffer and backing-store writes. It is acquired with spinlock_irqsave so that the LAPIC timer ISR (which calls vt_cursor_tick()) cannot race with kernel writers on the same or another CPU.

Supported Escape Sequences

Sequence Effect
ESC[H Move cursor to (1,1)
ESC[r;cH Move cursor to row r, column c
ESC[A/B/C/D Cursor up / down / right / left
ESC[2J Erase entire screen
ESC[K Erase to end of line
ESC[m Reset SGR attributes (foreground + background)
ESC[Nm Set foreground color N (30-37, 90-97)
ESC[N;5;Mm 256-color foreground (mapped to nearest VGA 16-color)
ESC[?25h/l Show / hide cursor

Color Mapping

256-color SGR requests (38;5;N) map to the nearest 16-color palette entry using squared Euclidean distance in RGB space. In GOP mode the 16-color palette is precomputed as 32bpp pixel values (respecting the framebuffer's red/green/blue bit positions) by vt_init_gop(). In VGA mode the mapping produces a 4-bit VGA attribute.

Window Size

vt_get_winsize() returns the active grid dimensions: 160×50 in GOP mode (1280×800 / 8×16 font), 80×25 in VGA text mode. TIOCGWINSZ returns these values.

TTY Line Discipline

The line discipline is the policy layer that decides how input characters from the keyboard are buffered and delivered to userspace read() calls.

Canonical Mode (default)

  • Input is buffered in a line buffer until \n (newline) or VEOF (^D).
  • Backspace removes the last UTF-8 character from the line buffer and echoes \b \b.
  • ^C (VINTR) raises SIGKILL to the current process (simplified SIGINT).
  • A completed line is moved to the read ring buffer; tty_read() returns it.

Raw Mode

  • Each character is immediately placed in the read ring buffer.
  • No line editing, no echo applied by the TTY (application manages echo).
  • Activated by clearing ICANON via TCSETS.

Echo

When ECHO is set in c_lflag, each input character from tty_keyboard_input() is forwarded to vt_write_byte() so it appears on screen.

termios and ioctls

The kernel termios structure uses Linux-compatible field layout:

uint32_t c_iflag; /* input flags */
uint32_t c_oflag; /* output flags */
uint32_t c_cflag; /* control flags */
uint32_t c_lflag; /* local flags: ICANON=0x2, ECHO=0x8, ISIG=0x1 */
uint8_t c_cc[8]; /* control characters: c_cc[VINTR]=0, c_cc[VEOF]=1 */
};
Definition tty.h:58
uint32_t c_oflag
Definition tty.h:60
uint8_t c_cc[TTY_NCCS]
Definition tty.h:63
uint32_t c_lflag
Definition tty.h:62
uint32_t c_cflag
Definition tty.h:61
uint32_t c_iflag
Definition tty.h:59
unsigned int uint32_t
Definition types.h:34
unsigned char uint8_t
Definition types.h:28

Supported ioctls:

Command Value Action
TCGETS 0x5401 Copy kernel termios to user pointer
TCSETS 0x5402 Copy user termios to kernel state
TCSETSW 0x5403 Same as TCSETS (drain not implemented)
TIOCGWINSZ 0x5413 Copy {ws_row, ws_col} to user pointer (dynamic; 160×50 GOP, 80×25 VGA)
TIOCSCTTY 0x540E Accept (no-op in single-TTY kernel)
TIOCNOTTY 0x5422 Accept (no-op)

Integration with the Shell

The shell performs the following at startup:

  1. Calls isatty(STDIN_FILENO) — routed to tty_isatty_fd(0), returns 1.
  2. Sets TERM=xterm-256color in the environment (so programs can use color).
  3. Issues TCGETS to save the current termios, then TCSETSW to enter raw mode for its own line editing loop.
  4. On exit, restores the saved termios via TCSETSW.