Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
67 lines
3.5 KiB
Markdown
67 lines
3.5 KiB
Markdown
---
|
|
name: schunix-console-tty
|
|
description: "The console has an ANSI parser, real termios and UTF-8; the font is CP437 not Latin-1, and five bugs that looked like broken commands"
|
|
metadata:
|
|
node_type: memory
|
|
type: project
|
|
originSessionId: a665e14b-86bd-4b41-a228-51377f3e9225
|
|
modified: 2026-08-04T21:10:44.835Z
|
|
---
|
|
|
|
**`kernel/core/console.c` was rewritten on 2026-08-04**: ANSI escape parser,
|
|
real termios, signals, and UTF-8.
|
|
|
|
**The serial line and the framebuffer are treated differently on purpose.**
|
|
Escapes are passed through to serial verbatim -- a real terminal emulator is
|
|
on the other end and understands them better than we ever will -- and
|
|
*interpreted* only for the framebuffer. So a serial transcript can never tell
|
|
you whether the escape parser works; it holds the raw bytes either way. Use
|
|
`pwsh -File tools/ttyshot.ps1`, which drives colour/cursor/erase/UTF-8 and
|
|
screendumps the result.
|
|
|
|
**The font is CP437, not Latin-1.** `include/gfx/font.h`'s per-glyph comments
|
|
label index N with Unicode codepoint N, which is fiction above 127: index 0xE9
|
|
is annotated "e-acute" and draws a capital theta. Trusting it turned "café"
|
|
into "caf" + theta. `console.c` maps Unicode through `latin1_to_cp437[]` plus
|
|
a small table of box-drawing characters. **Verify glyphs by rendering the
|
|
bitmap, never by reading the comment.**
|
|
|
|
`font_data` was also declared `[128][16]` while containing 256 glyphs, so the
|
|
compiler silently discarded the entire upper half -- the wall of "excess
|
|
elements in array initializer" warnings was that.
|
|
|
|
**Five bugs that all presented as broken commands, none of which were:**
|
|
|
|
- `ls /bin | wc -l` said 48 instead of 240 -- `sys_ioctl` answered *every*
|
|
unrecognised request with 0, so `TIOCGWINSZ` on a pipe "succeeded" and every
|
|
program thought pipes were terminals. It returns `-ENOTTY` now. isatty() is
|
|
built on this call, so the blast radius was everything.
|
|
- `yes | head -2` failed with "Invalid argument" -- `readv`/`writev` capped
|
|
iovcnt at 16 (POSIX's minimum) and toybox's `yes` builds 256. Now
|
|
`UIO_MAXIOV` = 1024, Linux's value.
|
|
- `cat big | head -c 4` killed the shell at `rip 0x1` -- **SIG_IGN is handler
|
|
value 1**, and `deliver_signals()` only skipped 0 (SIG_DFL), so it jumped to
|
|
address 1. Nothing sent SIGPIPE until pipes learned to, at which point every
|
|
shell that ignores SIGPIPE died.
|
|
- Pipes returned a bare `-1` with no signal on a closed read end; now SIGPIPE
|
|
plus `-EPIPE`.
|
|
- Ctrl-C did `proc->killed = 1`, and `proc` in an interrupt handler is
|
|
whatever was running on that CPU -- almost never the foreground job. It
|
|
sends a real SIGINT to `tty_fg_pgrp` now.
|
|
|
|
**Control characters must never reach the glyph path.** `vga_putc` had no tab
|
|
case, so `'\t'` fell through to `vga_draw_char(9)` -- CP437 glyph 9 is a
|
|
hollow circle, so pressing Tab printed a circle. The font has a picture for
|
|
every control code and none of them is what the character means. Tab now
|
|
advances to the next eight-column stop (erasing as it goes, so it overwrites
|
|
rather than skipping); everything else below 0x20 is dropped.
|
|
|
|
termios is real: TCGETS returns what was last set, TCSETS is honoured, and the
|
|
console implements ICANON, ECHO, ECHOE, ECHONL, ISIG, ICRNL, INLCR, IGNCR,
|
|
ISTRIP and VMIN. Raw mode returns bytes as they arrive rather than waiting for
|
|
a newline -- that is what zsh and any line editor need. EOF is a flag, not a
|
|
`^D` byte stuffed into the buffer.
|
|
|
|
Check with `pwsh -File tools/bootcheck.ps1 -Tty` (six assertions, including a
|
|
real Ctrl-C against a running `sleep`). See
|
|
[[schunix-verify-by-driving-shell]] and [[schunix-tty-fg-pgrp-coupling]].
|