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.
This commit is contained in:
Eremey Valetov
2026-03-30 16:50:43 -04:00
parent b22ccccf88
commit 15af9ad12c
6 changed files with 102 additions and 102 deletions
+7 -7
View File
@@ -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
+29 -29
View File
@@ -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
+1 -1
View File
@@ -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:
+12 -12
View File
@@ -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
+49 -49
View File
@@ -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 (015). 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 01 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 03) 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 = 03)
- `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
+4 -4
View File
@@ -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)