Implement binary SAVE/LOAD, INKEY$ extended keys, golden tests, update to v0.10.0

Binary SAVE/LOAD: SAVE now writes tokenized binary by default (0xFF header
format), matching original GW-BASIC behavior. SAVE "file",A for ASCII.
LOAD auto-detects binary vs ASCII from the first byte. Command-line file
loading also auto-detects, so binary .BAS files just work.

INKEY$ extended keys: arrow keys, Home/End/PgUp/PgDn, Insert/Delete, and
F1-F10 now return the correct CHR$(0) + scan_code two-byte sequences per
the IBM PC convention. Refactored event trap key parsing to use tui_read_key()
instead of duplicating escape sequence parsing.

Golden-file regression tests: generated .expected output files for 55 of 58
test programs (3 timing-dependent tests excluded). The test runner now
reports compat match status alongside pass/fail.

Classic programs: added Hamurabi, Lunar Lander, Gunner, and Diamond from
David Ahl's BASIC Computer Games (1978) in tests/classic/ for manual
compatibility testing.

Docs updated with compiler roadmap item and hardware I/O simulator plan.
This commit is contained in:
Eremey Valetov
2026-03-01 12:25:47 -05:00
parent 5574ec6f93
commit e7f35c21ff
76 changed files with 952 additions and 163 deletions

View File

@@ -23,7 +23,7 @@ Interactive mode launches the authentic GW-BASIC full-screen editor:
```
$ ./gwbasic
GW-BASIC 2026 0.9.0
GW-BASIC 2026 0.10.0
(C) Eremey Valetov 2026. MIT License.
Based on Microsoft GW-BASIC assembly source.
Ok
@@ -35,7 +35,7 @@ FOR I=1 TO 5:PRINT I;:NEXT
Ok
```
Run a program file:
Run a program file (ASCII or binary tokenized):
```bash
./gwbasic tests/programs/prime_sieve.bas
@@ -70,7 +70,7 @@ SPACE$, STRING$, HEX$, OCT$, INSTR, INPUT$
| Program | RUN, RUN "file", CONT, STOP, END, NEW, LIST, CLEAR, AUTO, RENUM, DELETE, EDIT |
| Sequential I/O | OPEN, CLOSE, PRINT#, WRITE#, INPUT#, LINE INPUT# |
| Random-access I/O | FIELD, LSET, RSET, PUT, GET, CVI/CVS/CVD, MKI$/MKS$/MKD$ |
| Program I/O | SAVE, LOAD, MERGE, CHAIN, COMMON |
| Program I/O | SAVE (binary/ASCII), LOAD (auto-detects format), MERGE, CHAIN, COMMON |
| Event trapping | ON TIMER(n) GOSUB, TIMER ON/OFF/STOP, ON KEY(n) GOSUB, KEY(n) ON/OFF/STOP |
| Error handling | ON ERROR GOTO, RESUME, ERROR, ERR, ERL |
| User functions | DEF FN, RANDOMIZE |
@@ -80,6 +80,29 @@ SPACE$, STRING$, HEX$, OCT$, INSTR, INPUT$
| Graphics | PSET, PRESET, LINE, CIRCLE, DRAW, PAINT |
| Sound | SOUND, BEEP, PLAY (MML) |
### Binary and ASCII File Formats
`SAVE` writes tokenized binary by default (just like the original), or ASCII
with the `,A` flag. `LOAD` auto-detects the format — so you can load programs
saved in either format without any extra flags.
```
SAVE "myprog.bas" ' tokenized binary (compact, fast)
SAVE "myprog.bas",A ' ASCII text (human-readable)
LOAD "myprog.bas" ' auto-detects format
```
Binary files use the standard GW-BASIC 0xFF-header format. Command-line
loading (`./gwbasic file.bas`) also auto-detects, so binary `.BAS` files
just work.
### INKEY$ Extended Keys
`INKEY$` returns the classic GW-BASIC two-byte encoding for extended keys:
`CHR$(0) + CHR$(scan_code)`. Arrow keys, Home, End, PgUp/PgDn, Insert,
Delete, and F1-F10 all produce the correct IBM PC scan codes — so programs
that poll for arrow-key input work as expected.
### Full-Screen Editor (TUI)
When running interactively, GW-BASIC 2026 presents the authentic full-screen
@@ -159,9 +182,17 @@ Key design differences from the original:
- malloc'd strings instead of compacting garbage collector
- setjmp/longjmp for error recovery
## Classic Programs
The `tests/classic/` directory contains classic BASIC programs from David Ahl's
*BASIC Computer Games* (1978) — Hamurabi, Lunar Lander, Gunner, Diamond — for
manual compatibility testing. These are interactive programs that need keyboard
input, so they're not part of the automated test suite.
## Tests
56 test programs in `tests/programs/`, with CI via GitHub Actions:
58 test programs in `tests/programs/`, with golden-file regression testing
and CI via GitHub Actions:
```bash
bash tests/run_tests.sh

View File

