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>
1574 lines
41 KiB
QBasic
1574 lines
41 KiB
QBasic
REM =====================================================================
|
|
REM
|
|
REM A K L A B S M E G A D E M O ' 8 5
|
|
REM
|
|
REM CODE AND FX ............. TACHIKOMA
|
|
REM DEDICATED TO ............ GAMECUBE
|
|
REM MEGAGREETZ .............. STARFORT
|
|
REM
|
|
REM A demoscene production for the akbasic interpreter, written as if
|
|
REM it were 1985 and this dialect were the machine under the tree.
|
|
REM It leans on every corner of the interpreter on purpose:
|
|
REM
|
|
REM - there is no RND, so it carries chapter 17's LCG and seeds it
|
|
REM from the jiffy clock
|
|
REM - the palette cannot be rewritten, so every "colour cycle" is an
|
|
REM honest redraw of the same strokes in the next colour
|
|
REM - there are eight sprites, so the lettering IS the sprite bank:
|
|
REM each letter of GAMECUBE is drawn with the stroke font, captured
|
|
REM with SSHAPE and installed with SPRSAV
|
|
REM - SOLID walls and COLLISION 2 bounce that lettering around the
|
|
REM screen with RCOLLISION(n, 7) saying which axis to reverse
|
|
REM - the PLAY queue is one queue -- monophonic, one instrument per
|
|
REM batch (TODO.md 4) -- so the soundtrack is beeper-style: one
|
|
REM interleaved line that is its own kick, bass and arpeggio,
|
|
REM with a noise drum break every third batch. Voice 1 is music,
|
|
REM voice 2 the collision blips, voice 3 the scene sweeps
|
|
REM - 3D wireframes with no matrices: two composed rotations and a
|
|
REM perspective divide per vertex, floats kept on the left the
|
|
REM whole way because the left operand decides the arithmetic
|
|
REM
|
|
REM It probes for its hardware like a proper boot loader. No audio
|
|
REM device mutes it. No text grid drops the status row. A host with no
|
|
REM clock -- tools/screenshot.c -- gets each scene as a still and the
|
|
REM finale as the picture, because every animation loop is gated on
|
|
REM TI# actually moving.
|
|
REM
|
|
REM Q or ESC quits. TODO.md section 4 records what this program wanted
|
|
REM and could not have.
|
|
REM
|
|
REM =====================================================================
|
|
|
|
REM ---- every pool up front: the DIM pool never frees (chapter 13) ----
|
|
DIM FNC#(41)
|
|
DIM FNI#(41)
|
|
DIM FNS#(280)
|
|
DIM RBW#(7)
|
|
DIM STC#(4)
|
|
DIM BARC#(20)
|
|
DIM SBG#(9)
|
|
DIM BYO%(4)
|
|
DIM WVX%(14)
|
|
DIM WVY%(14)
|
|
DIM WVZ%(14)
|
|
DIM WPX#(14)
|
|
DIM WPY#(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
|
|
PH% = 0
|
|
MT# = 0
|
|
W# = 320
|
|
H# = 200
|
|
|
|
REM A scalar FIRST assigned inside a GOSUB lives in that call's scope
|
|
REM and dies at RETURN, so a subroutine's OUTPUT must be assigned here
|
|
REM first to make it a global -- that is why chapter 18 predeclares.
|
|
REM But only the outputs: there are 128 variable slots in the whole
|
|
REM machine and a predeclared scratch variable occupies one forever,
|
|
REM where a scoped one gives its slot back. The first cut of this
|
|
REM program predeclared everything and ran the pool dry.
|
|
RMAX# = 0
|
|
RND# = 0
|
|
TX% = 0
|
|
TB# = 0
|
|
|
|
REM =====================================================================
|
|
REM Probe the machine. Graphics is mandatory; everything else degrades.
|
|
REM =====================================================================
|
|
GFX# = 1
|
|
TRAP NOGFX
|
|
W# = RGR(1)
|
|
H# = RGR(2)
|
|
TRAP
|
|
IF GFX# = 1 THEN GOTO HAVEGFX
|
|
PRINT "THIS DEMO NEEDS THE SDL BUILD (-DAKBASIC_WITH_AKGL=ON)."
|
|
END
|
|
LABEL HAVEGFX
|
|
|
|
SND# = 1
|
|
TRAP NOSID
|
|
SOUND 1, 17175, 4
|
|
VOL 12
|
|
TRAP
|
|
|
|
TXT# = 1
|
|
TRAP NOGRID
|
|
WINDOW 0, 0, 39, 0
|
|
TRAP
|
|
|
|
KEYS# = 1
|
|
K# = 0
|
|
TRAP NOKEYS
|
|
GET K#
|
|
TRAP
|
|
|
|
REM A host that never sets the clock is the screenshot tool. TI# stays
|
|
REM zero there, so every wait would spin forever: render stills instead.
|
|
HL# = 0
|
|
SLEEP 0.3
|
|
IF TI# = 0 THEN HL# = 1
|
|
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"
|
|
|
|
REM =====================================================================
|
|
REM SCENE 1 : TITLE. Starfield from the LCG, the logo in rainbow,
|
|
REM and the "palette cycle" that is really a redraw.
|
|
REM =====================================================================
|
|
GRAPHIC 1, 1
|
|
WIDTH 1
|
|
NS# = 120
|
|
GOSUB STARS
|
|
WIDTH 2
|
|
T$ = "AKLABS"
|
|
SZ% = 0.025 * W#
|
|
GOSUB CENTERX
|
|
TY% = 0.12 * H#
|
|
CM# = 1
|
|
GOSUB DRAWTEXT
|
|
CM# = 0
|
|
COLOR 1, 2
|
|
T$ = "PRESENTS"
|
|
SZ% = 0.008 * W#
|
|
GOSUB CENTERX
|
|
TY% = 0.45 * H#
|
|
GOSUB DRAWTEXT
|
|
COLOR 1, 4
|
|
T$ = "A 1985 DEMO FOR GAMECUBE"
|
|
SZ% = 0.006 * W#
|
|
GOSUB CENTERX
|
|
TY% = 0.58 * H#
|
|
GOSUB DRAWTEXT
|
|
GOSUB SWEEP
|
|
IF HL# = 1 THEN GOTO S1DONE
|
|
T$ = "AKLABS"
|
|
SZ% = 0.025 * W#
|
|
GOSUB CENTERX
|
|
TY% = 0.12 * H#
|
|
DL# = TI# + 420
|
|
DO WHILE TI# < DL#
|
|
RB# = RB# + 1
|
|
CM# = 1
|
|
GOSUB DRAWTEXT
|
|
CM# = 0
|
|
GOSUB POLLKEY
|
|
IF QF# = 1 THEN EXIT
|
|
SLEEP 0.12
|
|
LOOP
|
|
LABEL S1DONE
|
|
IF QF# = 1 THEN GOTO BYE
|
|
|
|
REM =====================================================================
|
|
REM SCENE 2 : LISSAJOUS BRAID. Twelve phase-shifted 3:2 curves, drawn
|
|
REM once each -- the drawing layer persists, so the braid accumulates.
|
|
REM =====================================================================
|
|
GRAPHIC 1, 1
|
|
WIDTH 1
|
|
CX% = 0.5 * W#
|
|
CY% = 0.5 * H#
|
|
R1% = 0.3 * W#
|
|
R2% = 0.38 * H#
|
|
PN# = 0
|
|
DO WHILE PN# < 12
|
|
COLOR 1, RBW#(MOD(PN#, 7))
|
|
GOSUB SPIRO1
|
|
IF HL# = 0 THEN SLEEP 0.25
|
|
GOSUB POLLKEY
|
|
IF QF# = 1 THEN EXIT
|
|
PN# = PN# + 1
|
|
LOOP
|
|
WIDTH 2
|
|
COLOR 1, 2
|
|
T$ = "TACHIKOMA FX"
|
|
SZ% = 0.007 * W#
|
|
GOSUB CENTERX
|
|
TY% = 0.88 * H#
|
|
GOSUB DRAWTEXT
|
|
IF HL# = 0 THEN SLEEP 1.5
|
|
IF QF# = 1 THEN GOTO BYE
|
|
|
|
REM =====================================================================
|
|
REM SCENE 3 : WIREFRAMES. A cube and an octahedron out of one pooled
|
|
REM vertex and edge table -- two composed rotations and a perspective
|
|
REM divide per vertex, no matrices anywhere. Each frame erases the
|
|
REM previous edges in black and draws the new ones, the raster bars'
|
|
REM trick applied to twenty-four lines. A clockless host gets five
|
|
REM exposures without the erase: a tumble, as a still.
|
|
REM
|
|
REM The way out is the way in: the solids drift together, the point
|
|
REM they share ignites -- pulsing through the bar ramp table, because
|
|
REM it is the origin of the next scene -- the camera dives through
|
|
REM the frames as the perspective scale outgrows the window, and the
|
|
REM origin splits in two, rides the Y axis to the bars' band, and
|
|
REM stretches into the first two raster bars itself.
|
|
REM =====================================================================
|
|
GRAPHIC 1, 1
|
|
WIDTH 1
|
|
NS# = 90
|
|
GOSUB STARS
|
|
WIDTH 2
|
|
COLOR 1, 2
|
|
T$ = "TWO SOLIDS - NO MATRICES - PURE SIN"
|
|
SZ% = 0.0045 * W#
|
|
GOSUB CENTERX
|
|
TY% = 0.06 * H#
|
|
GOSUB DRAWTEXT
|
|
SF% = 0.24 * H#
|
|
CY% = 0.55 * H#
|
|
A1% = 0.4
|
|
B1% = 0.9
|
|
A2% = 1.1
|
|
B2% = 0.3
|
|
FC# = 0
|
|
IF HL# = 0 THEN GOTO WIRELIVE
|
|
PN# = 0
|
|
DO WHILE PN# < 5
|
|
RA% = A1%
|
|
RB% = B1%
|
|
CX% = 0.3 * W#
|
|
VS# = 0
|
|
VE# = 8
|
|
ES# = 0
|
|
EE# = 12
|
|
GOSUB WIRESPIN
|
|
COLOR 1, RBW#(PN#)
|
|
GOSUB WIREDRAW
|
|
RA% = A2%
|
|
RB% = B2%
|
|
CX% = 0.7 * W#
|
|
VS# = 8
|
|
VE# = 14
|
|
ES# = 12
|
|
EE# = 24
|
|
GOSUB WIRESPIN
|
|
GOSUB WIREDRAW
|
|
A1% = A1% + 0.3
|
|
B1% = B1% + 0.19
|
|
A2% = A2% - 0.26
|
|
B2% = B2% + 0.17
|
|
PN# = PN# + 1
|
|
LOOP
|
|
GOTO WIREDONE
|
|
LABEL WIRELIVE
|
|
C1X% = 0.3 * W#
|
|
C2X% = 0.7 * W#
|
|
DL# = TI# + 360
|
|
DO WHILE TI# < DL#
|
|
GOSUB SPINSTEP
|
|
IF TI# > MT# THEN GOSUB TUNE
|
|
GOSUB POLLKEY
|
|
IF QF# = 1 THEN EXIT
|
|
GOSUB VSYNC
|
|
LOOP
|
|
IF QF# = 1 THEN GOTO WIREDONE
|
|
REM Converge: the two solids drift to the shared centre, spinning
|
|
REM the whole way in.
|
|
XS1% = (0.2 * W#) / 36
|
|
GF# = 0
|
|
DO WHILE GF# < 36
|
|
C1X% = C1X% + XS1%
|
|
C2X% = C2X% - XS1%
|
|
GOSUB SPINSTEP
|
|
IF TI# > MT# THEN GOSUB TUNE
|
|
GOSUB POLLKEY
|
|
GOSUB VSYNC
|
|
GF# = GF# + 1
|
|
LOOP
|
|