Stop the listings and their chapters stating fixed defects as fact
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m21s
akbasic CI Build / sanitizers (push) Failing after 4m30s
akbasic CI Build / coverage (push) Failing after 3m40s
akbasic CI Build / akgl_build (push) Failing after 21s
akbasic CI Build / mutation_test (push) Failing after 3m27s

You were right that this was still everywhere. The two `.bas` files carried
their workarounds as present-tense statements about the interpreter -- "a name
first created inside a GOSUB costs a value slot that is never handed back",
"writing PAST the terminator of a short row draws nothing at all", "SSHAPE and
GSHAPE ignore the subscript on a string array", "a FOR inside a BEGIN block does
not survive the RETURN" -- and every one of those is now false. A comment that
lies is worse than no comment, and these were the first thing anybody opening
the files would read.

**The characters game takes the geometry fix.** `CW# = 16` becomes `CW# =
RGR(3)`, and the grid comes from `RWINDOW`, so the game fits whatever window and
font the host gives it. That was the one thing in the listing that would break on
a different font, and `RGR(3)` and `RWINDOW` were added to the interpreter
because of it -- leaving the constant in place while Chapter 17 teaches the
function was the inconsistency worth closing. `RGR(1)` still comes first so a
build with no graphics device refuses by naming the device that is missing.

Everything else in both listings keeps its shape and says why. Declaring every
name up front, guarding loops with `GOTO`, six scalars for six brick stamps: none
is forced any more and none costs anything, so they stay, with the comments
marking which rules stand and which are history. The sprites header's five traps
are marked FIXED where they are fixed.

The chapters follow: Chapter 17 Step 2 no longer says "the listing still has
`CW# = 16`", and Chapter 18's trap section is "five things that did not do what
they looked like" with the two that still stand named up front rather than left
to be counted.

Verified: Chapter 17 Step 2's block is once again verbatim from the listing, so
every quoted fragment in both chapters matches its source with no exceptions;
both games run 40 seconds on the SDL frontend with no error line; both suites
green in both configurations; `docs_examples` and `docs_screenshots --check`
pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-02 06:32:48 -04:00
parent 89fca8007b
commit 0679c3042b
6 changed files with 120 additions and 78 deletions

View File

