From 15af9ad12cdd3b3553956589c1617066c626aa4e Mon Sep 17 00:00:00 2001 From: Eremey Valetov Date: Mon, 30 Mar 2026 16:50:43 -0400 Subject: [PATCH] Replace em dashes with ASCII -- across all docs Remove Unicode em dashes (U+2014) and en dashes (U+2013) from all Markdown files. Use ASCII -- for parenthetical breaks and - for hyphenation, matching standard plain-text conventions. --- README.md | 14 +++--- docs/architecture.md | 58 +++++++++++----------- docs/getting-started.md | 2 +- docs/index.md | 24 +++++----- docs/language-reference.md | 98 +++++++++++++++++++------------------- docs/roadmap.md | 8 ++-- 6 files changed, 102 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 0bb5340..6c1659e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A portable C reimplementation of Microsoft GW-BASIC, using the [original 8088 assembly source](https://github.com/microsoft/GW-BASIC) (released by Microsoft in 2020) as the authoritative reference. -This is not a transpilation — it reimplements the algorithms in clean C11 +This is not a transpilation -- it reimplements the algorithms in clean C11 with modern data structures while targeting bug-compatible behavior. ## Building @@ -14,12 +14,12 @@ cmake -B build && cmake --build build ``` Requires a C11 compiler and CMake 3.10+. PulseAudio (`libpulse-simple`) -is optional — detected at build time for `SOUND`/`BEEP`/`PLAY` support. +is optional -- detected at build time for `SOUND`/`BEEP`/`PLAY` support. Builds three targets: -- `gwbasic` — the interpreter -- `gwbasic-compile` — the ahead-of-time compiler -- `libgwrt.a` — runtime library for compiled programs +- `gwbasic` -- the interpreter +- `gwbasic-compile` -- the ahead-of-time compiler +- `libgwrt.a` -- runtime library for compiled programs ## Usage @@ -78,7 +78,7 @@ jupyter notebook # select "GW-BASIC 2026" kernel ## What Works -100% token coverage — all 144 GW-BASIC tokens are implemented. +100% token coverage -- all 144 GW-BASIC tokens are implemented. **Data types:** INTEGER (%), SINGLE (!), DOUBLE (#), STRING ($) @@ -109,7 +109,7 @@ jupyter notebook # select "GW-BASIC 2026" kernel ## Tests -72 interpreter tests, 14 kernel tests, 69 compiler tests — all passing. +72 interpreter tests, 14 kernel tests, 69 compiler tests -- all passing. ```bash bash tests/run_tests.sh # interpreter diff --git a/docs/architecture.md b/docs/architecture.md index 5bdde6c..c5d5499 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -14,8 +14,8 @@ Source text → Tokenizer (CRUNCH) → Token stream HAL (platform I/O) ``` -The interpreter mirrors the original GW-BASIC's internal pipeline, which — like -most Microsoft interpreters of the era — is a tight loop around three core +The interpreter mirrors the original GW-BASIC's internal pipeline, which -- like +most Microsoft interpreters of the era -- is a tight loop around three core routines. CRUNCH tokenizes source lines into a compact byte stream. NEWSTT dispatches each statement. FRMEVL evaluates expressions. All platform I/O goes through a HAL vtable (`hal_ops_t`), so the core interpreter has no idea @@ -33,8 +33,8 @@ HAL writes straight to stdout. | Tokenizer (CRUNCH/LIST) | `tokenizer.c` | GWMAIN.ASM | | Expression evaluator | `eval.c` | GWEVAL.ASM | | Execution loop + control flow | `interp.c` | BINTRP.ASM | -| TUI screen editor | `tui.c` | — | -| Graphics engine | `graphics.c` | — | +| TUI screen editor | `tui.c` | -- | +| Graphics engine | `graphics.c` | -- | | Token/keyword tables | `tokens.c`, `tokens.h` | IBMRES.ASM | | Error handling | `error.c` | GWDATA.ASM | | Integer arithmetic | `math_int.c` | MATH1.ASM | @@ -47,65 +47,65 @@ HAL writes straight to stdout. | File I/O + random access | `fileio.c` | BIPTRG.ASM | | Program I/O (SAVE/LOAD) | `program_io.c` | BIMISC.ASM | | INPUT/LINE INPUT | `input.c` | BINTRP.ASM | -| Sound engine | `sound.c` | — | -| Virtual memory (PEEK/POKE) | `virmem.c` | — | -| Hardware I/O ports | `portio.c` | — | +| Sound engine | `sound.c` | -- | +| Virtual memory (PEEK/POKE) | `virmem.c` | -- | +| Hardware I/O ports | `portio.c` | -- | | String space pool + GC | `strpool.c` | GWEVAL.ASM (GETSPA/GARBAG) | -| AOT compiler analysis | `analysis.c` | — | -| AOT compiler codegen | `codegen.c` | — | -| Compiled program runtime | `gwrt.c` | — | +| AOT compiler analysis | `analysis.c` | -- | +| AOT compiler codegen | `codegen.c` | -- | +| Compiled program runtime | `gwrt.c` | -- | | Platform abstraction | `hal_posix.c` | OEM*.ASM | ## Source Layout ``` -src/ — core interpreter + compiler (27 files) -include/ — headers (18 files) -platform/ — HAL backends (1 file) -gwbasickernel/ — Jupyter notebook kernel (Python, 6 files) -tests/ — 72 automated test programs, 4 classic interactive programs, compat harness -docs/ — Sphinx documentation +src/ -- core interpreter + compiler (27 files) +include/ -- headers (18 files) +platform/ -- HAL backends (1 file) +gwbasickernel/ -- Jupyter notebook kernel (Python, 6 files) +tests/ -- 72 automated test programs, 4 classic interactive programs, compat harness +docs/ -- Sphinx documentation ``` ## TUI Architecture The TUI (`tui.c`) implements the classic GW-BASIC full-screen editor: -- **Screen buffer** — `tui_cell_t *screen` is dynamically allocated at +- **Screen buffer** -- `tui_cell_t *screen` is dynamically allocated at `rows × cols` (default 25×80, or full terminal size with `--full`). Each cell stores a character and color attribute, accessed via `TUI_CELL(r, c)`. -- **HAL interception** — `tui_init()` swaps HAL function pointers so all +- **HAL interception** -- `tui_init()` swaps HAL function pointers so all existing PRINT/LIST/error output automatically goes through the screen buffer. No changes needed to `print.c`, `error.c`, or most of `interp.c`. -- **Line editor** — `tui_read_line()` implements the defining GW-BASIC UX: +- **Line editor** -- `tui_read_line()` implements the defining GW-BASIC UX: free cursor movement with arrow keys, and pressing Enter on any screen line re-enters that line's content as BASIC input. -- **Function keys** — F1-F10 with default GW-BASIC bindings, configurable via +- **Function keys** -- F1-F10 with default GW-BASIC bindings, configurable via the `KEY n, "string"` statement. `KEY ON` shows the bar on the bottom row. -- **Break handling** — SIGINT sets a flag checked each statement in the run loop. +- **Break handling** -- SIGINT sets a flag checked each statement in the run loop. ## Design Decisions ### Relation to Original Assembly Microsoft [released the original GW-BASIC source](https://github.com/microsoft/GW-BASIC) -in 2020 — 43,771 lines of 8088 assembly spread across 43 `.ASM` files, complete +in 2020 -- 43,771 lines of 8088 assembly spread across 43 `.ASM` files, complete with Greg Whitten's comments and Neil Konzen's transcendental math routines (which are, frankly, impressive for 16-bit fixed-point). This reimplementation -uses that assembly as a reference, not as input to a transliterator — the +uses that assembly as a reference, not as input to a transliterator -- the algorithms are reimplemented in idiomatic C with modern data structures. ### Key Differences from the Original -- **IEEE 754 floating point** — MBF (Microsoft Binary Format) conversion is used +- **IEEE 754 floating point** -- MBF (Microsoft Binary Format) conversion is used at the binary save/load boundary and for file I/O (CVI/CVS/CVD, MKI$/MKS$/MKD$), matching the original's on-disk format -- **Dynamic memory allocation** — `malloc`/`free` instead of a 64KB segment layout -- **String space pool** — 32KB contiguous pool with compacting GC at statement +- **Dynamic memory allocation** -- `malloc`/`free` instead of a 64KB segment layout +- **String space pool** -- 32KB contiguous pool with compacting GC at statement boundaries, matching the original's GETSPA/GARBAG approach -- **`setjmp`/`longjmp`** — for error recovery, matching the original's stack reset +- **`setjmp`/`longjmp`** -- for error recovery, matching the original's stack reset behavior -- **ANSI terminal** — TUI uses ANSI escape sequences and alternate screen buffer +- **ANSI terminal** -- TUI uses ANSI escape sequences and alternate screen buffer instead of direct CGA memory access -- **Dynamic screen buffer** — allocated at runtime based on terminal size, rather +- **Dynamic screen buffer** -- allocated at runtime based on terminal size, rather than hardcoded to 25×80 diff --git a/docs/getting-started.md b/docs/getting-started.md index f974d7a..96dcc28 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,7 +4,7 @@ - C11 compiler (GCC or Clang) - CMake 3.10+ -- PulseAudio development library (`libpulse-simple`) — optional, for `SOUND`/`BEEP`/`PLAY` +- PulseAudio development library (`libpulse-simple`) -- optional, for `SOUND`/`BEEP`/`PLAY` On Debian/Ubuntu: diff --git a/docs/index.md b/docs/index.md index ed1ea7e..8958d3e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,32 +4,32 @@ A portable C reimplementation of Microsoft GW-BASIC, using the [original 8088 assembly source](https://github.com/microsoft/GW-BASIC) (released by Microsoft in 2020) as the authoritative reference. -This is not a transpilation — it reimplements the algorithms in clean C11 +This is not a transpilation -- it reimplements the algorithms in clean C11 with modern data structures while targeting bug-compatible behavior. Unlike the original assembly (43,771 lines across 43 `.ASM` files), this version is structured as modular C suitable for new feature development. ## Highlights -- **Authentic full-screen editor** — dynamically sized screen buffer (25×80 +- **Authentic full-screen editor** -- dynamically sized screen buffer (25×80 default, full terminal with `--full`), free cursor movement, Enter-on-any-line, F1-F10 function keys, Insert/Overwrite toggle -- **Binary and ASCII file formats** — `SAVE` writes tokenized binary by default, +- **Binary and ASCII file formats** -- `SAVE` writes tokenized binary by default, `LOAD` auto-detects format (just like the real thing) -- **INKEY$ extended keys** — arrow keys, F-keys, and navigation keys return proper +- **INKEY$ extended keys** -- arrow keys, F-keys, and navigation keys return proper `CHR$(0)` + scan code two-byte sequences -- **Sixel graphics** — `SCREEN 1`/`SCREEN 2` rendering in compatible terminals -- **Sound** — `SOUND`, `BEEP`, `PLAY` (MML) via PulseAudio, plus continuous tone +- **Sixel graphics** -- `SCREEN 1`/`SCREEN 2` rendering in compatible terminals +- **Sound** -- `SOUND`, `BEEP`, `PLAY` (MML) via PulseAudio, plus continuous tone via `OUT` (8253 PIT / PPI speaker emulation) -- **Hardware I/O** — `OUT`, `INP`, `WAIT` port emulation (PIT, PPI, CGA, COM1, +- **Hardware I/O** -- `OUT`, `INP`, `WAIT` port emulation (PIT, PPI, CGA, COM1, game port) for classic programs that drive hardware directly -- **Full file I/O** — sequential, random-access, SAVE/LOAD/MERGE/CHAIN/COMMON -- **Printer output** — `LPRINT`/`LLIST` to file or real hardware via `--lpt` -- **Classic programs** — Hamurabi, Lunar Lander, Gunner, and Diamond from +- **Full file I/O** -- sequential, random-access, SAVE/LOAD/MERGE/CHAIN/COMMON +- **Printer output** -- `LPRINT`/`LLIST` to file or real hardware via `--lpt` +- **Classic programs** -- Hamurabi, Lunar Lander, Gunner, and Diamond from David Ahl's *BASIC Computer Games* (1978) run out of the box -- **Ahead-of-time compiler** — `gwbasic-compile prog.bas -c` produces native +- **Ahead-of-time compiler** -- `gwbasic-compile prog.bas -c` produces native executables via C codegen + GCC (69/69 tests pass, 100%) -- **Jupyter kernel** — inline Sixel graphics, INPUT support, Pygments syntax +- **Jupyter kernel** -- inline Sixel graphics, INPUT support, Pygments syntax highlighting; `pip install -e . && gwbasickernel-install --user` - **72 test programs** with golden-file regression testing and DOSBox-X compatibility testing against real GWBASIC.EXE diff --git a/docs/language-reference.md b/docs/language-reference.md index 7803b1f..7a03315 100644 --- a/docs/language-reference.md +++ b/docs/language-reference.md @@ -32,9 +32,9 @@ ## Memory Functions -- `FRE(x)` — free bytes in the string space pool. `FRE("")` triggers a +- `FRE(x)` -- free bytes in the string space pool. `FRE("")` triggers a garbage collection pass before reporting; `FRE(0)` reports without collecting. -- `VARPTR(var)` / `VARPTR$(var)` — variable address (internal index) +- `VARPTR(var)` / `VARPTR$(var)` -- variable address (internal index) ## Pseudo-variables @@ -140,24 +140,24 @@ which works in terminals like xterm, mlterm, foot, and WezTerm. ### Drawing Commands -- `PSET (x,y), color` / `PRESET (x,y)` — set/reset individual pixels -- `LINE (x1,y1)-(x2,y2), color [,B|BF]` — lines, boxes, filled boxes -- `CIRCLE (cx,cy), r [,color [,start, end [,aspect]]]` — circles and arcs -- `PAINT (x,y), fill, border` — flood fill -- `DRAW string` — turtle graphics mini-language (U/D/L/R/E/F/G/H, M, C, S, B, N) -- `POINT (x,y)` — read pixel color -- `COLOR fg, bg` — set foreground/background colors +- `PSET (x,y), color` / `PRESET (x,y)` -- set/reset individual pixels +- `LINE (x1,y1)-(x2,y2), color [,B|BF]` -- lines, boxes, filled boxes +- `CIRCLE (cx,cy), r [,color [,start, end [,aspect]]]` -- circles and arcs +- `PAINT (x,y), fill, border` -- flood fill +- `DRAW string` -- turtle graphics mini-language (U/D/L/R/E/F/G/H, M, C, S, B, N) +- `POINT (x,y)` -- read pixel color +- `COLOR fg, bg` -- set foreground/background colors ### Sprite Capture and Blit (GET / PUT) -- `GET (x1,y1)-(x2,y2), array` — capture a screen rectangle into an integer array -- `PUT (x,y), array [, action]` — blit a captured sprite back to the screen +- `GET (x1,y1)-(x2,y2), array` -- capture a screen rectangle into an integer array +- `PUT (x,y), array [, action]` -- blit a captured sprite back to the screen The `action` parameter controls how the sprite combines with the existing screen: | Action | Effect | |--------|--------| -| `XOR` (default) | Exclusive OR — drawing twice erases the sprite | +| `XOR` (default) | Exclusive OR -- drawing twice erases the sprite | | `PSET` | Overwrite screen with sprite pixels | | `PRESET` | Overwrite screen with inverted sprite pixels | | `AND` | Bitwise AND of screen and sprite | @@ -169,16 +169,16 @@ matching the original GW-BASIC representation. ### VIEW / WINDOW / PALETTE -- `VIEW [[SCREEN] (x1,y1)-(x2,y2) [,[fill][,border]]]` — define a graphics +- `VIEW [[SCREEN] (x1,y1)-(x2,y2) [,[fill][,border]]]` -- define a graphics viewport. Without `SCREEN`, drawing coordinates are relative to the viewport origin. With `SCREEN`, coordinates remain absolute. Without arguments, resets to full screen. -- `WINDOW [[SCREEN] (x1,y1)-(x2,y2)]` — map logical coordinates onto the +- `WINDOW [[SCREEN] (x1,y1)-(x2,y2)]` -- map logical coordinates onto the viewport. Without `SCREEN`, Y increases upward (Cartesian); with `SCREEN`, Y increases downward. Without arguments, resets to physical coordinates. -- `PALETTE [attribute, color]` — remap a color attribute to a different - physical color (0–15). Without arguments, resets to the default CGA palette. -- `PMAP(coordinate, function)` — convert between logical and physical +- `PALETTE [attribute, color]` -- remap a color attribute to a different + physical color (0-15). Without arguments, resets to the default CGA palette. +- `PMAP(coordinate, function)` -- convert between logical and physical coordinates. Function 0/1 = logical→physical X/Y; 2/3 = physical→logical X/Y. ### Example @@ -250,7 +250,7 @@ free bytes; `FRE(0)` returns the current free count without collecting. ## Hardware I/O (OUT / INP / WAIT) `OUT`, `INP`, and `WAIT` provide access to emulated IBM PC I/O ports, enabling -classic programs that drive hardware directly — speaker tones via the 8253 PIT, +classic programs that drive hardware directly -- speaker tones via the 8253 PIT, CGA palette tricks, and serial port polling. ``` @@ -264,29 +264,29 @@ OUT &H61, INP(&H61) AND &HFC ' speaker off | Port | Device | Behavior | |------|--------|----------| -| `&H42`–`&H43` | 8253 PIT channel 2 | Speaker frequency divisor (1193180 / divisor Hz) | -| `&H61` | PPI Port B | Bits 0–1 control speaker; both set = tone on, either clear = off | +| `&H42`-`&H43` | 8253 PIT channel 2 | Speaker frequency divisor (1193180 / divisor Hz) | +| `&H61` | PPI Port B | Bits 0-1 control speaker; both set = tone on, either clear = off | | `&H3D8` | CGA mode control | Mode register; writes with changed mode bits trigger `SCREEN` changes | -| `&H3D9` | CGA color select | Background color (bits 0–3) and palette select (bit 5) | +| `&H3D9` | CGA color select | Background color (bits 0-3) and palette select (bit 5) | | `&H201` | Game port | Returns `&HF0` (no joystick connected) | -| `&H3F8`–`&H3FE` | COM1 serial | Minimal stub; LSR (`&H3FD`) returns `&H60` (transmitter ready) | -| Default | — | Reads return `&HFF` (floating bus), writes silently discarded | +| `&H3F8`-`&H3FE` | COM1 serial | Minimal stub; LSR (`&H3FD`) returns `&H60` (transmitter ready) | +| Default | -- | Reads return `&HFF` (floating bus), writes silently discarded | ### Related Functions -- `INP(port)` — read a byte from an I/O port -- `STICK(n)` — joystick axis position (returns 128 = center, n = 0–3) -- `STRIG(n)` — joystick button state (returns 0 = not pressed) -- `WAIT port, mask [, xor_mask]` — busy-wait until `(INP(port) XOR xor_mask) AND mask` is nonzero; Ctrl+C breaks out -- `MOTOR [n]` — accepted and silently ignored (cassette motor control) +- `INP(port)` -- read a byte from an I/O port +- `STICK(n)` -- joystick axis position (returns 128 = center, n = 0-3) +- `STRIG(n)` -- joystick button state (returns 0 = not pressed) +- `WAIT port, mask [, xor_mask]` -- busy-wait until `(INP(port) XOR xor_mask) AND mask` is nonzero; Ctrl+C breaks out +- `MOTOR [n]` -- accepted and silently ignored (cassette motor control) When the PPI speaker bits are set, the PIT frequency divisor is used to generate a continuous tone via PulseAudio (same backend as `SOUND` / `PLAY`). ## Environment Variables -- `ENVIRON "var=value"` — set an environment variable (uses the C `putenv()` call) -- `ENVIRON$("var")` — read an environment variable (returns "" if not set) +- `ENVIRON "var=value"` -- set an environment variable (uses the C `putenv()` call) +- `ENVIRON$("var")` -- read an environment variable (returns "" if not set) ``` ENVIRON "GREETING=Hello" @@ -313,20 +313,20 @@ The following device-related statements and functions are accepted for compatibility with programs that reference them, but return stub values since there is no real device hardware: -- `ERDEV` — device error code (always 0) -- `ERDEV$` — device error name (always "") -- `IOCTL [#n,] string` — device control string output (accepted, ignored) -- `IOCTL$(n)` — device control string input (always "") -- `COM ON` / `COM OFF` / `COM STOP` — serial port event trapping (accepted, ignored) -- `LCOPY [n]` — screen dump to printer (accepted, ignored) -- `CALL` / `CALLS` — machine code execution (raises Illegal function call) -- `RESET` — close all open files (equivalent to `CLOSE`) +- `ERDEV` -- device error code (always 0) +- `ERDEV$` -- device error name (always "") +- `IOCTL [#n,] string` -- device control string output (accepted, ignored) +- `IOCTL$(n)` -- device control string input (always "") +- `COM ON` / `COM OFF` / `COM STOP` -- serial port event trapping (accepted, ignored) +- `LCOPY [n]` -- screen dump to printer (accepted, ignored) +- `CALL` / `CALLS` -- machine code execution (raises Illegal function call) +- `RESET` -- close all open files (equivalent to `CLOSE`) ## Sound -- `SOUND frequency, duration` — play a tone (frequency in Hz, duration in clock ticks) -- `BEEP` — play the default beep -- `PLAY string` — Music Macro Language (MML) string for melodies +- `SOUND frequency, duration` -- play a tone (frequency in Hz, duration in clock ticks) +- `BEEP` -- play the default beep +- `PLAY string` -- Music Macro Language (MML) string for melodies Sound output uses PulseAudio when available; commands are silently ignored otherwise. @@ -354,10 +354,10 @@ Default F1-F10 bindings match the original GW-BASIC: | F4 | `SAVE"` | F9 | `KEY ` | | F5 | `CONT` + Enter | F10 | `SCREEN 0,0,0` + Enter | -- `KEY ON` — show the function key bar on line 25 -- `KEY OFF` — hide the bar -- `KEY LIST` — display all definitions -- `KEY n, "string"` — redefine a function key +- `KEY ON` -- show the function key bar on line 25 +- `KEY OFF` -- hide the bar +- `KEY LIST` -- display all definitions +- `KEY n, "string"` -- redefine a function key ### Piped Mode @@ -367,10 +367,10 @@ suitable for scripting and test harnesses. ### Program Editing -- `EDIT [linenum]` — display a program line for editing in the TUI; press Enter to re-store it -- `AUTO [start][,increment]` — automatic line numbering mode -- `RENUM [new][,[old][,increment]]` — renumber program lines (patches all GOTO/GOSUB references) -- `DELETE range` — delete program lines (`DELETE 10-50`, `DELETE -100`, `DELETE 200-`) +- `EDIT [linenum]` -- display a program line for editing in the TUI; press Enter to re-store it +- `AUTO [start][,increment]` -- automatic line numbering mode +- `RENUM [new][,[old][,increment]]` -- renumber program lines (patches all GOTO/GOSUB references) +- `DELETE range` -- delete program lines (`DELETE 10-50`, `DELETE -100`, `DELETE 200-`) ## Event Trapping diff --git a/docs/roadmap.md b/docs/roadmap.md index 13796ba..6df0348 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -52,10 +52,10 @@ ERDEV/ERDEV$, IOCTL/IOCTL$, LCOPY, DATE$/TIME$ assignment, CALL, COM. ### Jupyter Kernel (v0.15.0) -`gwbasickernel/` — Jupyter notebook kernel using the persistent subprocess +`gwbasickernel/` -- Jupyter notebook kernel using the persistent subprocess model with sentinel protocol. -- **Inline Sixel graphics** — pure-Python Sixel decoder renders SCREEN +- **Inline Sixel graphics** -- pure-Python Sixel decoder renders SCREEN commands as inline PNG images in the notebook - **INPUT statement support** via Jupyter stdin protocol - **Pygments syntax highlighting** for code cells @@ -67,9 +67,9 @@ Install: `pip install -e . && gwbasickernel-install --user` ## Next Up ### IDE Integration -- **VS Code extension** — syntax highlighting (TextMate grammar), snippets, +- **VS Code extension** -- syntax highlighting (TextMate grammar), snippets, run/debug tasks, integrated terminal runner -- **JetBrains plugin (IntelliJ/CLion)** — syntax highlighting, code completion, +- **JetBrains plugin (IntelliJ/CLion)** -- syntax highlighting, code completion, run configurations, debugger integration (breakpoints via `STOP`, variable inspection), structure view (line number outline)