Files
gw-basic-2026/docs/architecture.md
T
Eremey Valetov e7f35c21ff Implement binary SAVE/LOAD, INKEY$ extended keys, golden tests, update to v0.10.0
Binary SAVE/LOAD: SAVE now writes tokenized binary by default (0xFF header
format), matching original GW-BASIC behavior. SAVE "file",A for ASCII.
LOAD auto-detects binary vs ASCII from the first byte. Command-line file
loading also auto-detects, so binary .BAS files just work.

INKEY$ extended keys: arrow keys, Home/End/PgUp/PgDn, Insert/Delete, and
F1-F10 now return the correct CHR$(0) + scan_code two-byte sequences per
the IBM PC convention. Refactored event trap key parsing to use tui_read_key()
instead of duplicating escape sequence parsing.

Golden-file regression tests: generated .expected output files for 55 of 58
test programs (3 timing-dependent tests excluded). The test runner now
reports compat match status alongside pass/fail.

Classic programs: added Hamurabi, Lunar Lander, Gunner, and Diamond from
David Ahl's BASIC Computer Games (1978) in tests/classic/ for manual
compatibility testing.

Docs updated with compiler roadmap item and hardware I/O simulator plan.
2026-03-01 12:25:47 -05:00

103 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture
## Pipeline
```
Source text → Tokenizer (CRUNCH) → Token stream
Expression evaluator (FRMEVL)
Statement dispatcher (NEWSTT)
TUI screen buffer (interactive)
HAL (platform I/O)
```
The interpreter mirrors the original GW-BASIC's internal pipeline, which — like
most Microsoft interpreters of the era — is a tight loop around three core
routines. CRUNCH tokenizes source lines into a compact byte stream. NEWSTT
dispatches each statement. FRMEVL evaluates expressions. All platform I/O
goes through a HAL vtable (`hal_ops_t`), so the core interpreter has no idea
whether it's talking to an ANSI terminal or a teletype from 1975.
In interactive mode, the TUI layer swaps in its own HAL function pointers and
redirects all output through a dynamically allocated screen buffer, displayed
via ANSI escape sequences. In piped mode the TUI stays out of the way and the
HAL writes straight to stdout.
## Module Map
| Module | Source | Original Assembly |
|--------|--------|--------------------|
| Tokenizer (CRUNCH/LIST) | `tokenizer.c` | GWMAIN.ASM |
| Expression evaluator | `eval.c` | GWEVAL.ASM |
| Execution loop + control flow | `interp.c` | BINTRP.ASM |
| TUI screen editor | `tui.c` | — |
| Graphics engine | `graphics.c` | — |
| Token/keyword tables | `tokens.c`, `tokens.h` | IBMRES.ASM |
| Error handling | `error.c` | GWDATA.ASM |
| Integer arithmetic | `math_int.c` | MATH1.ASM |
| Float ops + MBF conversion | `math_float.c` | MATH2.ASM |
| Transcendentals | `math_transcend.c` | MATH1.ASM |
| String functions | `strings.c` | BISTRS.ASM |
| PRINT / LPRINT | `print.c` | BINTRP.ASM |
| PRINT USING | `print_using.c` | BIPRTU.ASM |
| Variables + arrays | `vars.c`, `arrays.c` | GWMAIN.ASM |
| File I/O + random access | `fileio.c` | BIPTRG.ASM |
| Program I/O (SAVE/LOAD) | `program_io.c` | BIMISC.ASM |
| INPUT/LINE INPUT | `input.c` | BINTRP.ASM |
| Sound engine | `sound.c` | — |
| Platform abstraction | `hal_posix.c` | OEM*.ASM |
## Source Layout
```
src/ — core interpreter (20 files)
include/ — headers (12 files)
platform/ — HAL backends (1 file)
tests/ — 58 automated test programs, 4 classic interactive programs, compat harness
docs/ — Sphinx documentation
```
## TUI Architecture
The TUI (`tui.c`) implements the classic GW-BASIC full-screen editor:
- **Screen buffer** — `tui_cell_t *screen` is dynamically allocated at
`rows × cols` (default 25×80, or full terminal size with `--full`).
Each cell stores a character and color attribute, accessed via `TUI_CELL(r, c)`.
- **HAL interception** — `tui_init()` swaps HAL function pointers so all
existing PRINT/LIST/error output automatically goes through the screen buffer.
No changes needed to `print.c`, `error.c`, or most of `interp.c`.
- **Line editor** — `tui_read_line()` implements the defining GW-BASIC UX:
free cursor movement with arrow keys, and pressing Enter on any screen line
re-enters that line's content as BASIC input.
- **Function keys** — F1-F10 with default GW-BASIC bindings, configurable via
the `KEY n, "string"` statement. `KEY ON` shows the bar on the bottom row.
- **Break handling** — SIGINT sets a flag checked each statement in the run loop.
## Design Decisions
### Relation to Original Assembly
Microsoft [released the original GW-BASIC source](https://github.com/microsoft/GW-BASIC)
in 2020 — 43,771 lines of 8088 assembly spread across 43 `.ASM` files, complete
with Greg Whitten's comments and Neil Konzen's transcendental math routines
(which are, frankly, impressive for 16-bit fixed-point). This reimplementation
uses that assembly as a reference, not as input to a transliterator — the
algorithms are reimplemented in idiomatic C with modern data structures.
### Key Differences from the Original
- **IEEE 754 floating point** — MBF (Microsoft Binary Format) conversion is only
used for file I/O compatibility (CVI/CVS/CVD, MKI$/MKS$/MKD$)
- **Dynamic memory allocation** — `malloc`/`free` instead of a 64KB segment layout
- **malloc'd strings** — instead of a compacting garbage collector
- **`setjmp`/`longjmp`** — for error recovery, matching the original's stack reset
behavior
- **ANSI terminal** — TUI uses ANSI escape sequences and alternate screen buffer
instead of direct CGA memory access
- **Dynamic screen buffer** — allocated at runtime based on terminal size, rather
than hardcoded to 25×80