Files
akbasic/docs/12-function-reference.md

62 lines
3.7 KiB
Markdown
Raw Permalink Normal View History

# 12. Function reference
Every function, alphabetically, generated from the interpreter's own dispatch table.
A function is called with parentheses and yields a value; it cannot stand alone as a
statement. The arity column is how many arguments it takes — the interpreter checks it,
so a call with the wrong number is a syntax error rather than a surprise.
| Function | Args | Form | What it gives |
|---|---|---|---|
| `ABS` | 1 | `ABS(n)` | The absolute value of an integer or float. |
| `ATN` | 1 | `ATN(n)` | Arctangent, in radians. |
| `BUMP` | 1 | `BUMP(1)` | Which sprites have collided, as a bitmask. **Reading clears it.** |
| `CHR` | 1 | `CHR(n)` | The character for a Unicode code point, as a string. |
| `COS` | 1 | `COS(n)` | Cosine, in radians. |
| `ERR` | 1 | `ERR(n)` | The message text for an error code. `ERR(ER#)` names the last one. |
| `HEX` | 1 | `HEX(n)` | An integer as hexadecimal text. |
| `INSTR` | 2 | `INSTR(A$, B$)` | Where `B$` appears in `A$`, counting from zero, or -1. |
| `LEFT` | 2 | `LEFT(A$, n)` | The leftmost `n` characters. Clamped to the string's length. |
| `LEN` | 1 | `LEN(x)` | The length of a string, or the element count of an array. |
| `LOG` | 1 | `LOG(n)` | Natural logarithm. |
| `MID` | 3 | `MID(A$, start, len)` | A substring, counting from zero. |
| `MOD` | 2 | `MOD(a, b)` | The remainder of `a / b`. Integers only. |
| `PEEK` | 1 | `PEEK(addr)` | The byte at a real address. |
| `POINTER` | 1 | `POINTER(V)` | The address of a variable's value. |
| `POINTERVAR` | 1 | `POINTERVAR(V)` | The address of the variable structure itself, metadata included. |
| `RAD` | 1 | `RAD(n)` | Degrees converted to radians. |
Say what was hit, which way is out, and how far The narrowphase has been producing a contact since it went in, and the interpreter was throwing it away. `RCOLLISION(n, f)` reports it: what was hit (a sprite or a `SOLID` rectangle), which one, the contact normal, the penetration depth, the contact point, and which axis to reverse. **The normal points out of the other thing and toward this one**, so a program moves along it by the depth and is exactly clear. That sign is the one assertion in the new test that could not be caught any other way -- both parties of a sprite-against-sprite hit get their own record, each pointing the way *that* sprite has to move, and sharing one would tell both to go the same direction, which is how two things end up stuck inside each other. **Field 7 is the one that deletes the most BASIC.** It is the minimum translation axis, computed from the normal in C, and it is there because doing it in BASIC means comparing two floats -- which is exactly where this dialect's left-operand rule catches people. `BALLBRICKS`/`TESTCELL` in the artwork breakout spend six lines computing an overlap rectangle and comparing its width to its height to get this number. The record is **sticky and deepest-wins**: replaced whenever that sprite is in a contact and otherwise left alone, so `BUMP` stays the event and this stays the detail of it. Making it clear itself when nothing touches would break the pairing, because `BUMP` accumulates across steps and a once-a-frame poll would find the detail already gone. Reading `BUMP` clears both, so they cannot disagree. Deliberately narrower than `akgl_Contact`: no actor pointers, because BASIC has no actor; no tile fields, because there is no tilemap; no z, because every test is planar; and **no `dt` and no `sensor`**, which libakgl documents as filled in by the resolver. This interpreter never resolves anything, so those two come back zero and mean nothing, and an always-zero field in a reference table is a lie. Documented with the two caveats that matter: fields 2, 3 and 4 are floats and want a `%` variable, and the contact *point* is exact only for two boxes -- libakgl's solver returns a point on the portal it converged to, while the normal and depth are exact for every pair. Chapter 8's collision section stops claiming only type 1 exists, which has been false since the previous commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 10:53:26 -04:00
| `RCOLLISION` | 2 | `RCOLLISION(n, f)` | What sprite `n` last collided with and how: what, which, the normal, the depth, the point, and which axis to reverse. See Chapter 8. |
Let a program ask how big the text grid is `RGR(1)` and `RGR(2)` gave the window in pixels and nothing gave columns, rows or the cell size -- so anything placing a character *and* a sprite at the same spot had to hardcode a number measured by hand against whatever font the host loaded. The Breakout in examples/ does exactly that, `CW# = 16`, and it is the one thing in that listing that breaks on a different font or window. **`RWINDOW` is BASIC 7.0's own answer and had never been implemented here.** `RWINDOW(0)` is the current text window's rows and `RWINDOW(1)` its columns. `RWINDOW(2)` reports a C128's 40 or 80 column screen mode, and this interpreter has neither -- refused by name, because answering 0 would be a plausible lie, which is worse than a refusal that says why. The cell size in pixels is `RGR(3)` and `RGR(4)`, beside the surface's own dimensions rather than on `RWINDOW`. Two reasons: a cell size is a fact about the surface, and `RWINDOW` reports the *window*, so dividing `RGR(1)` by a column count stops being right the moment a program calls `WINDOW`. Both read a new optional `grid` entry point on `akbasic_TextSink` -- columns, rows, cell width, cell height -- implemented by the akgl sink and forwarded by the tee, in the shape `moveto` and `window` already had. NULL everywhere else, so both verbs refuse by name against a sink with no grid. `akbasic_sink_init_ stdio()` clears it for the same reason it now clears the other two. Measured on the standalone build: `RGR(3)` answers 16 and `RWINDOW` answers 50 columns by 37 rows -- the three numbers the Breakout listing had written out as constants -- and `RWINDOW` follows a `WINDOW` call while `RGR(3)` does not. tests/console_verbs.c drives the answers through a stand-in sink with a grid, since the harness sink is stdio and has none; tests/graphics_verbs.c covers the new `RGR` fields, their refusal, and the moved range bound. The `c excerpt=` block in docs/10-embedding.md moves with the header, which is `docs_examples` doing its job. TODO.md section 6 item 31's second half, struck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 00:05:17 -04:00
| `RGR` | 1 | `RGR(f)` | The `GRAPHIC` mode (0), the drawing surface's width (1) or height (2) in pixels, or a character cell's width (3) or height (4). |
| `RIGHT` | 2 | `RIGHT(A$, n)` | The rightmost `n` characters. Clamped. |
Let a program ask how big the text grid is `RGR(1)` and `RGR(2)` gave the window in pixels and nothing gave columns, rows or the cell size -- so anything placing a character *and* a sprite at the same spot had to hardcode a number measured by hand against whatever font the host loaded. The Breakout in examples/ does exactly that, `CW# = 16`, and it is the one thing in that listing that breaks on a different font or window. **`RWINDOW` is BASIC 7.0's own answer and had never been implemented here.** `RWINDOW(0)` is the current text window's rows and `RWINDOW(1)` its columns. `RWINDOW(2)` reports a C128's 40 or 80 column screen mode, and this interpreter has neither -- refused by name, because answering 0 would be a plausible lie, which is worse than a refusal that says why. The cell size in pixels is `RGR(3)` and `RGR(4)`, beside the surface's own dimensions rather than on `RWINDOW`. Two reasons: a cell size is a fact about the surface, and `RWINDOW` reports the *window*, so dividing `RGR(1)` by a column count stops being right the moment a program calls `WINDOW`. Both read a new optional `grid` entry point on `akbasic_TextSink` -- columns, rows, cell width, cell height -- implemented by the akgl sink and forwarded by the tee, in the shape `moveto` and `window` already had. NULL everywhere else, so both verbs refuse by name against a sink with no grid. `akbasic_sink_init_ stdio()` clears it for the same reason it now clears the other two. Measured on the standalone build: `RGR(3)` answers 16 and `RWINDOW` answers 50 columns by 37 rows -- the three numbers the Breakout listing had written out as constants -- and `RWINDOW` follows a `WINDOW` call while `RGR(3)` does not. tests/console_verbs.c drives the answers through a stand-in sink with a grid, since the harness sink is stdio and has none; tests/graphics_verbs.c covers the new `RGR` fields, their refusal, and the moved range bound. The `c excerpt=` block in docs/10-embedding.md moves with the header, which is `docs_examples` doing its job. TODO.md section 6 item 31's second half, struck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 00:05:17 -04:00
| `RWINDOW` | 1 | `RWINDOW(f)` | The current text window's rows (0) or columns (1). Field 2 is a C128 screen mode and is refused. |
| `RSPCOLOR` | 1 | `RSPCOLOR(n)` | One of `SPRCOLOR`'s two shared registers, 1 or 2. |
Let a program say what part of a sprite collides `SPRHIT n, kind [,x1, y1, x2, y2]` gives a sprite a collision shape: a box, a circle inscribed in it, or a capsule. `RSPHIT(n, f)` reads it back in SPRHIT's own argument order, the way RSPRITE and RSPPOS already do, and needs no device because it answers from interpreter state. The rectangle is two corners measured from the sprite's top-left, in device pixels -- the same `x1, y1, x2, y2` that `BOX` and `SSHAPE` take. A dialect with two spellings for a rectangle is one nobody can write from memory. Omit it and the shape fits whatever the picture turned out to be, which is what a sprite loaded from a file needs: `SPRSAV "ship.png", 1` takes the image's own size and the program never learns what that was. **A sprite nobody has shaped collides with its whole frame, expansion bits included, exactly as before.** That is a promise rather than a convenience, and it has its own test: the same two sprites in the same two places, once with no SPRHIT and once with a four-pixel box, reporting a collision and then not. Named SPRHIT rather than SPRSHAPE because "shape" already means "a region SSHAPE saved" in this dialect, in this very chapter -- `SPRSAV A$, 1` takes one -- and a reader who typed `SPRSHAPE A$, 1` would have had every reason to. Both names, and RSPHIT, were grepped against every label in docs/, examples/ and both corpora first: a bare word is a label here, so a verb and a label share one namespace and taking a name a checked-in listing already uses would break it silently. `SPRHIT n, 0` takes a sprite out of collision while leaving it on the screen -- the ghost, the flashing invulnerable player, the pickup already taken. Hiding it with `SPRITE n, 0` stops it colliding too, and is what you want when it should not be seen either. The circle answers the complaint chapter 8 already ships a figure of. That figure shows two discs whose *boxes* touch at a corner while the artwork is nowhere near, and `BUMP(1)` reporting a collision; two `SPRHIT n, 2` and it stops. The test asserts both halves so the figure's caption stays true. `tests/verbs_table.c` caught RSPHIT filed after RSPPOS rather than before it, which is the sorted-table test doing exactly the job it exists for. Docs: a new section in chapter 8, rows in the verb and function references in alphabetical order, and chapter 13's "collision is by bounding box" becomes "collision is by shape" with the addition named. 111 with akgl, 110 without, and the artwork breakout still runs clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 10:01:33 -04:00
| `RSPHIT` | 2 | `RSPHIT(n, f)` | One of `SPRHIT`'s settings for sprite `n`, in `SPRHIT`'s own argument order: 0 the kind, 1 to 4 the two corners. |
| `RSPPOS` | 2 | `RSPPOS(n, f)` | A sprite's x (0), y (1) or speed (2). |
| `RSPRITE` | 2 | `RSPRITE(n, f)` | One of a sprite's `SPRITE` settings, in `SPRITE`'s argument order. |
| `SGN` | 1 | `SGN(n)` | -1, 0 or 1 according to the sign. |
| `SHL` | 2 | `SHL(n, bits)` | Shift left. |
| `SHR` | 2 | `SHR(n, bits)` | Shift right. |
| `SIN` | 1 | `SIN(n)` | Sine, in radians. |
| `SPC` | 1 | `SPC(n)` | A string of `n` spaces. |
| `STR` | 1 | `STR(n)` | A number as a string. |
| `TAN` | 1 | `TAN(n)` | Tangent, in radians. |
| `VAL` | 1 | `VAL(A$)` | A string as a number. Refuses text that is not one. |
| `XOR` | 2 | `XOR(a, b)` | Bitwise exclusive or. |
## Reserved variables
Two things a C128 exposes as bare names are ordinary global variables here, because
this dialect has no variable name without a type suffix:
| Here | On a C128 | Holds |
|---|---|---|
| `ER#` | `ER` | the code of the last trapped error |
| `EL#` | `EL` | the line it happened on |
| `TI#` | `TI` | jiffies — sixtieths of a second — since the host's clock started |
| `TI$` | `TI$` | the same time as `HHMMSS` |
`ER#` carries this interpreter's error code, not a Commodore error number. There is no
correspondence to reproduce — the errors raised here are not the errors a 1985 ROM
raised — so print `ERR(ER#)` rather than the number. **[Chapter 15](15-error-codes.md)
lists every value it can hold.**