Eremey Valetov 1dcc39f45b Compiler: READ arrays, PRINT USING colon fix, array assign fix — 41/72
READ into array elements: parse subscripts after variable name in READ
handler, call gwrt_array_elem + gwrt_data_read. Unlocks matrix_mult,
roman_numerals (partial).

PRINT USING colon-in-string: scan past quoted strings when finding
statement-end colon for token embedding. Fixes truncated format strings
like "Pi estimate: #.####".

Array element assignment: don't zero element before RHS evaluation.
Previous code did `*_elem = {.type=4}` which zeroed fval before
reading C(I,J) in `C(I,J) = C(I,J) + A(I,K)*B(K,J)`, making
self-referencing assignments always read 0.

DEF FN call fix: skip past TOK_FN byte before calling gw_eval_fn_call.

DEFINT pre-scan: analysis pass processes DEFINT/DEFSNG/DEFDBL/DEFSTR
before variable type resolution.

Integer assignment rounding: use gw_cint() (rint) instead of (int16_t)
C truncation.

41/72 tests pass. 0 compile errors.
New: hundred_doors, matrix_mult, pascal_triangle, stats_calc.
2026-03-29 14:53:02 -04: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.14.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, GET/PUT (sprites), BSAVE, BLOAD, VIEW, WINDOW, PALETTE, PMAP
Sound SOUND, BEEP, PLAY (MML)
Memory DEF SEG, PEEK, POKE

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

GET and PUT capture and blit rectangular sprites using CGA-compatible packed pixel format. PUT supports action modes: XOR (default), PSET, PRESET, AND, OR.

DIM S%(50)
GET (0,0)-(15,15), S%
PUT (100,50), S%, XOR

DEF SEG / PEEK / POKE

A virtual 8086 address space emulates the memory layout that GW-BASIC programs expected on a real IBM PC:

Segment Description
0040 BIOS data area — video mode, cursor position, timer ticks (18.2 Hz), keyboard flags
B800 CGA text buffer (text mode) or CGA framebuffer (graphics mode)
DEF SEG = &HB800
POKE 0, 65             ' write 'A' to top-left screen cell
PRINT PEEK(1)          ' read the color attribute
DEF SEG                ' reset to default segment

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
Virtual memory virmem.c
Platform hal_posix.c OEM*.ASM

Key design differences from the original:

  • IEEE 754 floating point (MBF conversion for CVI/CVS/CVD/MKI$/MKS$/MKD$ file 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

64 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%