summary |
shortlog | log |
commit |
commitdiff |
tree
first ⋅ prev ⋅ next
Svjatoslav Agejenko [Thu, 3 Sep 2026 21:09:29 +0000 (00:09 +0300)]
docs(architecture): package map reflects the io/format/flow extraction
Adds the io package row (IoSubsystem/Channel/FileHandle/DosPaths/
QbBinaryCodec), lists PrintFormat/Jumps/LabelSearch under interp, and
fills in the previously undocumented machine and capture packages.
The audio row no longer claims "no audio yet".
Svjatoslav Agejenko [Thu, 3 Sep 2026 21:00:38 +0000 (00:00 +0300)]
refactor(interp): extract PRINT formatting into PrintFormat
The classic number style (SINGLE 7-digit / DOUBLE 16-digit, D±xx/E±xx
scientific, sign padding), the PRINT USING field walker, and the
14-column zone arithmetic lived in Interpreter; the zone math was
duplicated in two places with different wrap rules (console wraps at
its column count, file output at 255).
PrintFormat now owns all of it: number()/singleNumber(), usingFormat()
with an item-value callback (items still evaluate in the interpreter),
numericField(), and nextZone() shared by the console printZoneAdvance
and IoSubsystem's PRINT # zones — the wrap rule is now an explicit
parameter at both call sites instead of two divergent inline copies.
Verification: 93/93 tests green; CHECK (PRINT USING), FLPT (STR$/HEX$)
and BAR (PRINT USING + records) re-run pixel-identical.
Svjatoslav Agejenko [Thu, 3 Sep 2026 20:55:40 +0000 (23:55 +0300)]
refactor(interp): move control-flow signals and label search out of Interpreter
The GOTO/GOSUB plumbing (jump exception types, the GosubFrame chain,
nested-label search) sat inline in Interpreter between the variable
cell model and the statement dispatch, where it read as noise around
statement semantics.
- Jumps.java groups the seven stackless control-flow signals
(ExitJump, GotoJump, GosubJump, GosubReturnJump, ResumeJump,
ProgramEnd, GosubFrame) with their semantics documented once, up
front
- LabelSearch.java holds the pure Ast utilities (findNestedLabel,
childListsOf, indexOfIdentity) — no interpreter state involved
resumeNestedLabel/resumeChain/continueLoopAfterResume stay in
Interpreter: they are interlocked with execBlockFrom's pc loop and
the GOSUB rebuild state, and moving them would distort rather than
clarify. Also fixes a doubled/orphan javadoc above the old GosubFrame.
Verification: 93/93 tests green (the suite covers nested-label GOTO
and cross-block GOSUB/RETURN); EDPAT (EXIT DO) and BAR re-run
pixel-identical.
Svjatoslav Agejenko [Thu, 3 Sep 2026 20:51:52 +0000 (23:51 +0300)]
refactor(io): unify QB45 binary GET/PUT layout in QbBinaryCodec
The little-endian byte layout (INTEGER 2, LONG 4, SINGLE 4, DOUBLE 8,
fixed STRING*n space-padded) was written out four times in Interpreter:
scalar and record variants of both decode and encode, and the
record-target lookup (scalar cell vs array element) was copy-pasted
between decodeInto and encodeFrom.
QbBinaryCodec owns the layout now: decodeScalar/encodeScalar,
decodeRecord/encodeRecord over field descriptors. Numeric range
narrowing stays in the interpreter (it raises the trappable Overflow);
the codec is pure byte layout. Interpreter gains one recordTarget()
lookup used by byteSizeOf, decodeInto and encodeFrom.
Verification: 93/93 tests green; INDEX (RANDOM record GET/PUT),
QLBDUMP (BINARY GET into TYPE records) and SEARCH re-run — all
pixel-identical to the pre-refactor captures.
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.
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.
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
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
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)
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
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.
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.
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
Svjatoslav Agejenko [Thu, 20 Aug 2026 20:52:18 +0000 (23:52 +0300)]
initial commit