refactor(io): extract channel I/O from Interpreter into an io package
Interpreter grew a god-object sprawl of nine parallel maps keyed by
channel number (text readers, paths, writers, columns, lookahead,
remainder, binary handles, CON set, COM set) — every file feature had
to touch several of them, and CLOSE had to clean each separately.
New package eu.svjatoslav.crtbasic.io:
- Channel: one holder for all per-channel state (kind, reader, writer,
column, lookahead, remainder, byte handle) — new per-channel facts
become fields, not new maps
- FileHandle: the in-memory BINARY/RANDOM file image (moved out of
Interpreter, accessor-encapsulated)
- DosPaths: DOS path resolution (backslashes, drive letters,
case-insensitive components, drive-root anchoring) + CHDIR/MKDIR
- IoSubsystem: the channel table and all operations — OPEN/CLOSE,
FREEFILE, EOF/LOF/LOC/SEEK, readLine/readItem (INPUT # tokenizer),
readBytes (INPUT$ file form), PRINT # with 14-column zones and
column-255 wrap, NAME, KILL wildcards, SHELL "DIR" listings
- ConsoleOutput/IoErrors bridges keep the interpreter's screen and
ON ERROR semantics without io depending back on it
CP437 helpers consolidate on the existing Cp437 class (stringOf/
bytesOf/charset); Interpreter's private copies are gone. Docs: the
BINARY/RANDOM row no longer claims FIELD (never implemented).
Verification: 93/93 tests green; the full QB45 EXAMPLES suite re-run
through the GameDriver — every deterministic screen pixel-identical
to the pre-refactor captures, CRLF/ENTAB outputs byte-exact, WHEREIS
recursive SHELL-DIR scan pixel-identical.