Authentic GW-BASIC screen editor with 25x80 buffer, free cursor movement, enter-on-any-line, F1-F10 function keys, Insert/Overwrite toggle, KEY ON/OFF/LIST statement, and Ctrl+Break handling. HAL pointer swap routes all PRINT/LIST/error output through the TUI automatically. Piped mode unchanged (50/50 tests pass). Adds automated compatibility testing infrastructure: DOSBox-X headless config, PRINT-to-file transform script, and run_compat.sh with --generate and --compare modes for verifying output against real GWBASIC.EXE. Project renamed from gwbasic-c to GW-BASIC 2026.
4.1 KiB
Architecture
Pipeline
Source text → Tokenizer (CRUNCH) → Token stream
↓
Expression evaluator (FRMEVL)
↓
Statement dispatcher (NEWSTT)
↓
TUI screen buffer (interactive)
↓
HAL (platform I/O)
The interpreter follows the original GW-BASIC's internal structure. Source lines
are tokenized by CRUNCH into a compact token stream. The NEWSTT loop dispatches
each statement, calling FRMEVL for expression evaluation. All platform I/O goes
through a HAL vtable (hal_ops_t), keeping the core interpreter portable.
When running interactively, the TUI layer intercepts HAL output calls
(putch, puts, cls, locate) and routes them through a 25×80 screen
buffer rendered via ANSI escape sequences. In piped mode the TUI is not
activated and the HAL writes directly 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 statement | 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/ — test programs (50 .BAS files), compat test harness
TUI Architecture
The TUI (tui.c) implements the classic GW-BASIC full-screen editor:
- Screen buffer —
tui_cell_t screen[25][80]stores character + attribute per cell, matching the original CGA text mode layout. - HAL interception —
tui_init()swaps HAL function pointers so all existing PRINT/LIST/error output automatically goes through the screen buffer. No changes needed toprint.c,error.c, or most ofinterp.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 ONshows the bar on row 25. - Break handling — SIGINT sets a flag checked each statement in the run loop.
Design Decisions
Relation to Original Assembly
The original GW-BASIC source was
released by Microsoft in 2020 as 8088
assembly (43,771 lines across 43 .ASM files). This reimplementation uses that
assembly as a reference but is not a transpilation — it reimplements the
algorithms 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/freeinstead 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