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

GW-BASIC 2026

A portable C reimplementation of Microsoft GW-BASIC, using the original 8088 assembly source (released by Microsoft in 2020) as the authoritative reference.

This is not a transpilation — it reimplements the algorithms in clean C11 with modern data structures while targeting bug-compatible behavior.

Building

mkdir -p build && cd build
cmake .. && make

Requires a C11 compiler and CMake 3.10+. PulseAudio (libpulse-simple) is optional — detected at build time for SOUND/BEEP/PLAY support.

Usage

Interactive mode launches the authentic GW-BASIC full-screen editor:

$ ./gwbasic
GW-BASIC 2026 0.10.0
(C) Eremey Valetov 2026. MIT License.
Based on Microsoft GW-BASIC assembly source.
Ok
PRINT 2+2
 4
Ok
FOR I=1 TO 5:PRINT I;:NEXT
 1  2  3  4  5
Ok

Run a program file (ASCII or binary tokenized):

./gwbasic tests/programs/prime_sieve.bas

Pipe input:

echo '10 FOR I=1 TO 10:PRINT I*I;:NEXT' | ./gwbasic

What Works

Data types: INTEGER (%), SINGLE (!), DOUBLE (#), STRING ($)

Operators: + - * / ^ \ MOD AND OR XOR EQV IMP NOT < = > <= >= <>

Numeric functions: SGN, INT, ABS, SQR, SIN, COS, TAN, ATN, LOG, EXP, RND, FIX, CINT, CSNG, CDBL

String functions: LEN, ASC, CHR$, VAL, STR$, LEFT$, RIGHT$, MID$, SPACE$, STRING$, HEX$, OCT$, INSTR, INPUT$

Statements:

Category Statements
Output PRINT, LPRINT, LLIST, PRINT USING, WRITE, CLS
Variables LET, DIM, ERASE, SWAP, DEFINT/SNG/DBL/STR
Control flow GOTO, GOSUB/RETURN, FOR/NEXT, IF/THEN/ELSE, WHILE/WEND, ON...GOTO/GOSUB
Input INPUT, LINE INPUT, DATA/READ/RESTORE, INKEY$
Program RUN, RUN "file", CONT, STOP, END, NEW, LIST, CLEAR, AUTO, RENUM, DELETE, EDIT
Sequential I/O OPEN, CLOSE, PRINT#, WRITE#, INPUT#, LINE INPUT#
Random-access I/O FIELD, LSET, RSET, PUT, GET, CVI/CVS/CVD, MKI$/MKS$/MKD$
Program I/O SAVE (binary/ASCII), LOAD (auto-detects format), MERGE, CHAIN, COMMON
Event trapping ON TIMER(n) GOSUB, TIMER ON/OFF/STOP, ON KEY(n) GOSUB, KEY(n) ON/OFF/STOP
Error handling ON ERROR GOTO, RESUME, ERROR, ERR, ERL
User functions DEF FN, RANDOMIZE
File management KILL, NAME, FILES, MKDIR, RMDIR, CHDIR, SHELL
Date/time DATE$, TIME$, TIMER
Screen LOCATE, COLOR, WIDTH, SCREEN, KEY ON/OFF/LIST
Graphics PSET, PRESET, LINE, CIRCLE, DRAW, PAINT
Sound SOUND, BEEP, PLAY (MML)

Binary and ASCII File Formats

SAVE writes tokenized binary by default (just like the original), or ASCII with the ,A flag. LOAD auto-detects the format — so you can load programs saved in either format without any extra flags.

SAVE "myprog.bas"       ' tokenized binary (compact, fast)
SAVE "myprog.bas",A     ' ASCII text (human-readable)
LOAD "myprog.bas"       ' auto-detects format

Binary files use the standard GW-BASIC 0xFF-header format. Command-line loading (./gwbasic file.bas) also auto-detects, so binary .BAS files just work.

INKEY$ Extended Keys

INKEY$ returns the classic GW-BASIC two-byte encoding for extended keys: CHR$(0) + CHR$(scan_code). Arrow keys, Home, End, PgUp/PgDn, Insert, Delete, and F1-F10 all produce the correct IBM PC scan codes — so programs that poll for arrow-key input work as expected.

Full-Screen Editor (TUI)

When running interactively, GW-BASIC 2026 presents the authentic full-screen editor that people remember from the 1980s:

  • 25×80 screen buffer with free cursor movement (arrow keys)
  • Enter on any screen line re-enters it as BASIC input
  • Insert/Overwrite toggle (Insert key, cursor shape changes)
  • Function key bar on line 25 (KEY ON/KEY OFF/KEY LIST)
  • Default F1-F10 bindings (F1=LIST, F2=RUN, F3=LOAD", etc.)
  • Ctrl+C interrupts running programs
  • Piped input bypasses the TUI entirely — scripts and test harnesses work unchanged
  • --full flag adapts to the full terminal size instead of the classic 25×80

Printer Output (LPRINT/LLIST)

LPRINT and LLIST send output to a printer device or file:

  • Modern systems (default): output is appended to LPT1.TXT in the current directory
  • Real hardware: use --lpt /dev/lp0 (Linux) or --lpt LPT1 (FreeDOS) to send output to a physical parallel port printer
./gwbasic --lpt /dev/lp0 myprogram.bas   # print to hardware
./gwbasic --lpt report.txt myprogram.bas  # print to file
./gwbasic myprogram.bas                   # default: LPT1.TXT

Graphics

Graphics mode is activated with SCREEN 1 (320×200, 4 colors) or SCREEN 2 (640×200, monochrome). Drawing commands render to a virtual framebuffer and output via Sixel graphics, which works in terminals like xterm, mlterm, foot, and WezTerm.

SCREEN 1
LINE (0,0)-(319,199), 1
CIRCLE (160,100), 80, 2
PAINT (160,100), 3, 2

Architecture

The interpreter follows the original GW-BASIC's internal structure:

Source text → Tokenizer (CRUNCH) → Token stream
                                      ↓
                              Expression evaluator (FRMEVL)
                                      ↓
                              Statement dispatcher (NEWSTT)
                                      ↓
                              TUI screen buffer (interactive)
                                      ↓
                              HAL (platform I/O)
Module File Original
Tokenizer tokenizer.c GWMAIN.ASM
Evaluator eval.c GWEVAL.ASM
Interpreter interp.c BINTRP.ASM
TUI editor tui.c
Graphics graphics.c
Tokens tokens.c IBMRES.ASM
Errors error.c GWDATA.ASM
Math math_int.c, math_float.c, math_transcend.c MATH1/2.ASM
Strings strings.c BISTRS.ASM
File I/O fileio.c BIPTRG.ASM
PRINT USING print_using.c BIPRTU.ASM
Sound sound.c
Platform hal_posix.c OEM*.ASM

Key design differences from the original:

  • IEEE 754 floating point (MBF conversion only for CVI/CVS/CVD compatibility)
  • Dynamic memory allocation instead of 64KB segment layout
  • malloc'd strings instead of compacting garbage collector
  • setjmp/longjmp for error recovery

Classic Programs

The tests/classic/ directory contains classic BASIC programs from David Ahl's BASIC Computer Games (1978) — Hamurabi, Lunar Lander, Gunner, Diamond — for manual compatibility testing. These are interactive programs that need keyboard input, so they're not part of the automated test suite.

Tests

58 test programs in tests/programs/, with golden-file regression testing and CI via GitHub Actions:

bash tests/run_tests.sh

Compatibility testing against real GWBASIC.EXE under DOSBox-X:

bash tests/run_compat.sh --generate   # generate .expected from GWBASIC.EXE
bash tests/run_compat.sh              # compare gwbasic output against .expected

License

MIT License. See LICENSE.

Description
Portable C reimplementation of Microsoft GW-BASIC, with an ahead-of-time compiler. Mirror of github.com/evvaletov/gw-basic-2026.
Readme 446 KiB
Languages
C 83.3%
BASIC 6.5%
Python 5.4%
Shell 4.3%
CMake 0.4%