Draw: implement the BASIC 7.0 graphics verbs
GRAPHIC, COLOR, DRAW, BOX, CIRCLE, PAINT, SCALE, SSHAPE, GSHAPE and LOCATE, all against the akbasic_GraphicsBackend record rather than akgl_draw_* directly, so src/runtime_graphics.c includes no SDL and the whole group is testable in a build with no SDL on the machine. The reference lists every one of these as unimplemented, so the semantics come from Commodore BASIC 7.0 rather than from a port, and four places where a modern renderer cannot do what a C128 did are recorded in TODO.md section 5 rather than silently substituted: - CIRCLE is drawn as a polygon of inc-degree segments and akgl_draw_circle is deliberately unused. 7.0's CIRCLE takes two radii, an arc range and a rotation, so the primitive could serve only the fully-defaulted call, and a shape that changed character depending on whether the radii happened to be equal would be worse than one uniformly a polygon. - SSHAPE puts a SHAPE:<n> handle in the string variable rather than the pixels, because a value's string is a fixed 256 bytes and a region is a device surface. GSHAPE refuses a string without that prefix instead of parsing whatever digits it finds and pasting an unrelated slot. - BOX fills on a negative angle; 7.0 puts the fill flag after the rotation, which would make a filled box a seventh argument. - GRAPHIC stores its mode and honours only the one consequence that means anything here -- mode 0 is text -- while still refusing an out-of-range mode, since that is a typo worth catching. PAINT surfaces the flood fill's AKERR_OUTOFBOUNDS as an error rather than success. The device gives up when its span stack runs out having filled *part* of the region, and a program that cannot tell that happened cannot recover from it. Note the shape of that handler: HANDLE sets handled = true on the context, so a FAIL_RETURN from inside the HANDLE block hands the caller something already marked handled, whose FINISH_LOGIC then declines to pass it up and releases it -- the error disappears and PAINT reports success. Flag inside the block, raise after FINISH. COLOR, LOCATE and SCALE need no device on purpose, so a program can set itself up before a host has lent it a renderer. Adds a second golden corpus under tests/language/. The corpus in deps/basicinterpret is a submodule and nothing here may add files to it, but goal 2's new verbs still need the .bas/.txt half of their coverage. Registered under local_ so a failure names which corpus it came from. What it can cover is limited -- these verbs draw rather than print -- so the behaviour that reaches a device is asserted against tests/mockdevice.h instead. 65/65 ctest, clean under -Wall -Wextra, doxygen clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
45
TODO.md
45
TODO.md
@@ -478,6 +478,51 @@ and each one has to be defensible against the golden suite.
|
||||
leaves the parent waiting for a `NEXT` that never arrives (§6 item 8) — in C that is a hang
|
||||
rather than a misbehaviour, and no golden case depends on it.
|
||||
|
||||
### Deviations in the verbs the reference never implemented
|
||||
|
||||
Items 1–12 above are deviations *from ported code*. These are deviations from **Commodore
|
||||
BASIC 7.0**, in verbs the reference lists as unimplemented and which therefore had no Go
|
||||
behaviour to port. They matter to somebody typing in a listing out of a C128 manual.
|
||||
|
||||
13. **Hardware verbs reach a device through a backend record, and refuse when there is none.**
|
||||
`akbasic_GraphicsBackend`, `_AudioBackend` and `_InputBackend` are records of function
|
||||
pointers on the runtime; all three may be `NULL`, which is what the standalone driver
|
||||
gives them. A verb that needs a device it was not given raises `AKBASIC_ERR_DEVICE` naming
|
||||
itself. `COLOR`, `LOCATE`, `SCALE`, `ENVELOPE`, `TEMPO` and `VOL` deliberately do *not*
|
||||
require one — they change interpreter state, so a program can configure itself before the
|
||||
host lends it a renderer.
|
||||
|
||||
14. **`CIRCLE` is a polygon, always.** BASIC 7.0's `CIRCLE` takes two radii, a start and end
|
||||
angle, a rotation and a degree increment, which makes it an inc-degree polygon by
|
||||
definition. `akgl_draw_circle` exists and is deliberately **not** used: it draws one
|
||||
radius, full sweep, unrotated, so it could serve only the case where every optional
|
||||
argument is defaulted — and a shape that changed character depending on whether the two
|
||||
radii happened to be equal is worse than one that is uniformly a polygon.
|
||||
|
||||
15. **`SSHAPE` stores a handle in the string, not the pixels.** A real C128 packs the
|
||||
region's bitmap into the string variable. Here a value's string is a fixed 256 bytes
|
||||
(§1.2) and a saved region is a device surface, so the variable gets `SHAPE:<n>` and the
|
||||
surface stays in a fixed pool on the backend. `GSHAPE` accepts only a string carrying that
|
||||
prefix, so a hand-written one is refused rather than pasting an unrelated slot. **What
|
||||
this costs:** a shape cannot be written to disk, cannot be concatenated, and `LEN` of it
|
||||
is not its size. Nothing in BASIC does any of those to a shape string except a program
|
||||
deliberately poking at it.
|
||||
|
||||
16. **`BOX` fills on a negative angle rather than on a seventh argument.** 7.0's last
|
||||
argument selects outline or fill and sits *after* the rotation, which would make a filled
|
||||
box a seven-argument call. That is filed rather than implemented; today a rotation of zero
|
||||
outlines through the renderer's rectangle and any other rotation draws four lines.
|
||||
|
||||
17. **`GRAPHIC` records its mode but honours only one consequence of it.** 7.0's five modes
|
||||
differ in bitmap resolution and in whether the bottom of the screen stays text. Neither
|
||||
means anything against a host's renderer, whose size the interpreter does not know and
|
||||
does not own. The mode is stored, an out-of-range one is refused because that is a typo
|
||||
worth catching, and the one observable rule is that mode 0 is text.
|
||||
|
||||
18. **Coordinates reaching a backend are BASIC's 320x200 space, and scaling to the window is
|
||||
the host's job.** The interpreter cannot ask the renderer how big it is without owning it.
|
||||
`SCALE` maps user coordinates onto that space; with `SCALE` off they pass through.
|
||||
|
||||
---
|
||||
|
||||
## 6. Reference defects — ported faithfully, fix them deliberately
|
||||
|
||||
Reference in New Issue
Block a user