* tests: automated headless FreeDOS QEMU smoke
Fully-automated counterpart to the manual run_freedos_qemu.sh: overlays the
FreeDOS image (no mutation), stages the interpreter and a SYSTEM-terminated
smoke on C:, injects the run plus poweroff into the image's startup batch,
boots headless, and diffs OUT.TXT against the golden file. Local-dev only
(needs qemu, a FreeDOS qcow2, mtools, nbd, and passwordless sudo); CI keeps
using the DOSBox-X path. Exercises the binary on a real FreeDOS install
rather than DOSBox-X emulation.
* Release 0.18.0
Cross-language linking (link BASIC into C/Fortran, call C from BASIC via
'$EXTERN), the v0.18 codegen/perf batch (paren string-comparison fix,
--no-gc-check/--fast-math, larger 32-bit caps, process-local DATE$/TIME$),
and the automated FreeDOS QEMU smoke. Bumps GW_VERSION and updates the
banners, CHANGES.TXT, and the development history table.
---------
Co-authored-by: Eremey Valetov <evvaletov@users.noreply.github.com>
QA findings from a multi-round review of the FreeDOS submission prep work:
- TUI rendering refactor: src/tui.c emitted ANSI escape sequences via
printf, which displays as raw text on bare FreeDOS (no ANSI.SYS).
Add four HAL ops (tui_enter, tui_leave, render_run, set_cursor_shape)
and route per-cell rendering through them. POSIX backend keeps the
ANSI path; DOS backend drives BIOS INT 10h via the existing
bios_set_cursor / bios_write_char helpers. The TUI's logical cursor
goes through the saved orig_locate to avoid recursing through the
swapped-in gw_hal->locate.
- DOS extended-key mapping: dos_getch returns 0x100 | scancode for
arrows / F-keys; tui_read_key wasn't translating those to its TK_*
constants, so the editor never saw arrow keys or F1-F10 on DOS.
Add a __MSDOS__-conditional translation table in tui_read_key.
- Version banner: GW_VERSION was still 0.16.0 even though the v0.17.0
release prep was already in CHANGES.TXT. Bump.
- Compiler PulseAudio link: gwbasic-compile -c hardcoded
'-lgwrt -lm -lpthread' on the gcc command line. When libgwrt was
built against libpulse-simple (the default on any host with the
PulseAudio dev headers installed), the compile workflow failed with
'undefined reference to pa_simple_drain'. CMake now passes
GWRT_HAS_PULSEAUDIO to gwbasic-compile when libpulse is present, and
the compiler appends -lpulse-simple to the link line.
- FRE("") garbage collection: the interpreter skipped strpool_gc with a
comment 'unsafe during expression eval', but that's exactly what real
GW-BASIC's FRE("") does (and the AOT compiler path already did). Add
the GC call; strpool_pin/unpin is the existing escape hatch if a
caller has live pool pointers on the C stack. Fixes the string_gc
compat test.
- Test harness normalization: run_tests.sh stripped trailing whitespace
on the actual output but not the expected file, causing spurious
mismatches against golden files captured from real GWBASIC.EXE.
Normalize both sides identically. Fixes the peek_gfx mismatch.
- Print_using: snprintf into mantissa[32] with %.*f and an unbounded
dec triggered a -Wformat-truncation warning. Clamp dec to 20 (IEEE
double has at most ~17 significant decimal digits).
- Doc/version consistency: 16-bit binary size reported as 127KB in one
place and 128KB in three; standardize on 128KB. HAL backend count
said '1 file' but is now 2. CI test count said 'all 66 test
programs' but is 72. Add a v0.17.0 row to the development.md table.
Update getting-started.md DOS section to match the BIOS-rendering
reality and add a manual TUI verification checklist.
- dos_init now writes back BIOS-reported cols/rows to dos_hal struct
fields (forward-declared so dos_init can reference it).
After these changes: 72/72 interpreter tests pass, compat 68/68
matched, no warnings on the Linux build.
Binary tokenized SAVE/LOAD now stores float constants in Microsoft Binary
Format (MBF) on disk, matching original GWBASIC.EXE. A token-walking function
(convert_floats) converts IEEE↔MBF at the save_binary()/load_binary() boundary.
Also fixes a latent bug where load_binary() scanned for 0x00 to find the end
of each token line — this fails when float bytes contain null (e.g. MBF for
100.5 is 00 00 49 87). The loader now uses the next-line pointer to compute
token data length, matching the original's approach.
BSAVE/BLOAD: save and load virtual memory blocks with 0xFD-header
binary format, operating on the current DEF SEG segment.
TUI color: tui_refresh emits ANSI SGR codes from cell attributes;
COLOR statement sets tui.current_attr when TUI is active.
Extended PEEK/POKE: CGA graphics framebuffer (interlaced layout) via
gfx_cga_peek/poke routed through virmem when gfx_active(); BIOS
keyboard shift flags (offset 0x17 bit 7 = insert mode).
Add bibliography to language reference. 64 tests, all passing.
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.
Add event-driven programming: ON TIMER(n) GOSUB with TIMER ON/OFF/STOP,
ON KEY(n) GOSUB with KEY(n) ON/OFF/STOP for F1-F10. Fix F-key escape
sequence parser (F9/F10 detection, push back consumed bytes on unmatched
sequences). Add EDIT statement for TUI line editing. Guard key trap
polling so keystrokes aren't consumed when no traps are configured.
LPRINT and LLIST now output to a printer device or file instead of the
screen. By default, output is appended to LPT1.TXT in the current
directory. Use --lpt to redirect to a real printer device (/dev/lp0,
LPT1 on FreeDOS) or a custom file path.
Includes full PRINT format support (semicolons, commas, TAB, SPC,
PRINT USING) and LLIST with line number range parsing.
The TUI screen buffer is now dynamically allocated instead of using a
fixed 25x80 array. When launched with --full/-f, the editor queries the
terminal size via ioctl(TIOCGWINSZ) and adapts accordingly. The default
remains the authentic 25x80.
AUTO generates line numbers during interactive editing, RENUM
renumbers program lines and patches all GOTO/GOSUB/ON references,
DELETE removes line ranges. COMMON declares variables preserved
across CHAIN. Also fixed LIST/DELETE range parsing (was broken by
expression evaluator consuming the dash as subtraction).
Bump to v0.7.0, 53 tests.
DATE$, TIME$, and TIMER now return real system date/time instead of
hardcoded values. Added directory and shell access statements with
proper GW-BASIC error codes (Path not found 76, File already exists 60).
Bump to v0.6.0, 52 tests.
Add GitHub Actions CI with automated build and test. Implement real
terminal I/O with raw mode (enable_raw/disable_raw, proper INKEY$
polling via VMIN=0/VTIME=0, INPUT$ function). Add Sixel graphics
engine with virtual framebuffer (SCREEN 1: 320x200, SCREEN 2:
640x200), Bresenham line drawing, midpoint circle, flood fill PAINT,
DRAW mini-language parser, and Sixel encoder with RLE. Replace all
graphics stubs with real implementations (PSET, LINE, CIRCLE, DRAW,
PAINT, COLOR, SCREEN, POINT). Fix AND/OR/XOR operator precedence
to be lower than relational operators. Add 13 classic test programs
(39 total). Bump version to 0.5.0.
Add CHAIN statement for loading and running chained programs with
optional ALL flag to preserve variables. Extend RUN to accept a
filename string argument. Implement random-access file I/O with
FIELD, LSET, RSET, PUT#, GET# and the MBF conversion functions
CVI/CVS/CVD/MKI$/MKS$/MKD$. Add COMMON statement (parse and skip).
Five new test programs covering all new features (27 total).
Add OPEN/CLOSE with both modern (OPEN "f" FOR OUTPUT AS #n) and compact
(OPEN "O",#n,"f") syntaxes. PRINT#, WRITE#, INPUT#, LINE INPUT# for
sequential file access. EOF() now returns real file status with peek-ahead.
LOC/LOF return approximate values.
PRINT USING with numeric (#, ., +, -, $$, **, ^^^^, comma grouping) and
string (!, &, \ \) format specifiers. Shared by PRINT USING and PRINT# USING.
SAVE (ASCII), LOAD (with ,R auto-run), and MERGE for program persistence.
MID$ assignment (MID$(var$, start [,len]) = expr) for in-place string
modification. Works with both scalar variables and array elements.
Graphics stubs for SCREEN, PSET, PRESET, LINE, CIRCLE, DRAW, PAINT,
VIEW, WINDOW, PALETTE - parse and discard arguments so graphics-heavy
programs don't crash.
SYSTEM and NEW/CLEAR now close all open files. Version bumped to 0.3.0.
22 tests pass (16 existing + 6 new).
Add variable storage (scalar and array), program line storage with
RUN/LIST/NEW, and full control flow: FOR/NEXT, GOTO, GOSUB/RETURN,
IF/THEN/ELSE, WHILE/WEND, ON GOTO/GOSUB, DATA/READ/RESTORE.
New modules: interp.c (1445 lines - execution loop and statement
dispatcher), vars.c, arrays.c, input.c. Version bumped to 0.2.0.
16 test programs pass including Leibniz pi and prime sieve.
GW-BASIC reimplementation in C11, using Microsoft's open-sourced 8088
assembly as the authoritative reference.
Tokenizer (CRUNCH/LIST), expression evaluator with operator precedence,
all math functions (SIN, COS, TAN, ATN, SQR, LOG, EXP, RND, etc.),
string functions (LEFT$, RIGHT$, MID$, CHR$, ASC, VAL, STR$, etc.),
PRINT statement with comma/semicolon zones, TAB(), SPC().
Handles integer/single/double types with correct promotion, D exponent
for double-precision literals, type suffixes (%, !, #), &H hex/&O octal
literals, MBF conversion routines, and GW-BASIC-compatible number
formatting.
Platform-independent via HAL vtable; POSIX backend included.