Commit Graph

2 Commits

Author SHA1 Message Date
01adf80751 Rework the megademo to fit the 80-column source line limit
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m29s
akbasic CI Build / coverage (push) Failing after 3m58s
akbasic CI Build / sanitizers (push) Failing after 4m36s
akbasic CI Build / mutation_test (push) Failing after 3m52s
akbasic CI Build / akgl_build (push) Failing after 7m29s
AKBASIC_MAX_LINE_LENGTH's cut from 256 to 80 left seventeen lines of
examples/megademo unloadable: the sixteen IM$() picture strings (up to
252 characters) and TUNEA/TUNEB's four-bar PLAY strings (174 and 175).
The real ceiling is 78 characters, not 80 -- stdio_readline() refuses a
read that fills the 80-byte buffer without a newline, so content plus
its terminator must fit in 79.

The picture: vaporwave.py's PAYLOAD drops from 240 to 64, so every
emitted IM$(NN) = "..." line fits under the ceiling. chop() no longer
slices blind; it walks the stream a record at a time -- two characters
for a run, three for an R row record -- and never cuts inside one,
because the decoder reads a record's tail with MID on the string it is
walking and a record straddling two IM$ entries decodes as garbage.
The old blind slice at 240 only happened to be safe. verify() now
simulates the CHOPPED strings with the cursor threaded across the
boundaries exactly the way DRAWSTREAM executes them, so a bad cut is
an assertion failure instead of a corrupted screen, and emit_block()
asserts every emitted line fits. The picture is 56 strings where it
was 16; the decoder needed no changes at all, since it already carries
X#/Y# from one IM$ entry to the next.

The music: TUNEA and TUNEB each become four PLAY statements, one bar
apiece. play.c keeps voice, envelope, level and duration state on the
runtime across statements and every PLAY appends to the same queue, so
four bars queue exactly as one long string did. Each bar restates the
V1T3U9S prefix so a bar dropped by QFULL cannot leave the next batch
playing on the drum kit's envelope.

Everything still clears the shrunken pools with room to spare: 1625
source lines of 2048, ~704 array slots of 2048, identifiers within the
24-character symtab key. Verified end to end against this branch's
build: the demo loads, the offscreen host renders every scene, and the
scene-5 still is pixel-identical to vaporwave.py's own preview. The
test suite fails the same seventeen cases with and without this
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACffnV6F7sxQuG3Y8a1L3s
2026-08-03 22:57:57 -04:00
16aa052119 Decompress a full-screen picture and six frames of video from strings
The megademo gains a scene: an 800x600 vaporwave sunset carried inside
the listing, then six looping delta frames of full motion video -- the
floor grid rolling forward and the sun's slices crawling, about 220
bytes a frame. DATA cannot carry an image (512 items, ~350 owned by the
stroke font), so the picture rides in RLE-encoded string literals
decoded with INSTR: 2 KB of base frame, 1.3 KB of video. Row records
jump the decoder between changed rows, an unreachable colour is the
skip, black draws so a delta can erase, and the rays are struck live
over every frame rather than encoded. The 63-byte bird gliding over it
is the one image DATA does have room for, via SPRSAV's type-in form.

examples/megademo/vaporwave.py composes the image in palette space,
dithers by row so the gradients band instead of shattering the RLE,
proves base-plus-deltas reproduces every frame against a simulation of
the decoder, and owns the generated block between the PICTURE markers.
TODO.md section 4 records the DATA ceiling and the wanted verb.

Co-Authored-By: Claude Code (Fable 5) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-02 16:26:56 -04:00