2026-02-15 16:56:02 -05:00
|
|
|
|
# Architecture
|
|
|
|
|
|
|
|
|
|
|
|
## Pipeline
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Source text → Tokenizer (CRUNCH) → Token stream
|
|
|
|
|
|
↓
|
|
|
|
|
|
Expression evaluator (FRMEVL)
|
|
|
|
|
|
↓
|
|
|
|
|
|
Statement dispatcher (NEWSTT)
|
|
|
|
|
|
↓
|
2026-02-22 12:18:17 -05:00
|
|
|
|
TUI screen buffer (interactive)
|
|
|
|
|
|
↓
|
2026-02-15 16:56:02 -05:00
|
|
|
|
HAL (platform I/O)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-30 16:50:43 -04:00
|
|
|
|
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
|
2026-03-01 12:25:47 -05:00
|
|
|
|
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.
|
2026-02-22 12:18:17 -05:00
|
|
|
|
|
2026-02-15 16:56:02 -05:00
|
|
|
|
## 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 |
|
2026-03-30 16:50:43 -04:00
|
|
|
|
| TUI screen editor | `tui.c` | -- |
|
|
|
|
|
|
| Graphics engine | `graphics.c` | -- |
|
2026-02-15 16:56:02 -05:00
|
|
|
|
| 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 |
|
2026-02-22 14:09:18 -05:00
|
|
|
|
| PRINT / LPRINT | `print.c` | BINTRP.ASM |
|
2026-02-15 16:56:02 -05:00
|
|
|
|
| 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 |
|
2026-03-30 16:50:43 -04:00
|
|
|
|
| Sound engine | `sound.c` | -- |
|
|
|
|
|
|
| Virtual memory (PEEK/POKE) | `virmem.c` | -- |
|
|
|
|
|
|
| Hardware I/O ports | `portio.c` | -- |
|
2026-03-29 04:58:29 -04:00
|
|
|
|
| String space pool + GC | `strpool.c` | GWEVAL.ASM (GETSPA/GARBAG) |
|
2026-03-30 16:50:43 -04:00
|
|
|
|
| AOT compiler analysis | `analysis.c` | -- |
|
|
|
|
|
|
| AOT compiler codegen | `codegen.c` | -- |
|
|
|
|
|
|
| Compiled program runtime | `gwrt.c` | -- |
|
2026-04-10 18:14:41 -04:00
|
|
|
|
| Platform abstraction | `hal_posix.c`, `hal_dos.c` | OEM*.ASM |
|
2026-02-15 16:56:02 -05:00
|
|
|
|
|
|
|
|
|
|
## Source Layout
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-03-30 16:50:43 -04:00
|
|
|
|
src/ -- core interpreter + compiler (27 files)
|
|
|
|
|
|
include/ -- headers (18 files)
|
|
|
|
|
|
platform/ -- HAL backends (1 file)
|
|
|
|
|
|
gwbasickernel/ -- Jupyter notebook kernel (Python, 6 files)
|
|
|
|
|
|
tests/ -- 72 automated test programs, 4 classic interactive programs, compat harness
|
|
|
|
|
|
docs/ -- Sphinx documentation
|
2026-02-15 16:56:02 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-22 12:18:17 -05:00
|
|
|
|
## TUI Architecture
|
|
|
|
|
|
|
|
|
|
|
|
The TUI (`tui.c`) implements the classic GW-BASIC full-screen editor:
|
|
|
|
|
|
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **Screen buffer** -- `tui_cell_t *screen` is dynamically allocated at
|
2026-02-22 14:09:18 -05:00
|
|
|
|
`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)`.
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **HAL interception** -- `tui_init()` swaps HAL function pointers so all
|
2026-02-22 12:18:17 -05:00
|
|
|
|
existing PRINT/LIST/error output automatically goes through the screen buffer.
|
|
|
|
|
|
No changes needed to `print.c`, `error.c`, or most of `interp.c`.
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **Line editor** -- `tui_read_line()` implements the defining GW-BASIC UX:
|
2026-02-22 12:18:17 -05:00
|
|
|
|
free cursor movement with arrow keys, and pressing Enter on any screen line
|
|
|
|
|
|
re-enters that line's content as BASIC input.
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **Function keys** -- F1-F10 with default GW-BASIC bindings, configurable via
|
2026-02-22 14:09:18 -05:00
|
|
|
|
the `KEY n, "string"` statement. `KEY ON` shows the bar on the bottom row.
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **Break handling** -- SIGINT sets a flag checked each statement in the run loop.
|
2026-02-15 16:56:02 -05:00
|
|
|
|
|
2026-04-09 13:14:26 -04:00
|
|
|
|
## Ahead-of-Time Compiler
|
|
|
|
|
|
|
|
|
|
|
|
`gwbasic-compile` translates tokenized `.bas` programs to C source linked
|
|
|
|
|
|
against `libgwrt.a`:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
.bas → gw_crunch() → analysis pass → C codegen → gcc → native binary
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The **analysis pass** (`analysis.c`) collects variables, GOTO/GOSUB targets,
|
|
|
|
|
|
DATA literals, and DEFINT/DEFSNG/DEFDBL/DEFSTR type defaults. When `--warn`
|
|
|
|
|
|
is active, it also tracks variable assignment vs. use context to detect
|
|
|
|
|
|
uninitialized variables, GOTO to nonexistent lines, and unreachable code.
|
|
|
|
|
|
|
|
|
|
|
|
The **codegen** (`codegen.c`) walks the token stream and emits C source:
|
|
|
|
|
|
variables become `static int16_t var_A_int`, control flow uses `goto L_100`
|
|
|
|
|
|
labels, and expressions are compiled inline using the same precedence-climbing
|
|
|
|
|
|
structure as `eval.c`. Optimizations include constant folding, dead code
|
|
|
|
|
|
elimination, FOR step=1 elision, and fast-path expression emission.
|
|
|
|
|
|
|
|
|
|
|
|
The **`--safe` mode** modifies generated C to use overflow-checked integer
|
|
|
|
|
|
arithmetic (`gw_int_add/sub/mul` from `math_int.c`) instead of bare C
|
|
|
|
|
|
operators, and enhanced-diagnostic array/GOSUB runtime functions that report
|
|
|
|
|
|
variable names, subscript values, and BASIC line numbers on error.
|
|
|
|
|
|
|
|
|
|
|
|
The **runtime library** (`gwrt.c` / `libgwrt.a`) provides initialization,
|
|
|
|
|
|
DATA/READ support, GOSUB return-label stack, and wrappers around the existing
|
|
|
|
|
|
interpreter modules. It includes all interpreter code except the execution
|
|
|
|
|
|
loop, so compiled programs share the same string pool, GC, file I/O, and
|
|
|
|
|
|
graphics implementation.
|
|
|
|
|
|
|
2026-02-15 16:56:02 -05:00
|
|
|
|
## Design Decisions
|
|
|
|
|
|
|
|
|
|
|
|
### Relation to Original Assembly
|
|
|
|
|
|
|
2026-03-01 12:25:47 -05:00
|
|
|
|
Microsoft [released the original GW-BASIC source](https://github.com/microsoft/GW-BASIC)
|
2026-03-30 16:50:43 -04:00
|
|
|
|
in 2020 -- 43,771 lines of 8088 assembly spread across 43 `.ASM` files, complete
|
2026-03-01 12:25:47 -05:00
|
|
|
|
with Greg Whitten's comments and Neil Konzen's transcendental math routines
|
|
|
|
|
|
(which are, frankly, impressive for 16-bit fixed-point). This reimplementation
|
2026-03-30 16:50:43 -04:00
|
|
|
|
uses that assembly as a reference, not as input to a transliterator -- the
|
2026-03-01 12:25:47 -05:00
|
|
|
|
algorithms are reimplemented in idiomatic C with modern data structures.
|
2026-02-15 16:56:02 -05:00
|
|
|
|
|
|
|
|
|
|
### Key Differences from the Original
|
|
|
|
|
|
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **IEEE 754 floating point** -- MBF (Microsoft Binary Format) conversion is used
|
2026-03-21 01:38:53 -04:00
|
|
|
|
at the binary save/load boundary and for file I/O (CVI/CVS/CVD, MKI$/MKS$/MKD$),
|
|
|
|
|
|
matching the original's on-disk format
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **Dynamic memory allocation** -- `malloc`/`free` instead of a 64KB segment layout
|
|
|
|
|
|
- **String space pool** -- 32KB contiguous pool with compacting GC at statement
|
2026-03-29 04:58:29 -04:00
|
|
|
|
boundaries, matching the original's GETSPA/GARBAG approach
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **`setjmp`/`longjmp`** -- for error recovery, matching the original's stack reset
|
2026-02-15 16:56:02 -05:00
|
|
|
|
behavior
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **ANSI terminal** -- TUI uses ANSI escape sequences and alternate screen buffer
|
2026-02-22 12:18:17 -05:00
|
|
|
|
instead of direct CGA memory access
|
2026-03-30 16:50:43 -04:00
|
|
|
|
- **Dynamic screen buffer** -- allocated at runtime based on terminal size, rather
|
2026-02-22 14:09:18 -05:00
|
|
|
|
than hardcoded to 25×80
|