7 Commits

Author SHA1 Message Date
1fb808f480 Rewrite the two game tutorials as instructions rather than commentary
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m21s
akbasic CI Build / sanitizers (push) Failing after 4m32s
akbasic CI Build / coverage (push) Failing after 3m37s
akbasic CI Build / akgl_build (push) Failing after 22s
akbasic CI Build / mutation_test (push) Failing after 3m30s
Chapters 17 and 18 read as a code review of a finished listing: they explained
why each decision had been made, walked through the project's own history, and
led with what had once been broken. A reader who wanted to build the game got
the reasoning and had to reconstruct the program.

Both are now step-by-step. Each opens with a picture of the finished game and a
bullet list of the steps, each bullet is a section, and each section states its
goal, shows the code, and says how to check it. Chapter 17 is sixteen steps and
Chapter 18 is thirteen, and the last of each is the assembly: the order of the
file, the full declaration block, and the routines the earlier steps referred to.
Project history is gone -- it belongs in Chapter 14 and in git -- and where a
listing has to do something awkward, the tutorial shows how first and names the
`TODO.md` item that will make it unnecessary second.

**Three defects had no entry anywhere**, which the rewrite found by trying to
state each rule as a rule. §6 item 35: `a - b + c` computes `a - (b + c)`,
because `subtraction()` sits above `addition()` as its own precedence level and
the inner loop eats the `+`. Item 36: only one unparenthesised `AND` or `OR` is
matched, which is item 12's `if`-where-`while` on the one operator pair item 12
did not reach. Item 37: a `GOTO` out of a `FOR` or a `DO` leaks the loop's scope,
so a main loop written that way stops on the thirty-second lost life -- which is
why both games are built out of `LABEL` and `GOTO`, and it is a workaround rather
than a preference. Chapter 3 gains the identifier rule the third trial ran into:
there is no underscore in a name, and the error says `UNKNOWN TOKEN _`.

**`tools/screenshot.c` learned to draw the text layer**, behind a new `text=1`
fence attribute, because Chapter 17's game is characters in the grid and a figure
without that layer is two sprites on black. It opens the bundled font at the size
the standalone frontend uses, so a figure's cell size is the reader's cell size,
and it uses the akgl sink alone rather than a tee so the program's output lands in
the picture instead of on the stdout the caller reads to decide a figure failed.
Both new figures -- `breakout-game.png` and `breakout-game-artwork.png` -- are
generated from listings in the chapters like every other one.

Verified by handing each chapter, alone, to an agent on a much smaller model and
telling it to build the game from the tutorial text with the `examples/` tree off
limits. The first pass scored 3.5 and 3 out of 10 and named what was missing:
routines referred to but never shown, the third level layout, the sprite `DATA`,
the font table, edits to earlier routines that were never marked as edits. Those
are now in. The second pass built a 658-line Chapter 17 game that plays itself
for ninety seconds with the score at 1890 and no error line, and the third built
a 1053-line Chapter 18 game with 61 labels, no invented routines, no gaps found,
and forty seconds clean. 9/10 and 8/10.

One real bug in the new prose, caught in review: Chapter 18's `HITBAR` did not
set the `HIT#` that `BALLPADDLE` reads to decide whether the paddle already
caught the ball. Both suites green in both configurations, `docs_examples` and
`docs_screenshots --check` pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 08:08:23 -04:00
cb0e2d0800 Add two Breakout examples and the tutorials that build them
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m10s
akbasic CI Build / sanitizers (push) Failing after 4m5s
akbasic CI Build / coverage (push) Failing after 3m29s
akbasic CI Build / akgl_build (push) Failing after 21s
akbasic CI Build / mutation_test (push) Failing after 3m19s
Two complete games in `examples/breakout/`, both 100% BASIC: `characters/`
draws its wall in the text grid with two `DATA` sprites for the ball and
paddle, and `sprites/` loads CC0 artwork and captures its whole screen with
`SSHAPE`/`SPRSAV`. They take opposite shapes for reasons that are entirely
this interpreter's, which is what the chapters are for.

`docs/17-tutorial-breakout.md` and `docs/18-tutorial-breakout-artwork.md`
build each one a step at a time, and end in a checklist of the rules a real
program runs into: create every name before the loop starts, write a text row
whole, loop with `GOTO` rather than `DO`, put the float on the left. Every
trap is a runnable block with its own output rather than a claim -- the value
pool dying at four thousand names, the skipped `BEGIN` block that breaks its
caller's `RETURN`, `SSHAPE` ignoring a subscript, `READ`'s single cursor.

Five figures, generated from the listings beside them by `docs_screenshots`,
and a `breakout_art` setup so the ones that load artwork load the example's
own. The character game's wall cannot be photographed -- the screenshot host
omits the text layer on purpose -- so it is shown as compared output instead.

