30 Commits

Author SHA1 Message Date
Eremey Valetov
cabd93caea Release 0.18.0 + automated FreeDOS QEMU smoke (#2)
* 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>
2026-06-13 15:37:45 +03:00
Eremey Valetov
89fe0fb0b3 Compiler: $EXTERN pragma for calling C functions from BASIC (Level 2 FFI) (#1)
Add a '$EXTERN NAME(ARGTYPES) AS RET pragma so compiled BASIC can call C
functions directly, the natural follow-up to Level 1 (--emit-obj /
--main-name). The pragma is an apostrophe comment, so the interpreter
ignores it while the compiler registers it.

Map INTEGER/SINGLE/DOUBLE/STRING to int16_t/float/double/const char* at the
boundary: a string argument crosses as a temporary C copy that is freed
after the call, and a string return is copied into the pool. The call name
is matched case-insensitively but emitted as the C symbol with the case
written in the pragma. Names are recognized before parse_var() truncates
identifiers to two significant characters, so multi-character C function
names work.

A string return that aliases a char* argument is copied before the argument
temporaries are freed, which avoids a use-after-free. Over-supplied
arguments are consumed without desyncing the token stream and warn on arity
mismatch.

Docs: getting-started.md "Foreign Functions from BASIC". Test:
tests/run_ffi_test.sh, wired into CI. 63/63 compiler, 72/72 interpreter,
68/68 compat still pass.

Also refile the roadmap "Next Up" backlog as git-bug issues and prune
docs/roadmap.md to point at git-bug as the source of truth for planned work.

Co-authored-by: Eremey Valetov <evvaletov@users.noreply.github.com>
2026-06-13 15:06:23 +03:00
Eremey Valetov
da1e6cebf1 CI: fix dos-cross-compile env; codegen: accept concat in CVI/CVS/CVD/cmp
- .github/workflows/ci.yml: the dos-cross-compile job failed on the
  first push because build_dos.sh sources $HOME/openwatcom-v2/setvars.sh
  if WATCOM is unset, but that file isn't part of the OpenWatcom V2
  snapshot — I'd been creating it locally by hand.  Add a "Configure
  OpenWatcom env" step that generates setvars.sh after extraction (so
  build_dos.sh works) AND exports WATCOM/PATH/INCLUDE via $GITHUB_ENV
  (so subsequent steps work even if setvars.sh sourcing changes).
  Also stash both DOS binaries before the next-mode clean wipes them,
  so the artifact upload actually has both .exe files.

- src/codegen.c: switch the four remaining emit_str_atom callers
  (CVI/CVS/CVD function args + string-comparison left/right) to
  emit_str_expr.  Now `CVS(A$+B$)` and `A$+B$ < C$` accept
  concatenation in their string operands; previously the atom-level
  caller stopped at the first identifier and the trailing `+` confused
  downstream parsing.  Verified: CVS(MKS$(3.14)+MKS$(0)) round-trips
  to 3.14 in both interpreter and compiled binary.  All 72 interpreter
  + 63 compiler tests still pass.

- docs/getting-started.md: document that gwbasic-compile auto-numbers
  unnumbered direct-mode lines (last_num + 10) so scratchpad-style
  programs compile without manual renumbering.

- tests/run_freedos_qemu.sh: helper for going through the manual TUI
  checklist on bare FreeDOS.  Modern qemu-kvm doesn't expose -fda on
  the default machine type and fat:rw: protocol is gone, so a fully
  automated FreeDOS smoke isn't tractable from userspace; this script
  builds a FAT data image (mtools), attaches it as -hdb to the FreeDOS
  qcow2, and points the user at the manual sequence in the script
  header.  The DOSBox-X harness (run_dos_smoke.sh) remains the
  automated DOS smoke.
2026-05-04 18:16:21 -04:00
Eremey Valetov
c317d683fb Compiler: accept unnumbered programs, fix string concat in PRINT
Three fixes that lift seven test programs from skipped to passing,
bringing the AOT compiler harness from 56/56 to 63/63.

- Unnumbered programs (compiler_main.c): src/compiler_main.c skipped
  any line that didn't start with a digit, so direct-mode .bas files
  like hello.bas, math_ops.bas, string_ops.bas (no line numbers)
  failed with "No program lines found".  load_file now auto-assigns
  line numbers (last_num + 10) to unnumbered lines, with overflow
  protection at line 65520.

- String concatenation in PRINT (codegen.c): emit_str_atom had a
  broken concat loop that emitted "; _cat = gw_str_concat(&...
  _cat.sval ...)" — _cat was never declared, so any program with a
  string-literal concat in PRINT (like PRINT "ABC" + "DEF") failed
  to link.  Concat is properly handled by emit_str_expr's outer
  loop; remove the dead/broken code in the atom.  Fixes
  string_ops.bas.

