Record the API gaps blocking akbasic
akbasic is a C port of the BASIC interpreter being built to link into libakgl as
a scripting engine for game authors. Its interpreter core is complete and passes
its whole acceptance corpus against a stdio text sink; four gaps here block the
libakgl-backed sink and the graphics, sound and console verbs of Commodore
BASIC 7.0.
Filed rather than worked around downstream, because growing libakgl to serve a
consumer is the wanted outcome. Each entry says what the BASIC verb needs, what
the akgl_* entry point should look like, and what would cover it.
Only the first blocks work already designed and waiting. text.h can load a font
and render a string and cannot say how large that string will be, so there is no
way to build a terminal-style surface on it -- a cursor needs the advance width
of a cell and wrapping needs to know where a string crosses the margin. The
reference interpreter derived its whole character grid from SDL2_ttf's
SizeUTF8("A"). akgl_text_measure() over TTF_GetStringSize closes it.
The other three block verbs not yet started: draw.h declares one function and
src/draw.c is at 0%, so DRAW/BOX/CIRCLE/PAINT have nothing to plot with; there
is no audio API at all despite SDL3_mixer being vendored, and PLAY/ENVELOPE want
a synthesised voice SDL3_mixer does not provide; and controller.h is built
around event handlers the host pumps, which suits a game loop but not GET/GETKEY
-- those ask whether a keystroke is waiting and must not require the interpreter
to own the event loop, which goal 3 forbids anyway.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
TODO.md
67
TODO.md
@@ -626,6 +626,73 @@ not saw every test abort before `main()` with "cannot open shared object file".
|
||||
`CMakeLists.txt` now sets `BUILD_RPATH` on the library, the utility, and every
|
||||
test target, and prepends the build tree to `LD_LIBRARY_PATH` for the CTest run.
|
||||
|
||||
## API gaps blocking akbasic
|
||||
|
||||
`akbasic` (the C port of the BASIC interpreter, `source.starfort.tech/andrew/akbasic`) is
|
||||
being built to link into `libakgl` as a scripting engine for game authors. Its interpreter
|
||||
core is complete and passes its whole acceptance corpus against a stdio text sink; the
|
||||
`libakgl`-backed sink and the graphics, sound and console verbs of Commodore BASIC 7.0 are
|
||||
blocked on the four gaps below.
|
||||
|
||||
These are filed here rather than worked around in `akbasic` because growing `libakgl` to serve
|
||||
a consumer is the wanted outcome. Each entry says what the BASIC verb needs, what the
|
||||
`akgl_*` entry point should look like, and what would cover it.
|
||||
|
||||
1. **No way to measure rendered text.** `include/akgl/text.h` exposes `akgl_text_loadfont()`
|
||||
and `akgl_text_rendertextat()`, and nothing that reports how large a string will be in a
|
||||
given font. A terminal-style text surface cannot be built on that: a cursor needs the
|
||||
advance width of one cell, and wrapping needs to know where a string crosses the right
|
||||
margin. The reference interpreter got this from SDL2_ttf's `font.SizeUTF8("A")` and derived
|
||||
its whole character grid from it.
|
||||
|
||||
Wants `akerr_ErrorContext AKERR_NOIGNORE *akgl_text_measure(TTF_Font *font, char *text, int *w, int *h);`
|
||||
over `TTF_GetStringSize`, and probably a companion
|
||||
`akgl_text_measure_wrapped(font, text, wraplength, w, h)` matching the `wraplength`
|
||||
argument `akgl_text_rendertextat` already takes. Tests: a known string in a known font at a
|
||||
known size, the empty string, and a wrapped string wide enough to force two lines.
|
||||
|
||||
This is the only one of the four that blocks work already designed and waiting. **`akbasic`
|
||||
cannot render any output through `libakgl` until it lands.**
|
||||
|
||||
2. **No immediate-mode drawing.** `include/akgl/draw.h` declares exactly one function,
|
||||
`akgl_draw_background(int w, int h)`, and `src/draw.c` is at 0% coverage. BASIC 7.0's
|
||||
graphics verbs are all immediate-mode plotting against the current screen: `DRAW` (line and
|
||||
point), `BOX`, `CIRCLE`, `PAINT` (flood fill), `LOCATE` (set the pixel cursor), `COLOR`,
|
||||
and `SSHAPE`/`GSHAPE` (save and restore a rectangle of pixels).
|
||||
|
||||
Wants an `akgl_draw_*` family taking the renderer the host already initialized --
|
||||
`akgl_draw_line`, `_rect`, `_filled_rect`, `_circle`, `_point`, `_flood_fill`,
|
||||
`_copy_region` -- in the shape of the existing `akgl_render_2d_draw_texture`. SDL3's
|
||||
`SDL_RenderLine`/`SDL_RenderRect`/`SDL_RenderFillRect` cover most of it; the circle and the
|
||||
flood fill do not exist in SDL3 and need writing. Tests belong with the offscreen renderer
|
||||
harness described under "Remaining work": render a known shape, read the target back, and
|
||||
compare against a reference surface with the existing `akgl_compare_sdl_surfaces`.
|
||||
|
||||
3. **No audio API at all.** `SDL3_mixer` is a vendored dependency and `registry.h` declares
|
||||
`AKGL_REGISTRY_MUSIC`, but there is no `src/audio.c`, no `include/akgl/audio.h`, and no
|
||||
`akgl_*` symbol that opens a mixer, loads a chunk, or plays a note. BASIC 7.0's sound verbs
|
||||
are `SOUND` (a tone on a voice, with a duration), `PLAY` (a string of notes in a
|
||||
Commodore-specific notation), `ENVELOPE` (ADSR per voice), `FILTER`, `VOL` and `TEMPO`.
|
||||
|
||||
`PLAY` and `ENVELOPE` want a synthesised voice rather than a sample, which SDL3_mixer does
|
||||
not provide directly -- the honest first step is a small tone generator feeding
|
||||
`SDL_AudioStream`, with `akgl_audio_init`, `akgl_audio_tone(voice, hz, ms)`,
|
||||
`akgl_audio_envelope(voice, a, d, s, r)` and `akgl_audio_volume(level)`. This is the largest
|
||||
of the four and the one most worth designing before writing. Tests can run under the dummy
|
||||
audio driver and assert state transitions rather than sound.
|
||||
|
||||
4. **No non-blocking keystroke read.** `include/akgl/controller.h` is built around SDL event
|
||||
handlers the host pumps (`akgl_controller_handle_event` and friends), which suits a game
|
||||
loop and does not suit `GET` and `GETKEY` -- those ask "is there a keystroke waiting, yes or
|
||||
no" and must not require the interpreter to own the event loop. Goal 3 of `akbasic` forbids
|
||||
it owning one.
|
||||
|
||||
Wants `akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_key(int *keycode, bool *available);`
|
||||
reading a small ring buffer that `akgl_controller_handle_event` already fills, so the host
|
||||
keeps pumping events and the interpreter drains characters at its own pace. Tests: push
|
||||
synthetic `SDL_EVENT_KEY_DOWN` events through the existing handler and drain them, plus the
|
||||
empty-buffer and overflow cases.
|
||||
|
||||
## Carried over
|
||||
|
||||
1. **Make character-to-sprite state bindings release their references symmetrically.**
|
||||
|
||||
Reference in New Issue
Block a user