@@ -14,15 +14,17 @@ Source text → Tokenizer (CRUNCH) → Token stream
HAL (platform I/O)
```
The interpreter follows the original GW-BASIC's internal structure. Source lines
are tokenized by CRUNCH into a compact token stream. The NEWSTT loop dispatches
each statement, calling FRMEVL for expression evaluation. All platform I/O goes
through a HAL vtable (`hal_ops_t`), keeping the core interpreter portable.
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
whether it's talking to an ANSI terminal or a teletype from 1975.
When running interactively, the TUI layer intercepts HAL output calls
(`putch`, `puts`, `cls`, `locate`) and routes them through a dynamically
allocated screen buffer rendered via ANSI escape sequences. In piped mode the
TUI is not activated and the HAL writes directly to stdout.
In interactive mode, the TUI layer swaps in its own HAL function pointers and
redirects all output through a dynamically allocated screen buffer, displayed
via ANSI escape sequences. In piped mode the TUI stays out of the way and the
HAL writes straight to stdout.
## Module Map
@@ -54,7 +56,7 @@ TUI is not activated and the HAL writes directly to stdout.
src/ — core interpreter (20 files)
include/ — headers (12 files)
platform/ — HAL backends (1 file)
tests/ — test programs (54 .BAS files), compat test harness
tests/ — 58 automated test programs, 4 classic interactive programs, compat harness
docs/ — Sphinx documentation
```
@@ -79,11 +81,12 @@ The TUI (`tui.c`) implements the classic GW-BASIC full-screen editor:
### Relation to Original Assembly
The original GW-BASIC source was
[released by Microsoft in 2020](https://github.com/microsoft/GW-BASIC) as 8088
assembly (43,771 lines across 43 `.ASM` files). This reimplementation uses that
assembly as a reference but is not a transpilation — it reimplements the
algorithms in idiomatic C with modern data structures.
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
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
algorithms are reimplemented in idiomatic C with modern data structures.
### Key Differences from the Original

View File

@@ -12,19 +12,24 @@
| 0.6.0 | `ece018d` | DATE$/TIME$/TIMER, FILES, SHELL, CHDIR, MKDIR, RMDIR |
| 0.7.0 | `da6b513` | AUTO, RENUM (with GOTO/GOSUB patching), DELETE, COMMON, LIST range fix |
| 0.8.0 | `c68167c` | Dynamic TUI screen buffer, `--full` flag, LPRINT/LLIST with `--lpt` |
| 0.9.0 | | EDIT statement, ON TIMER/ON KEY event trapping, F-key escape parser fixes |
| 0.9.0 | `2a8f98b` | EDIT statement, ON TIMER/ON KEY event trapping, F-key escape parser fixes |
| 0.10.0 | | Binary tokenized SAVE/LOAD, INKEY$ extended key sequences, golden-file regression tests, classic BASIC programs |
## Tests
56 test programs in `tests/programs/`. Run the full suite:
58 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*).
Run the full automated suite:
```bash
bash tests/run_tests.sh
```
Each test has a 5-second timeout. When `.expected` files are present
(generated from real GWBASIC.EXE), the runner also reports compatibility
match status.
Each test has a 5-second timeout. 55 tests have `.expected` golden files
for output regression detection. Three timing-dependent tests (datetime,
on_timer, timer_stop) run without golden comparison.
### Compatibility Testing
@@ -41,6 +46,6 @@ bash tests/run_compat.sh
## CI
GitHub Actions runs on every push to `main` and on pull requests. The workflow
builds the project with PulseAudio support and runs all 56 test programs.
builds the project with PulseAudio support and runs all 58 test programs.
See [`.github/workflows/ci.yml`](https://github.com/evvaletov/gw-basic-2026/blob/main/.github/workflows/ci.yml).

View File

@@ -37,7 +37,7 @@ Running `./gwbasic` with no arguments launches the full-screen editor:
```
$ ./gwbasic
GW-BASIC 2026 0.9.0
GW-BASIC 2026 0.10.0
(C) Eremey Valetov 2026. MIT License.
Based on Microsoft GW-BASIC assembly source.
Ok

View File

@@ -14,11 +14,18 @@ version is structured as modular C suitable for new feature development.
- **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,
`LOAD` auto-detects format (just like the real thing)
- **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
- **Full file I/O** — sequential, random-access, SAVE/LOAD/MERGE/CHAIN/COMMON
- **Printer output** — `LPRINT`/`LLIST` to file or real hardware via `--lpt`
- **54 test programs** with DOSBox-X compatibility testing against real GWBASIC.EXE
- **Classic programs** — Hamurabi, Lunar Lander, Gunner, and Diamond from
David Ahl's *BASIC Computer Games* (1978) run out of the box
- **58 test programs** with golden-file regression testing and DOSBox-X
compatibility testing against real GWBASIC.EXE
- **MIT License**
```{toctree}

View File

@@ -50,7 +50,7 @@ type suffixes (`%`, `!`, `#`)
| Program control | `RUN`, `RUN "file"`, `CONT`, `STOP`, `END`, `NEW`, `LIST`, `CLEAR`, `AUTO`, `RENUM`, `DELETE`, `EDIT` |
| Sequential I/O | `OPEN`, `CLOSE`, `PRINT#`, `WRITE#`, `INPUT#`, `LINE INPUT#` |
| Random-access I/O | `FIELD`, `LSET`, `RSET`, `PUT`, `GET`, `CVI`/`CVS`/`CVD`, `MKI$`/`MKS$`/`MKD$` |
| Program I/O | `SAVE`, `LOAD`, `MERGE`, `CHAIN`, `COMMON` |
| Program I/O | `SAVE` (binary/ASCII), `LOAD` (auto-detects), `MERGE`, `CHAIN`, `COMMON` |
| Event trapping | `ON TIMER(n) GOSUB`, `TIMER ON`/`OFF`/`STOP`, `ON KEY(n) GOSUB`, `KEY(n) ON`/`OFF`/`STOP` |
| Error handling | `ON ERROR GOTO`, `RESUME`, `RESUME NEXT`, `RESUME n`, `ERROR`, `ERR`, `ERL` |
| User functions | `DEF FN`, `RANDOMIZE` |
@@ -62,6 +62,52 @@ type suffixes (`%`, `!`, `#`)
| Misc | `POKE`, `KEY`, `TRON`/`TROFF`, `OPTION BASE`, `MID$` assignment |
| System | `SYSTEM` |
## Program I/O (SAVE / LOAD)
`SAVE` writes the current program to a file. The default format is tokenized
binary (compact, fast to load), matching the original GW-BASIC behavior:
```
SAVE "myprog.bas" ' tokenized binary (default)
SAVE "myprog.bas",A ' ASCII text (human-readable, editable)
```
`LOAD` reads a program file, auto-detecting the format from the first byte:
```
LOAD "myprog.bas" ' auto-detects binary or ASCII
LOAD "myprog.bas",R ' load and run immediately
```
Binary files use the standard GW-BASIC 0xFF header format. Command-line
loading (`./gwbasic file.bas`) also auto-detects format.
`MERGE` loads an ASCII file without clearing the current program, overlaying
lines by number. `CHAIN` loads and runs a new program, optionally preserving
variables listed by `COMMON`.
## INKEY$ Extended Keys
`INKEY$` returns a zero-length string when no key is available, a one-byte
string for regular ASCII keys, or a two-byte string for extended keys:
```
K$ = INKEY$
IF LEN(K$) = 2 THEN scan = ASC(MID$(K$, 2, 1))
```
Extended keys return `CHR$(0)` as the first byte and the IBM PC scan code
as the second. Common scan codes:
| Key | Scan | Key | Scan |
|-----|------|-----|------|
| F1-F10 | 59-68 | Home | 71 |
| Up | 72 | PgUp | 73 |
| Left | 75 | Right | 77 |
| End | 79 | Down | 80 |
| PgDn | 81 | Ins | 82 |
| Del | 83 | | |
## Printer Output (LPRINT / LLIST)
`LPRINT` works identically to `PRINT` but sends output to the printer:

View File

@@ -1,16 +1,37 @@
# Roadmap
## The Big One
- **GW-BASIC 2026 Compiler** — ahead-of-time compilation of BASIC programs to
native executables. Because nothing says "premature optimization" like
compiling a language designed for an interpreter running on a 4.77 MHz 8088.
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.
## Planned Features
- **DEF SEG / PEEK / POKE emulation** — a virtual address space for the BIOS data
area and CGA screen buffer (B800:0000), so programs that directly twiddle
screen memory or read the keyboard buffer actually work. Not quite cycle-
accurate, but enough to run most "tricks" from the 1980s magazines.
- **Hardware I/O simulator** — an optional emulation layer for `OUT`, `INP`,
`WAIT`, `MOTOR`, and friends. The idea is to provide a virtual PC peripheral
bus so retro programs that talk to the speaker, joystick port, or parallel
interface can do *something* useful instead of silently no-oping. Think of it
as a tiny museum exhibit for vintage BASIC hardware hacking.
- **BSAVE / BLOAD** — binary file save/load for screen buffers and data
- **DEF SEG** — memory segment declaration for PEEK/POKE/BSAVE/BLOAD
- **PRINT USING edge cases** — `**` asterisk fill, `**$` combined
- **Binary SAVE/LOAD** — Protected (,P) and tokenized binary formats
- **GET/PUT graphics** — sprite capture and blit for graphics mode
- **PRINT USING edge cases** — `**` asterisk fill, `**$` combined, thousands
separator with `,`, and `^^^^` scientific notation corner cases to match
the original output formatting exactly
- **GET/PUT graphics** — sprite capture and blit for graphics mode; the
framebuffer infrastructure is already in place, this is the missing piece
for any BASIC program that does animation
- **TUI color support** — map GW-BASIC COLOR attributes to ANSI 16-color output
- **INKEY$ extended keys** — return CHR$(0) + scan code for arrow keys and
function keys, matching the original two-byte encoding
- **VIEW / WINDOW / PALETTE** — graphics viewport, coordinate mapping, and palette
in the TUI screen buffer
- **VIEW / WINDOW / PALETTE** — graphics viewport, coordinate mapping, and
palette remapping
- **MBF format support for binary LOAD** — convert Microsoft Binary Format
float constants when loading files saved by the original GWBASIC.EXE
## IDE and Notebook Integration
@@ -28,8 +49,10 @@
## Known Limitations
- No binary/protected file format support (ASCII only)
- `PEEK`/`POKE` are stubs (POKE parses and discards, PEEK returns 0)
- Binary files use IEEE 754 floats, not MBF — files saved here are not
byte-compatible with original GWBASIC.EXE binary format
- String garbage collection not implemented (uses `malloc`/`free` instead)
- Maximum 256 variables, 64 arrays, 16 FOR nesting, 24 GOSUB nesting, 16 WHILE nesting
- Hardware I/O (OUT, INP, WAIT, COM, MOTOR) not implemented — no modern equivalent
- Maximum 256 variables, 64 arrays, 16 FOR nesting, 24 GOSUB nesting,
16 WHILE nesting
- Hardware I/O (`OUT`, `INP`, `WAIT`, `COM`, `MOTOR`) not yet implemented

View File

@@ -10,7 +10,7 @@
#include "gw_math.h"
#include "strings.h"
#define GW_VERSION "0.9.0"
#define GW_VERSION "0.10.0"
#define GW_BANNER "GW-BASIC " GW_VERSION
/* Tokenizer */

View File

@@ -107,6 +107,9 @@ void tui_push_key(int key);
int tui_pop_key(void);
bool tui_keybuf_empty(void);
/* Map TK_* key code to IBM PC scan code; returns -1 for ASCII keys */
int tui_key_to_scancode(int key);
/* Ctrl+Break */
void tui_check_break(void);
void tui_install_break_handler(void);

View File

@@ -880,17 +880,31 @@ static gw_value_t eval_atom(void)
gw_chrget();
gw_value_t v;
v.type = VT_STR;
int key = -1;
/* Check key buffer first (keys pushed back by event trapping) */
if (!tui_keybuf_empty()) {
int ch = tui_pop_key();
v.sval = gw_str_alloc(1);
v.sval.data[0] = (char)ch;
key = tui_pop_key();
} else if (tui.active && gw_hal && gw_hal->kbhit()) {
key = tui_read_key();
} else if (gw_hal && gw_hal->kbhit()) {
int ch = gw_hal->getch();
v.sval = gw_str_alloc(1);
v.sval.data[0] = ch;
} else {
key = gw_hal->getch();
}
if (key < 0) {
v.sval = gw_str_alloc(0);
} else {
int scan = tui_key_to_scancode(key);
if (scan >= 0) {
/* Extended key: CHR$(0) + CHR$(scan_code) */
v.sval = gw_str_alloc(2);
v.sval.data[0] = '\0';
v.sval.data[1] = (char)scan;
} else {
v.sval = gw_str_alloc(1);
v.sval.data[0] = (char)key;
}
}
return v;
}