@@ -7,12 +7,18 @@ REM Five things about this interpreter shape everything below, and they
REM are worth knowing before the first line stops making sense.
REM
REM 1. The text layer repaints every row of the window opaque black after
REM the program's steps have run, so anything DRAW or BOX put on the
REM screen is wiped before it is ever presented. Sprites are drawn
REM after the text layer, so a sprite is the only thing that can be
REM seen. Everything drawn here is therefore captured with SSHAPE and
REM installed as a sprite with SPRSAV -- that is what sprites 1 and 2
REM are. They are the screen.
REM the program's steps have run, so by default anything DRAW or BOX
REM put on the screen is wiped before it is ever presented. Sprites are
REM drawn after the text layer, so a sprite is the one thing that is
REM seen for nothing. Everything drawn here is therefore captured with
REM SSHAPE and installed as a sprite with SPRSAV -- that is what
REM sprites 1 and 2 are. They are the screen.
REM
REM (WINDOW can shrink the text area and hand the rest of the window to
REM the drawing verbs; it could not be reached from this build when
REM this was written. It would not help here anyway: a drawing has to
REM be re-issued every frame and fit inside one batch, and a sprite has
REM neither constraint.)
REM
REM 2. The host runs 256 source lines and then presents the frame, and a
REM present throws the drawing buffer away. So all the drawing between
@@ -33,9 +39,10 @@ REM enough to both that a constant is spelled out rather than named
REM and several routines that would read better as subroutines are
REM BEGIN blocks instead.
REM
REM 5. Five things in this dialect do not do what they look like they do,
REM and each one cost an evening. They are noted where they bite, and
REM collected here so nobody has to find them twice:
REM 5. Five things in this dialect did not do what they looked like they
REM did, and each one cost an evening. THREE OF THEM HAVE SINCE BEEN
REM FIXED, and are marked below; the shape of this listing is still
REM theirs, which is why they are still written down:
REM
REM * **The left operand decides whether an expression is done in
REM integers or in floats.** 0 - V% throws V%'s fraction away and
@@ -48,12 +55,16 @@ REM result somewhere with a % on it.
REM * A FOR whose bounds are equal does not run its body at all, so
REM every loop over a list has the one-item case written out beside
REM it.
REM * A FOR inside a BEGIN block does not survive the RETURN of the
REM routine holding it; the interpreter loses the GOSUB. Loops in
REM this program are guarded by GOTO rather than wrapped in a block.
REM * SSHAPE and GSHAPE ignore the subscript on a string array, so
REM every element resolves to element zero. The six brick stamps are
REM therefore six separate scalars.
REM * FIXED. A FOR inside a BEGIN block that was SKIPPED did not
REM survive the RETURN of the routine holding it; the interpreter
REM lost the GOSUB and reported "RETURN outside the context of
REM GOSUB". Loops in this program are guarded by GOTO rather than
REM wrapped in a block, and are left that way because it costs
REM nothing. TODO.md section 9 item 2.
REM * FIXED. SSHAPE and GSHAPE ignored the subscript on a string array,
REM so every element resolved to element zero. The six brick stamps
REM are six separate scalars for that reason; an array works now.
REM TODO.md section 9 item 6.
REM * READ walks one cursor through every DATA item in the file in the
REM order they were written, no matter which routine is reading.
REM
@@ -140,10 +151,11 @@ REM ------------------------------------------------------- shapes and dirt
REM SHN# counts the SSHAPE slots spent. There are sixteen, nothing gives
REM one back, and GRAPHIC 5 gives back all of them at once -- so the brick
REM stamps are thrown away and rebuilt every time the pool runs dry.
REM One scalar per row colour, and scalars rather than an array on
REM purpose: SSHAPE and GSHAPE read the leaf they are handed without
REM applying its subscript, so a string array element always resolves to
REM element zero and every brick comes out the same colour.
REM One scalar per row colour, and scalars rather than an array because
REM SSHAPE and GSHAPE used to read the leaf they were handed without
REM applying its subscript -- a string array element always resolved to
REM element zero, and every brick came out the same colour. That is fixed
REM and an array would work now; six scalars is what this listing has.
S0$ = ""
S1$ = ""
S2$ = ""
@@ -330,9 +342,10 @@ REM
REM Rebuilding the brick stamps comes first, because everything else
REM draws with them and GRAPHIC 5 has just thrown them away.
REM =====================================================================
REM Labels rather than BEGIN blocks here, and it is not a style choice: a
REM RETURN inside a block leaves the interpreter with no GOSUB to return
REM from and stops the program.
REM Labels rather than BEGIN blocks here. When this was written it was not
REM a style choice -- a loop inside a block that was skipped left the
REM interpreter with no GOSUB to return from and stopped the program. That
REM is fixed; the shape is kept because it reads the same either way.
LABEL DRAWJOB
IF SHN# < 14 THEN GOTO DRAWJOB2
GOSUB DRAWPROTOS
@@ -399,8 +412,9 @@ Z$ = S2$ : T1# = RS#(2) : T2# = RC#(2) : GOSUB STAMPROW
Z$ = S3$ : T1# = RS#(3) : T2# = RC#(3) : GOSUB STAMPROW
Z$ = S4$ : T1# = RS#(4) : T2# = RC#(4) : GOSUB STAMPROW
Z$ = S5$ : T1# = RS#(5) : T2# = RC#(5) : GOSUB STAMPROW
REM A FOR inside a BEGIN block does not survive the RETURN at the end of
REM the routine that holds it, so this loop is guarded by a GOTO instead.
REM A skipped BEGIN block holding a FOR used to break the RETURN at the end
REM of the routine holding it, so this loop is guarded by a GOTO instead.
REM Fixed since; left alone because a GOTO guard costs nothing.
IF PN# = 1 THEN DRAW PC#(0), PX1#(0), PY1#(0) TO PX2#(0), PY2#(0)
IF PN# < 2 THEN GOTO PLDONE
FOR I# = 0 TO PN# - 1