diff --git a/docs/architecture.md b/docs/architecture.md index 2e5ac01..1c801a3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -49,15 +49,16 @@ HAL writes straight to stdout. | INPUT/LINE INPUT | `input.c` | BINTRP.ASM | | Sound engine | `sound.c` | — | | Virtual memory (PEEK/POKE) | `virmem.c` | — | +| Hardware I/O ports | `portio.c` | — | | Platform abstraction | `hal_posix.c` | OEM*.ASM | ## Source Layout ``` -src/ — core interpreter (21 files) -include/ — headers (13 files) +src/ — core interpreter (22 files) +include/ — headers (14 files) platform/ — HAL backends (1 file) -tests/ — 66 automated test programs, 4 classic interactive programs, compat harness +tests/ — 70 automated test programs, 4 classic interactive programs, compat harness docs/ — Sphinx documentation ``` diff --git a/docs/development.md b/docs/development.md index 649450a..3bf3556 100644 --- a/docs/development.md +++ b/docs/development.md @@ -18,10 +18,11 @@ | 0.12.0 | | BSAVE/BLOAD, TUI ANSI 16-color rendering, CGA graphics framebuffer PEEK/POKE, keyboard shift flags | | 0.13.0 | | VIEW/WINDOW/PALETTE graphics, PMAP function, MBF float format for CVS/CVD/MKS$/MKD$ | | 0.14.0 | | MBF binary file compatibility (IEEE↔MBF at save/load boundary), fix binary loader null-byte truncation, fix DRAW M/S/A bugs, add TA/=var;/X substring | +| 0.15.0 | | Hardware I/O simulator: OUT/INP/WAIT/MOTOR statements, port emulation (8253 PIT, PPI speaker, CGA mode/color, COM1, game port), continuous tone via PulseAudio | ## Tests -68 test programs in `tests/programs/`, plus 4 classic interactive programs in +70 test programs in `tests/programs/`, plus 4 classic interactive programs in `tests/classic/` (Hamurabi, Lunar Lander, Gunner, Diamond from David Ahl's *BASIC Computer Games*). @@ -31,10 +32,10 @@ Run the full automated suite: bash tests/run_tests.sh ``` -Each test has a 5-second timeout. 62 tests have `.expected` golden files -for output regression detection. Three timing-dependent tests (datetime, -on_timer, timer_stop) and one visual test (color_test) run without golden -comparison. +Each test has a 5-second timeout. 65 tests have `.expected` golden files +for output regression detection. Tests without golden comparison: datetime, +on_timer, timer_stop (timing-dependent), color_test (visual), play_music +(audio duration). ### Compatibility Testing diff --git a/docs/index.md b/docs/index.md index 8c419ed..1cfb5c3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,12 +19,15 @@ version is structured as modular C suitable for new feature development. - **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 +- **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, + 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 David Ahl's *BASIC Computer Games* (1978) run out of the box -- **66 test programs** with golden-file regression testing and DOSBox-X +- **70 test programs** with golden-file regression testing and DOSBox-X compatibility testing against real GWBASIC.EXE - **MIT License** diff --git a/docs/language-reference.md b/docs/language-reference.md index bf848ed..d066981 100644 --- a/docs/language-reference.md +++ b/docs/language-reference.md @@ -60,6 +60,7 @@ type suffixes (`%`, `!`, `#`) | Graphics | `PSET`, `PRESET`, `LINE`, `CIRCLE`, `DRAW`, `PAINT`, `GET`, `PUT`, `VIEW`, `WINDOW`, `PALETTE`, `PMAP` | | Sound | `SOUND`, `BEEP`, `PLAY` (MML parser, PulseAudio backend) | | Memory | `DEF SEG`, `PEEK`, `POKE`, `BSAVE`, `BLOAD` | +| Hardware I/O | `OUT`, `INP`, `WAIT`, `MOTOR`, `STICK`, `STRIG` | | Misc | `KEY`, `TRON`/`TROFF`, `OPTION BASE`, `MID$` assignment | | System | `SYSTEM` | @@ -222,6 +223,42 @@ The file format is compatible with the original GW-BASIC: byte 0 = `0xFD`, bytes 1-2 = segment (LE), bytes 3-4 = offset (LE), bytes 5-6 = length (LE), followed by the raw data bytes. +## 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, +CGA palette tricks, and serial port polling. + +``` +OUT &H43, &HB6 ' PIT channel 2: lobyte/hibyte, mode 3 +OUT &H42, &HD3 : OUT &H42, &H04 ' set frequency divisor (1235 → 966 Hz) +OUT &H61, INP(&H61) OR 3 ' speaker on +OUT &H61, INP(&H61) AND &HFC ' speaker off +``` + +### Emulated Ports + +| 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 | +| `&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) | +| `&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 | + +### 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) + +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`). + ## Sound - `SOUND frequency, duration` — play a tone (frequency in Hz, duration in clock ticks) diff --git a/docs/roadmap.md b/docs/roadmap.md index b4c12f1..f23b2dc 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -8,35 +8,21 @@ But we've come this far, so why not? Likely approach: translate the token stream to C and lean on GCC/Clang for the heavy lifting. +## Completed + +### Hardware I/O Simulator (v0.15.0) + +Implemented in `portio.c` / `portio.h` following the `virmem.c` dispatch +pattern. Emulates 8253 PIT channel 2 (speaker frequency), PPI port B +(speaker on/off with continuous tone via PulseAudio), CGA mode/color +registers, game port (joystick stub), and COM1 serial (transmitter-ready +stub). Default: reads return 0xFF (floating bus), writes discarded. + +Statements: `OUT`, `WAIT`, `MOTOR`. Functions: `INP()`, `STICK()`, `STRIG()`. + ## Next Up -### Hardware I/O Simulator - -An optional emulation layer for `OUT`, `INP`, `WAIT`, `MOTOR`, and friends, -following the established `virmem.c` dispatch-by-address pattern. - -**New module:** `portio.c` / `portio.h` with `portio_inp(port)` / -`portio_out(port, value)` dispatch. - -**Priority ports:** - -| Port | Device | Emulation | -|------|--------|-----------| -| 0x42–0x43 | 8253 PIT channel 2 | Speaker frequency divisor (1193180 / divisor Hz) | -| 0x61 | PPI Port B | Speaker on/off (bits 0–1), routes to `snd_start_continuous()` / `snd_stop_continuous()` | -| 0x3D8 | CGA mode control | Mode register storage, feeds `gfx_init()` | -| 0x3D9 | CGA color select | Palette/background bits, feeds `gfx_palette_set()` | -| 0x201 | Game port | Joystick stub (returns 0xF0 = no buttons) | -| 0x3F8–0x3FE | COM1 serial | Minimal stub (LSR reports transmitter ready to prevent spin loops) | -| Default | — | Reads return 0xFF (floating bus), writes silently discarded | - -**Statement/function changes:** - -- `OUT port, value` → `portio_out(port, value)` -- `WAIT port, mask [, xor_mask]` → busy loop with `portio_inp()`, break check, timeout -- `MOTOR` → accept and silently ignore -- `INP(port)` → `portio_inp(port)` (currently returns 0) -- `STICK(n)` / `STRIG(n)` → joystick state from port 0x201 +*(Looking for the next major feature — see IDE and Notebook Integration below.)* ## IDE and Notebook Integration diff --git a/include/gwbasic.h b/include/gwbasic.h index 018f366..5fbc149 100644 --- a/include/gwbasic.h +++ b/include/gwbasic.h @@ -10,7 +10,7 @@ #include "gw_math.h" #include "strings.h" -#define GW_VERSION "0.14.0" +#define GW_VERSION "0.15.0" #define GW_BANNER "GW-BASIC " GW_VERSION /* Tokenizer */