- Transcendental result type (codegen.c): peek_expr_type returned
  VT_DBL for ATN/LOG/EXP/VAL, so PRINT formatted them with 15-digit
  double precision (e.g. 3.141592653589793) while real GW-BASIC and
  the interpreter format the single-precision result as 3.141593.
  Real GW-BASIC's transcendentals are single-precision; only CDBL
  forces double.  Demote ATN/LOG/EXP/VAL to VT_SNG; CDBL stays
  VT_DBL.  Fixes math_ops.bas.

Also: tests/run_compiler_tests.sh now runs the compiled binary from
the project root rather than the tempdir where it was built, so
test programs that reference tests/programs/ via relative paths
(chain_test, common_test, run_file, misc_stmts) resolve their
targets.  Earlier I'd misdiagnosed those failures as ON ERROR
divergence — they were just CWD-dependent path lookups.

Doc/test counts: 56 → 63 in README, docs/index.md, docs/development.md,
docs/roadmap.md.  Roadmap updated to note the compiler now accepts
unnumbered programs.
2026-05-04 16:32:09 -04:00
Eremey Valetov
99eb992ead Add DOS build script, compiler/DOS test harnesses, FreeDOS package, CI
- build_dos.sh: Linux-friendly cross-compile to DOS via OpenWatcom V2.
  OpenWatcom's wmake on Linux can't apply the .c.obj implicit rule for
  subdirectory paths, and Makefile.dos / Makefile.dos16 rely on DOS-
  only commands like 'del'.  Script invokes wcc / wcc386 directly,
  tracks 16-bit vs 32-bit mode via a stamp file (auto-cleans on
  switch), generates a wlink directive file (the brace-delimited file
  list wouldn't survive shell quoting), and supports clean.  The DOS
  Makefiles still work on Windows / DOS hosts.

- tests/run_compiler_tests.sh: AOT compiler harness.  For each .bas
  in tests/programs/, compiles via gwbasic-compile -c, runs the
  resulting executable, normalizes output and diffs against the
  golden file from tests/expected/.  Skip list covers chain/common
  multi-file flows, hardware/timing-dependent programs, unnumbered
  direct-mode programs (compiler requires line numbers), and
  misc_stmts/run_file (interpreter-vs-compiler ON ERROR divergence).
  Result: 56/56 pass.

- tests/run_dos_smoke.sh + dos_smoke.bas + expected: runs gwbasic16.exe
  under DOSBox-X (flatpak) with a program that exercises arithmetic,
  strings, control flow, GOSUB, FOR/NEXT, DATA/READ, DEF FN, OPEN/
  PRINT#/CLOSE, and diffs against the interpreter's golden output.
  Uses $HOME for the staging dir (DOSBox-X flatpak doesn't see /tmp).

- pkg/GWBASIC.LSM + pkg/build_pkg.sh: FreeDOS submission package.
  Produces dist/gwbasic-<VERSION>.zip with the standard FreeDOS
  layout (APPINFO/GWBASIC.LSM, BIN/GWBASIC.EXE, DOC/GWBASIC/{README,
  CHANGES,LICENSE} with CRLF, SOURCE/GWBASIC/<full source>).  Source
  tree is filtered through git ls-files to exclude build artifacts.

- docs/Makefile: standard Sphinx Makefile so 'cd docs && make html'
  works as documented in README.md.

- .github/workflows/ci.yml: split into two jobs.  build-and-test now
  also runs the compiler harness.  New dos-cross-compile job caches
  ~/openwatcom-v2, downloads the OpenWatcom V2 snapshot if not
  cached, builds both 16-bit and 32-bit DOS binaries, asserts size
  bounds, and uploads them as artifacts.

- .gitignore: ignore .dos_build_mode (script's stamp), .link_dir/
  (transient wlink directive dir), dist/ (package output).
2026-05-03 12:26:09 -04:00
Eremey Valetov
70ffd39562 v0.17.0: BIOS-routed TUI on DOS, version banner, compiler PulseAudio link
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.
2026-05-03 12:25:41 -04:00
Eremey Valetov
eb32021c19 Implement string space pool with compacting garbage collector
Replaces individual malloc/free string management with a contiguous pool
(default 32KB) and compacting GC, matching the original GW-BASIC's
GETSPA/GARBAG architecture. Allocation is a bump pointer; gw_str_free()
is a no-op (descriptors are nulled, data reclaimed by GC). Compaction
runs at statement boundaries when the pool drops below 4KB free, walking
the variable table and array storage to relocate live strings.

FRE() now returns actual free pool space. FRE("") triggers a GC pass
before reporting. CLEAR n sets the string space size.
2026-03-29 04:55:48 -04:00
Eremey Valetov
612d3972dc Implement remaining statement/function gaps: RESET, ENVIRON/ENVIRON$, ERDEV/ERDEV$, IOCTL/IOCTL$, LCOPY, DATE$/TIME$ assignment, CALL, COM
Closes the last unimplemented extended statement tokens. ENVIRON uses
putenv/getenv for real environment variable access. DATE$/TIME$
assignment is accepted but silently ignored (no system clock modification).
ERDEV/ERDEV$/IOCTL$/PEN return stub values (no real device hardware).
CALL/CALLS raise Illegal function call. COM event trapping silently
accepted.
2026-03-28 18:48:38 -04:00
Eremey Valetov
e100290dd3 Implement Hardware I/O Simulator (OUT/INP/WAIT/MOTOR, port emulation)
New portio.c module following the virmem.c dispatch-by-address pattern,
emulating 8253 PIT channel 2 (speaker frequency), PPI port B (speaker
on/off), CGA mode/color select registers, game port (joystick stub),
COM1 serial (transmitter-ready stub), and floating bus default (0xFF).

OUT/WAIT/MOTOR statements and INP()/STICK()/STRIG() functions now fully
functional. Continuous tone generation via PulseAudio pthread worker for
programs that drive the speaker through OUT &H43/&H42/&H61.
2026-03-28 18:27:19 -04:00
Eremey Valetov
0d9f29aa7a Fix DRAW M/S/A bugs, add TA rotation, =variable; substitution, X substring execution
Rewrites gfx_draw() as a recursive draw_engine() to support all DRAW
mini-language features:

Bug fixes:
- M command parsing: skip generic arg parser so M100,50 correctly
  parses both coordinates instead of consuming x as a generic arg
- S (scale) semantics: distance is now (arg ?: 1) * scale / 4, matching
  original GW-BASIC where S4 means 1 pixel per unit, not 4
- A (rotation): implements 90-degree rotation state with direction
  vector transform for all 8 direction commands

New features:
- TA n: arbitrary rotation angle (-360 to 360 degrees) via cos/sin
- =variable;: numeric variable substitution in DRAW strings
- X stringvar;: execute substring from string variable (recursive)
- Scale factor applied to relative M coordinates
2026-03-21 02:48:17 -04:00
Eremey Valetov
3f3104c385 Implement MBF binary file compatibility, fix binary loader null-byte truncation, update to v0.14.0
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.
2026-03-21 01:38:53 -04:00
Eremey Valetov
000116b2a2 Fix POINT coordinate mapping, remove orphaned palette array, expand tests
Apply coordinate mapping (VIEW/WINDOW) to POINT(x,y) function so it
returns correct pixel values when WINDOW is active. Remove unused
palette[] array from graphics.c (Sixel encoder uses palette_map[]
directly). Expand view_window.bas test to cover WINDOW SCREEN mode,
VIEW+WINDOW combination, and PMAP inverse mapping. Fix CI test count
in docs.
2026-03-10 22:38:42 -04:00
Eremey Valetov
0bacfcef6c Implement VIEW/WINDOW/PALETTE, PMAP, fix MBF float format, update to v0.13.0
Graphics viewport and coordinate mapping:
- VIEW [[SCREEN] (x1,y1)-(x2,y2) [,[fill][,border]]] with clipping
- WINDOW [[SCREEN] (x1,y1)-(x2,y2)] with Cartesian/screen modes
- PALETTE [attribute, color] with CGA 16-color remapping
- PMAP(coord, func) for logical/physical coordinate conversion
- All graphics statements (PSET, LINE, CIRCLE, PAINT, GET/PUT) respect
  viewport clipping and WINDOW coordinate mapping

MBF (Microsoft Binary Format) float support:
- CVS/CVD now interpret bytes as MBF format (compatible with real GW-BASIC)
- MKS$/MKD$ now produce MBF-encoded bytes
- Fixed shift errors in MBF↔IEEE conversion routines (single: 1→0, double: 4→3)
- Random-access file I/O now byte-compatible with original GWBASIC.EXE

66 tests (2 new), 61 compat matches (up from 58).
2026-03-10 22:20:58 -04:00
Eremey Valetov
4551c88a50 Implement BSAVE/BLOAD, TUI color, extended PEEK/POKE, update to v0.12.0
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.
2026-03-01 13:37:05 -05:00
Eremey Valetov
0743757029 Implement DEF SEG/PEEK/POKE, GET/PUT sprites, fix PRINT USING, update to v0.11.0 2026-03-01 13:07:28 -05:00
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
Eremey Valetov
3fa8c6f034 Implement EDIT statement and ON TIMER/ON KEY event trapping, update to v0.9.0
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.
2026-02-27 17:29:09 -05:00
Eremey Valetov
35e4b12131 Implement LPRINT and LLIST with configurable printer output
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.
2026-02-22 13:55:27 -05:00
Eremey Valetov
da6b513b07 Add AUTO, RENUM, DELETE, COMMON; fix LIST range parsing
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.
2026-02-22 12:58:47 -05:00
Eremey Valetov
ece018d06a Implement DATE$/TIME$/TIMER, FILES, SHELL, CHDIR, MKDIR, RMDIR
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.
2026-02-22 12:40:18 -05:00
Eremey Valetov
ad21350003 Add full-screen TUI editor, DOSBox-X compat testing, rename to GW-BASIC 2026
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.
2026-02-22 12:18:17 -05:00
Eremey Valetov
691031a7f9 Fix RESTORE with line number, add 8 Rosetta Code test programs
RESTORE n now correctly positions past the DATA token so the next
READ gets actual values instead of the token byte.

New tests: Roman numerals, Luhn validator, Towers of Hanoi, 100 Doors,
Pascal's triangle, type declarations, Hailstone sequence, multiplication
table.  50 tests now pass.
2026-02-15 16:22:55 -05:00
Eremey Valetov
169f16d465 Implement SOUND, BEEP, and PLAY with PulseAudio backend 2026-02-15 16:14:36 -05:00
Eremey Valetov
9ca23e5d10 Fix variable names with digits, ERL, update docs
- Tokenizer now copies entire identifier (letters+digits) when no
  keyword matches, fixing variables like K2, V1, AB3%
- ERL returns actual error line number instead of 0
- Update README and Sphinx docs for v0.5.0
2026-02-10 17:01:50 -05:00
Eremey Valetov
1f4c460f4f Phase 5: CI, terminal I/O, Sixel graphics, classic programs
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.
2026-02-10 16:46:34 -05:00
Eremey Valetov
df5c3083b1 Add README, implement KILL/NAME, use relative paths in tests
Add README.md for public release. Implement KILL and NAME statements
for file management. Change test programs to use relative paths
instead of /tmp/ and clean up temp files with KILL. Update .gitignore
for test artifacts.
2026-02-10 15:53:57 -05:00
Eremey Valetov
66479b5d6e Phase 4: CHAIN, RUN "file", random-access I/O, MBF conversions
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).
2026-02-10 12:11:25 -05:00
Eremey Valetov
c2d73e9c24 Phase 3: file I/O, PRINT USING, SAVE/LOAD, MID$ assignment, graphics stubs
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).
2026-02-10 11:53:28 -05:00
Eremey Valetov
616259537a Phase 2: variables, arrays, program execution, and control flow
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.
2026-02-10 11:16:58 -05:00
Eremey Valetov
d8e8375366 Phase 1: expression calculator with direct mode
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.
2026-02-10 10:25:08 -05:00