crtbasic.git
7 weeks agorefactor(io): extract channel I/O from Interpreter into an io package
Svjatoslav Agejenko [Thu, 3 Sep 2026 20:49:00 +0000 (23:49 +0300)]
refactor(io): extract channel I/O from Interpreter into an io package

Interpreter grew a god-object sprawl of nine parallel maps keyed by
channel number (text readers, paths, writers, columns, lookahead,
remainder, binary handles, CON set, COM set) — every file feature had
to touch several of them, and CLOSE had to clean each separately.

New package eu.svjatoslav.crtbasic.io:

- Channel: one holder for all per-channel state (kind, reader, writer,
  column, lookahead, remainder, byte handle) — new per-channel facts
  become fields, not new maps
- FileHandle: the in-memory BINARY/RANDOM file image (moved out of
  Interpreter, accessor-encapsulated)
- DosPaths: DOS path resolution (backslashes, drive letters,
  case-insensitive components, drive-root anchoring) + CHDIR/MKDIR
- IoSubsystem: the channel table and all operations — OPEN/CLOSE,
  FREEFILE, EOF/LOF/LOC/SEEK, readLine/readItem (INPUT # tokenizer),
  readBytes (INPUT$ file form), PRINT # with 14-column zones and
  column-255 wrap, NAME, KILL wildcards, SHELL "DIR" listings
- ConsoleOutput/IoErrors bridges keep the interpreter's screen and
  ON ERROR semantics without io depending back on it

CP437 helpers consolidate on the existing Cp437 class (stringOf/
bytesOf/charset); Interpreter's private copies are gone. Docs: the
BINARY/RANDOM row no longer claims FIELD (never implemented).

Verification: 93/93 tests green; the full QB45 EXAMPLES suite re-run
through the GameDriver — every deterministic screen pixel-identical
to the pre-refactor captures, CRLF/ENTAB outputs byte-exact, WHEREIS
recursive SHELL-DIR scan pixel-identical.

7 weeks agofeat: run the full QB45 EXAMPLES suite (23 programs) oracle-verified
Svjatoslav Agejenko [Thu, 3 Sep 2026 20:13:51 +0000 (23:13 +0300)]
feat: run the full QB45 EXAMPLES suite (23 programs) oracle-verified

Implements every language feature the classic QuickBASIC 4.5 example
programs (qbasicapps/QB45/EXAMPLES) exercise, verified pixel- and
byte-exact against QB.EXE /RUN under DOSBox (xdotool-driven key
scripts, DOSBox Ctrl-F5 captures as ground truth):

- Records: TYPE/END TYPE, fixed STRING*n fields, member access
  (x.field), arrays of records, LEN(recvar) = record byte size
- Files: OPEN FOR BINARY/RANDOM with LEN=n and bare channel numbers,
  FIELD, typed GET/PUT, SEEK statement/function, LOF, LOC, FREEFILE
  (bare form too), INPUT$(n,#ch), DOS-absolute paths resolve against
  the emulated drive root, INPUT$/GET/SEEK on text channels via lazy
  byte handle
- Statements: EXIT FOR/DO/SUB/FUNCTION, STATIC procedures (SHARED
  names resolve to module scope before static locals), LINE INPUT
  "prompt",var, MID$ assignment, PRINT USING (numeric field width =
  spec char count), NAME, KILL with DOS wildcards, SHELL "DIR > file"
  (WHEREIS.BAS-compatible 8.3 listing), ERR error codes, COMMAND$,
  INSTR, &H&/&O& long-suffixed literals, OPEN COM1 null-modem
- Graphics: WINDOW / WINDOW SCREEN (plain form is Cartesian),
  graphics VIEW with border that survives plain CLS, PMAP, DRAW macro
  language (U/D/L/R/E/F/G/H, M, B, N, C, S, TA, P), PALETTE /
  PALETTE USING, CLS 0/1/2, full 5-slot LOCATE, LINE -STEP and
  16-bit dash style, pattern-string PAINT, PRESET; CIRCLE under
  WINDOW keeps y-radius visually round (mode aspect, not y-scale);
  PUT under a Cartesian WINDOW anchors the region's logical top-left;
  same-mode SCREEN page flips preserve video memory
- Input: ALT+letter reaches programs as CHR$(0)+BIOS scancode
  (KeyboardQueue VK_ALT_BASE scheme)

Verified: BALLPSET, BALLXOR, BAR, CAL, CHECK, COLORS, CRLF, CUBE,
EDPAT, ENTAB, FILERR, FLPT, INDEX, MANDEL, PALETTE, PLOTTER,
QLBDUMP, SEARCH, SINEWAVE, STRTONUM, TERMINAL, TOKEN, WHEREIS —
screenshots match the oracle (many pixel-identical), file outputs
(CRLF TEST.BAK, ENTAB OUT.TXT, INDEX STOCK.DAT) byte-identical.
TERMINAL also hits "Device timeout" in DOSBox itself (no COM1) —
environment limit, not a program bug. 93/93 tests green.
Documentation/language/index.org updated with all new statements.

7 weeks agofeat(install): install system-wide under /usr/local with preflight checks
Svjatoslav Agejenko [Sun, 30 Aug 2026 21:47:41 +0000 (00:47 +0300)]
feat(install): install system-wide under /usr/local with preflight checks

- abort early with a clear error if mvn is missing or the available
  JDK is older than 21 (handles legacy 1.8 version scheme)
- hardcode the prefix to /usr/local so all users get the jar,
  launcher and desktop entry; drop CRT_BASIC_PREFIX and the per-user
  ~/.local default; require sudo and fail fast before building
- hide the .desktop entry from the app menu (NoDisplay) while keeping
  the text/x-basic "Open With" association
- document dependencies, install layout and window mouse-grab
  behavior in index.org; refresh crtbasic.png

7 weeks agofeat: broaden QB45 compatibility across interpreter, mouse, and grab
Svjatoslav Agejenko [Sat, 29 Aug 2026 22:54:42 +0000 (01:54 +0300)]
feat: broaden QB45 compatibility across interpreter, mouse, and grab

Driven by making more qbasicapps programs run correctly:

- interp: cross-block GOSUB/RETURN. GosubJump collects the origin
  context chain as it unwinds; RETURN rebuilds the nested origin via a
  pending resume inside the pc loop's try, so enclosing loops keep
  iterating and later GOSUBs still resolve (truth.bas NOT handling)
- interp: Cell stores arrays separately from scalars, so a scalar and
  an array with the same name coexist like in QB45 (3D graph.bas)
- interp: LINE accepts the omitted-start form, drawing from the
  graphics cursor (Interferogram.bas)
- parser: THEN may be omitted before GOTO/GOSUB; punctuation peeks are
  type-aware so string literals like ASC(")") parse (Lottery
  analysis.bas, truth.bas)
- machine: mouse TSR tracks held buttons and re-arms the latch on
  every read-and-reset, like the real per-tick poll (mousedrv.bas)
- frontend: one transparent grab overlay per screen fixes cursor
  escape on multi-monitor setups; mouseReleased forwarded as button-up;
  -Dcrtbasic.grab.auto diagnostic
- drivers: GameDriver gains --block, $ mid-run screenshot token, and
  b<n>- mouse release token
- tests: regression coverage for all of the above

8 weeks agofeat: add emulated mouse driver, file output, and GUI mouse grab
Svjatoslav Agejenko [Thu, 27 Aug 2026 22:08:34 +0000 (01:08 +0300)]
feat: add emulated mouse driver, file output, and GUI mouse grab

- machine/: 1 MiB emulated RAM behind DEF SEG/PEEK/POKE, with the
  qbext mouse TSR always loaded (INT 79h vector, 8-byte table: magic,
  X/Y accumulators, button latch; accumulate + read-and-reset)
- mouse input: SwingFrontend pushes scaled window deltas and button
  latches; GameDriver gains scripted --mouse deltas/buttons for
  headless verification; mousedrv.bas runs unmodified
- GUI mouse grab: first click captures (transparent always-on-top
  overlay spans all screens, cursor hidden), Ctrl+F10 releases; no
  Robot warping (Wayland RemoteDesktop popup, Xvfb EDT hang)
- language: VAL parses &H/&O with signed literal semantics, HEX$,
  LOCATE with omitted column, OPEN FOR OUTPUT/APPEND + PRINT # (CRLF,
  console formatting), CHDIR/MKDIR with DOS path semantics
  (backslashes, drive letters, case-insensitive components)
- SCREEN 13 default foreground corrected to 15 (QB.EXE/DOSBox oracle;
  255 is black in the BIOS palette, so text printed black-on-black)

8 weeks agofeat: make SOUND/BEEP audible, add SPACE$ and LET, guard empty loops
Svjatoslav Agejenko [Wed, 26 Aug 2026 17:40:32 +0000 (20:40 +0300)]
feat: make SOUND/BEEP audible, add SPACE$ and LET, guard empty loops

- SoundQueue renders a phase-continuous square wave (44.1 kHz 16-bit)
  through a daemon-fed SourceDataLine in GUI mode; falls back to
  timing-only in headless/tests and when no audio device opens
- SPACE$ returns n spaces (previously misread as an array reference,
  surfacing as "Subscript out of range")
- LET keyword parses as plain scalar or array-element assignment
- empty-body DO/WHILE spins now enforce the --steps budget, so
  headless INKEY$ wait loops terminate instead of hanging

2 months agofeat: add console INPUT, BEEP, nested-label GOTO and screen fixes
Svjatoslav Agejenko [Sat, 22 Aug 2026 15:28:12 +0000 (18:28 +0300)]
feat: add console INPUT, BEEP, nested-label GOTO and screen fixes

- Console INPUT: prompt with ';' (adds "? ") or ',' (prompt only),
  echoed line editing with Backspace, comma-delimited items; headless
  runs give up after 5 s of silence instead of hanging.
- BEEP desugars to SOUND 800, 4.55 (the classic ~1/4 s beep).
- GOTO may jump INTO a label nested in IF/FOR/DO bodies with flat
  QBasic semantics: headers are not re-evaluated and enclosing loops
  resume with current variable values (checkers2.bas "GoTo 8").
- AS-typed SUB/FUNCTION parameters claim their bare name inside the
  body (winning over DEFxxx defaults), while suffix parameters like
  a$ leave the bare name as a distinct DEFxxx-typed variable.
- Bare SUB calls accept a parenthesized first argument; the parens
  parse as grouping and the comma list continues the arguments.
- Graphics modes default the foreground to the mode's highest
  attribute (1/3/15/255) instead of always 15, so colorless PAINT
  stays inside the walls (checkers.bas SCREEN 2 flood); SCREEN 2's
  palette now maps attribute 1 to white.
- 640x200 modes (SCREEN 2, 8) are doubled vertically in the Swing
  window and in dumpPng to match their 4:3 CRT pixel aspect; the
  window re-packs on every mode switch via a mode listener.

2 months agofeat: add GOSUB/RETURN, CONST, SWAP, WIDTH, ELSEIF, and screen capture
Svjatoslav Agejenko [Sat, 22 Aug 2026 10:28:41 +0000 (13:28 +0300)]
feat: add GOSUB/RETURN, CONST, SWAP, WIDTH, ELSEIF, and screen capture

- Language: GOSUB/RETURN (nested calls, same-block labels), CONST
  module-level constants, SWAP for scalars and array elements, ELSEIF
  chains in block IF, and bare parameterless FUNCTION calls.
- WIDTH statement for SCREEN 0: 80x25, 40x25 (double-wide glyphs) and
  80x50 (8x8 font) text grids; clears the screen like the original.
- PAINT gets classic border semantics: fills every reachable non-border
  pixel instead of confining to the seed color; off-screen seeds clip.
- INPUT # follows classic delimiters: numbers split on comma, space or
  newline; strings on comma/newline with quotes stripped; empty slots
  read as 0 / ""; statements may share and span lines.
- VIEW PRINT now confines printing and scrolling to the band; bare
  VIEW PRINT restores the full screen.
- New capture package: ScreenRecorder samples the visual page at 30 Hz
  into a lossless XOR-delta+deflate .crtrec; CrtrecEncoder pipes raw
  frames to ffmpeg (H.264, CRF 18 with a VBV cap). Swing frontend:
  F12 screenshot, Shift+F12 recording, background transcode queue that
  drains before exit; --encode CLI re-encodes a recording later.
- Docs: README.org replaced by AGENTS.org (agent operating guide);
  language reference updated for the new statements and semantics.

2 months agodocs: simplify index page and add source code section
Svjatoslav Agejenko [Thu, 20 Aug 2026 21:08:56 +0000 (00:08 +0300)]
docs: simplify index page and add source code section

- Trim the intro to a one-liner and link to the example BASIC
  programs collection
- Shorten quick start to install + interactive run, noting the
  Java 21+ requirement up front
- Append a Source code section with CC0 license, author info and
  download/clone links

2 months agoinitial commit
Svjatoslav Agejenko [Thu, 20 Aug 2026 20:52:18 +0000 (23:52 +0300)]
initial commit