Skip to content

RGC BASIC — language reference

This page documents statements, functions, directives, and system variables as implemented in the current interpreter. Behaviour can change between releases — confirm edge cases in the CHANGELOG and README on GitHub.

Related: Terminal & PETSCII (CLI, CHR$, colours) · Graphics (basic-gfx) (sprites, bitmap, PEEK, gamepad) · Web IDE (WASM) (HTTP$, browser host)


Program shape

Topic Behaviour
Line numbers Classic 10 PRINT … / 20 … programs.
Numberless If the first non-blank line has no leading digits, the loader assigns internal line numbers; labels and structured blocks still work.
Shebang #!/usr/bin/env basic on line 1 is ignored.
Compact CBM syntax At load time, forms like IFX<0THEN, FORI=1TO9, GOTO410 are normalized with spaces so the parser accepts them.
Statement separator : runs multiple statements on one line: A=1 : B=2 : PRINT A+B.

Operators and expressions

Kind Operators
Relational <, >, =, <=, >=, <>
Arithmetic +, -, *, /, \ (integer divide, truncate-toward-zero, classic BASIC / QBasic-style), ^ (power), MOD (floored modulo). \ pairs with MOD: (a \ b) * b + (a MOD b) == a.
Bitwise <<, >>, AND, OR, XOR (integer parts of operands)
Strings Concatenation with +; string/numeric comparisons where applicable. Backslash escapes inside double-quoted literals: \n (LF, CHR$(10)), \r (CR, CHR$(13)), \t (TAB, CHR$(9)), \0 (NUL), \\ (literal backslash), \" (literal quote). Unknown \x passes through as \x. Expanded at load time. Works in ASCII + PETSCII modes, terminal + gfx builds, SCREEN 0/1/2.

RND(x) — Returns a value in 0..1 (uniform via rand()). If x < 0, the generator is reseeded (C64-style negative seed); the implementation uses time so runs differ, similar in spirit to RND(-TI) on a C64.


Variables

Kind Rules
Numeric Names up to 31 significant characters; _ allowed.
String Name ends with $.
Arrays DIM A(10), DIM B(2,3) — indices are 0-based: DIM A(10) allows 0..10; DIM C(2,3) is a 3×4 matrix.
Reserved words Identifiers that match keywords (IF, FOR, PRINT, …) cannot be variable names. Labels may still use names that look like keywords in some cases (e.g. CLR:).