`docs/07-sound.md` never said `SOUND`'s frequency is a SID register value
rather than hertz, which both games depend on. It says so now, with the
conversion from `src/audio_tables.c:84`.

`TODO.md` gains the thirteen defects the two games turned up -- §6 items 30
to 33 and all of §9 -- each with a reduction that fits on a screen, the file
and line of the cause, and what a fix would touch.

Verified: `docs_examples` passes in both build configurations,
`docs_screenshots --check` re-renders all thirteen figures and byte-compares
them, the full 109-test suite passes in both builds, every quoted fragment
was checked to appear verbatim in the listing it came from, and every
relative link and anchor resolves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 22:54:42 -04:00
6bac929901 Document structures: a chapter, the architecture, and the differences
docs/16-structures.md is the feature: records, nesting, copy-on-assign, strict
pointers, lists, what is checked and what is not, and how a host shares its own
C structs. Every example in it is executed by docs_examples and byte-compared,
including the refusals -- so a message that changes fails the suite rather than
quietly making the chapter wrong.

The chapter makes one contrast explicitly, because it is the question a reader
will actually have: a misspelled *field* is refused and a misspelled *variable*
still prints zero. The rule underneath is that what the program declared gets
checked and what it did not gets shrugged at -- a variable's name is never
declared, a TYPE's field list is. Structures end up the strictest thing in the
language, not from a higher standard but because they are the only named thing
whose valid spellings are written down.

Chapter 14 gains the layout: an instance is a contiguous run of value slots with
a diagram of where the fields sit, the three-pass prescan and why each pass
exists, why the copy cannot live in akbasic_value_clone(), and why the render
depth bound is four rather than eight. Chapter 3 gains the @ suffix, chapter 13
records that all of this is an addition BASIC 7.0 has nothing like, and the verb
reference gains TYPE, POINT and DIM ... AS.

MAINTENANCE.md gains the two rules that are on a maintainer rather than on a
test: a structure copy must not go through clone, and a field chain gets its own
leaf field. TODO.md section 5 records what was invented and the three limits
that are ours, and section 8 records the two defects the work exposed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 12:03:02 -04:00
737fdc760f Document every error code a script can see, as chapter 15
ER# held numbers nothing explained. The appendix lists the four error classes
the interpreter prints, the eight codes it owns and what raises each, and the
codes that reach ER# from errno, libakerror and libakgl underneath it.

The table is not asserted. A program in the chapter trips seven of the eight and
prints what it got, and docs_examples byte-compares the result -- so the numbers
are checked rather than claimed. The eighth, 516, is not usefully trappable and
the chapter says why: entering a handler takes a scope, and the pool being empty
is what raised it.

Two things worth a reader's attention came out of writing it. Two codes register
the same ERR() text, so a program must compare the number and print the text.
And VAL reports libakerror's Value Error rather than the interpreter's 517,
which makes that number the platform's rather than ours -- filed as section 6
item 21, not fixed here, because deciding which libakstdlib failures to
translate is a boundary question and ENOENT out of DOPEN is the counter-case.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:36:36 -04:00
23ccb66f69 Document the interpreter's architecture as chapter fourteen
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m2s
akbasic CI Build / sanitizers (push) Successful in 3m50s
akbasic CI Build / coverage (push) Failing after 3m22s
akbasic CI Build / akgl_build (push) Failing after 20s
akbasic CI Build / mutation_test (push) Successful in 12m19s
Chapters 1 through 13 describe the language. Nothing described the machine
that runs it, and the answers were spread across header comments, TODO.md
sections written for a different purpose, and the source itself. Somebody
embedding the interpreter, debugging something it did, or adding a verb had
to reconstruct the shape from all three.

docs/14-architecture.md is that shape, and only that: the three targets and
the driver, the single akbasic_Runtime and why nothing is file-scope,
akbasic_runtime_step() unrolled with the reason each stage sits where it
does, the four modes and what set_mode() does beyond assigning, a line's
journey from text through tokens and leaves to a verb handler, the dispatch
table, the pool map with what each exhaustion actually says, environments
doubling as block state, the two kinds of error, devices, and interrupts.

It defers rather than restates. The headers are the authority on every
function's contract and the chapter says so up front; where a rule is subtle
the header comment already states it at more length than a chapter should.
MAINTENANCE.md keeps the conventions and now points here for the mechanism,
so there is still one copy of each.

Two sections are the reason it exists at all. Debugging: reading a TRON trace
as evidence about the loop rather than the lines, reading an akerror stack
trace and what it is not, four breakpoints and the expressions worth printing
at them, narrowing with ctest -R and the mock devices, and a symptom-to-cause
table. Changing it: the verb recipe end to end including the private
src/verbs.h prototype that is easy to miss, the rule that a missing
dependency capability gets filed upstream rather than worked around, and the
five constraints goal 3 puts on any change.

