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:
39
README.md
39
README.md
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
28
src/eval.c
28
src/eval.c
@@ -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;
|
||||
}
|
||||
|
||||
69
src/interp.c
69
src/interp.c
@@ -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 */
|
||||
|
||||
47
src/main.c
47
src/main.c
@@ -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 */
|
||||
|
||||
198
src/program_io.c
198
src/program_io.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
29
src/tui.c
29
src/tui.c
@@ -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
27
tests/classic/diamond.bas
Normal 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
50
tests/classic/gunner.bas
Normal 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
118
tests/classic/hamurabi.bas
Normal 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
49
tests/classic/lunar.bas
Normal 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
|
||||
4
tests/expected/arrays.expected
Normal file
4
tests/expected/arrays.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
0 1 4 9 16 25 36 49 64 81 100
|
||||
Zero
|
||||
One
|
||||
Two
|
||||
11
tests/expected/bubble_sort.expected
Normal file
11
tests/expected/bubble_sort.expected
Normal file
@@ -0,0 +1,11 @@
|
||||
apple
|
||||
banana
|
||||
cherry
|
||||
date
|
||||
elderberry
|
||||
fig
|
||||
grape
|
||||
honeydew
|
||||
kiwi
|
||||
lemon
|
||||
Bubble sort OK
|
||||
3
tests/expected/caesar_cipher.expected
Normal file
3
tests/expected/caesar_cipher.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
Encoded: URYYB JBEYQ
|
||||
Decoded: HELLO WORLD
|
||||
Caesar cipher OK
|
||||
5
tests/expected/calendar.expected
Normal file
5
tests/expected/calendar.expected
Normal file
@@ -0,0 +1,5 @@
|
||||
2026 1 1 Thursday
|
||||
2026 7 4 Saturday
|
||||
2000 1 1 Saturday
|
||||
1969 7 20 Sunday
|
||||
Calendar OK
|
||||
2
tests/expected/chain_test.expected
Normal file
2
tests/expected/chain_test.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
Before CHAIN
|
||||
Chained OK
|
||||
4
tests/expected/common_test.expected
Normal file
4
tests/expected/common_test.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
X preserved
|
||||
N$ preserved
|
||||
Y cleared
|
||||
COMMON test passed
|
||||
5
tests/expected/control_flow.expected
Normal file
5
tests/expected/control_flow.expected
Normal file
@@ -0,0 +1,5 @@
|
||||
Start
|
||||
I = 1
|
||||
(subroutine called)I = 2
|
||||
I = 3
|
||||
End
|
||||
3
tests/expected/data_read.expected
Normal file
3
tests/expected/data_read.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
10 20 30
|
||||
hello world
|
||||
After RESTORE: 10
|
||||
3
tests/expected/def_fn.expected
Normal file
3
tests/expected/def_fn.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
5 squared = 25
|
||||
3 cubed = 27
|
||||
Sum = 17
|
||||
1
tests/expected/def_types.expected
Normal file
1
tests/expected/def_types.expected
Normal file
@@ -0,0 +1 @@
|
||||
Type declarations OK
|
||||
14
tests/expected/diamond.expected
Normal file
14
tests/expected/diamond.expected
Normal file
@@ -0,0 +1,14 @@
|
||||
*
|
||||
***
|
||||
*****
|
||||
*******
|
||||
*********
|
||||
***********
|
||||
*************
|
||||
***********
|
||||
*********
|
||||
*******
|
||||
*****
|
||||
***
|
||||
*
|
||||
Diamond OK
|
||||
7
tests/expected/error_handler.expected
Normal file
7
tests/expected/error_handler.expected
Normal 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
|
||||
21
tests/expected/fibonacci.expected
Normal file
21
tests/expected/fibonacci.expected
Normal 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
|
||||
2
tests/expected/file_io.expected
Normal file
2
tests/expected/file_io.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
Hello from GW-BASIC
|
||||
Second line
|
||||
1
tests/expected/filesystem.expected
Normal file
1
tests/expected/filesystem.expected
Normal file
@@ -0,0 +1 @@
|
||||
All filesystem tests passed
|
||||
2
tests/expected/for_next.expected
Normal file
2
tests/expected/for_next.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
1 2 3 4 5
|
||||
10 8 6 4 2 0
|
||||
7
tests/expected/gosub.expected
Normal file
7
tests/expected/gosub.expected
Normal 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
|
||||
1
tests/expected/graphics_stubs.expected
Normal file
1
tests/expected/graphics_stubs.expected
Normal file
File diff suppressed because one or more lines are too long
1
tests/expected/hailstone.expected
Normal file
1
tests/expected/hailstone.expected
Normal file
@@ -0,0 +1 @@
|
||||
Hailstone OK: 27 -> 111 steps, max 9232
|
||||
1
tests/expected/hanoi.expected
Normal file
1
tests/expected/hanoi.expected
Normal file
@@ -0,0 +1 @@
|
||||
Hanoi OK: 15 moves
|
||||
1
tests/expected/hello.expected
Normal file
1
tests/expected/hello.expected
Normal file
@@ -0,0 +1 @@
|
||||
HELLO, WORLD!
|
||||
1
tests/expected/hundred_doors.expected
Normal file
1
tests/expected/hundred_doors.expected
Normal file
@@ -0,0 +1 @@
|
||||
100 Doors OK: 10 open
|
||||
3
tests/expected/if_then_else.expected
Normal file
3
tests/expected/if_then_else.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
X > 3
|
||||
X >= 3
|
||||
Jumped to 80
|
||||
2
tests/expected/inkey_ext.expected
Normal file
2
tests/expected/inkey_ext.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
Empty INKEY$ ok
|
||||
INKEY$ extended keys test passed
|
||||
12
tests/expected/invoice.expected
Normal file
12
tests/expected/invoice.expected
Normal 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
|
||||
1
tests/expected/leibniz.expected
Normal file
1
tests/expected/leibniz.expected
Normal file
@@ -0,0 +1 @@
|
||||
Pi approx: 3.140593
|
||||
1
tests/expected/lprint.expected
Normal file
1
tests/expected/lprint.expected
Normal file
@@ -0,0 +1 @@
|
||||
LPRINT test complete
|
||||
1
tests/expected/luhn.expected
Normal file
1
tests/expected/luhn.expected
Normal file
@@ -0,0 +1 @@
|
||||
Luhn OK
|
||||
19
tests/expected/math_ops.expected
Normal file
19
tests/expected/math_ops.expected
Normal 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
|
||||
4
tests/expected/matrix_mult.expected
Normal file
4
tests/expected/matrix_mult.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
30 24 18
|
||||
84 69 54
|
||||
138 114 90
|
||||
Matrix mult OK
|
||||
2
tests/expected/mid_assign.expected
Normal file
2
tests/expected/mid_assign.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
HELLO BASIC
|
||||
XYCDEF
|
||||
3
tests/expected/mkicvi.expected
Normal file
3
tests/expected/mkicvi.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
12345
|
||||
3.14
|
||||
2.718281745910645
|
||||
3
tests/expected/monte_carlo.expected
Normal file
3
tests/expected/monte_carlo.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
Pi estimate: 3.1288
|
||||
Actual pi: 3.1416
|
||||
Monte Carlo OK
|
||||
15
tests/expected/mult_table.expected
Normal file
15
tests/expected/mult_table.expected
Normal 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
|
||||
5
tests/expected/nested_for.expected
Normal file
5
tests/expected/nested_for.expected
Normal 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
|
||||
2
tests/expected/number_guess.expected
Normal file
2
tests/expected/number_guess.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
Found 71 in 5 tries
|
||||
Number guess OK
|
||||
3
tests/expected/on_goto.expected
Normal file
3
tests/expected/on_goto.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
Branch 1
|
||||
Branch 2
|
||||
Branch 3
|
||||
9
tests/expected/pascal_triangle.expected
Normal file
9
tests/expected/pascal_triangle.expected
Normal 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
|
||||
1
tests/expected/play_music.expected
Normal file
1
tests/expected/play_music.expected
Normal file
@@ -0,0 +1 @@
|
||||
play_music OK
|
||||
1
tests/expected/play_scale.expected
Normal file
1
tests/expected/play_scale.expected
Normal file
@@ -0,0 +1 @@
|
||||
play_scale OK
|
||||
2
tests/expected/prime_sieve.expected
Normal file
2
tests/expected/prime_sieve.expected
Normal 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
|
||||
6
tests/expected/print_using.expected
Normal file
6
tests/expected/print_using.expected
Normal file
@@ -0,0 +1,6 @@
|
||||
3.14
|
||||
-42.50
|
||||
+ 99.10
|
||||
H
|
||||
Hello
|
||||
$1234.56
|
||||
1
tests/expected/random_access.expected
Normal file
1
tests/expected/random_access.expected
Normal file
@@ -0,0 +1 @@
|
||||
Alice 25 Bob 30
|
||||
7
tests/expected/roman_numerals.expected
Normal file
7
tests/expected/roman_numerals.expected
Normal file
@@ -0,0 +1,7 @@
|
||||
1990 =MCMXC
|
||||
2008 =MMVIII
|
||||
1666 =MDCLXVI
|
||||
3999 =MMMCMXCIX
|
||||
42 =XLII
|
||||
14 =XIV
|
||||
Roman numerals OK
|
||||
2
tests/expected/run_file.expected
Normal file
2
tests/expected/run_file.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
Before RUN
|
||||
Chained OK
|
||||
1
tests/expected/save_binary.expected
Normal file
1
tests/expected/save_binary.expected
Normal file
@@ -0,0 +1 @@
|
||||
Binary round-trip ok
|
||||
1
tests/expected/save_load.expected
Normal file
1
tests/expected/save_load.expected
Normal file
@@ -0,0 +1 @@
|
||||
Program saved
|
||||
1
tests/expected/sound_test.expected
Normal file
1
tests/expected/sound_test.expected
Normal file
@@ -0,0 +1 @@
|
||||
sound_test OK
|
||||
4
tests/expected/stats_calc.expected
Normal file
4
tests/expected/stats_calc.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
Mean: 50.50
|
||||
Variance: 820.25
|
||||
Std Dev: 28.64
|
||||
Stats calc OK
|
||||
14
tests/expected/string_ops.expected
Normal file
14
tests/expected/string_ops.expected
Normal file
@@ -0,0 +1,14 @@
|
||||
HELLO
|
||||
WORLD
|
||||
WORLD
|
||||
4
|
||||
A
|
||||
65
|
||||
3.14
|
||||
42
|
||||
X
|
||||
**********
|
||||
FF
|
||||
377
|
||||
7
|
||||
ABCDEF
|
||||
11
tests/expected/temp_table.expected
Normal file
11
tests/expected/temp_table.expected
Normal 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
|
||||
8
tests/expected/text_adventure.expected
Normal file
8
tests/expected/text_adventure.expected
Normal 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
|
||||
3
tests/expected/variables.expected
Normal file
3
tests/expected/variables.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
42 3.14 2.718281745910645
|
||||
Hello
|
||||
8
|
||||
2
tests/expected/while_wend.expected
Normal file
2
tests/expected/while_wend.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
2 4 6 8 10
|
||||
Done
|
||||
2
tests/expected/write_input.expected
Normal file
2
tests/expected/write_input.expected
Normal file
@@ -0,0 +1,2 @@
|
||||
Alice 25
|
||||
Bob 30
|
||||
40
tests/harness/hamurabi_input.txt
Normal file
40
tests/harness/hamurabi_input.txt
Normal 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
|
||||
5
tests/programs/inkey_ext.bas
Normal file
5
tests/programs/inkey_ext.bas
Normal 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"
|
||||
9
tests/programs/save_binary.bas
Normal file
9
tests/programs/save_binary.bas
Normal 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
|
||||
Reference in New Issue
Block a user