Meta directives (# prefix)

Processed at load time. #OPTION values in the file override the same CLI flag.

Directive Purpose
#INCLUDE "path" Splice another file at this line. Avoid duplicate line numbers across includes; mixed numbered/numberless rules apply — errors are load-time.
#OPTION petscii Same as -petscii.
#OPTION petscii-plain Same as -petscii-plain.
#OPTION charset upper / #OPTION charset lower PETSCII letter set.
#OPTION palette ansi / #OPTION palette c64 PETSCII colour mapping.
#OPTION maxstr N String length limit (1–4096; default large).
#OPTION columns N Print width (1–255; default 40).
#OPTION nowrap Do not wrap at column width.
#OPTION memory c64 / pet / default basic-gfx / canvas WASM only — virtual memory layout for POKE/PEEK.
#OPTION screen $addr, #OPTION colorram …, #OPTION charmem …, #OPTION keymatrix …, #OPTION bitmap … Override individual regions (decimal or hex).
#OPTION gfx_title "text" basic-gfx — window title (see also -gfx-title).
#OPTION border N or #OPTION border N colour basic-gfx — padding around the drawable area (see Graphics).

Design notes and history: meta-directives-plan.md in the repo.


Statements (by category)

Output and input

Statement Summary
PRINT / ? Expressions; ; suppresses newline; , advances to comma zones (width scales with -columns).
INPUT Read into variables; optional "prompt"; form. In basic-gfx, reads from the window queue. With stdin (terminal), supports pipes.
GET Non-blocking single-character read into a string variable. If nothing waiting, result is "". Enter is CHR$(13) so ASC(K$)=13 works.

Assignment and control

Statement Summary
LET Optional; A=1 is fine without LET.
Compound assign A++ / A-- increment / decrement by 1. A += expr / -= / *= / /= = A = A op expr. S$ += "x" concatenates; -= *= /= raise on strings. Statement-only (not expression).
IF … THEN Inline (IF X THEN 100, IF X THEN PRINT "Y") or block: IF[ELSE IF cond THEN][ELSE]END IF. Nested blocks supported.
ELSE IF cond THEN / ELSEIF cond THEN Multi-way block-form chain inside an existing IF … END IF. Both spellings (two-word ELSE IF and one-word ELSEIF) tokenise to the same handler; mix freely in one chain. Block-form only — no inline IF a THEN x ELSE IF b THEN y (use nested inline IF … THEN … ELSE (IF … THEN …) instead). Conditions are evaluated top-to-bottom; the first true branch fires and the rest skip to END IF. See tests/else_if_test.bas for the full behavior matrix.
WHILEWEND Pre-test loop.
DOLOOP Infinite until EXIT; or LOOP UNTIL expr. EXIT exits the innermost DO.
FORNEXT Numeric FOR with optional STEP (positive or negative).
FOREACH var IN arr[()]NEXT var Iterate each element of a 1-D array (numeric or string). NEXT var advances; EXIT pops the innermost FOR/FOREACH. Empty arrays run zero iterations.
GOTO Target is a line number or label.
GOSUB / RETURN Subroutine stack; target line or label.
ON expr GOTO / ON expr GOSUB Multi-way branch (e.g. ON N GOTO 100,200,300).

Data and comments

Statement Summary
READ / DATA Read literals into variables.
RESTORE Reset DATA pointer; RESTORE line positions to first DATA at or after line.
REM / ' Comment to end of line.
END / STOP Stop execution.

Arrays and structured data

Statement Summary
DIM Declare numeric or string arrays (1-D or multi-D).
SORT arr [, mode] In-place 1-D sort. mode: 1 or "asc" (default) ascending; -1 or "desc" descending; numeric or string arrays.
SPLIT str$, delim$ INTO arr$ Split into a pre-DIMmed 1-D string array.
JOIN arr$, delim$ INTO result$ [, count] Join; if count omitted, uses count from last SPLIT.

Simple functions (DEF FN)

Form Example
DEF FNname(params) = expr One-line numeric function, e.g. DEF FNY(X)=SIN(X).

User-defined procedures (FUNCTION)

Form Rules
FUNCTION name [(p1, p2, …)]RETURN [expr]END FUNCTION Call with parentheses: n = add(3,5), sayhi(). Parameters are local; recursion supported. RETURN expr returns a value; bare RETURN / END FUNCTION without a prior return yields 0 / "" in expression context.

File I/O (CBM-style)

Statement / var Summary
OPEN lfn, device, sec, "filename" Device 1 = host files; secondary 0 read, 1 write, 2 append.
PRINT# lfn, … Write like PRINT, to file.
INPUT# lfn, var [, …] Read tokens into variables.
GET# lfn, str$ One character into string variable.
CLOSE [lfn [, …]] Close listed channels or all if omitted.
ST After INPUT# / GET# / GETBYTE / PUTBYTE: 0 = success, 64 = end of file, 1 = error / not open.
PUTBYTE #lfn, byte_expr Single-byte raw write (binary file output). See detailed reference.
GETBYTE #lfn, var Single-byte raw read into numeric scalar; -1 at EOF. See detailed reference.

Memory and loading (basic-gfx / canvas)

Statement Summary
POKE addr, byte Terminal: no-op (compatibility). Gfx: writes virtual memory.
PEEK(addr) Terminal: returns 0. Gfx: reads virtual memory (16-bit address).
LOAD "path" INTO addr [, length] Load raw bytes from file; optional max length.
LOAD @label INTO addr [, length] Load from DATA bytes at label.
MEMSET addr, len, val Fill virtual memory.
MEMCPY dest, src, len Copy virtual memory.

Screen, timing, terminal styling

Statement Summary
SLEEP n Pause n ticks at 60 Hz (≈ SLEEP 60 ≈ one second in gfx; same tick unit used broadly).
LOCATE col, row 0-based cursor move (ANSI in terminal; gfx text cursor).
TEXTAT col, row, expr$ Move and print string at absolute position.
CURSOR ON / CURSOR OFF Terminal: ANSI show/hide cursor.
DOWNLOAD path$ Browser WASM: reads the file from MEMFS, wraps it in a Blob with a guessed MIME type, triggers a real browser download. Native: no-op (file is already on disk); prints a one-shot hint on stderr.
COLOR n / COLOUR n, BACKGROUND n, PAPER n C64-style indices 0–15 (foreground / background / per-cell paper). Named constants resolve everywhere a numeric colour is expected: BLACK WHITE RED CYAN PURPLE GREEN BLUE YELLOW ORANGE BROWN PINK DARKGRAY MEDGRAY LIGHTGREEN LIGHTBLUE LIGHTGRAY (plus DARKGREY/MEDGREY/LIGHTGREY spellings).
DOUBLEBUFFER ON / DOUBLEBUFFER OFF Gfx: toggle bitmap-plane double-buffering (default OFF). With ON, renderer displays a committed back-buffer and only updates on VSYNC — matches the cell list so whole frames commit atomically. Shorthand for SCREEN DRAW 0 : SCREEN SHOW 1 + auto-flip on VSYNC.
SCREEN BUFFER n, SCREEN DRAW n, SCREEN SHOW n, SCREEN FREE n, SCREEN SWAP a, b, SCREEN COPY src, dst Gfx: multi-plane bitmap buffers (8 slots). Slot 0 = live bitmap[], slot 1 = DOUBLEBUFFER back-buffer, slots 2..7 = user-allocated via SCREEN BUFFER n. SCREEN DRAW n retargets all bitmap writes (PSET, LINE, CLS, DRAWTEXT, ...); SCREEN SHOW n moves the renderer. FREE is refused if the slot is the active draw or show. SWAP sets draw=a, show=b atomically; COPY blits one plane into another.
SCREEN 2 Gfx: 320×200 RGBA. Each pixel carries its own RGBA. 256 KB per plane, lazy-allocated on first entry. Use COLORRGB r, g, b [, a] / BACKGROUNDRGB for full-colour pens; palette COLOR n still works and syncs the RGBA pen via the palette table.
SCREEN 3 Gfx: 320×200 8bpp palette-indexed. 64 KB per plane (reuses the SCREEN 1 colour plane). COLOR / BACKGROUND accept 0..255 in this mode. 256-entry palette shared with SCREEN 1/2 — PALETTESET / PALETTEROTATE visibly retint every drawn pixel on the next frame without any redraw. Entries 0..15 = C64 defaults; 16..255 = HSV rainbow + greyscale by default.
SCREEN 4 Gfx: 640×400 RGBA "QB64-style" desktop canvas. Same primitives and pen system as SCREEN 2 (COLORRGB, BACKGROUNDRGB, LINE, RECT, FILLRECT, CIRCLE, DRAWTEXT, LOADSCREEN, IMAGE LOAD/BLEND) — only the coordinate range and plane size differ (1 MB per plane vs 256 KB). Use for level editors, multi-pane HUDs, IDE-ish workspaces where 320×200 is too cramped. Demo: examples/gfx_screen4_demo.bas.
PALETTESET i, r, g, b [, a], PALETTESETHEX i, "#RRGGBB[AA]", PALETTERESET, PALETTEROTATE first, last [, step] Gfx: retune the shared 256-entry palette. PALETTEROTATE shifts entries [first..last] in-place (one C-side memcpy swap, not 256 PALETTESETs) — classic palette cycling for water / fire / plasma retro effects. Read via PALETTE(i, chan) and PALETTEHEX$(i).
PALETTELOAD path$ / PALETTESAVE path$ Gfx: plain-text .pal file I/O (JASC-PAL compatible). Round-trips with the live palette. See detailed reference.
LOADSCREEN path$ [, x [, y]] Gfx: load a PNG / BMP / JPG / TGA / GIF into the current screen plane; behaviour depends on SCREEN mode. See detailed reference.
OVERLAY ON / OVERLAY OFF / OVERLAY CLS Gfx (raylib backend): redirect bitmap-plane writes to an RGBA HUD plane that composites above tiles + sprites. See detailed reference.
IMAGE CREATE slot, w, h, IMAGE BLEND src, sx, sy, sw, sh TO dst, dx, dy, IMAGE DRAW slot Gfx (SCREEN 2 / SCREEN 4): RGBA off-screen surfaces + alpha-composited blits + draw-target retargeting. See detailed reference.
MAPSAVE path$ [, layer$] Gfx: rewrite the named tile layer in the JSON last opened by MAPLOAD. See detailed reference.
CLS, CLS x, y TO x2, y2 Full-screen clear (terminal + gfx) or, in basic-gfx bitmap mode, clear only the given pixel rectangle on the current draw plane (same shape as FILLRECT with COLOR 0). Handy for redrawing only a HUD strip inside DOUBLEBUFFER / SCREEN BUFFER loops.
DRAWTEXT x, y, text$ [, scale] / DRAWTEXT x, y, text$, fg, bg [, font [, scale]] Gfx: stamp string onto the bitmap at pixel (x, y) via active chargen. scale 1..8 pixel-doubles each source pixel into a scale × scale block. Extended 5-arg form sets per-call foreground / background palette indices; bg = -1 means transparent paper (non-glyph pixels untouched). font is parsed but currently ignored — reserved for the eventual LOADFONT work. Inline PETSCII tokens (2.1): 16 colour tokens ({RED}, {WHITE}, {GREEN}, …) swap the pen mid-string so one call paints multi-colour text. {REVERSE ON} / {REVERSE OFF} toggle reverse-video. With bg >= 0 reverse fills the cell with fg and stamps glyph in bg (classic Commodore swap). With bg < 0 reverse fills the cell with fg and leaves glyph pixels UNTOUCHED — whatever is painted underneath reads through the letter shape, giving a "gradient-coloured text" / knockout effect. Cursor-move tokens (\n / \r / {HOME} / {CLEAR} / {DOWN} / {UP} / {LEFT} / {RIGHT}) are consumed silently — DRAWTEXT is pixel-space, use multiple calls for multi-line layout. See examples/gfx_drawtext_tokens_demo.bas.
IMAGE DRAW slot (2.1) Gfx SCREEN 2 / 4 only: retarget every RGBA primitive (LINE, FILLRECT, CIRCLE, DRAWTEXT, PSET, POLYGON, …) to an off-screen IMAGE CREATE surface instead of the live framebuffer. Mirror of SCREEN DRAW n, but routed into the IMAGE pool so the canvas size is arbitrary (not 320×200). IMAGE DRAW 0 restores the live screen. Used to pre-bake wide scroller strips, world maps, HUD layers, reusable sprite atlases — then blit back with IMAGE BLEND. Non-RGBA modes raise a runtime error. See examples/gfx_imagedraw_demo.bas.
SCROLL ZONE id, y, h, SCROLL ZONE id, dx, SCROLL ZONE CLEAR id, SCROLL ZONE RESET id (2.1) Gfx RGBA: declare up to 15 horizontal bands (ids 1..15) that scroll independently at composite time. y, h form declares the rect; two-arg form advances the zone's running dx (modular wrap on plane width, state persists across frames so one-per-frame = smooth scroll). Classic demo message-bar / foreground-only scroll / multi-band parallax.
SCROLL LINE y, dx, SCROLL LINE RESET, SCROLL RESET (2.1) Gfx RGBA: per-scanline horizontal offset. Each row gets its own dx, applied at composite time. Stacks on top of SCROLL ZONE offsets. Raster-warp trick reproduced in software — water ripple, flag wave, heat haze, CRT jitter. SCROLL LINE RESET zeroes every row; SCROLL RESET nukes both zones and per-line state. See examples/gfx_scrollzone_demo.bas.
PAPER n Per-cell background index (0–15); only subsequent PRINT output stamps bgcolor[]. Leaves the global BACKGROUND register untouched.
ANTIALIAS ON / ANTIALIAS OFF Gfx: bilinear vs nearest-neighbour filter for sprites and the upscaled framebuffer (default OFF).
TIMER id, interval_ms, FuncName / TIMER STOP id / TIMER ON id / TIMER CLEAR id Register, disable, re-enable, or remove a periodic timer. 12 timers max (ids 1–12); minimum interval 16 ms; FuncName is a zero-arg FUNCTION/END FUNCTION block. Re-entry is skipped, not queued.
SCREEN 0 / SCREEN 1, PSET, PRESET, LINE, SCREENCODES, SCROLL, sprite statements Gfx / canvas — see Graphics.
LOADSOUND slot, "file.wav", PLAYSOUND slot, STOPSOUND, UNLOADSOUND slot, SOUNDPLAYING() basic-gfx + basic-wasm-raylib (canvas WASM stays frozen): single-voice WAV playback, 32 slots. PLAYSOUND is non-blocking and stops whatever was already playing. SOUNDPLAYING() returns 1 while audible, 0 when idle — self-clears at natural end-of-sample. Browsers require a user gesture (key / click) before AudioContext resumes, so gate the first cue on KEYPRESS or ISMOUSEBUTTONPRESSED.
LOADMUSIC slot, "song.mod", PLAYMUSIC slot, STOPMUSIC slot, PAUSEMUSIC slot / RESUMEMUSIC slot, MUSICVOLUME slot, 0.0..1.0, MUSICLOOP slot, 0|1, UNLOADMUSIC slot, MUSICPLAYING(slot), MUSICLENGTH(slot), MUSICTIME(slot), MUSICPEAK(), MUSICTITLE$(slot), MUSICSAMPLENAME$(slot, idx), MUSICCHANNELS(slot), MUSICPATTERNS(slot), MUSICORDERS(slot), MUSICSAMPLECOUNT(slot) basic-gfx + basic-wasm-raylib (canvas WASM stays frozen): streaming tracker-module / long-form playback via raylib raudioMOD / XM / S3M / IT / OGG / MP3. Eight slots, separate pool from LOADSOUND. MUSICLENGTH(slot) returns total seconds (MOD length computed via pattern/tempo walk at load — jar_mod's built-in probe freezes Windows MinGW for tens of seconds, so we do it ourselves). MUSICTIME(slot) is elapsed seconds (wraps on loop). MUSICPEAK() is the 0..1 master-mix held-peak for VU meters. Same browser-autoplay rules as LOADSOUND — wait for a user gesture before the first LOADMUSIC. Example: examples/gfx_music_demo.bas with bundled Public-Domain tracks.

Sprites and gamepad (gfx / canvas)

Statements / functions See
LOADSPRITE / SPRITE LOAD, DRAWSPRITE / SPRITE DRAW, DRAWSPRITETILE / TILE DRAW, UNLOADSPRITE / SPRITE FREE, SPRITEVISIBLE, SPRITEMODULATE, SPRITEFRAME / SPRITE FRAME, SPRITECOPY, SPRITE STAMP (multi-instance) Graphics — PNG sprites
TILEMAP DRAW (batched array → tile grid), DRAWTILEMAP (alias) Graphics — tilemaps
IMAGE NEW, IMAGE FREE, IMAGE COPY … TO …, IMAGE LOAD, IMAGE GRAB, IMAGE SAVE Graphics — blitter surfaces
RECT / FILLRECT, CIRCLE / FILLCIRCLE, ELLIPSE / FILLELLIPSE, TRIANGLE / FILLTRIANGLE, POLYGON / FILLPOLYGON, FLOODFILL, DRAWTEXT Graphics — bitmap mode
VSYNC (frame commit + wait one display frame) Graphics — keyboard & time
SPRITEW, SPRITEH, SPRITETILES / TILE COUNT / SPRITE FRAMES, SHEET COLS/ROWS/WIDTH/HEIGHT, SPRITEFRAME(), SPRITECOLLIDE, ISMOUSEOVERSPRITE(slot [, alpha_cutoff]), SPRITEAT(x, y), SCROLLX/SCROLLY, JOY, JOYAXIS, KEYDOWN/KEYUP/KEYPRESS, ANIMFRAME Same page

Other

Statement Summary
CLR Clear variables, GOSUB/FOR stacks, DATA pointer; DEF FN kept.

Intrinsic functions

Parentheses are required where shown. String functions use a trailing $ in the usual BASIC style.

Math

Function Notes
SIN, COS, TAN Radians.
ABS, INT, SQR, SGN, EXP, LOG Standard meanings.
RND(x) See Operators.

Strings

Function Signature / notes
LEN(s$) Length.
STR$(n) Number → string ( %g style).
VAL(s$) Parse leading numeric.
CHR$(n) Byte n & 0xFF; in -petscii terminal mode, maps control/colour to ANSI; in gfx, raw byte for screen semantics.
ASC(s$) First character code, or 0 if empty.
INSTR(s$, find$ [, start]) 1-based index, or 0; optional 1-based start.
REPLACE(s$, find$, repl$) Replace all occurrences.
TRIM$(s$), LTRIM$(s$), RTRIM$(s$) Whitespace trim.
FIELD$(s$, delim$, n) nth field (1-based), awk-like.
UCASE$(s$), LCASE$(s$) ASCII case.
MID$(s$, start [, len]) 1-based start.
LEFT$(s$, n), RIGHT$(s$, n) Substrings.
STRING$(n, c$) or STRING$(n, code) Repeat character: count n and one-char string or numeric code.

Arrays

Function Notes
INDEXOF(arr, value), LASTINDEXOF(arr, value) 1-based index in 1-D array, or 0; works for numeric or string arrays.

JSON

Function Notes
JSON$(json$, path$) Extract by path ("key", "key[0]", "a.b", …); returns string. Use VAL(JSON$(…)) for numbers.
JSONLEN(json$, path$) Count of entries at path (array / object), 0 for scalars or missing paths. Pairs with FOR I = 0 TO JSONLEN(j$, "items") - 1.
JSONKEY$(json$, path$, n) 0-based Nth key when path resolves to an object; "" for arrays or scalars. Enumerate fields without hard-coding names.

Environment and host

Function Notes
ENV$(name$) Environment variable, or "".
FILEEXISTS(path$) 1 if the path is openable for reading, 0 otherwise. Works against MEMFS in browser WASM and the host filesystem natively. Idiomatic post-save check: IF FILEEXISTS(P$) THEN DOWNLOAD P$.
CWD$(), CHDIR path$ Current working directory + change. Native getcwd/chdir; MEMFS on browser WASM via the same POSIX layer. CHDIR raises a runtime error on missing paths.
DIR$(path$ [, delim$]) Delimiter-joined (default newline) list of non-hidden names in path$. Returns "" on failure. Capped by #OPTION maxstr.
DIR path$ INTO arr$ [, count] Statement form: populate a 1-D string array (must be DIMmed) with filenames and optionally assign the count. Mirrors SPLIT … INTO arr$ [, count].
TICKUS(), TICKMS() Monotonic microsecond / millisecond counters. Origin is implementation-defined — differences are meaningful. Native: clock_gettime(CLOCK_MONOTONIC). Browser WASM: emscripten_get_now().
PLATFORM$() Host string — see Web IDE for browser vs native.

Evaluation and conversion

Function Notes
EVAL(expr$) Evaluate expr$ as a BASIC expression (numeric or string). Use CHR$(34) for embedded quotes.
DEC(s$) Hex string → number; invalid → 0.
HEX$(n) Integer part → uppercase hex without $.
Function Notes
TAB(n), SPC(n) Column advance / spaces ( TAB wraps within print width).

Scripting and shell (native OS)

Function Notes
ARGC() Count of arguments after the script path.
ARG$(n) ARG$(0) = script path; ARG$(1)… = args. Out of range → "".
SYSTEM(cmd$) Run shell command; returns exit status. Browser WASM: not available — returns -1.
EXEC$(cmd$) stdout as string (up to interpreter max string size; trailing newline trimmed). Browser WASM: returns "".

HTTP and buffers (browser WASM + basic-gfx)

Function / statement Notes
HTTP$(url$ [, method$ [, body$]]) fetch; response body as string, capped at #OPTION maxstr (≤4 KB). Alias: HTTP(url) without $ calls the same intrinsic.
HTTPSTATUS() Status from last HTTP$ / HTTPFETCH / BUFFERFETCH; 0 if failed / not WASM.
HTTPFETCH(url$, path$ [, method$ [, body$]]) One-shot HTTP-to-file. Bypasses the 4 KB string cap. See detailed reference.
BUFFERNEW slot, BUFFERFETCH slot, url$ [, method$ [, body$]], BUFFERFREE slot, BUFFERLEN(slot), BUFFERPATH$(slot) Slot-based file-backed HTTP. Pulls arbitrary-size responses into a temp file you can OPEN / GETBYTE / LOADSCREEN / etc. See Network & buffers and detailed reference.

Details: Web IDE — HTTP$; Network & buffers.

Graphics-only (see Graphics)

INKEY$(), PEEK, KEYDOWN(code), KEYUP(code), KEYPRESS(code) (rising-edge latch), ANIMFRAME(first, last, jiffies) (time-cycled frame index), SPRITEW, SPRITEH, SPRITETILES / TILE COUNT / SPRITE FRAMES, SHEET COLS/ROWS/WIDTH/HEIGHT, SPRITEFRAME(), SPRITECOLLIDE, ISMOUSEOVERSPRITE(slot [, alpha_cutoff]) (SCROLL-aware; one arg = bbox, two args = pixel-perfect alpha sampling), SPRITEAT(x, y) (topmost visible slot at point, Z tie-break; −1 if none), SCROLLX(), SCROLLY(), JOY, JOYSTICK, JOYAXIS.


System variables

Name Behaviour
ST Set by INPUT# / GET# (see File I/O).
TI basic-gfx / canvas: 60 Hz jiffy counter (wraps per README). Native terminal: not jiffies — implementation uses Unix time (seconds) as a fallback so RND(-TI) still reseeds. Canvas WASM (gfx): derived from monotonic clock when per-frame ticks are not used.
TI$ Gfx: string HHMMSS from jiffy clock. Terminal: wall-clock HHMMSS from local time.

Identifiers starting with TI are resolved with CBM-style rules ( TI vs TI$ ).


Recently added — detailed reference

Every entry below follows the same shape: Purpose, Parameters, Returns, Example. Older entries already covered in the tables above are not repeated.

Buffer slots — file-backed HTTP fetches

A BUFFER is a numbered slot (0..15) backed by a temp file the interpreter manages on disk (or in MEMFS on browser WASM). The point: HTTP$() returns a string capped by #OPTION maxstr (4 KB max), so you can't pull a 100 KB JSON or a 2 MB binary into memory in one shot. BUFFER* lets you stream the response into a file you can then OPEN like any other and walk byte-by-byte with GETBYTE or INPUT#.

Available in basic-gfx and browser WASM (basic-wasm-raylib); plain terminal basic returns 0 / "" from BUFFERLEN / BUFFERPATH$.

BUFFERNEW slot

  • Purpose: allocate a buffer slot and create its empty backing file. Replaces any prior occupant of the same slot.
  • Parameters: slot — integer 0..15.
  • Returns: nothing (statement). Errors hint "slot out of range" or "cannot create backing file".
  • Example:
    BUFFERNEW 0
    

BUFFERFETCH slot, url$ [, method$ [, body$]]

  • Purpose: HTTP fetch into the slot's backing file. Browser WASM only — native builds set HTTPSTATUS() to 0 and write nothing.
  • Parameters:
  • slot — integer 0..15, must have been BUFFERNEW'd.
  • url$ — full URL (must allow CORS from your page origin).
  • method$ — optional, "GET" (default) / "POST" / "PUT" / "DELETE".
  • body$ — optional request body for POST/PUT.
  • Returns: nothing (statement). Sets HTTPSTATUS() to the HTTP status code on completion.
  • Example:
    BUFFERNEW 0
    BUFFERFETCH 0, "https://api.example.com/data"
    PRINT "got "; BUFFERLEN(0); " bytes, status "; HTTPSTATUS()
    

BUFFERFREE slot

  • Purpose: unlink the backing file and release the slot. Idempotent — safe to call on an empty slot.
  • Parameters: slot — integer 0..15.
  • Returns: nothing (statement).
  • Example:
    BUFFERFREE 0
    

BUFFERLEN(slot)

  • Purpose: byte length of the slot's backing file (i.e., how much was downloaded).
  • Parameters: slot — integer 0..15.
  • Returns: numeric bytes; 0 if slot empty / unallocated / on the terminal build.
  • Example:
    IF BUFFERLEN(0) = 0 THEN PRINT "empty response" : END
    

BUFFERPATH$(slot)

  • Purpose: filesystem path of the slot's backing file. Pass to OPEN, IMAGE LOAD, LOADSCREEN, MAPLOAD, etc. so any path-taking command can consume HTTP-fetched bytes.
  • Parameters: slot — integer 0..15.
  • Returns: string path; "" if slot empty / unallocated.
  • Example:
    OPEN 1, 1, 0, BUFFERPATH$(0)
    GETBYTE #1, B
    CLOSE 1
    

See also: full walkthrough in Network & buffers.

HTTPFETCH(url$, path$ [, method$ [, body$]])

  • Purpose: one-shot HTTP-to-file. Fetches url$ and writes the body to path$ directly (no slot bookkeeping). Use when you already have the destination path you want; use BUFFER* when you want the interpreter to pick a temp path for you.
  • Parameters:
  • url$ — full URL, CORS-aware on browser WASM.
  • path$ — destination file path (MEMFS in browser, host FS native).
  • method$ — optional, default GET.
  • body$ — optional request body.
  • Returns: numeric — non-zero on success, 0 on failure. Also sets HTTPSTATUS().
  • Example:
    IF HTTPFETCH("https://example.com/sky.png", "sky.png") THEN
      LOADSCREEN "sky.png"
    END IF
    

Binary file I/O — PUTBYTE and GETBYTE

Pair with OPEN lfn, device, sec, "filename" (device 1 = host file, secondary 0/1/2 = read/write/append). PRINT#/INPUT# are line/token oriented; PUTBYTE/GETBYTE are raw single-byte for binary formats (PNG inspection, MOD parsing, custom save files).

PUTBYTE #lfn, byte_expr

  • Purpose: write one byte (0..255) to an open file channel.
  • Parameters:
  • lfn — logical file number from OPEN (1..255).
  • byte_expr — numeric, masked with & 255.
  • Returns: nothing. Sets ST = 0 on success, ST = 1 if the channel is not open or the write failed.
  • Example:
    OPEN 2, 1, 1, "save.bin"
    FOR I = 0 TO 255 : PUTBYTE #2, I : NEXT I
    CLOSE 2
    

GETBYTE #lfn, var

  • Purpose: read one byte (0..255) from an open file channel into a numeric scalar.
  • Parameters:
  • lfn — logical file number (1..255).
  • var — numeric scalar variable (not array, not string). Receives 0..255 on success or -1 at EOF / on error.
  • Returns: nothing. Sets ST = 0 on success, ST = 64 at EOF, ST = 1 if the channel is not open.
  • Example:
    OPEN 1, 1, 0, "data.bin"
    DO
      GETBYTE #1, B
      IF B = -1 THEN EXIT
      PRINT HEX$(B); " ";
    LOOP
    CLOSE 1
    

Object overlays — OBJLOAD / OBJSAVE

Decouple object placement from terrain so a single base map can drive multiple gameplay variants (easy / hard, wave 1 / wave 2 / wave 3, base / mod, etc.). Pair with MAPLOAD: load the base map first, then OBJLOAD an overlay file that replaces or extends the MAP_OBJ_* arrays.

Overlay schema (Shape A):

{
  "format": 1,
  "kind": "objects-overlay",
  "appliesTo": "level1-overworld",
  "mode": "replace",
  "objects": [
    { "id": 100, "type": "enemy", "kind": "octorok",
      "shape": "rect", "x": 64, "y": 64, "w": 16, "h": 16 }
  ]
}

A Shape B overlay (full map JSON whose only populated layer is obj) is also accepted as a fallback so editors that only emit the wider schema still work. The runtime ignores appliesTo — it's documentation; check it from BASIC if you want a hard match.

OBJLOAD path$ [, mode$]

  • Purpose: load an objects-overlay file into the existing MAP_OBJ_* arrays. Caller must DIM the arrays large enough for the total objects (base + overlay if appending).
  • Parameters:
  • path$ — overlay file. Browser WASM: must be bundled in the IDE preset.
  • mode$ — optional. "replace" (default) clears MAP_OBJ_COUNT to 0 before loading. "append" stacks overlay objects on top of whatever's already in the arrays.
  • Returns: nothing. Sets MAP_OBJ_COUNT to the new total. Errors on missing file, file > 4 MiB, unsupported format, missing objects array.
  • Example:
    MAPLOAD "level1.json"
    IF DIFF$ = "hard" THEN
      OBJLOAD "level1.hard.objects.json"
    ELSE
      OBJLOAD "level1.easy.objects.json"
    END IF
    

OBJSAVE path$

  • Purpose: write the current MAP_OBJ_* arrays as a Shape A objects-overlay file. Used by editors and by procedural-spawn programs that want to capture a generated wave.
  • Parameters: path$ — destination file.
  • Returns: nothing. Errors if the path is unwritable.
  • Limitation: props are not preserved in this build — overlay output is regenerated from the live arrays only. Hand-edit the JSON if you need props round-trip; a future revision will read MAP_OBJ_PROPS$().
  • Shape detection: when both MAP_OBJ_W(i) = 0 and MAP_OBJ_H(i) = 0, the entry is emitted as "shape": "point" (no w/h fields). Otherwise "shape": "rect" with w/h.
  • Example:
    OBJSAVE "level1.hard.objects.json"
    IF FILEEXISTS("level1.hard.objects.json") THEN DOWNLOAD "level1.hard.objects.json"
    

Map I/O — companion to MAPLOAD

MAPSAVE path$ [, layer$]

  • Purpose: rewrite the named tile layer's data array in the JSON last opened by MAPLOAD and write the patched JSON to path$. Use to round-trip live edits (a built-in level editor that mutates MAP_BG() then saves).
  • Parameters:
  • path$ — destination file. Browser WASM persists via the host's MEMFS — pair with DOWNLOAD to deliver as a real file.
  • layer$ — optional, default "bg". Use "fg" to save the foreground layer; the loader reads MAP_FG() for that case.
  • Returns: nothing. Errors if no prior MAPLOAD, if the layer / data array is missing, or if MAP_W * MAP_H doesn't match the array DIM.
  • Example:
    MAPLOAD "level1.json"
    MAP_BG(0) = 17                 : REM water at top-left
    MAPSAVE "level1.json"
    IF FILEEXISTS("level1.json") THEN DOWNLOAD "level1.json"
    

HUD overlay (raylib basic-gfx and canvas WASM)

OVERLAY ON | OVERLAY OFF | OVERLAY CLS

  • Purpose: redirect bitmap-plane primitives (PSET, LINE, FILLRECT, RECT, DRAWTEXT, CLS) to a second RGBA plane composited above the cell list (TILEMAP DRAW + SPRITE STAMP), so HUD text and dialog boxes always sit above world tiles. The raylib backend composites bitmap → cells → overlay; canvas WASM (frozen) currently flattens the overlay onto the bitmap plane (works for static HUDs but not for above-tile sorting).
  • Screen-mode requirement: redirection is honoured only in SCREEN 2 and SCREEN 4 (the RGBA modes). The compositor renders the overlay on top in every mode, but in SCREEN 0 (text), SCREEN 1 (1bpp), and SCREEN 3 (indexed) the primitives keep writing to their own planes (text RAM, bitmap[], bitmap_color[]) — OVERLAY ON is accepted, the buffer auto-allocates, but stays empty so nothing visible composites on top. If you need a HUD over an indexed-mode world, paint it directly to the indexed plane last (after world tiles), or switch to SCREEN 2 / SCREEN 4.
  • Parameters:
  • ON — start redirecting bitmap-plane writes to the overlay (lazy-allocates the buffer).
  • OFF — back to the main bitmap (default).
  • CLS — clear the overlay to fully transparent (alpha 0).
  • Returns: nothing.
  • Example:
    DO
      CLS                                       : REM clear world bitmap
      TILEMAP DRAW 0, 0, 0, COLS, ROWS, MAP()
      SPRITE STAMP 1, PX, PY, 0, 50
      OVERLAY ON
        CLS                                     : REM clear overlay
        COLORRGB 0,0,0,255 : FILLRECT 0,0 TO 319,23
        COLORRGB 255,240,80,255 : DRAWTEXT 4,4,"LIFE 3"
      OVERLAY OFF
      VSYNC
    LOOP
    

LOADSCREEN path$ [, x [, y]]

  • Purpose: load a PNG / BMP / JPG / TGA / GIF into the current screen plane. Behaviour dispatches on the active SCREEN mode:
  • SCREEN 0 (text) — cell-quantises to one fg + one bg hardware colour per cell + a PETSCII block glyph.
  • SCREEN 1 (1bpp) — Floyd-Steinberg dithers to the 16 hardware colours; per-pixel index stored in bitmap_color[].
  • SCREEN 2 (RGBA 320×200) — full RGBA copy.
  • SCREEN 3 (256-colour indexed) — nearest-RGB match into the 256-entry palette; alpha < 128 maps to current BACKGROUND.
  • SCREEN 4 (RGBA 640×400) — same as SCREEN 2 with 4× pixels.
  • Parameters:
  • path$ — image file. Must be a literal quoted string in browser WASM so the IDE's asset pre-load regex sees the filename and stages it into MEMFS before run.
  • x, y — optional offset. Character cells in SCREEN 0; pixels otherwise. Clipped to plane bounds.
  • Returns: nothing. Errors if file unreadable / wrong format / out-of-mode.
  • Example:
    SCREEN 2
    LOADSCREEN "sky.png"
    

Palette file I/O

PALETTELOAD path$

  • Purpose: read a plain-text .pal file into the live 256-entry palette. Tolerates JASC-PAL headers, # comments, blank lines. Missing entries beyond the file's count stay untouched. Pairs with PALETTEROTATE for retro palette-cycling on a hand-tuned palette.
  • Parameters: path$ — source file. Format: 256 lines of R G B [A] decimal 0..255.
  • Returns: nothing. Errors if file missing / unreadable.
  • Example:
    PALETTELOAD "sunset.pal"
    PALETTEROTATE 16, 255             : REM cycle bands 16..255
    

PALETTESAVE path$

  • Purpose: write the live palette to a plain-text .pal file (256 entries, R G B A decimal). Round-trips through PALETTELOAD.
  • Parameters: path$ — destination file.
  • Returns: nothing.
  • Example:
    PALETTESAVE "my-tuned.pal"
    IF FILEEXISTS("my-tuned.pal") THEN DOWNLOAD "my-tuned.pal"
    

RGBA blitter — IMAGE CREATE / IMAGE BLEND / IMAGE DRAW

The IMAGE NEW / IMAGE COPY / IMAGE GRAB / IMAGE LOAD / IMAGE SAVE family is documented under Graphics — blitter surfaces. The three commands below are the RGBA companions for full alpha-composited work in SCREEN 2 / SCREEN 4.

IMAGE CREATE slot, w, h

  • Purpose: allocate an RGBA off-screen surface (vs IMAGE NEW which is 1bpp). Must be RGBA before IMAGE LOAD if the source PNG has alpha that you want preserved through IMAGE BLEND — otherwise the legacy 1bpp path takes over and alpha is lost.
  • Parameters:
  • slot — 1..31 (slot 0 is the live framebuffer).
  • w, h — pixel dimensions, both > 0. Any size; subsequent IMAGE LOAD resizes to match the PNG.
  • Returns: nothing. Errors on bad slot / size / out of memory.
  • Example:
    IMAGE CREATE 1, 64, 64
    IMAGE LOAD   1, "chick.png"
    

IMAGE BLEND src, sx, sy, sw, sh TO dst, dx, dy

  • Purpose: alpha-composited blit (Porter-Duff "source over") between two RGBA slots. Semi-transparent source pixels smooth-blend against destination pixels.
  • Parameters:
  • src — RGBA source slot (1..31).
  • sx, sy, sw, sh — rectangle inside src.
  • dst — RGBA destination slot. Use 0 to route to the live SCREEN 2 / SCREEN 4 framebuffer.
  • dx, dy — top-left in dst.
  • Returns: nothing. Errors if either slot is non-RGBA, or dst = 0 outside SCREEN 2 / SCREEN 4.
  • Example:
    SCREEN 2
    LOADSCREEN "sky.png"
    IMAGE CREATE 1, 32, 32 : IMAGE LOAD 1, "chick.png"
    IMAGE BLEND 1, 0, 0, 32, 32 TO 0, X, Y
    

IMAGE DRAW slot

  • Purpose: retarget every RGBA primitive (LINE, FILLRECT, CIRCLE, DRAWTEXT, PSET, POLYGON, …) to an off-screen IMAGE CREATE surface instead of the live framebuffer. Mirrors SCREEN DRAW n but routed into the IMAGE pool, so canvas size is arbitrary (not tied to 320×200 / 640×400). Used to pre-bake wide scroller strips, world maps, HUD layers, reusable sprite atlases — then blit back with IMAGE BLEND.
  • Parameters: slot0 to restore the live screen, or 1..31 for an IMAGE CREATE-allocated RGBA surface. Non-RGBA modes raise a runtime error.
  • Returns: nothing.
  • Example:
    SCREEN 2
    IMAGE CREATE 1, 1280, 200          : REM 4-screen-wide strip
    IMAGE DRAW 1                       : REM redirect drawing
    FOR I = 0 TO 1279 STEP 8 : LINE I,0 TO I,199 : NEXT I
    IMAGE DRAW 0                       : REM back to live
    IMAGE BLEND 1, SX, 0, 320, 200 TO 0, 0, 0
    

Charset variants

#OPTION charset and the -charset CLI flag accept more than the original upper / lower:

Token Effect
upper C64 default — uppercase + graphics
lower C64 default — lower + uppercase mixed
c64-upper, c64-lower Same as above, explicit family
pet-upper, pet-lower PET-style alternate ROM (pet_*.64c). Aliases: pet-graphics (= upper), pet-text (= lower)
  • Purpose: pick which character ROM family + letter set is loaded into the chargen.
  • Returns: load-time only; no runtime feedback. Unknown values print to stderr.
  • Example:
    #OPTION charset pet-lower
    PRINT "PET-style lowercase"
    

Where features work

Feature basic (terminal) basic-gfx Browser WASM
File I/O, ARG$, SYSTEM, EXEC$ Yes Yes SYSTEM/EXEC$ stubbed; HTTP$ for network
HTTP$ / HTTPSTATUS Returns "" / 0 Same Yes (CORS)
POKE / PEEK No-op / 0 Virtual memory Canvas host
Sprites, bitmap, SCROLL, gamepad Error / stub Yes Canvas parity (see upstream)
INKEY$() "" (no key queue) Yes Yes (hosted input)

Reserved words (identifiers)

The interpreter keeps a fixed reserved_words[] table in basic.c: every keyword for statements and intrinsics (PRINT, FUNCTION, SPRITECOLLIDE, HTTPSTATUS, LOOP, EXIT, …) plus tokens such as INK, DOWN, JOIN, XOR, etc. Graphics 1.0 additions — RECT, FILLRECT, CIRCLE, FILLCIRCLE, ELLIPSE, FILLELLIPSE, TRIANGLE, FILLTRIANGLE, POLYGON, FILLPOLYGON, FLOODFILL, DRAWTEXT, VSYNC — are reserved too. Do not use those spellings as variable names. (Exact rules for labels vs identifiers are described in the upstream README.)

Authoritative list: reserved_words[] in basic.c.


Further reading