Decompress a full-screen picture and six frames of video from strings
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m23s
akbasic CI Build / sanitizers (push) Failing after 4m33s
akbasic CI Build / coverage (push) Failing after 3m45s
akbasic CI Build / akgl_build (push) Failing after 24s
akbasic CI Build / mutation_test (push) Failing after 3m30s

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
This commit is contained in:
2026-08-02 16:26:56 -04:00
parent 6536658c92
commit 0d79a3f52c
4 changed files with 578 additions and 12 deletions

View File

@@ -57,9 +57,14 @@ DIM WQX#(14)
DIM WQY#(14)
DIM WE1#(24)
DIM WE2#(24)
DIM PBRD#(63)
ALPHA$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :-.!"
GC$ = "GAMECUBE"
REM The picture decoder's alphabets: a run is two characters, colour
REM then length, and INSTR turns each back into a number.
CPAL$ = "ABCDEFGHIJKLMNOP"
LTAB$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
SEED# = 314159
QF# = 0
RB# = 0
@@ -120,6 +125,7 @@ SEED# = SEED# + TI#
GOSUB LOADTABLES
GOSUB LOADWIRE
GOSUB LOADFONT
GOSUB LOADBIRD
IF SND# = 1 THEN GOSUB SIDINIT
GOSUB TUNE
IF TXT# = 1 THEN CHAR 1, 0, 0, "AKLABS MEGADEMO 85 - PRESS Q TO EXIT"
@@ -429,7 +435,117 @@ LABEL BARDONE
IF QF# = 1 THEN GOTO BYE
REM =====================================================================
REM SCENE 5 : THE SPRITE BANK IS THE SCROLLTEXT, AND THEN IT EXPLODES.
REM SCENE 5 : THE PICTURE, AND THEN IT MOVES. A full 800x600
REM vaporwave sunset carried inside the listing, then six delta
REM frames of full motion video in a loop. DATA could never hold any
REM of it -- the pool is 512 items and the font owns most of them
REM (TODO.md section 4) -- so it all rides in string literals:
REM run-length encoded, two characters a run, decoded with INSTR and
REM painted as 5x5 blocks. A video frame re-encodes only the rows
REM that changed ('R' records jump the cursor), 'Q' skips what stayed
REM and black draws over what moved, so a frame of grid-scroll and
REM sun-shimmer is about two hundred bytes and paints in a few
REM presents. The rays are not encoded at all: they are struck live
REM over every frame, which is what keeps the floor rows cheap. The
REM loop is exact -- the grid advances one geometric step over six
REM frames and the slice pattern's period is six. The paint is the
REM reveal; the exit is a stride-63 column dissolve. The bird is the
REM one thing DATA does hold: a real 24x21 SPRSAV sprite, 63 bytes,
REM gliding across its own sunset. vaporwave.py draws the picture,
REM proves base plus deltas reproduce every frame, and owns the
REM generated block.
REM =====================================================================
GRAPHIC 1, 1
WIDTH 2
BS% = W# / 160
IF BS% < 1 THEN BS% = 1
REM ---- PICTURE-BEGIN (generated by vaporwave.py; do not
REM ---- hand-edit -- rerun the script to change the picture)
DIM IM$(16)
DIM VA#(6)
DIM VB#(6)
NS# = 9
VA#(0) = 9
VB#(0) = 9
VA#(1) = 10
VB#(1) = 10
VA#(2) = 11
VB#(2) = 12
VA#(3) = 13
VB#(3) = 13
VA#(4) = 14
VB#(4) = 14
VA#(5) = 15
VB#(5) = 15
IM$(0) = "G9G9G9G9GPGHEHG9GTEHG9GTEHGPGPEHG9GTEHG9GTEHGHGXEHGTBAG8EHG9GTEHG5EHGPEHG5EHGPEHG5EHGAPAG3EHGPEHG5EHGPEHGXGHEHG5EHGPEHG5EHGFBAGIEHGPGPEHGPEHGHEHGPEHGPEHGHEHGPEHGHEHGPEHGPEHGHEHGBPAGMEHGPEHGHEHGPEHGHEHGPEHGPEHGHEHGPEHGPEHGHEHGPEHGHEPGHEHGPEH"
IM$(1) = "GHEPGHEHGPEHGHEPGHEHGHEGBAEHGHEHGPEHGHEPGHEHGPEHGHEHGPEHGHEPGHEHGPEHGHEPGHEHGPECPAEDGHEPGHEHGHEPGHEPGHEHGHEPGHEPGHEHGHEPGHEHGHEPGHEPGHECBAEDGHEPGHEPGHEHGHEPGHEHGHEPGHEPGHEHGHEPGHEPEPGHEPGHE5GHEPGHE5GHEHEXGHEPGHEEPAEZGHEPGHE5GHE5GHEPGHE5GHEP"
IM$(2) = "GHE5GHE9ETGHE9ETGHEXEHGHE9ETGHHAE9ESGHEPEPGBBAGEE9EMHOE9ETGHEHE9E7HUE9E6E9E4H0E9E3E9E3H2E9EHBAETEHKHE9ELH6E9ECKHEPEPKHE9EBH9HAE9EIKHEHEXKHE2H9HCE9EPKHE5KHEPKDH9HEKCEPKHE5KHE5KHEKH9HGEBKHEPKHEXEHKHE5KHEBH9HIEIKHEPKHEPEPKHEPKHEHKAH9HKKHEHKHEP"
IM$(3) = "KHEHKHEPKHEPKHH9HMEGKHEHKHEPKHEHKHEPKHEPH9HMEOKHEHKHEPKHEHKPEHKHEGH9HOKFEPKHEHKPEHKHEHKPEHKGH9HOEFKHEPKHEHKHEPKHEHKPEFH9HQKEEHKHEPKHEHKHEPKHEHKMH9HSKLEHKHEPKHEHKPEHKHEHKEH9HSEDKPEHKPEHKHEHKPEHKHEEH9HSKDEHKPEHKPKPEHKPEHKDH9HUECKHEHKPEHKHKXEH"
IM$(4) = "KPEDH9HUKCEHK5EHK5EHKLH9HUKKEHK5EHK5EHKCH9HWEBKPEHKXKHEHK8I9IWKZEHKPKPEHK0I9IWK7EHKHKXEHKSI9IWK9KFEHK9KOI9IWK9KNK9KOI9IWK9KNK9KOI9IWK9KNKPIHKZI9IYK6IHKHKXIHKSI9IWK9KFIHK5IHKKI9IWK9KNIHK5IHKCI9IYKPIHKXKHIHK5IHKPIHK5IHKPIHKPKPIHK0I9IWKJIHKPIH"
IM$(5) = "KHIHKPIHKPI9IZKBIHKHIHKPIHKHIHKPIHKKI9IWKJIHKHIHKPIHKHIHKPIHKDI9IXKPIHKHIHKHKHIHKHIPKHI9IYKCIHKPIHKHIHKPIHKHIPKHIHKPIHKHIPKHIHKPIHKHIHKPIHKHI9I9IHKHIHKPIHKHIPKHIHKHIEC9CSKDIPKHIPKHIHKHIPKHIHKEC9CSIDKHIPKHIPIPKHIPKHIFC9CQKEIHKHIPKHIHIXKHIPKG"
IM$(6) = "C9COIFKHI5KHI5KHIPKHI5KHIPKHI5KHI5KHIPKHI5KHIPKHIXIHKHI9IDC9CMI4KHIPIPKHI6C9CKI9IDKHIHIXKHIZC9CII9IMKHI9IWC9CGI9IVI9I9I9I9IPI9I9I9I9IPQ9Q9Q9Q9QPE9E9E9E9EPE9E9E9E9EPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9"
IM$(7) = "Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9"
IM$(8) = "Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QP"
IM$(9) = "RBRQ9QOKMQHK5RBSQ9QTI9IRRBXQ9QTKPQHKHQPKERBYQ9QPI9IHRB3Q9QSKAI5KHIJRB5Q9QTC9CMRB9Q9QWI9IGRCBQ9QYC9CCRCIA9A9A9A9APRCJE9E9E9E9EPRCNA9A9A9A9APRCOE9E9E9E9EPRCUA9A9A9A9APRCVE9E9E9E9EPRC5A9A9A9A9APRC7E9E9E9E9EP"
IM$(10) = "RBQQ9QOK9KIQHKFRBRQ9QOI9IQRBWQ9QPKLQHKHQHKPRBXQ9QTI9QLIERB2Q9QRIBKHIPKHIPKCRB4Q9QSC9CORB8Q9QVI3KHIGRCAQ9QXC9CERCJA9A9A9A9APRCKE9E9E9E9EPRCOA9A9A9A9APRCPE9E9E9E9EPRCVA9A9A9A9APRCXE9E9E9E9EPRC7A9A9A9A9APRDAE9E9E9E9EP"
IM$(11) = "RBPQ9QOK9KAQHKNRBQQ9QOI9IWRBVQ9QOKEQHKHQHKPQHKFRBWQ9QPI9IPRB1Q9QQKCIPKHIPKHIDRB3Q9QSC9CORB7Q9QUIWKHIPRB9Q9QWC9CGRCEA9A9A9A9APRCFE9E9E9E9EPRCGA9A9A9A9APRCHE9E9E9E9EPRCPA9A9A9A9APRCQE9E9E9E9EPRCXA9A9A9A9APRCZE9E9E9E9EPRDAA9A9A9A9APRDCE9E9E9E9"
IM$(12) = "EP"
IM$(13) = "RBOQ9QNK3QHKWRBPQ9QOI9IWRBUQ9QTKHQHKPQHKNRBVQ9QOI9IWRB0Q9QQIKKHIPKHIHKDRB1Q9QQC9CSRB2Q9QRC9CQRB6Q9QTIPKHIYRB8Q9QVC9CIRCKA9A9A9A9APRCLE9E9E9E9EPRCQA9A9A9A9APRCRE9E9E9E9EPRCZA9A9A9A9APRC1E9E9E9E9EPRDCA9A9A9A9APRDFE9E9E9E9EP"
IM$(14) = "RBOQ9QNI9IYRBTQ9QOKEQHKPQHKVRBUQ9QTI9IRRBZQ9QTKHQHKPQHKHRB0Q9QQC9CSRB5Q9QTIHKHI5KARB7Q9QUC9CKRCBQ9QYI9ICRCHA9A9A9A9APRCIE9E9E9E9EPRCLA9A9A9A9APRCME9E9E9E9EPRCRA9A9A9A9APRCTE9E9E9E9EPRC1A9A9A9A9APRC3E9E9E9E9EPRDFA9A9A9A9APRDIE9E9E9E9EP"
IM$(15) = "RBSQ9QTKPQHK3RBTQ9QOI9IWRBYQ9QPKDQHKPQHKHRBZQ9QTI9ILRB4Q9QSIAKHI5KHIBRB6Q9QTC9CMRCAQ9QXI9IERCEE9E9E9E9EPRCFA9A9A9A9APRCGE9E9E9E9EPRCMA9A9A9A9APRCNE9E9E9E9EPRCTA9A9A9A9APRCUE9E9E9E9EPRC3A9A9A9A9APRC5E9E9E9E9EPRDIA9A9A9A9AP"
REM ---- PICTURE-END
SS# = 0
SE# = NS# - 1
GOSUB DRAWSTREAM
GOSUB RAYS
SPRSAV PBRD#, 1
SPRITE 1, 1, 1, 0, 1, 1
MOVSPR 1, 0.16 * W#, 0.14 * H#
IF HL# = 0 THEN MOVSPR 1, 96 # 1
COLOR 1, 2
T$ = "THE PICTURE IS STRINGS - THE BIRD IS DATA"
SZ% = 0.0038 * W#
GOSUB CENTERX
TY% = 0.02 * H#
GOSUB DRAWTEXT
IF HL# = 1 THEN GOTO PICDONE
REM Six delta frames in a loop: the grid rolls toward you and the
REM sun's slices crawl. Each frame repaints only the rows that
REM changed, and the rays are struck over the top again.
VI# = 0
DL# = TI# + 480
DO WHILE TI# < DL#
SS# = VA#(VI#)
SE# = VB#(VI#)
GOSUB DRAWSTREAM
GOSUB RAYS
VI# = VI# + 1
IF VI# >= 6 THEN VI# = 0
IF TI# > MT# THEN GOSUB TUNE
GOSUB POLLKEY
IF QF# = 1 THEN EXIT
LOOP
REM The dissolve: black out the 160 columns in stride-63 order.
COLOR 1, 1
K2# = 0
DO WHILE K2# < 160
CO# = MOD(K2# * 63, 160)
X3% = CO# * BS%
DRAW 1, X3%, 0 TO X3%, H# - 1
X3% = X3% + 2
DRAW 1, X3%, 0 TO X3%, H# - 1
X3% = X3% + 2
DRAW 1, X3%, 0 TO X3%, H# - 1
IF MOD(K2#, 5) = 0 THEN GOSUB VSYNC
K2# = K2# + 1
LOOP
MOVSPR 1, 0 # 0
SPRITE 1, 0
LABEL PICDONE
IF QF# = 1 THEN GOTO BYE
REM =====================================================================
REM SCENE 6 : THE SPRITE BANK IS THE SCROLLTEXT, AND THEN IT EXPLODES.
REM Eight letters, eight sprites: draw each with the stroke font,
REM SSHAPE it, SPRSAV it, surf it on a sine. Then the letters fall
REM into the middle, a supernova throws them at the walls -- SOLID
@@ -573,7 +689,7 @@ LABEL WAVEDONE
IF QF# = 1 THEN GOTO BYE
REM =====================================================================
REM SCENE 6 : THE FINALE CARD. Everything at once: stars, bars, the
REM SCENE 7 : THE FINALE CARD. Everything at once: stars, bars, the
REM logo, the greetz, and the sprite bank surfing a sine. This is the
REM frame a clockless host leaves on the screen.
REM =====================================================================
@@ -869,6 +985,7 @@ REM A ball of light: concentric circles three pixels apart, solid at
REM WIDTH 2. DCX%/DCY% centre it, DR% sizes it, DCI# colours it --
REM DCI# = 1 is the eraser.
LABEL DISC
IF DR% < 1 THEN RETURN
COLOR 1, DCI#
R3% = DR%
DO WHILE R3% > 1
@@ -881,13 +998,11 @@ REM The origin point: erase it where it was, redraw it pulsing on
REM SIN(BF#) and strobing through the bar ramp table -- the colours
REM of the next scene, because this is where its bars come from.
LABEL BALLFX
IF PB% <= 0 THEN GOTO BALLFX2
DCI# = 1
DR% = PB%
DCX% = C1X%
DCY% = CY%
GOSUB DISC
LABEL BALLFX2
DR% = BBR% + ((0.45 * BBR%) * SIN(1.1 * BF#))
DCI# = BARC#(MOD(BF#, 20))
DCX% = C1X%
@@ -920,6 +1035,72 @@ IF SND# = 0 THEN RETURN
SOUND 3, 4000, 40, 1, 52000, 800
RETURN
REM Decode picture strings IM$(SS#) through IM$(SE#) and paint them
REM as 5x5 blocks. A run is colour-char then length-char. 'Q' misses
REM the colour table, so it advances without drawing -- the skip. 'A'
REM is black and DOES draw, which is how a video delta erases. 'R'
REM opens a row record: two base-36 digits of row, cursor to column
REM zero -- how a delta jumps between the rows that changed.
LABEL DRAWSTREAM
X# = 0
Y# = 0
S# = SS#
DO WHILE S# <= SE#
T2$ = IM$(S#)
TL2# = LEN(T2$)
P# = 0
DO WHILE P# < TL2#
C2$ = MID(T2$, P#, 1)
IF C2$ = "R" THEN GOSUB PICROW ELSE GOSUB PICRUN2
LOOP
GOSUB POLLKEY
S# = S# + 1
LOOP
RETURN
LABEL PICROW
Y# = INSTR(LTAB$, MID(T2$, P# + 1, 1)) * 36
Y# = Y# + INSTR(LTAB$, MID(T2$, P# + 2, 1))
X# = 0
P# = P# + 3
RETURN
LABEL PICRUN2
CI# = INSTR(CPAL$, C2$) + 1
LN# = INSTR(LTAB$, MID(T2$, P# + 1, 1)) + 1
IF CI# > 0 THEN GOSUB PICRUN
X# = X# + LN#
IF X# >= 160 THEN X# = 0
IF X# = 0 THEN Y# = Y# + 1
P# = P# + 2
RETURN
REM One run: LN# cells of colour CI# at cell (X#, Y#), scaled by BS%.
LABEL PICRUN
X3% = X# * BS%
X4% = ((X# + LN#) * BS%) - 1
Y3% = Y# * BS%
COLOR 1, CI#
DRAW 1, X3%, Y3% TO X4%, Y3%
Y3% = Y3% + 2
DRAW 1, X3%, Y3% TO X4%, Y3%
Y3% = Y3% + 2
DRAW 1, X3%, Y3% TO X4%, Y3%
RETURN
REM The rays live outside the encoding, redrawn over every video
REM frame: nineteen lines instead of thirty runs a grid row, and they
REM are what makes the encoded floor nearly free.
LABEL RAYS
COLOR 1, 4
RI# = 0 - 9
DO WHILE RI# < 10
X3% = (0.5 * W#) + (RI# * 26 * BS%)
DRAW 1, 0.5 * W#, (74 * BS%) + 2 TO X3%, H# + 99
RI# = RI# + 1
LOOP
RETURN
REM Park the eight letter sprites on a sine.
LABEL WAVE
L# = 1
@@ -954,11 +1135,9 @@ RETURN
REM One frame of fireball: erase the previous radius, draw the new
REM one. PR% carries the radius to erase between calls.
LABEL BOOMFX
IF PR% <= 0 THEN GOTO BOOMFX2
BCF# = 1
BR2% = PR%
GOSUB BOOMPASS
LABEL BOOMFX2
BCF# = 0
BR2% = ER%
GOSUB BOOMPASS
@@ -969,6 +1148,7 @@ REM The fireball at radius BR2%: three rings, white heart to orange
REM rim, and twelve red spokes thrown past the outer ring. BCF# = 1
REM draws the whole thing in black, which is the eraser.
LABEL BOOMPASS
IF BR2% < 1 THEN RETURN
R3% = BR2%
IF BCF# = 1 THEN COLOR 1, 1 ELSE COLOR 1, 2
CIRCLE 1, CX0%, CY0%, R3%, R3%
@@ -1220,6 +1400,15 @@ FOR K# = 1 TO N# * 2
NEXT K#
RETURN
REM The bird: SPRSAV's type-in form, sixty-three bytes, three per row,
REM most significant bit leftmost.
LABEL LOADBIRD
FOR I# = 0 TO 62
READ D#
PBRD#(I#) = D#
NEXT I#
RETURN
REM =====================================================================
REM Trap handlers for the hardware probes. Each marks its device gone
REM and lets the probe fall through to the next line.
@@ -1367,3 +1556,18 @@ REM full stop
DATA 1, 15,16
REM exclamation mark
DATA 2, 10,14, 15,16
REM The bird, 24 by 21, one bit a pixel:
REM ..XXX...........XXX.....
REM .X...XX.......XX...X....
REM X.....XXX...XXX.....X...
REM .......XXXXXX...........
REM .........XX.............
REM and sixteen empty rows of sky.
DATA 0, 0, 0, 56, 0, 224, 70, 3, 16
DATA 131, 142, 8, 1, 248, 0, 0, 96, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0