# Language Reference ## Data Types | Type | Suffix | Description | |------|--------|-------------| | INTEGER | `%` | 16-bit signed | | SINGLE | `!` | 32-bit float | | DOUBLE | `#` | 64-bit float | | STRING | `$` | Up to 255 bytes | ## Operators `+`, `-`, `*`, `/`, `^`, `\` (integer div), `MOD`, `AND`, `OR`, `XOR`, `EQV`, `IMP`, `NOT`, `<`, `=`, `>`, `<=`, `>=`, `<>` ## Numeric Functions `SGN`, `INT`, `ABS`, `SQR`, `SIN`, `COS`, `TAN`, `ATN`, `LOG`, `EXP`, `RND`, `FIX`, `CINT`, `CSNG`, `CDBL` `RND` can be called with or without parentheses: `RND` is equivalent to `RND(1)`. ## String Functions `LEN`, `ASC`, `CHR$`, `VAL`, `STR$`, `LEFT$`, `RIGHT$`, `MID$`, `SPACE$`, `STRING$`, `HEX$`, `OCT$`, `INSTR`, `INPUT$` ## File Functions `EOF`, `LOC`, `LOF` ## Memory Functions - `FRE(x)` — free bytes in the string space pool. `FRE("")` triggers a garbage collection pass before reporting; `FRE(0)` reports without collecting. - `VARPTR(var)` / `VARPTR$(var)` — variable address (internal index) ## Pseudo-variables `ERL`, `ERR`, `CSRLIN`, `INKEY$`, `DATE$`, `TIME$`, `TIMER`, `POS(0)` ## Literals Decimal, `&H` hex, `&O` octal, `D` exponent (double), `E` exponent (single), type suffixes (`%`, `!`, `#`) ## Statements | Category | Statements | |----------|------------| | Output | `PRINT`, `LPRINT`, `LLIST`, `PRINT USING`, `WRITE`, `CLS` | | Variables | `LET`, `DIM`, `ERASE`, `SWAP`, `DEFINT`, `DEFSNG`, `DEFDBL`, `DEFSTR` | | Control flow | `GOTO`, `GOSUB`/`RETURN`, `FOR`/`NEXT`, `IF`/`THEN`/`ELSE`, `WHILE`/`WEND`, `ON...GOTO`, `ON...GOSUB` | | Input | `INPUT`, `LINE INPUT`, `DATA`/`READ`/`RESTORE`, `INKEY$` | | 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` (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` | | File management | `KILL`, `NAME`, `FILES`, `MKDIR`, `RMDIR`, `CHDIR`, `SHELL`, `RESET` | | Date/time | `DATE$`, `TIME$`, `TIMER` (read/write; `DATE$`/`TIME$` assignment accepted) | | Environment | `ENVIRON`, `ENVIRON$` | | Screen | `LOCATE`, `COLOR`, `WIDTH`, `SCREEN`, `KEY ON`/`OFF`/`LIST`, `KEY n,"string"` | | Graphics | `PSET`, `PRESET`, `LINE`, `CIRCLE`, `DRAW`, `PAINT`, `GET`, `PUT`, `VIEW`, `WINDOW`, `PALETTE`, `PMAP` | | Sound | `SOUND`, `BEEP`, `PLAY` (MML parser, PulseAudio backend) | | Memory | `DEF SEG`, `PEEK`, `POKE`, `BSAVE`, `BLOAD` | | Hardware I/O | `OUT`, `INP`, `WAIT`, `MOTOR`, `STICK`, `STRIG` | | Device stubs | `ERDEV`, `ERDEV$`, `IOCTL`, `IOCTL$`, `COM`, `LCOPY` | | Misc | `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: - **Default:** output is appended to `LPT1.TXT` in the current directory - **`--lpt /dev/lp0`** (Linux) or **`--lpt LPT1`** (FreeDOS): send to real hardware - **`--lpt report.txt`**: redirect to a custom file `LLIST` lists the program to the printer, with optional line number ranges (`LLIST`, `LLIST 10-50`, `LLIST -100`). Both support `PRINT USING`, semicolons, commas, `TAB()`, and `SPC()`. ## Graphics Graphics mode is activated with `SCREEN 1` (320x200, 4 colors) or `SCREEN 2` (640x200, monochrome). Drawing commands render to an internal framebuffer and output via [Sixel graphics](https://en.wikipedia.org/wiki/Sixel), which works in terminals like xterm, mlterm, foot, and WezTerm. ### Drawing Commands - `PSET (x,y), color` / `PRESET (x,y)` — set/reset individual pixels - `LINE (x1,y1)-(x2,y2), color [,B|BF]` — lines, boxes, filled boxes - `CIRCLE (cx,cy), r [,color [,start, end [,aspect]]]` — circles and arcs - `PAINT (x,y), fill, border` — flood fill - `DRAW string` — turtle graphics mini-language (U/D/L/R/E/F/G/H, M, C, S, B, N) - `POINT (x,y)` — read pixel color - `COLOR fg, bg` — set foreground/background colors ### Sprite Capture and Blit (GET / PUT) - `GET (x1,y1)-(x2,y2), array` — capture a screen rectangle into an integer array - `PUT (x,y), array [, action]` — blit a captured sprite back to the screen The `action` parameter controls how the sprite combines with the existing screen: | Action | Effect | |--------|--------| | `XOR` (default) | Exclusive OR — drawing twice erases the sprite | | `PSET` | Overwrite screen with sprite pixels | | `PRESET` | Overwrite screen with inverted sprite pixels | | `AND` | Bitwise AND of screen and sprite | | `OR` | Bitwise OR of screen and sprite | Sprite data is stored in CGA-compatible packed format: word 0 is the width in bits, word 1 is the height, and remaining words contain packed pixel data matching the original GW-BASIC representation. ### VIEW / WINDOW / PALETTE - `VIEW [[SCREEN] (x1,y1)-(x2,y2) [,[fill][,border]]]` — define a graphics viewport. Without `SCREEN`, drawing coordinates are relative to the viewport origin. With `SCREEN`, coordinates remain absolute. Without arguments, resets to full screen. - `WINDOW [[SCREEN] (x1,y1)-(x2,y2)]` — map logical coordinates onto the viewport. Without `SCREEN`, Y increases upward (Cartesian); with `SCREEN`, Y increases downward. Without arguments, resets to physical coordinates. - `PALETTE [attribute, color]` — remap a color attribute to a different physical color (0–15). Without arguments, resets to the default CGA palette. - `PMAP(coordinate, function)` — convert between logical and physical coordinates. Function 0/1 = logical→physical X/Y; 2/3 = physical→logical X/Y. ### Example ``` SCREEN 1 LINE (0,0)-(319,199), 1 CIRCLE (160,100), 80, 2 PAINT (160,100), 3, 2 ``` ## DEF SEG / PEEK / POKE `DEF SEG`, `PEEK`, and `POKE` provide access to a virtual 8086 address space that emulates the memory layout programs expected on a real IBM PC: ``` DEF SEG = &HB800 ' select CGA video buffer segment POKE 0, 65 ' write 'A' to top-left screen cell PRINT PEEK(1) ' read the color attribute DEF SEG ' reset to default segment ``` ### Emulated Memory Regions | Segment | Address Range | Description | |---------|---------------|-------------| | `0040` | BIOS data area | Video mode (`0049`), column count (`004A`), cursor position (`0050-0051`), timer ticks (`006C-006F`), screen rows (`0084`), keyboard shift flags (`0017`: bit 7 = insert mode) | | `B800` | CGA text buffer | Character/attribute pairs in text mode (even byte = char, odd byte = attr, 80 columns × 25 rows = 4000 bytes) | | `B800` | CGA graphics buffer | In SCREEN 1/2: CGA interlaced layout (even rows at offset 0, odd rows at 0x2000, 80 bytes/row) | All other segments read as 0 and writes are silently discarded. The timer tick counter at `0040:006C` tracks real time at the original 18.2 Hz rate. ## BSAVE / BLOAD `BSAVE` saves a block of virtual memory to a binary file with a 7-byte header. `BLOAD` loads it back. These operate on whichever segment was last set by `DEF SEG`. ``` DEF SEG = &HB800 BSAVE "screen.bin", 0, 4000 ' save the CGA text buffer CLS BLOAD "screen.bin" ' restore it BLOAD "screen.bin", 100 ' load to a different offset ``` The file format is compatible with the original GW-BASIC: byte 0 = `0xFD`, bytes 1-2 = segment (LE), bytes 3-4 = offset (LE), bytes 5-6 = length (LE), followed by the raw data bytes. ## String Space and Garbage Collection All string data lives in a contiguous pool (default 32,768 bytes), matching the original GW-BASIC's string space architecture. Allocation is a bump pointer; dead strings are reclaimed by a compacting garbage collector that runs automatically at statement boundaries when the pool is running low. ``` CLEAR ,8192 ' set string space to 8KB and clear variables PRINT FRE("") ' trigger GC and print free bytes ``` `CLEAR n` (or `CLEAR ,n`) resizes the string space to *n* bytes and clears all variables. `FRE("")` forces a garbage collection pass and returns the free bytes; `FRE(0)` returns the current free count without collecting. ## Hardware I/O (OUT / INP / WAIT) `OUT`, `INP`, and `WAIT` provide access to emulated IBM PC I/O ports, enabling classic programs that drive hardware directly — speaker tones via the 8253 PIT, CGA palette tricks, and serial port polling. ``` OUT &H43, &HB6 ' PIT channel 2: lobyte/hibyte, mode 3 OUT &H42, &HD3 : OUT &H42, &H04 ' set frequency divisor (1235 → 966 Hz) OUT &H61, INP(&H61) OR 3 ' speaker on OUT &H61, INP(&H61) AND &HFC ' speaker off ``` ### Emulated Ports | Port | Device | Behavior | |------|--------|----------| | `&H42`–`&H43` | 8253 PIT channel 2 | Speaker frequency divisor (1193180 / divisor Hz) | | `&H61` | PPI Port B | Bits 0–1 control speaker; both set = tone on, either clear = off | | `&H3D8` | CGA mode control | Mode register; writes with changed mode bits trigger `SCREEN` changes | | `&H3D9` | CGA color select | Background color (bits 0–3) and palette select (bit 5) | | `&H201` | Game port | Returns `&HF0` (no joystick connected) | | `&H3F8`–`&H3FE` | COM1 serial | Minimal stub; LSR (`&H3FD`) returns `&H60` (transmitter ready) | | Default | — | Reads return `&HFF` (floating bus), writes silently discarded | ### Related Functions - `INP(port)` — read a byte from an I/O port - `STICK(n)` — joystick axis position (returns 128 = center, n = 0–3) - `STRIG(n)` — joystick button state (returns 0 = not pressed) - `WAIT port, mask [, xor_mask]` — busy-wait until `(INP(port) XOR xor_mask) AND mask` is nonzero; Ctrl+C breaks out - `MOTOR [n]` — accepted and silently ignored (cassette motor control) When the PPI speaker bits are set, the PIT frequency divisor is used to generate a continuous tone via PulseAudio (same backend as `SOUND` / `PLAY`). ## Environment Variables - `ENVIRON "var=value"` — set an environment variable (uses the C `putenv()` call) - `ENVIRON$("var")` — read an environment variable (returns "" if not set) ``` ENVIRON "GREETING=Hello" PRINT ENVIRON$("GREETING") ' prints: Hello PRINT ENVIRON$("PATH") ' prints the system PATH ``` ## Date/Time Assignment `DATE$` and `TIME$` can be assigned to set the date and time: ``` DATE$ = "01-15-2026" TIME$ = "14:30:00" ``` These assignments are accepted for compatibility but do not modify the system clock. Reading `DATE$` and `TIME$` always returns the current system date and time. ## Device Stubs The following device-related statements and functions are accepted for compatibility with programs that reference them, but return stub values since there is no real device hardware: - `ERDEV` — device error code (always 0) - `ERDEV$` — device error name (always "") - `IOCTL [#n,] string` — device control string output (accepted, ignored) - `IOCTL$(n)` — device control string input (always "") - `COM ON` / `COM OFF` / `COM STOP` — serial port event trapping (accepted, ignored) - `LCOPY [n]` — screen dump to printer (accepted, ignored) - `CALL` / `CALLS` — machine code execution (raises Illegal function call) - `RESET` — close all open files (equivalent to `CLOSE`) ## Sound - `SOUND frequency, duration` — play a tone (frequency in Hz, duration in clock ticks) - `BEEP` — play the default beep - `PLAY string` — Music Macro Language (MML) string for melodies Sound output uses PulseAudio when available; commands are silently ignored otherwise. ## Full-Screen Editor (TUI) When running interactively, GW-BASIC 2026 presents the authentic full-screen editor: - 25×80 screen buffer by default, or full terminal size with `--full` - Press Enter on any screen line to re-enter it as BASIC input - Insert/Overwrite toggle (Insert key) - Home/End/Delete/Backspace/Escape for line editing - Ctrl+C interrupts running programs - Uses the ANSI alternate screen buffer for clean terminal restore on exit ### Function Keys Default F1-F10 bindings match the original GW-BASIC: | Key | Default | Key | Default | |-----|---------|-----|---------| | F1 | `LIST ` | F6 | `,"LPT1:"` + Enter | | F2 | `RUN` + Enter | F7 | `TRON` + Enter | | F3 | `LOAD"` | F8 | `TROFF` + Enter | | F4 | `SAVE"` | F9 | `KEY ` | | F5 | `CONT` + Enter | F10 | `SCREEN 0,0,0` + Enter | - `KEY ON` — show the function key bar on line 25 - `KEY OFF` — hide the bar - `KEY LIST` — display all definitions - `KEY n, "string"` — redefine a function key ### Piped Mode When stdin is not a terminal (piped input), the TUI is not activated. The interpreter reads lines from stdin and writes output directly to stdout, suitable for scripting and test harnesses. ### Program Editing - `EDIT [linenum]` — display a program line for editing in the TUI; press Enter to re-store it - `AUTO [start][,increment]` — automatic line numbering mode - `RENUM [new][,[old][,increment]]` — renumber program lines (patches all GOTO/GOSUB references) - `DELETE range` — delete program lines (`DELETE 10-50`, `DELETE -100`, `DELETE 200-`) ## Event Trapping GW-BASIC supports event-driven programming through trap handlers that fire between statements during program execution. ### Timer Events ``` ON TIMER(n) GOSUB line ' register handler (fires every n seconds) TIMER ON ' enable timer trapping TIMER STOP ' suspend trapping (events are queued) TIMER OFF ' disable trapping (events are discarded) ``` ### Function Key Events ``` ON KEY(n) GOSUB line ' register handler for F-key n (1-10) KEY(n) ON ' enable trapping for key n KEY(n) STOP ' suspend trapping (events are queued) KEY(n) OFF ' disable trapping ``` Event handlers execute as implicit GOSUBs. The `RETURN` statement returns to the interrupted code and clears the handler's in-progress flag. Events do not fire inside their own handler (re-entrant protection). `TIMER STOP` / `KEY(n) STOP` queue events while stopped; switching to `TIMER ON` / `KEY(n) ON` fires the pending event immediately. ## References - Microsoft Corporation. *Microsoft GW-BASIC: User's Guide and Reference*. Microsoft Press, 1989. ISBN 978-1-55615-260-3. - Inman, Don and Bob Albrecht. *The GW-BASIC Reference*. Osborne McGraw-Hill, 1990. ISBN 978-0-07-881644-4. - Ahl, David H. *BASIC Computer Games: Microcomputer Edition*. Workman, 1978. ISBN 978-0-89480-052-8. - Microsoft Corporation. *GW-BASIC User's Manual*. Microsoft, 1987. (OEM bundled; no ISBN.) - Microsoft Corporation. [GW-BASIC Source Code](https://github.com/microsoft/GW-BASIC). Released 2020.