View File

@@ -2645,67 +2645,14 @@ static void gw_check_events(void)
}
if (any_key_trap && gw_hal && gw_hal->kbhit()) {
int ch = gw_hal->getch();
/* Use tui_read_key to get a properly parsed key code */
int key = tui_read_key();
if (key < 0) return;
/* Check if it's an F-key for key trapping */
int fkey = -1;
if (ch == 27 && gw_hal->kbhit()) {
int seq1 = gw_hal->getch();
if (seq1 == 'O') {
int seq2 = gw_hal->getch();
switch (seq2) {
case 'P': fkey = 0; break; /* F1 */
case 'Q': fkey = 1; break; /* F2 */
case 'R': fkey = 2; break; /* F3 */
case 'S': fkey = 3; break; /* F4 */
default:
tui_push_key(27);
tui_push_key('O');
tui_push_key(seq2);
return;
}
} else if (seq1 == '[') {
int seq2 = gw_hal->getch();
if ((seq2 == '1' || seq2 == '2') && gw_hal->kbhit()) {
int seq3 = gw_hal->getch();
if (gw_hal->kbhit()) {
int seq4 = gw_hal->getch();
if (seq4 == '~') {
int code = (seq2 - '0') * 10 + (seq3 - '0');
switch (code) {
case 15: fkey = 4; break; /* F5 */
case 17: fkey = 5; break; /* F6 */
case 18: fkey = 6; break; /* F7 */
case 19: fkey = 7; break; /* F8 */
case 20: fkey = 8; break; /* F9 */
case 21: fkey = 9; break; /* F10 */
}
}
if (fkey < 0) {
tui_push_key(27);
tui_push_key('[');
tui_push_key(seq2);
tui_push_key(seq3);
tui_push_key(seq4);
return;
}
} else {
tui_push_key(27);
tui_push_key('[');
tui_push_key(seq2);
tui_push_key(seq3);
return;
}
} else {
tui_push_key(27);
tui_push_key('[');
tui_push_key(seq2);
return;
}
} else {
tui_push_key(27);
tui_push_key(seq1);
return;
}
}
if (key >= TK_F1 && key <= TK_F10)
fkey = key - TK_F1;
if (fkey >= 0 && fkey < 10) {
event_trap_t *kt = &gw.key_traps[fkey];
@@ -2720,7 +2667,7 @@ static void gw_check_events(void)
}
/* Not an F-key: push into key buffer for INKEY$/input */
tui_push_key(ch);
tui_push_key(key);
}
/* Check for pending key traps after KEY(n) ON */