A `text` fence tag comes with it. Every fenced block in docs/ is executed and
an untagged one is a hard error, so six block diagrams had nowhere to live.
The tag means never executed, it is counted in the skip line like `cmake`,
and MAINTENANCE.md documents it -- the alternative was an indented block the
extractor never sees, and a picture nobody decided about is indistinguishable
from a test nobody ran.

tests/docs_examples.sh now makes --root and --basic absolute before it
starts. Both are used from inside a sandbox directory it cd's into, so the
invocation MAINTENANCE.md itself documents -- --root . --basic ./build/basic
-- failed every example with "exited 127" and every setup= with "setup
failed". CTest passes absolute paths and never saw it; running one document
by hand hits it immediately.

Writing the error section turned up a defect and TODO.md section 8 records
it. The ATTEMPT blocks that turn a script's mistake into an error line wrap
parsing and interpretation but not scanning, so a line with more than 32
tokens escapes as an interpreter error: stack trace, exit 1, and at a prompt
the REPL is gone. That is the same shape as section 8 item 2, on a path that
fix did not cover. Not fixed here -- it is a behaviour change and wants its
own tests -- but written down with the three call sites and what would cover
them.

Both configurations stay green: 95/95 and 94/94. docs_examples now runs 37
programs, 9 transcripts, 45 output comparisons, 3 C snippets, 2 excerpts and
2 shell blocks, and skips 9 text blocks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:14:17 -04:00
342e4c07da Execute every documented example as a test
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m2s
akbasic CI Build / sanitizers (push) Successful in 3m52s
akbasic CI Build / coverage (push) Failing after 3m24s
akbasic CI Build / akgl_build (push) Failing after 20s
akbasic CI Build / mutation_test (push) Has been cancelled
docs/ and README.md carry 85 fenced blocks. Every one was checked by hand
exactly once, when it was written, which is not a standard that survives a
changing interpreter -- and four were already wrong: two transcripts showing a
leading space PRINT does not emit, akbasic_TextSink in README.md missing the
two members it had grown hours earlier, and FILTER's refusal quoted with
wording the code does not use.

tests/docs_examples.sh reads a fence-tag vocabulary and runs what it finds.
BASIC programs and transcripts run and are byte-compared against an `output`
block; C snippets compile with -fsyntax-only against the real include path,
which CMake writes out because it is transitive through akerror, akstdlib and
akgl; shell blocks run in a sandbox. Anything that would reconfigure the build
tree, hit the network or re-enter the suite is tagged norun with the reason in
MAINTENANCE.md, and the two cmake blocks stay hand-maintained by decision.

An untagged block is a failure rather than a default, and the pass line
reports what it executed by kind. Both exist because the way a harness like
this dies is by quietly matching nothing and passing -- which it duly did on
the first CTest run, where a generator expression evaluating to nothing still
contributed an empty argument that the script read as a filename. The count is
what caught it.

The excerpt check earns its own mention: a block tagged
`c excerpt=include/akbasic/sink.h` must still appear in that header, comments
and whitespace ignored. Compiling it would only redefine the type, so a
compile check could not have found the stale struct, and did not.

Registered as the CTest case docs_examples in both configurations. Fixing the
four wrong examples turned up two interpreter defects, fixed in the previous
commit and recorded in TODO.md section 8.

MAINTENANCE.md is new: the fence-tag reference, what to do when the case
fails, and the conventions that until now only existed inside source comments
-- the three test lists and how two of them invert "passed", the sorted verb
table, that a golden file is never edited to suit this interpreter, and that a
fix gets mutation-checked with a file copy rather than git checkout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:41:36 -04:00
cdaefcc941 Write the usage guide as thirteen chapters in docs/
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m0s
akbasic CI Build / sanitizers (push) Successful in 3m45s
akbasic CI Build / coverage (push) Failing after 3m22s
akbasic CI Build / akgl_build (push) Failing after 21s
akbasic CI Build / mutation_test (push) Successful in 11m26s
Organised the way the C128 Programmer's Reference Guide is: the language
first, then each hardware area, then the reference sections. One markdown
file per chapter.

The verb and function references are generated from the interpreter's own
dispatch table, with an assertion that every row is described, so they cannot
drift out of step with what the program accepts. 98 verbs and 30 functions.

Every example was run before it was written down, which caught three claims
that were wrong: a whole FOR loop on one line prints nothing rather than
looping once, MID and INSTR count from zero where a C128 counts from one, and
a multi-line DEF returns a value the caller has to assign away.

Chapter 13 is the list a BASIC 7.0 programmer needs -- roughly sixty
documented differences, including the two known FOR defects and the fact that
drawing does not survive a frame.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 21:50:50 -04:00