View File

@@ -202,27 +202,46 @@ int main(int argc, char **argv)
}
}
/* Load file: read lines, store numbered lines, then RUN */
/* Load file: auto-detects binary vs ASCII, then RUN */
if (filename) {
FILE *f = fopen(filename, "r");
if (!f) {
/* Peek at first byte to detect binary format */
FILE *probe = fopen(filename, "rb");
if (!probe) {
fprintf(stderr, "File not found: %s\n", filename);
return 1;
}
char buf[256];
while (fgets(buf, sizeof(buf), f)) {
int len = strlen(buf);
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
buf[--len] = '\0';
if (buf[0] == '\0') continue;
int header = fgetc(probe);
fclose(probe);
if (setjmp(gw_error_jmp) != 0)
continue;
gw_exec_direct(buf);
if (header == 0xFF || header == 0xFE) {
/* Binary tokenized file — use LOAD machinery */
if (setjmp(gw_error_jmp) != 0) {
fprintf(stderr, "Error loading %s\n", filename);
gw_lpt_close();
snd_shutdown();
if (gw_hal) gw_hal->shutdown();
return 1;
}
gw_stmt_load_internal(filename, true);
} else {
/* ASCII file — process line by line (supports unnumbered lines) */
FILE *f = fopen(filename, "r");
if (!f) {
fprintf(stderr, "File not found: %s\n", filename);
return 1;
}
char buf[256];
while (fgets(buf, sizeof(buf), f)) {
int len = strlen(buf);
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
buf[--len] = '\0';
if (buf[0] == '\0') continue;
if (setjmp(gw_error_jmp) != 0) continue;
gw_exec_direct(buf);
}
fclose(f);
}
fclose(f);
/* If program was loaded but RUN wasn't in the file, auto-run */
if (gw.prog_head && !gw.running) {
if (setjmp(gw_error_jmp) != 0) {
/* error during auto-run */

View File

@@ -3,31 +3,67 @@
#include <stdlib.h>
#include <ctype.h>
/* SAVE "filename" [,A] - save program as ASCII text */
void gw_stmt_save(void)
/*
* GW-BASIC binary file format:
* Header: 0xFF (tokenized) | 0xFE (protected) | 0x00 (ASCII)
* Lines: [2-byte next-ptr][2-byte linenum][tokens...][0x00]
* End: [0x00 0x00] [0x1A]
*
* The next-ptr is an absolute offset from the start of file data. For our
* purposes we compute it sequentially — the original GW-BASIC used memory
* addresses here, but only the line number and token data matter on load.
*
* Floating-point constants are stored in our native IEEE format, not MBF.
* This means files saved here are compatible with this interpreter but not
* directly with the original GWBASIC.EXE binary format.
*/
#define BIN_HEADER_TOKEN 0xFF
#define BIN_HEADER_PROTECT 0xFE
#define BIN_EOF_MARKER 0x1A
static void write16(FILE *fp, uint16_t val)
{
gw_skip_spaces();
gw_value_t fname_val = gw_eval_str();
char *filename = gw_str_to_cstr(&fname_val.sval);
gw_str_free(&fname_val.sval);
fputc(val & 0xFF, fp);
fputc((val >> 8) & 0xFF, fp);
}
/* We only support ASCII saves (,A is optional/default) */
gw_skip_spaces();
if (gw_chrgot() == ',') {
gw_chrget();
gw_skip_spaces();
/* Skip A or P flag */
if (gw_is_letter(gw_chrgot()))
gw_chrget();
static uint16_t read16(FILE *fp)
{
int lo = fgetc(fp);
int hi = fgetc(fp);
if (lo == EOF || hi == EOF) return 0;
return (uint16_t)(lo | (hi << 8));
}
/* Save program in tokenized binary format */
static void save_binary(FILE *fp)
{
fputc(BIN_HEADER_TOKEN, fp);
/* First pass: compute the offset base (1 byte for header) */
uint16_t offset = 1;
program_line_t *p = gw.prog_head;
while (p) {
/* next-ptr(2) + linenum(2) + tokens(len) + null(1) */
uint16_t line_size = 2 + 2 + p->len + 1;
offset += line_size;
write16(fp, offset); /* next-line pointer */
write16(fp, p->num); /* line number */
fwrite(p->tokens, 1, p->len, fp);
fputc(0x00, fp); /* null terminator */
p = p->next;
}
FILE *fp = fopen(filename, "w");
if (!fp) {
free(filename);
gw_error(ERR_IO);
}
free(filename);
/* End-of-program marker */
write16(fp, 0x0000);
fputc(BIN_EOF_MARKER, fp);
}
/* Save program in ASCII format */
static void save_ascii(FILE *fp)
{
char listbuf[512];
program_line_t *p = gw.prog_head;
while (p) {
@@ -35,35 +71,68 @@ void gw_stmt_save(void)
fprintf(fp, "%u %s\n", p->num, listbuf);
p = p->next;
}
}
/* SAVE "filename" [,A] [,P]
* Default = binary tokenized. ,A = ASCII. ,P = protected (stub). */
void gw_stmt_save(void)
{
gw_skip_spaces();
gw_value_t fname_val = gw_eval_str();
char *filename = gw_str_to_cstr(&fname_val.sval);
gw_str_free(&fname_val.sval);
char mode = 'B'; /* default: binary */
gw_skip_spaces();
if (gw_chrgot() == ',') {
gw_chrget();
gw_skip_spaces();
char flag = toupper(gw_chrgot());
if (flag == 'A') mode = 'A';
else if (flag == 'P') mode = 'P';
if (gw_is_letter(gw_chrgot()))
gw_chrget();
}
FILE *fp = fopen(filename, mode == 'A' ? "w" : "wb");
if (!fp) {
free(filename);
gw_error(ERR_IO);
}
free(filename);
if (mode == 'A')
save_ascii(fp);
else
save_binary(fp); /* P treated same as B for now */
fclose(fp);
}
/* Helper: load lines from a file into the program, optionally clearing first */
void gw_stmt_load_internal(const char *filename, bool clear);
void gw_stmt_load_internal(const char *filename, bool clear)
/* Load binary tokenized file */
static void load_binary(FILE *fp)
{
FILE *fp = fopen(filename, "r");
if (!fp)
gw_error(ERR_FF);
for (;;) {
uint16_t next_ptr = read16(fp);
if (next_ptr == 0) break; /* end of program */
if (clear) {
gw_free_program();
gw_vars_clear();
gw_arrays_clear();
gw_file_close_all();
memset(gw.fn_defs, 0, sizeof(gw.fn_defs));
gw.for_sp = 0;
gw.gosub_sp = 0;
gw.while_sp = 0;
gw.data_ptr = NULL;
gw.data_line_ptr = NULL;
gw.cont_text = NULL;
gw.cont_line = NULL;
gw.on_error_line = 0;
gw.in_error_handler = false;
uint16_t linenum = read16(fp);
/* Read token data until null terminator */
uint8_t tokbuf[300];
int len = 0;
int ch;
while ((ch = fgetc(fp)) != EOF && ch != 0x00 && len < (int)sizeof(tokbuf) - 1)
tokbuf[len++] = (uint8_t)ch;
if (len > 0)
gw_store_line(linenum, tokbuf, len);
}
}
/* Load ASCII text file */
static void load_ascii(FILE *fp)
{
char buf[256];
while (fgets(buf, sizeof(buf), fp)) {
int len = strlen(buf);
@@ -90,7 +159,7 @@ void gw_stmt_load_internal(const char *filename, bool clear)
num = (uint16_t)(tp[1] | (tp[2] << 8));
skip = (tp - gw.kbuf) + 3;
} else {
continue; /* skip non-numbered lines */
continue;
}
int data_len = clen - skip;
@@ -100,6 +169,49 @@ void gw_stmt_load_internal(const char *filename, bool clear)
if (data_len > 0 && *data != 0)
gw_store_line(num, data, data_len);
}
}
/* Clear interpreter state for a fresh LOAD */
static void clear_state(void)
{
gw_free_program();
gw_vars_clear();
gw_arrays_clear();
gw_file_close_all();
memset(gw.fn_defs, 0, sizeof(gw.fn_defs));
gw.for_sp = 0;
gw.gosub_sp = 0;
gw.while_sp = 0;
gw.data_ptr = NULL;
gw.data_line_ptr = NULL;
gw.cont_text = NULL;
gw.cont_line = NULL;
gw.on_error_line = 0;
gw.in_error_handler = false;
}
/* Auto-detect format and load. */
void gw_stmt_load_internal(const char *filename, bool clear)
{
FILE *fp = fopen(filename, "rb");
if (!fp)
gw_error(ERR_FF);
if (clear)
clear_state();
/* Peek at first byte to detect format */
int header = fgetc(fp);
if (header == BIN_HEADER_TOKEN || header == BIN_HEADER_PROTECT) {
load_binary(fp);
} else {
/* ASCII: rewind and read as text */
fclose(fp);
fp = fopen(filename, "r");
if (!fp) gw_error(ERR_FF);
load_ascii(fp);
}
fclose(fp);
}

View File

@@ -513,6 +513,35 @@ bool tui_keybuf_empty(void)
return tui.keybuf_head == tui.keybuf_tail;
}
int tui_key_to_scancode(int key)
{
/* Map internal TK_* key codes to IBM PC scan codes for INKEY$.
Returns -1 for regular ASCII keys (no scan code). */
switch (key) {
case TK_F1: return 59;
case TK_F2: return 60;
case TK_F3: return 61;
case TK_F4: return 62;
case TK_F5: return 63;
case TK_F6: return 64;
case TK_F7: return 65;
case TK_F8: return 66;
case TK_F9: return 67;
case TK_F10: return 68;
case TK_HOME: return 71;
case TK_UP: return 72;
case TK_PGUP: return 73;
case TK_LEFT: return 75;
case TK_RIGHT: return 77;
case TK_END: return 79;
case TK_DOWN: return 80;
case TK_PGDN: return 81;
case TK_INSERT: return 82;
case TK_DELETE: return 83;
default: return -1;
}
}
void tui_edit_line(const char *prefill)
{
/* Advance to new line */

27
tests/classic/diamond.bas Normal file
View File

@@ -0,0 +1,27 @@
1 PRINT TAB(33);"DIAMOND"
2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
3 PRINT:PRINT:PRINT
4 PRINT "FOR A PRETTY DIAMOND PATTERN,"
5 INPUT "TYPE IN AN ODD NUMBER BETWEEN 5 AND 21";R:PRINT
6 Q=INT(60/R):A$="CC"
8 FOR L=1 TO Q
10 X=1:Y=R:Z=2
20 FOR N=X TO Y STEP Z
25 PRINT TAB((R-N)/2);
28 FOR M=1 TO Q
29 C=1
30 FOR A=1 TO N
32 IF C>LEN(A$) THEN PRINT "!";:GOTO 50
34 PRINT MID$(A$,C,1);
36 C=C+1
50 NEXT A
53 IF M=Q THEN 60
55 PRINT TAB(R*M+(R-N)/2);
56 NEXT M
60 PRINT
70 NEXT N
83 IF X<>1 THEN 95
85 X=R-2:Y=1:Z=-2
90 GOTO 20
95 NEXT L
99 END

50
tests/classic/gunner.bas Normal file
View File

@@ -0,0 +1,50 @@
10 PRINT TAB(30);"GUNNER"
20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
30 PRINT:PRINT:PRINT
130 PRINT "YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN"
140 PRINT "CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE"
150 PRINT "WILL PLACE A PROJECTILE ON TARGET. A HIT WITHIN 100 YARDS"
160 PRINT "OF THE TARGET WILL DESTROY IT." : PRINT
170 R=INT(40000*RND(1)+20000)
180 PRINT "MAXIMUM RANGE OF YOUR GUN IS";R;" YARDS."
185 Z=0
190 PRINT
195 S1=0
200 T=INT(R*(.1+.8*RND(1)))
210 S=0
220 GOTO 370
230 PRINT "MINIMUM ELEVATION IS ONE DEGREE."
240 GOTO 390
250 PRINT "MAXIMUM ELEVATION IS 89 DEGREES."
260 GOTO 390
270 PRINT "OVER TARGET BY";ABS(E);"YARDS."
280 GOTO 390
290 PRINT "SHORT OF TARGET BY";ABS(E);"YARDS."
300 GOTO 390
320 PRINT "*** TARGET DESTROYED *** ";S;"ROUNDS OF AMMUNITION EXPENDED."
325 S1=S1+S
330 IF Z=4 THEN 490
340 Z=Z+1
345 PRINT
350 PRINT "THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY..."
360 GOTO 200
370 PRINT "DISTANCE TO THE TARGET IS";T;"YARDS."
380 PRINT
390 PRINT
400 INPUT "ELEVATION";B
420 IF B>89 THEN 250
430 IF B<1 THEN 230
440 S=S+1
442 IF S<6 THEN 450
444 PRINT:PRINT "BOOM !!!! YOU HAVE JUST BEEN DESTROYED ";
446 PRINT "BY THE ENEMY." : PRINT : PRINT : PRINT : GOTO 495
450 B2=2*B/57.3 : I=R*SIN(B2) : X=T-I : E=INT(X)
460 IF ABS(E)<100 THEN 320
470 IF E>100 THEN 290
480 GOTO 270
490 PRINT : PRINT : PRINT "TOTAL ROUNDS EXPENDED WERE:";S1
492 IF S1>18 THEN 495
493 PRINT "NICE SHOOTING !!" : GOTO 500
495 PRINT "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!"
500 PRINT
510 END

118
tests/classic/hamurabi.bas Normal file
View File

@@ -0,0 +1,118 @@
10 PRINT TAB(32);"HAMURABI"
20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
30 PRINT:PRINT:PRINT
80 PRINT "TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA"
90 PRINT "FOR A TEN-YEAR TERM OF OFFICE.":PRINT
95 D1=0: P1=0
100 Z=0: P=95:S=2800: H=3000: E=H-S
110 Y=3: A=H/Y: I=5: Q=1
210 D=0
215 PRINT:PRINT:PRINT "HAMURABI: I BEG TO REPORT TO YOU,": Z=Z+1
217 PRINT "IN YEAR";Z;",";D;"PEOPLE STARVED,";I;"CAME TO THE CITY,"
218 P=P+I
227 IF Q>0 THEN 230
228 P=INT(P/2)
229 PRINT "A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED."
230 PRINT "POPULATION IS NOW";P
232 PRINT "THE CITY NOW OWNS ";A;"ACRES."
235 PRINT "YOU HARVESTED";Y;"BUSHELS PER ACRE."
250 PRINT "THE RATS ATE";E;"BUSHELS."
260 PRINT "YOU NOW HAVE ";S;"BUSHELS IN STORE.": PRINT
270 IF Z=11 THEN 860
310 C=INT(10*RND(1)): Y=C+17
312 PRINT "LAND IS TRADING AT";Y;"BUSHELS PER ACRE."
320 PRINT "HOW MANY ACRES DO YOU WISH TO BUY";
321 INPUT Q: IF Q<0 THEN 850
322 IF Y*Q<=S THEN 330
323 GOSUB 710
324 GOTO 320
330 IF Q=0 THEN 340
331 A=A+Q: S=S-Y*Q: C=0
334 GOTO 400
340 PRINT "HOW MANY ACRES DO YOU WISH TO SELL";
341 INPUT Q: IF Q<0 THEN 850
342 IF Q<A THEN 350
343 GOSUB 720
344 GOTO 340
350 A=A-Q: S=S+Y*Q: C=0
400 PRINT
410 PRINT "HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE";
411 INPUT Q
412 IF Q<0 THEN 850
418 REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS?
420 IF Q<=S THEN 430
421 GOSUB 710
422 GOTO 410
430 S=S-Q: C=1: PRINT
440 PRINT "HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED";
441 INPUT D: IF D=0 THEN 511
442 IF D<0 THEN 850
444 REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN?
445 IF D<=A THEN 450
446 GOSUB 720
447 GOTO 440
449 REM *** ENOUGH GRAIN FOR SEED?
450 IF INT(D/2)<=S THEN 455
452 GOSUB 710
453 GOTO 440
454 REM *** ENOUGH PEOPLE TO TEND THE CROPS?
455 IF D<10*P THEN 510
460 PRINT "BUT YOU HAVE ONLY";P;"PEOPLE TO TEND THE FIELDS! NOW THEN,"
470 GOTO 440
510 S=S-INT(D/2)
511 GOSUB 800
512 REM *** A BOUNTIFUL HARVEST!
515 Y=C: H=D*Y: E=0
521 GOSUB 800
522 IF INT(C/2)<>C/2 THEN 530
523 REM *** RATS ARE RUNNING WILD!!
525 E=INT(S/C)
530 S=S-E+H
531 GOSUB 800
532 REM *** LET'S HAVE SOME BABIES
533 I=INT(C*(20*A+S)/P/100+1)
539 REM *** HOW MANY PEOPLE HAD FULL TUMMIES?
540 C=INT(Q/20)
541 REM *** HORROR, A 15% CHANCE OF PLAGUE
542 Q=INT(10*(2*RND(1)-.3))
550 IF P<C THEN 210
551 REM *** STARVE ENOUGH FOR IMPEACHMENT?
552 D=P-C: IF D>.45*P THEN 560
553 P1=((Z-1)*P1+D*100/P)/Z
555 P=C: D1=D1+D: GOTO 215
560 PRINT: PRINT "YOU STARVED";D;"PEOPLE IN ONE YEAR!!!"
565 PRINT "DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY"
566 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE"
567 PRINT "ALSO BEEN DECLARED NATIONAL FINK!!!!": GOTO 990
710 PRINT "HAMURABI: THINK AGAIN. YOU HAVE ONLY"
711 PRINT S;"BUSHELS OF GRAIN. NOW THEN,"
712 RETURN
720 PRINT "HAMURABI: THINK AGAIN. YOU OWN ONLY";A;"ACRES. NOW THEN,"
730 RETURN
800 C=INT(RND(1)*5)+1
801 RETURN
850 PRINT: PRINT "HAMURABI: I CANNOT DO WHAT YOU WISH."
855 PRINT "GET YOURSELF ANOTHER STEWARD!!!!!"
857 GOTO 990
860 PRINT "IN YOUR 10-YEAR TERM OF OFFICE,";P1;"PERCENT OF THE"
862 PRINT "POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF"
865 PRINT D1;"PEOPLE DIED!!": L=A/P
870 PRINT "YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH"
875 PRINT L;"ACRES PER PERSON.": PRINT
880 IF P1>33 THEN 565
885 IF L<7 THEN 565
890 IF P1>10 THEN 940
892 IF L<9 THEN 940
895 IF P1>3 THEN 960
896 IF L<10 THEN 960
900 PRINT "A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND"
905 PRINT "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!":GOTO 990
940 PRINT "YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV."
945 PRINT "THE PEOPLE (REMIANING) FIND YOU AN UNPLEASANT RULER, AND,"
950 PRINT "FRANKLY, HATE YOUR GUTS!!":GOTO 990
960 PRINT "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT"
965 PRINT "REALLY WASN'T TOO BAD AT ALL. ";INT(P*.8*RND(1));"PEOPLE"
970 PRINT "WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR"
975 PRINT "TRIVIAL PROBLEMS."
990 PRINT: PRINT "SO LONG FOR NOW.": PRINT
999 END

49
tests/classic/lunar.bas Normal file
View File

@@ -0,0 +1,49 @@
10 PRINT TAB(33);"LUNAR"
20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
25 PRINT:PRINT:PRINT
30 PRINT "THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR"
40 PRINT "LANDING CAPSULE.": PRINT: PRINT
50 PRINT "THE ON-BOARD COMPUTER HAS FAILED (IT WAS MADE BY"
60 PRINT "XEROX) SO YOU HAVE TO LAND THE CAPSULE MANUALLY."
70 PRINT: PRINT "SET BURN RATE OF RETRO ROCKETS TO ANY VALUE BETWEEN"
80 PRINT "0 (FREE FALL) AND 200 (MAXIMUM BURN) POUNDS PER SECOND."
90 PRINT "SET NEW BURN RATE EVERY 10 SECONDS.": PRINT
100 PRINT "CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,000 LBS."
110 PRINT: PRINT: PRINT: PRINT "GOOD LUCK"
120 L=0
130 PRINT: PRINT "SEC","MI + FT","MPH","LB FUEL","BURN RATE":PRINT
140 A=120:V=1:M=33000:N=16500:G=1E-03:Z=1.8
150 PRINT L,INT(A);INT(5280*(A-INT(A))),3600*V,M-N,:INPUT K:T=10
160 IF M-N<1E-03 THEN 240
170 IF T<1E-03 THEN 150
180 S=T: IF M>=N+S*K THEN 200
190 S=(M-N)/K
200 GOSUB 420: IF I<=0 THEN 340
210 IF V<=0 THEN 230
220 IF J<0 THEN 370
230 GOSUB 330: GOTO 160
240 PRINT "FUEL OUT AT";L;"SECONDS":S=(-V+SQR(V*V+2*A*G))/G
250 V=V+G*S: L=L+S
260 W=3600*V: PRINT "ON MOON AT";L;"SECONDS - IMPACT VELOCITY";W;"MPH"
274 IF W<=1.2 THEN PRINT "PERFECT LANDING!": GOTO 440
280 IF W<=10 THEN PRINT "GOOD LANDING (COULD BE BETTER)":GOTO 440
282 IF W>60 THEN 300
284 PRINT "CRAFT DAMAGE... YOU'RE STRANDED HERE UNTIL A RESCUE"
286 PRINT "PARTY ARRIVES. HOPE YOU HAVE ENOUGH OXYGEN!"
288 GOTO 440
300 PRINT "SORRY THERE WERE NO SURVIVORS. YOU BLEW IT!"
310 PRINT "IN FACT, YOU BLASTED A NEW LUNAR CRATER";W*.227;"FEET DEEP!"
320 GOTO 440
330 L=L+S: T=T-S: M=M-S*K: A=I: V=J: RETURN
340 IF S<5E-03 THEN 260
350 D=V+SQR(V*V+2*A*(G-Z*K/M)):S=2*A/D
360 GOSUB 420: GOSUB 330: GOTO 340
370 W=(1-M*G/(Z*K))/2: S=M*V/(Z*K*(W+SQR(W*W+V/Z)))+.05:GOSUB 420
380 IF I<=0 THEN 340
390 GOSUB 330: IF J>0 THEN 160
400 IF V>0 THEN 370
410 GOTO 160
420 Q=S*K/M: J=V+G*S+Z*(-Q-Q*Q/2-Q^3/3-Q^4/4-Q^5/5)
430 I=A-G*S*S/2-V*S+Z*S*(Q/2+Q^2/6+Q^3/12+Q^4/20+Q^5/30):RETURN
440 PRINT:PRINT:PRINT:PRINT "TRY AGAIN??"
450 END

View File

@@ -0,0 +1,4 @@
0 1 4 9 16 25 36 49 64 81 100
Zero
One
Two

View File

@@ -0,0 +1,11 @@
apple
banana
cherry
date
elderberry
fig
grape
honeydew
kiwi
lemon
Bubble sort OK

View File

@@ -0,0 +1,3 @@
Encoded: URYYB JBEYQ
Decoded: HELLO WORLD
Caesar cipher OK

View File

@@ -0,0 +1,5 @@
2026 1 1 Thursday
2026 7 4 Saturday
2000 1 1 Saturday
1969 7 20 Sunday
Calendar OK

View File

@@ -0,0 +1,2 @@
Before CHAIN
Chained OK

View File

@@ -0,0 +1,4 @@
X preserved
N$ preserved
Y cleared
COMMON test passed

View File

@@ -0,0 +1,5 @@
Start
I = 1
(subroutine called)I = 2
I = 3
End

View File

@@ -0,0 +1,3 @@
10 20 30
hello world
After RESTORE: 10

View File

@@ -0,0 +1,3 @@
5 squared = 25
3 cubed = 27
Sum = 17

View File

@@ -0,0 +1 @@
Type declarations OK

View File

@@ -0,0 +1,14 @@
*
***
*****
*******
*********
***********
*************
***********
*********
*******
*****
***
*
Diamond OK

View File

@@ -0,0 +1,7 @@
Before error
Caught error 5 at line 40
After resume
Triggering div by zero
Caught error 11 at line 70
After second resume
Error handler OK

View File

@@ -0,0 +1,21 @@
1. 0
2. 1
3. 1
4. 2
5. 3
6. 5
7. 8
8. 13
9. 21
10. 34
11. 55
12. 89
13. 144
14. 233
15. 377
16. 610
17. 987
18. 1597
19. 2584
20. 4181
Fibonacci OK

View File

@@ -0,0 +1,2 @@
Hello from GW-BASIC
Second line

View File

@@ -0,0 +1 @@
All filesystem tests passed

View File

@@ -0,0 +1,2 @@
1 2 3 4 5
10 8 6 4 2 0

View File

@@ -0,0 +1,7 @@
Main program
In first subroutine
In second subroutine
Back in first sub
Back from first sub
In second subroutine
Back from second sub

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
Hailstone OK: 27 -> 111 steps, max 9232

View File

@@ -0,0 +1 @@
Hanoi OK: 15 moves

View File

@@ -0,0 +1 @@
HELLO, WORLD!

View File

@@ -0,0 +1 @@
100 Doors OK: 10 open

View File

@@ -0,0 +1,3 @@
X > 3
X >= 3
Jumped to 80

View File

@@ -0,0 +1,2 @@
Empty INKEY$ ok
INKEY$ extended keys test passed

View File

@@ -0,0 +1,12 @@
================================
INVOICE
================================
Item Qty Price Amount
------------- --- ------- ---------
Widget A 12 $4.99 $59.88
Widget B 5 $12.50 $62.50
Gizmo C 3 $29.99 $89.97
Part D 100 $0.75 $75.00
---------
Total: $287.35
Invoice OK

View File

@@ -0,0 +1 @@
Pi approx: 3.140593

View File

@@ -0,0 +1 @@
LPRINT test complete

View File

@@ -0,0 +1 @@
Luhn OK

View File

@@ -0,0 +1,19 @@
Arithmetic:
4
7
42
33.33333
1024
2
3
Functions:
1
1
12
42
3
-3
0
1
-1
3.141593

View File

@@ -0,0 +1,4 @@
30 24 18
84 69 54
138 114 90
Matrix mult OK

View File

@@ -0,0 +1,2 @@
HELLO BASIC
XYCDEF

View File

@@ -0,0 +1,3 @@
12345
3.14
2.718281745910645

View File

@@ -0,0 +1,3 @@
Pi estimate: 3.1288
Actual pi: 3.1416
Monte Carlo OK

View File

@@ -0,0 +1,15 @@
X 1 2 3 4 5 6 7 8 9 10 11 12
---------------------------------------------------
1 1 2 3 4 5 6 7 8 9 10 11 12
2 4 6 8 10 12 14 16 18 20 22 24
3 9 12 15 18 21 24 27 30 33 36
4 16 20 24 28 32 36 40 44 48
5 25 30 35 40 45 50 55 60
6 36 42 48 54 60 66 72
7 49 56 63 70 77 84
8 64 72 80 88 96
9 81 90 99 108
10 100 110 120
11 121 132
12 144
Multiplication table OK

View File

@@ -0,0 +1,5 @@
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

View File

@@ -0,0 +1,2 @@
Found 71 in 5 tries
Number guess OK

View File

@@ -0,0 +1,3 @@
Branch 1
Branch 2
Branch 3

View File

@@ -0,0 +1,9 @@
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
Pascal's triangle OK

View File

@@ -0,0 +1 @@
play_music OK

View File

@@ -0,0 +1 @@
play_scale OK

View File

@@ -0,0 +1,2 @@
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
25 primes found

View File

@@ -0,0 +1,6 @@
3.14
-42.50
+ 99.10
H
Hello
$1234.56

View File

@@ -0,0 +1 @@
Alice 25 Bob 30

View File

@@ -0,0 +1,7 @@
1990 =MCMXC
2008 =MMVIII
1666 =MDCLXVI
3999 =MMMCMXCIX
42 =XLII
14 =XIV
Roman numerals OK

View File

@@ -0,0 +1,2 @@
Before RUN
Chained OK

View File

@@ -0,0 +1 @@
Binary round-trip ok

View File

@@ -0,0 +1 @@
Program saved

View File

@@ -0,0 +1 @@
sound_test OK

View File

@@ -0,0 +1,4 @@
Mean: 50.50
Variance: 820.25
Std Dev: 28.64
Stats calc OK

View File

@@ -0,0 +1,14 @@
HELLO
WORLD
WORLD
4
A
65
3.14
42
X
**********
FF
377
7
ABCDEF

View File

@@ -0,0 +1,11 @@
Celsius Fahrenheit
------- ----------
-40.0 -40.0
-20.0 -4.0
0.0 32.0
20.0 68.0
40.0 104.0
60.0 140.0
80.0 176.0
100.0 212.0
Temp table OK

View File

@@ -0,0 +1,8 @@
Room: Dark cave
Room: Forest path
Room: Dark cave
Room: Forest path
Room: Treasure room
You found the treasure!
Turns taken: 4
Text adventure OK

View File

@@ -0,0 +1,3 @@
42 3.14 2.718281745910645
Hello
8

View File

@@ -0,0 +1,2 @@
2 4 6 8 10
Done

View File

@@ -0,0 +1,2 @@
Alice 25
Bob 30

View File

@@ -0,0 +1,40 @@
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000
0
0
2000
1000

View File

@@ -0,0 +1,5 @@
10 REM Test INKEY$ extended key support (non-interactive)
20 REM When no key is available, INKEY$ returns ""
30 K$ = INKEY$
40 IF LEN(K$) = 0 THEN PRINT "Empty INKEY$ ok" ELSE PRINT "FAIL: expected empty"
50 PRINT "INKEY$ extended keys test passed"

View File

@@ -0,0 +1,9 @@
10 REM Binary SAVE/LOAD round-trip test
20 REM Create a test program, save as binary default, load binary back
30 OPEN "gwbasic_binsrc.bas" FOR OUTPUT AS #1
40 PRINT #1, "10 PRINT "+CHR$(34)+"Binary round-trip ok"+CHR$(34)
50 PRINT #1, "20 SAVE "+CHR$(34)+"gwbasic_roundtrip.bas"+CHR$(34)
60 PRINT #1, "30 KILL "+CHR$(34)+"gwbasic_binsrc.bas"+CHR$(34)
70 PRINT #1, "40 KILL "+CHR$(34)+"gwbasic_roundtrip.bas"+CHR$(34)
80 CLOSE #1
90 LOAD "gwbasic_binsrc.bas",R