Add two Breakout examples and the tutorials that build them
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
|
|
|
REM =====================================================================
|
|
|
|
|
REM BREAKOUT, for the AKGL BASIC interpreter, using sprite artwork.
|
|
|
|
|
REM
|
|
|
|
|
REM Run it with the SDL build: build-akgl/basic breakout-sprites.bas
|
|
|
|
|
REM
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM Four things about this interpreter shape everything below, and they are
|
|
|
|
|
REM worth knowing before the first line stops making sense.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM 1. The text layer repaints every row it owns, opaque, after the
|
|
|
|
|
REM program's steps have run -- and by default it owns the whole window,
|
|
|
|
|
REM so anything DRAW or BOX put on the screen would be wiped before it
|
|
|
|
|
REM was ever seen. WINDOW moves it to two rows at the bottom, which is
|
|
|
|
|
REM the first executable line of this program, and everything below
|
|
|
|
|
REM then draws into a layer the frame composites and *keeps*.
|
Stop the listings and their chapters stating fixed defects as fact
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>
2026-08-02 06:32:48 -04:00
|
|
|
REM
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM Nothing here is captured into a sprite. It used to be: sprites 1
|
|
|
|
|
REM and 2 held an SSHAPE of the HUD strip and of the whole play field,
|
|
|
|
|
REM because a sprite was the one thing drawn from state the interpreter
|
|
|
|
|
REM kept and a drawing lasted exactly one frame. Both slots are free
|
|
|
|
|
REM now, and the field is drawn once per level rather than rebuilt.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM 2. Nothing has a drawing deadline. The host runs 256 source lines and
|
|
|
|
|
REM then presents, and a present used to throw the drawing buffer away
|
|
|
|
|
REM -- so a capture spanning that boundary came back half empty, and a
|
|
|
|
|
REM PACE routine spun on TI# to start each frame's drawing at the top
|
|
|
|
|
REM of a batch. With nothing captured, a drawing that spans two batches
|
|
|
|
|
REM simply arrives over two frames and the layer keeps both halves.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM What survives from that era is worth keeping anyway: the lettering
|
|
|
|
|
REM is turned into a list of strokes when a number changes and merely
|
|
|
|
|
REM replayed when it is drawn. Arithmetic away from the frame is free.
|
|
|
|
|
REM
|
|
|
|
|
REM 3. There are eight sprites and no more. Five are the artwork, one is
|
|
|
|
|
REM the gem, and two are spare. The bricks are still drawn rather than
|
|
|
|
|
REM made of art -- sixty of them would not fit in eight slots however
|
|
|
|
|
REM many are free -- but they are *collidable* without being sprites,
|
|
|
|
|
REM because SOLID registers a rectangle and costs no slot.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM
|
|
|
|
|
REM 4. There are 128 variables and 64 labels, and this program is close
|
|
|
|
|
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
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM And five things in this dialect did not do what they looked like they
|
Stop the listings and their chapters stating fixed defects as fact
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>
2026-08-02 06:32:48 -04:00
|
|
|
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:
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
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
|
|
|
|
|
REM 0.0 - V% does not; LEVEL# * 0.45 is zero and 0.45 * LEVEL# is
|
|
|
|
|
REM not. This is the dangerous one, because nothing fails -- the
|
|
|
|
|
REM program simply computes something else. It silently quantised
|
|
|
|
|
REM every bounce in this game to whole pixels and silently cancelled
|
|
|
|
|
REM the per-level speed increase. Put the float first, and put the
|
|
|
|
|
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.
|
Stop the listings and their chapters stating fixed defects as fact
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>
2026-08-02 06:32:48 -04:00
|
|
|
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.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
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
|
|
|
|
|
REM Artwork: Kenney's Puzzle Pack 1, CC0. See art/PROVENANCE.md.
|
|
|
|
|
REM Written by Claude Opus (1M)
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
|
|
|
|
|
REM --------------------------------------------------------------- state
|
|
|
|
|
REM 0 title, 1 waiting to serve, 2 playing, 3 ball lost, 4 level cleared,
|
|
|
|
|
REM 5 game over, 6 paused.
|
|
|
|
|
STATE# = 0
|
|
|
|
|
RUNNING# = 1
|
|
|
|
|
SCORE# = 0
|
|
|
|
|
LIVES# = 3
|
|
|
|
|
LEVEL# = 1
|
|
|
|
|
HISCORE# = 0
|
|
|
|
|
STIMER# = 0
|
|
|
|
|
LASTT# = 0
|
|
|
|
|
KEYV# = 0
|
|
|
|
|
SNDON# = 1
|
|
|
|
|
|
|
|
|
|
REM ------------------------------------------------------------- geometry
|
|
|
|
|
REM WALLL and WALLR are the inside faces of the side walls; BRKX and BRKY
|
|
|
|
|
REM are the top left corner of the brick field.
|
|
|
|
|
WALLL# = 12
|
|
|
|
|
WALLR# = 788
|
|
|
|
|
BRKX# = 42
|
|
|
|
|
BRKY# = 108
|
|
|
|
|
REM The rest of the layout is written out where it is used rather than
|
|
|
|
|
REM named. A brick is 68 by 16 on a 72 by 24 pitch, ten columns by six
|
|
|
|
|
REM rows; the ceiling is at 72 and a ball below 596 is gone. This program
|
|
|
|
|
REM sits a handful of variables under the interpreter's ceiling of 128 and
|
|
|
|
|
REM a name costs one whether it holds a constant or not.
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------- paddle
|
|
|
|
|
PDX% = 348
|
|
|
|
|
PDY# = 540
|
|
|
|
|
PDW# = 104
|
|
|
|
|
PDDIR# = 0
|
|
|
|
|
PDCO# = 0
|
|
|
|
|
PDEXP# = 0
|
|
|
|
|
CTON# = 0
|
|
|
|
|
CTX% = 348
|
|
|
|
|
CTTM# = 0
|
|
|
|
|
|
|
|
|
|
REM ----------------------------------------------------------------- balls
|
|
|
|
|
DIM BLON#(3)
|
|
|
|
|
DIM BLX%(3)
|
|
|
|
|
DIM BLY%(3)
|
|
|
|
|
DIM BLVX%(3)
|
|
|
|
|
DIM BLVY%(3)
|
|
|
|
|
DIM BLST#(3)
|
|
|
|
|
BLN# = 0
|
|
|
|
|
BLOFF# = 41
|
|
|
|
|
SPD% = 6.0
|
|
|
|
|
REM What the balls on screen are actually travelling at, which is only the
|
|
|
|
|
REM same as SPD% between a change and the RESCALE that answers it. RAT% is
|
|
|
|
|
REM the ratio between the two, and it is a float because an integer one
|
|
|
|
|
REM would hold the 0.75 of a slowdown as 0 and stop the ball dead.
|
|
|
|
|
BSPD% = 6.0
|
|
|
|
|
RAT% = 1.0
|
|
|
|
|
SLOWT# = 0
|
|
|
|
|
STKON# = 0
|
|
|
|
|
STKT# = 0
|
|
|
|
|
|
|
|
|
|
REM ------------------------------------------------------------------ gem
|
|
|
|
|
GMON# = 0
|
|
|
|
|
GMTYP# = 0
|
|
|
|
|
GMX% = 0
|
|
|
|
|
GMY% = 0
|
|
|
|
|
|
|
|
|
|
REM --------------------------------------------------------------- bricks
|
|
|
|
|
DIM BRK#(60)
|
|
|
|
|
DIM BRC#(6)
|
|
|
|
|
DIM BRV#(6)
|
|
|
|
|
BRN# = 0
|
|
|
|
|
|
|
|
|
|
REM ------------------------------------------------------- shapes and dirt
|
2026-08-02 13:13:51 -04:00
|
|
|
REM Eight of the sixteen SSHAPE slots, spent once in DRAWPROTOS and never
|
|
|
|
|
REM again: six brick stamps and the two erasers. Nothing captures per frame
|
|
|
|
|
REM any more, so the pool never runs dry and GRAPHIC 5 is never called.
|
Stop the listings and their chapters stating fixed defects as fact
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>
2026-08-02 06:32:48 -04:00
|
|
|
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.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
S0$ = ""
|
|
|
|
|
S1$ = ""
|
|
|
|
|
S2$ = ""
|
|
|
|
|
S3$ = ""
|
|
|
|
|
S4$ = ""
|
|
|
|
|
S5$ = ""
|
|
|
|
|
Z$ = ""
|
|
|
|
|
DHUD# = 1
|
|
|
|
|
DPLAY# = 1
|
|
|
|
|
BAN$ = ""
|
|
|
|
|
BANT# = 0
|
|
|
|
|
|
|
|
|
|
REM ------------------------------------------------------------- the font
|
|
|
|
|
ALPHA$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :-.!"
|
|
|
|
|
DIM FNS#(280)
|
|
|
|
|
DIM FNI#(41)
|
|
|
|
|
DIM FNC#(41)
|
|
|
|
|
|
|
|
|
|
REM Strokes waiting to be drawn: H is the HUD strip, P the field. Each
|
|
|
|
|
REM stroke carries the colour source it wants, so one pass over the list
|
|
|
|
|
REM draws a line in as many colours as it likes.
|
|
|
|
|
DIM HX1#(120)
|
|
|
|
|
DIM HY1#(120)
|
|
|
|
|
DIM HX2#(120)
|
|
|
|
|
DIM HY2#(120)
|
|
|
|
|
DIM HC#(120)
|
|
|
|
|
HN# = 0
|
|
|
|
|
DIM PX1#(110)
|
|
|
|
|
DIM PY1#(110)
|
|
|
|
|
DIM PX2#(110)
|
|
|
|
|
DIM PY2#(110)
|
|
|
|
|
DIM PC#(110)
|
|
|
|
|
PN# = 0
|
|
|
|
|
BUILDH# = 0
|
|
|
|
|
|
|
|
|
|
REM ------------------------------------------------- the text being built
|
|
|
|
|
TX$ = ""
|
|
|
|
|
TXX# = 0
|
|
|
|
|
TXY# = 0
|
|
|
|
|
TXS# = 3
|
|
|
|
|
TXC# = 1
|
|
|
|
|
TLEN# = 0
|
|
|
|
|
TXI# = 0
|
|
|
|
|
GC# = 0
|
|
|
|
|
GN# = 0
|
|
|
|
|
GP# = 0
|
|
|
|
|
GX# = 0
|
|
|
|
|
GK# = 0
|
|
|
|
|
P1# = 0
|
|
|
|
|
P2# = 0
|
|
|
|
|
NUM$ = ""
|
|
|
|
|
|
|
|
|
|
REM ------------------------------------------------------ the paddle bounce
|
|
|
|
|
REM Eight landing zones across the bar, each with a velocity of very
|
|
|
|
|
REM nearly the same length, so the ball changes direction without the
|
|
|
|
|
REM program ever needing a square root -- there is no SQR in this dialect.
|
|
|
|
|
DIM ZVX%(8)
|
|
|
|
|
DIM ZVY%(8)
|
|
|
|
|
|
|
|
|
|
REM --------------------------------------------------------------- scratch
|
|
|
|
|
I# = 0
|
|
|
|
|
K# = 0
|
|
|
|
|
N# = 0
|
|
|
|
|
R# = 0
|
|
|
|
|
C# = 0
|
|
|
|
|
D# = 0
|
|
|
|
|
B# = 0
|
|
|
|
|
T1# = 0
|
|
|
|
|
T2# = 0
|
|
|
|
|
T3# = 0
|
|
|
|
|
T4# = 0
|
|
|
|
|
BX1# = 0
|
|
|
|
|
BY1# = 0
|
|
|
|
|
RR1# = 0
|
|
|
|
|
RR2# = 0
|
|
|
|
|
HIT# = 0
|
|
|
|
|
SEED# = 1
|
|
|
|
|
RNMAX# = 2
|
|
|
|
|
RNVAL# = 0
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM The collision handler answers through these, so they are created out here
|
|
|
|
|
REM like everything else: a name first seen inside a GOSUB dies at its RETURN.
|
|
|
|
|
MB# = 0
|
|
|
|
|
AX# = 0
|
|
|
|
|
DP% = 0.0
|
|
|
|
|
SLX# = 0
|
|
|
|
|
SLY# = 0
|
|
|
|
|
SLI# = 0
|
|
|
|
|
REM The two eraser stamps. Declared here for exactly the same reason -- built
|
|
|
|
|
REM inside DRAWPROTOS and left undeclared, they were SHAPE:6 and SHAPE:7 inside
|
|
|
|
|
REM it and empty everywhere else.
|
|
|
|
|
BL$ = ""
|
|
|
|
|
HBL$ = ""
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Start up
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Tables before font, and the order is not a preference: READ walks one
|
|
|
|
|
REM cursor through every DATA item in the program in the order they appear
|
|
|
|
|
REM in the file, so whichever loader runs first gets the DATA that is
|
|
|
|
|
REM written first. The tables are written first.
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM The text layer repaints every row it owns, opaque, so it has to be moved
|
|
|
|
|
REM out of the way before anything drawn can be seen. Two rows at the bottom is
|
|
|
|
|
REM enough for the final score, and hands the other thirty-five to the drawing
|
|
|
|
|
REM verbs. Everything this game draws then simply stays there -- a drawing goes
|
|
|
|
|
REM into a layer the frame composites, so nothing here is captured into a sprite
|
|
|
|
|
REM and nothing is redrawn every frame.
|
|
|
|
|
WINDOW 0, 35, 49, 36
|
|
|
|
|
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
GOSUB LOADTABLES
|
|
|
|
|
GOSUB LOADFONT
|
|
|
|
|
SEED# = MOD(1 + TI# * 7919, 2147483648)
|
|
|
|
|
RNMAX# = 97
|
|
|
|
|
GOSUB NEXTRAND
|
|
|
|
|
|
|
|
|
|
SPRSAV "art/paddleBlu.png", 3
|
|
|
|
|
SPRSAV "art/paddleRed.png", 4
|
|
|
|
|
SPRSAV "art/ballBlue.png", 5
|
|
|
|
|
SPRSAV "art/ballGrey.png", 6
|
|
|
|
|
SPRSAV "art/ballGrey.png", 7
|
|
|
|
|
SPRSAV "art/element_red_polygon_glossy.png", 8
|
|
|
|
|
SPRITE 3, 0, 2
|
|
|
|
|
SPRITE 4, 0, 2
|
|
|
|
|
SPRITE 5, 0, 2
|
|
|
|
|
SPRITE 6, 0, 2
|
|
|
|
|
SPRITE 7, 0, 2
|
|
|
|
|
SPRITE 8, 0, 2
|
|
|
|
|
|
|
|
|
|
REM SOUND's frequency argument is a SID register value rather than hertz --
|
|
|
|
|
REM register * 1022730 / 16777216 is the pitch -- so the numbers below are
|
|
|
|
|
REM notes worked out once and written down: 17175 is C6, 12861 G5, 8579 C5,
|
|
|
|
|
REM 6431 G4, 4298 C4, 3609 A3, 2411 D3, 1804 A2.
|
|
|
|
|
REM
|
|
|
|
|
REM Three voices. Voice 1 is the ball hitting things, voice 2 is the gem
|
|
|
|
|
REM and the ball being lost, voice 3 is PLAY's fanfares -- kept apart so a
|
|
|
|
|
REM brick going does not cut a tune off mid-note.
|
|
|
|
|
VOL 8
|
|
|
|
|
ENVELOPE 0, 0, 6, 0, 4
|
|
|
|
|
TEMPO 12
|
|
|
|
|
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
COLLISION 2, BRICKHIT
|
|
|
|
|
REM The stamps, once. They used to be rebuilt whenever the SSHAPE pool ran dry,
|
|
|
|
|
REM because every frame's capture spent another slot; nothing captures now, so
|
|
|
|
|
REM eight slots are spent here and never again.
|
|
|
|
|
GOSUB DRAWPROTOS
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
GOSUB TITLESCREEN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM The frame loop
|
|
|
|
|
REM
|
2026-08-02 13:13:51 -04:00
|
|
|
REM At most one drawing job, then the game, which may take as long as it
|
|
|
|
|
REM likes. Nothing here has a deadline: a drawing that spans two host
|
|
|
|
|
REM batches simply arrives over two frames and the layer keeps both halves.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL FRAME
|
|
|
|
|
GOSUB DRAWJOB
|
|
|
|
|
GOSUB READKEYS
|
|
|
|
|
IF STATE# = 0 THEN GOSUB TITLETICK
|
|
|
|
|
IF STATE# = 1 THEN GOSUB SERVETICK
|
|
|
|
|
IF STATE# = 2 THEN GOSUB PLAYTICK
|
|
|
|
|
IF STATE# = 3 THEN GOSUB LOSTTICK
|
|
|
|
|
IF STATE# = 4 THEN GOSUB CLEARTICK
|
|
|
|
|
IF RUNNING# = 0 THEN GOTO SHUTDOWN
|
|
|
|
|
GOTO FRAME
|
|
|
|
|
|
|
|
|
|
LABEL SHUTDOWN
|
|
|
|
|
FOR I# = 1 TO 8
|
|
|
|
|
SPRITE I#, 0
|
|
|
|
|
NEXT I#
|
|
|
|
|
IF SCORE# > HISCORE# THEN HISCORE# = SCORE#
|
|
|
|
|
PRINT "FINAL SCORE " + SCORE# + " BEST " + HISCORE#
|
|
|
|
|
END
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
2026-08-02 13:13:51 -04:00
|
|
|
REM One drawing job per frame, and only one
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM
|
2026-08-02 13:13:51 -04:00
|
|
|
REM The field is the expensive one -- sixty stamps and a border -- so it
|
|
|
|
|
REM never shares a frame with the HUD. Both are flagged rather than drawn
|
|
|
|
|
REM where the change happens, so a routine that changes three things costs
|
|
|
|
|
REM one drawing.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM =====================================================================
|
Stop the listings and their chapters stating fixed defects as fact
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>
2026-08-02 06:32:48 -04:00
|
|
|
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.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
LABEL DRAWJOB
|
|
|
|
|
LABEL DRAWJOB2
|
|
|
|
|
IF DPLAY# = 0 THEN GOTO DRAWJOB3
|
|
|
|
|
GOSUB DRAWPLAY
|
|
|
|
|
DPLAY# = 0
|
|
|
|
|
RETURN
|
|
|
|
|
LABEL DRAWJOB3
|
|
|
|
|
IF DHUD# = 0 THEN RETURN
|
|
|
|
|
GOSUB DRAWHUD
|
|
|
|
|
DHUD# = 0
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------------
|
|
|
|
|
REM Six brick stamps, one per row colour. Filled two rules at a time so
|
|
|
|
|
REM the set costs about a hundred and thirty lines rather than the two
|
|
|
|
|
REM hundred and seventy a rule-at-a-time loop would take. PAINT would be
|
|
|
|
|
REM one statement instead of sixteen and is not an option: it costs nearly
|
|
|
|
|
REM four milliseconds a call, which is an eighth of a frame.
|
|
|
|
|
LABEL DRAWPROTOS
|
|
|
|
|
GRAPHIC 1, 1
|
|
|
|
|
WIDTH 1
|
|
|
|
|
FOR R# = 0 TO 5
|
|
|
|
|
COLOR 1, BRC#(R#)
|
|
|
|
|
T1# = R# * 20
|
|
|
|
|
T2# = T1# + 8
|
|
|
|
|
FOR K# = 0 TO 7
|
|
|
|
|
DRAW 1, 0, T1# + K# TO 67, T1# + K# : DRAW 1, 0, T2# + K# TO 67, T2# + K#
|
|
|
|
|
NEXT K#
|
|
|
|
|
NEXT R#
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM A seventh stamp, in the background colour, is how a broken brick is taken
|
|
|
|
|
REM off the screen. There is no filled-rectangle verb and PAINT costs four
|
|
|
|
|
REM milliseconds a call, so erasing is a GSHAPE of something blank -- one line
|
|
|
|
|
REM instead of sixteen, and it reuses machinery that is already here.
|
|
|
|
|
COLOR 1, 1
|
|
|
|
|
FOR K# = 0 TO 15
|
|
|
|
|
DRAW 1, 0, 130 + K# TO 67, 130 + K#
|
|
|
|
|
NEXT K#
|
|
|
|
|
REM And an eighth the width of the HUD strip, for the same reason: the strip is
|
|
|
|
|
REM rewritten whenever a number in it changes, and the old digits have to go
|
|
|
|
|
REM somewhere first.
|
|
|
|
|
FOR K# = 0 TO 59
|
|
|
|
|
DRAW 1, 0, 160 + K# TO 799, 160 + K#
|
|
|
|
|
NEXT K#
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
SSHAPE Z$, 0, 0, 68, 16 : S0$ = Z$
|
|
|
|
|
SSHAPE Z$, 0, 20, 68, 36 : S1$ = Z$
|
|
|
|
|
SSHAPE Z$, 0, 40, 68, 56 : S2$ = Z$
|
|
|
|
|
SSHAPE Z$, 0, 60, 68, 76 : S3$ = Z$
|
|
|
|
|
SSHAPE Z$, 0, 80, 68, 96 : S4$ = Z$
|
|
|
|
|
SSHAPE Z$, 0, 100, 68, 116 : S5$ = Z$
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
SSHAPE Z$, 0, 130, 68, 146 : BL$ = Z$
|
|
|
|
|
SSHAPE Z$, 0, 160, 800, 220 : HBL$ = Z$
|
|
|
|
|
GRAPHIC 1, 1
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
DPLAY# = 1
|
|
|
|
|
DHUD# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM Take one brick off the screen: stamp the blank over it. Called when a brick
|
|
|
|
|
REM breaks, so the field is never redrawn as a whole during play -- which is
|
|
|
|
|
REM what lets the whole live-list machinery go.
|
|
|
|
|
LABEL ERASEBRICK
|
|
|
|
|
Z$ = BL$
|
|
|
|
|
GSHAPE Z$, BRKX# + C# * 72, BRKY# + R# * 24
|
|
|
|
|
RETURN
|
|
|
|
|
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
REM ---------------------------------------------------------------------
|
|
|
|
|
REM The field: walls, whatever bricks are still standing, and whatever
|
|
|
|
|
REM lettering belongs on the field. Nothing here computes anything it
|
|
|
|
|
REM could have computed on an earlier frame.
|
|
|
|
|
LABEL DRAWPLAY
|
|
|
|
|
GRAPHIC 1, 1
|
|
|
|
|
WIDTH 2
|
|
|
|
|
COLOR 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
|
|
|
|
|
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
|
|
|
|
|
BOX 5, 2, 62, 797, 597 : BOX 1, 6, 66, 793, 593
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM Every brick still standing, stamped where it belongs. This runs when a
|
|
|
|
|
REM level is laid out and not again -- a broken brick is erased in place by
|
|
|
|
|
REM ERASEBRICK, so there is no reason to redraw sixty of them to remove one.
|
|
|
|
|
REM That is what took away the flattened live list, its four arrays, and the
|
|
|
|
|
REM run-per-row loop that walked it.
|
|
|
|
|
FOR R# = 0 TO 5
|
|
|
|
|
IF R# = 0 THEN Z$ = S0$
|
|
|
|
|
IF R# = 1 THEN Z$ = S1$
|
|
|
|
|
IF R# = 2 THEN Z$ = S2$
|
|
|
|
|
IF R# = 3 THEN Z$ = S3$
|
|
|
|
|
IF R# = 4 THEN Z$ = S4$
|
|
|
|
|
IF R# = 5 THEN Z$ = S5$
|
|
|
|
|
FOR C# = 0 TO 9
|
|
|
|
|
IF BRK#(R# * 10 + C#) > 0 THEN GSHAPE Z$, BRKX# + C# * 72, BRKY# + R# * 24
|
|
|
|
|
NEXT C#
|
|
|
|
|
NEXT R#
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
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
|
|
|
|
|
DRAW PC#(I#), PX1#(I#), PY1#(I#) TO PX2#(I#), PY2#(I#)
|
|
|
|
|
NEXT I#
|
|
|
|
|
LABEL PLDONE
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------------
|
|
|
|
|
REM The HUD strip. Same shape as DRAWPLAY: one pass over a stroke list
|
|
|
|
|
REM that was built on some earlier frame, when there was time.
|
|
|
|
|
LABEL DRAWHUD
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
GRAPHIC 1, 0
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
WIDTH 1
|
|
|
|
|
COLOR 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
|
|
|
|
|
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM The old strip goes first. Nothing here clears the screen -- a drawing
|
|
|
|
|
REM stays, which is the whole point -- so the digits that were there have to be
|
|
|
|
|
REM stamped over before the new ones are drawn.
|
|
|
|
|
Z$ = HBL$
|
|
|
|
|
GSHAPE Z$, 0, 0
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
BOX 5, 0, 56, 799, 57
|
|
|
|
|
IF HN# = 1 THEN DRAW HC#(0), HX1#(0), HY1#(0) TO HX2#(0), HY2#(0)
|
|
|
|
|
IF HN# < 2 THEN GOTO HUDONE
|
|
|
|
|
FOR I# = 0 TO HN# - 1
|
|
|
|
|
DRAW HC#(I#), HX1#(I#), HY1#(I#) TO HX2#(I#), HY2#(I#)
|
|
|
|
|
NEXT I#
|
|
|
|
|
LABEL HUDONE
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Input
|
|
|
|
|
REM
|
|
|
|
|
REM GET takes one keystroke per call from a ring the host fills, so the
|
|
|
|
|
REM loop empties it every frame. A held arrow arrives as about twenty six
|
|
|
|
|
REM repeats a second, which is nearly one a frame -- but the first repeat
|
|
|
|
|
REM is a quarter of a second behind the press, and a paddle that stalled
|
|
|
|
|
REM for a quarter of a second would be unusable. So a press buys seven
|
|
|
|
|
REM frames of travel (PDCO#) and a held key keeps renewing it.
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL READKEYS
|
|
|
|
|
GET KEYV#
|
|
|
|
|
IF KEYV# = 0 THEN RETURN
|
|
|
|
|
IF KEYV# = 1073741904 THEN PDDIR# = 0 - 1 : PDCO# = 7
|
|
|
|
|
IF KEYV# = 1073741903 THEN PDDIR# = 1 : PDCO# = 7
|
|
|
|
|
IF KEYV# = 32 THEN GOSUB PRESSFIRE
|
|
|
|
|
IF KEYV# = 112 THEN GOSUB PRESSPAUSE
|
|
|
|
|
IF KEYV# = 115 THEN GOSUB PRESSMUTE
|
|
|
|
|
IF KEYV# = 113 THEN RUNNING# = 0
|
|
|
|
|
IF KEYV# = 27 THEN RUNNING# = 0
|
|
|
|
|
GOTO READKEYS
|
|
|
|
|
|
|
|
|
|
LABEL PRESSFIRE
|
|
|
|
|
IF STATE# = 0 THEN GOSUB STARTGAME : RETURN
|
|
|
|
|
IF STATE# = 5 THEN GOSUB TITLESCREEN : RETURN
|
|
|
|
|
IF STATE# = 1 THEN GOSUB LAUNCH : RETURN
|
|
|
|
|
IF STATE# = 2 THEN GOSUB UNSTICK
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM VOL rather than a test around every SOUND: one line here beats eleven
|
|
|
|
|
REM scattered through the game, and a silenced voice costs nothing to issue.
|
|
|
|
|
LABEL PRESSMUTE
|
|
|
|
|
SNDON# = 1 - SNDON#
|
|
|
|
|
IF SNDON# = 1 THEN VOL 8
|
|
|
|
|
IF SNDON# = 0 THEN VOL 0
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL PRESSPAUSE
|
|
|
|
|
IF STATE# = 2 THEN STATE# = 6 : GMTYP# = 0 : BAN$ = "PAUSED" : GOSUB SETBANNER : RETURN
|
|
|
|
|
IF STATE# = 6 THEN STATE# = 2 : BAN$ = "" : GOSUB SETBANNER
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM The states
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL TITLETICK
|
|
|
|
|
STIMER# = STIMER# + 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL SERVETICK
|
|
|
|
|
GOSUB MOVEPADDLE
|
|
|
|
|
BLX%(0) = PDX% + BLOFF#
|
|
|
|
|
BLY%(0) = PDY# - 23
|
|
|
|
|
MOVSPR 5, BLX%(0), BLY%(0)
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL PLAYTICK
|
|
|
|
|
GOSUB MOVEPADDLE
|
|
|
|
|
GOSUB MOVEBALLS
|
|
|
|
|
GOSUB MOVEGEM
|
|
|
|
|
GOSUB TIMERS
|
|
|
|
|
IF BRN# = 0 THEN GOSUB LEVELDONE
|
|
|
|
|
IF BLN# = 0 AND STATE# = 2 THEN GOSUB BALLGONE
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL LOSTTICK
|
|
|
|
|
STIMER# = STIMER# - 1
|
|
|
|
|
IF STIMER# > 0 THEN RETURN
|
|
|
|
|
IF LIVES# > 0 THEN GOSUB SERVEAGAIN
|
|
|
|
|
IF LIVES# < 1 THEN GOSUB GAMEISOVER
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL CLEARTICK
|
|
|
|
|
STIMER# = STIMER# - 1
|
|
|
|
|
IF STIMER# > 0 THEN RETURN
|
|
|
|
|
LEVEL# = LEVEL# + 1
|
|
|
|
|
GOSUB SETUPLEVEL
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Paddle
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL MOVEPADDLE
|
|
|
|
|
IF PDCO# > 0 THEN BEGIN
|
|
|
|
|
PDCO# = PDCO# - 1
|
|
|
|
|
PDX% = PDX% + PDDIR# * 9
|
|
|
|
|
BEND
|
|
|
|
|
IF PDCO# < 1 THEN PDDIR# = 0
|
|
|
|
|
IF PDX% < WALLL# THEN PDX% = WALLL#
|
|
|
|
|
IF PDX% > WALLR# - PDW# THEN PDX% = WALLR# - PDW#
|
|
|
|
|
MOVSPR 3, PDX%, PDY#
|
|
|
|
|
IF CTON# = 0 THEN RETURN
|
|
|
|
|
REM The catcher mirrors the player, so it guards the side the paddle left.
|
|
|
|
|
CTX% = 0.0 - PDX% + WALLL# + WALLR# - 104
|
|
|
|
|
MOVSPR 4, CTX%, 470
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Balls
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL MOVEBALLS
|
|
|
|
|
FOR B# = 0 TO 2
|
|
|
|
|
IF BLON#(B#) = 1 THEN GOSUB MOVEONE
|
|
|
|
|
NEXT B#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL MOVEONE
|
|
|
|
|
IF BLST#(B#) = 1 THEN BEGIN
|
|
|
|
|
BLX%(B#) = PDX% + BLOFF#
|
|
|
|
|
BLY%(B#) = PDY# - 23
|
|
|
|
|
BEND
|
|
|
|
|
IF BLST#(B#) = 0 THEN BEGIN
|
|
|
|
|
BLX%(B#) = BLX%(B#) + BLVX%(B#)
|
|
|
|
|
BLY%(B#) = BLY%(B#) + BLVY%(B#)
|
|
|
|
|
GOSUB BALLWALLS
|
|
|
|
|
BEND
|
|
|
|
|
IF BLON#(B#) = 0 THEN RETURN
|
|
|
|
|
IF BLST#(B#) = 0 THEN BEGIN
|
|
|
|
|
GOSUB BALLPADDLE
|
|
|
|
|
BEND
|
|
|
|
|
MOVSPR 5 + B#, BLX%(B#), BLY%(B#)
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL BALLWALLS
|
|
|
|
|
IF BLX%(B#) < WALLL# THEN BEGIN
|
|
|
|
|
BLX%(B#) = WALLL#
|
|
|
|
|
BLVX%(B#) = 0.0 - BLVX%(B#)
|
|
|
|
|
SOUND 1, 3609, 2
|
|
|
|
|
BEND
|
|
|
|
|
IF BLX%(B#) > WALLR# - 22 THEN BEGIN
|
|
|
|
|
BLX%(B#) = WALLR# - 22
|
|
|
|
|
BLVX%(B#) = 0.0 - BLVX%(B#)
|
|
|
|
|
SOUND 1, 3609, 2
|
|
|
|
|
BEND
|
|
|
|
|
IF BLY%(B#) < 72 THEN BEGIN
|
|
|
|
|
BLY%(B#) = 72
|
|
|
|
|
BLVY%(B#) = 0.0 - BLVY%(B#)
|
|
|
|
|
SOUND 1, 3609, 2
|
|
|
|
|
BEND
|
|
|
|
|
IF BLY%(B#) < 596 THEN RETURN
|
|
|
|
|
BLON#(B#) = 0
|
|
|
|
|
BLN# = BLN# - 1
|
|
|
|
|
SOUND 2, 2411, 6
|
|
|
|
|
SPRITE 5 + B#, 0
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------------
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM Brick collision, all of it.
|
|
|
|
|
REM
|
|
|
|
|
REM COLLISION 2 fires between source lines when a ball overlaps one of the
|
|
|
|
|
REM rectangles SOLID registered, and RCOLLISION says which and which way out.
|
|
|
|
|
REM That replaced a bounding-box narrowing that turned the ball's box into a
|
|
|
|
|
REM range of candidate cells, a nested DO walking them, and a cell test that
|
|
|
|
|
REM computed an overlap rectangle and compared its width to its height to pick
|
|
|
|
|
REM an axis -- about sixty lines.
|
|
|
|
|
REM
|
|
|
|
|
REM The handler cannot know which ball hit, so it asks each live one. Three is
|
|
|
|
|
REM not a loop worth writing.
|
|
|
|
|
REM The mask is by *sprite*, and the balls are sprites 5, 6 and 7 -- so their
|
|
|
|
|
REM bits are 16, 32 and 64, not 1, 2 and 4. Ball B# is sprite 5 + B#, which is
|
|
|
|
|
REM the same arithmetic MOVSPR and RCOLLISION use for them everywhere else.
|
|
|
|
|
LABEL BRICKHIT
|
|
|
|
|
MB# = BUMP(2)
|
|
|
|
|
IF MB# = 0 THEN RETURN
|
|
|
|
|
B# = 0
|
|
|
|
|
IF (MB# AND 16) <> 0 THEN GOSUB ONEBRICK
|
|
|
|
|
B# = 1
|
|
|
|
|
IF (MB# AND 32) <> 0 THEN GOSUB ONEBRICK
|
|
|
|
|
B# = 2
|
|
|
|
|
IF (MB# AND 64) <> 0 THEN GOSUB ONEBRICK
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
RETURN
|
|
|
|
|
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM One ball against whatever it hit. B# is which ball; sprite 5 + B# is its
|
|
|
|
|
REM slot, and RCOLLISION is keyed on the sprite number.
|
|
|
|
|
LABEL ONEBRICK
|
|
|
|
|
IF BLON#(B#) = 0 THEN RETURN
|
|
|
|
|
N# = RCOLLISION(5 + B#, 1)
|
|
|
|
|
IF N# < 1 THEN RETURN
|
|
|
|
|
N# = N# - 1
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
IF BRK#(N#) = 0 THEN RETURN
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
DP% = RCOLLISION(5 + B#, 4)
|
|
|
|
|
BLX%(B#) = BLX%(B#) + (RCOLLISION(5 + B#, 2) * DP%)
|
|
|
|
|
BLY%(B#) = BLY%(B#) + (RCOLLISION(5 + B#, 3) * DP%)
|
|
|
|
|
AX# = RCOLLISION(5 + B#, 7)
|
|
|
|
|
IF AX# = 1 THEN BLVX%(B#) = 0.0 - BLVX%(B#)
|
|
|
|
|
IF AX# = 2 THEN BLVY%(B#) = 0.0 - BLVY%(B#)
|
|
|
|
|
R# = N# / 10
|
|
|
|
|
C# = MOD(N#, 10)
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
BRK#(N#) = 0
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
SOLID N# + 1
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
BRN# = BRN# - 1
|
|
|
|
|
SCORE# = SCORE# + BRV#(R#)
|
|
|
|
|
REM The top row rings at C6 and each row down drops about a third, so the
|
|
|
|
|
REM wall plays itself down a scale as it comes apart.
|
|
|
|
|
SOUND 1, 17175 - R# * 2100, 4
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
BX1# = BRKX# + C# * 72
|
|
|
|
|
BY1# = BRKY# + R# * 24
|
|
|
|
|
GOSUB ERASEBRICK
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
GOSUB HUDTEXT
|
|
|
|
|
IF GMON# = 1 THEN RETURN
|
|
|
|
|
RNMAX# = 7
|
|
|
|
|
GOSUB NEXTRAND
|
|
|
|
|
IF RNVAL# = 0 THEN GOSUB SPAWNGEM
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------------
|
|
|
|
|
REM The bars. T1# is the bar's left edge, T2# its top, T3# its width.
|
|
|
|
|
LABEL BALLPADDLE
|
|
|
|
|
IF BLVY%(B#) < 0 THEN RETURN
|
|
|
|
|
HIT# = 0
|
|
|
|
|
T1# = PDX% : T2# = PDY# : T3# = PDW#
|
|
|
|
|
GOSUB HITBAR
|
|
|
|
|
IF CTON# = 0 THEN RETURN
|
|
|
|
|
IF HIT# = 1 THEN RETURN
|
|
|
|
|
T1# = CTX% : T2# = 470 : T3# = 104
|
|
|
|
|
GOSUB HITBAR
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL HITBAR
|
|
|
|
|
IF BLY%(B#) + 21 < T2# THEN RETURN
|
|
|
|
|
IF BLY%(B#) > T2# + 23 THEN RETURN
|
|
|
|
|
IF BLX%(B#) + 21 < T1# THEN RETURN
|
|
|
|
|
IF BLX%(B#) > T1# + T3# THEN RETURN
|
|
|
|
|
T4# = (BLX%(B#) + 11 - T1#) * 8 / T3#
|
|
|
|
|
IF T4# < 0 THEN T4# = 0
|
|
|
|
|
IF T4# > 7 THEN T4# = 7
|
|
|
|
|
BLVX%(B#) = ZVX%(T4#) * SPD%
|
|
|
|
|
BLVY%(B#) = ZVY%(T4#) * SPD%
|
|
|
|
|
BLY%(B#) = T2# - 23
|
|
|
|
|
SOUND 1, 4298, 5
|
|
|
|
|
HIT# = 1
|
|
|
|
|
IF STKON# = 0 THEN RETURN
|
|
|
|
|
BLST#(B#) = 1
|
|
|
|
|
BLOFF# = BLX%(B#) - PDX%
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL UNSTICK
|
|
|
|
|
FOR B# = 0 TO 2
|
|
|
|
|
IF BLST#(B#) = 1 THEN BEGIN
|
|
|
|
|
BLST#(B#) = 0
|
|
|
|
|
BLVX%(B#) = ZVX%(4) * SPD%
|
|
|
|
|
BLVY%(B#) = ZVY%(4) * SPD%
|
|
|
|
|
BEND
|
|
|
|
|
NEXT B#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM The gem, and what catching one does
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL SPAWNGEM
|
|
|
|
|
RNMAX# = 5
|
|
|
|
|
GOSUB NEXTRAND
|
|
|
|
|
GMTYP# = RNVAL# + 1
|
|
|
|
|
GMX% = BX1# + 10
|
|
|
|
|
GMY% = BY1#
|
|
|
|
|
IF GMTYP# = 1 THEN SPRSAV "art/element_red_polygon_glossy.png", 8
|
|
|
|
|
IF GMTYP# = 2 THEN SPRSAV "art/element_yellow_polygon_glossy.png", 8
|
|
|
|
|
IF GMTYP# = 3 THEN SPRSAV "art/element_green_polygon_glossy.png", 8
|
|
|
|
|
IF GMTYP# = 4 THEN SPRSAV "art/element_blue_polygon_glossy.png", 8
|
|
|
|
|
IF GMTYP# = 5 THEN SPRSAV "art/element_purple_polygon_glossy.png", 8
|
|
|
|
|
GMON# = 1
|
|
|
|
|
SOUND 2, 10810, 3
|
|
|
|
|
SPRITE 8, 1, 2
|
|
|
|
|
MOVSPR 8, GMX%, GMY%
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL MOVEGEM
|
|
|
|
|
IF GMON# = 0 THEN RETURN
|
|
|
|
|
GMY% = GMY% + 3.2
|
|
|
|
|
MOVSPR 8, GMX%, GMY%
|
|
|
|
|
HIT# = 0
|
|
|
|
|
IF GMY% > 596 THEN HIT# = 1
|
|
|
|
|
IF GMY% + 46 > PDY# AND GMY% < PDY# + 24 THEN GOSUB CATCHGEM
|
|
|
|
|
IF HIT# = 0 THEN RETURN
|
|
|
|
|
GMON# = 0
|
|
|
|
|
SPRITE 8, 0
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL CATCHGEM
|
|
|
|
|
IF GMX% + 48 < PDX% THEN RETURN
|
|
|
|
|
IF GMX% > PDX% + PDW# THEN RETURN
|
|
|
|
|
GOSUB TAKEGEM
|
|
|
|
|
HIT# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM The x-expand bit is the only scaling a sprite has, and doubling the
|
|
|
|
|
REM artwork is exactly what EXPAND wants.
|
|
|
|
|
LABEL TAKEGEM
|
|
|
|
|
SOUND 2, 8579, 14, 1, 17175, 400
|
|
|
|
|
SCORE# = SCORE# + 50
|
|
|
|
|
GOSUB HUDTEXT
|
|
|
|
|
IF GMTYP# = 1 THEN BEGIN
|
|
|
|
|
PDW# = 208
|
|
|
|
|
PDEXP# = 600
|
|
|
|
|
SPRITE 3, 1, 2, 0, 1, 0
|
|
|
|
|
BAN$ = "EXPAND"
|
|
|
|
|
BEND
|
|
|
|
|
IF GMTYP# = 2 THEN BEGIN
|
|
|
|
|
BAN$ = "MULTI"
|
|
|
|
|
GOSUB SPLITBALLS
|
|
|
|
|
BEND
|
|
|
|
|
IF GMTYP# = 3 THEN BEGIN
|
|
|
|
|
SLOWT# = 480
|
|
|
|
|
SPD% = 4.2
|
|
|
|
|
GOSUB RESCALE
|
|
|
|
|
BAN$ = "SLOW"
|
|
|
|
|
BEND
|
|
|
|
|
IF GMTYP# = 4 THEN BEGIN
|
|
|
|
|
STKON# = 1
|
|
|
|
|
STKT# = 540
|
|
|
|
|
BAN$ = "STICKY"
|
|
|
|
|
BEND
|
|
|
|
|
IF GMTYP# = 5 THEN BEGIN
|
|
|
|
|
CTON# = 1
|
|
|
|
|
CTTM# = 720
|
|
|
|
|
SPRITE 4, 1, 2
|
|
|
|
|
BAN$ = "CATCH"
|
|
|
|
|
BEND
|
|
|
|
|
BANT# = 90
|
|
|
|
|
GOSUB SETBANNER
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM Two more balls, thrown off the first one at slightly different angles
|
|
|
|
|
REM so they do not travel as a stack.
|
|
|
|
|
LABEL SPLITBALLS
|
|
|
|
|
IF BLN# > 2 THEN RETURN
|
|
|
|
|
IF BLON#(0) = 0 THEN RETURN
|
|
|
|
|
FOR B# = 1 TO 2
|
|
|
|
|
IF BLON#(B#) = 0 THEN BEGIN
|
|
|
|
|
BLON#(B#) = 1
|
|
|
|
|
BLST#(B#) = 0
|
|
|
|
|
BLX%(B#) = BLX%(0)
|
|
|
|
|
BLY%(B#) = BLY%(0)
|
|
|
|
|
BLVX%(B#) = ZVX%(B# * 5 - 4) * SPD%
|
|
|
|
|
BLVY%(B#) = ZVY%(B# * 5 - 4) * SPD%
|
|
|
|
|
BLN# = BLN# + 1
|
|
|
|
|
SPRITE 5 + B#, 1, 2
|
|
|
|
|
BEND
|
|
|
|
|
NEXT B#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM Bring every ball in play to the current speed without changing where it
|
|
|
|
|
REM is heading.
|
|
|
|
|
REM
|
|
|
|
|
REM Every velocity this program assigns is one of the eight unit vectors in
|
|
|
|
|
REM ZVX%/ZVY# times SPD%, and a bounce only ever flips a sign -- so a ball
|
|
|
|
|
REM is travelling at exactly the SPD% that was in force when it last left a
|
|
|
|
|
REM bar. BSPD% remembers what that was, which makes the rescale a ratio of
|
|
|
|
|
REM two speeds rather than a change of magnitude, and no square root is
|
|
|
|
|
REM needed. There isn't one in this dialect.
|
|
|
|
|
REM
|
|
|
|
|
REM Scaling by the vertical component instead, which is what this routine
|
|
|
|
|
REM did first, is not a slowdown at all: a shallow ball's small vertical
|
|
|
|
|
REM gets stretched up to the new speed and drags the large horizontal with
|
|
|
|
|
REM it. SLOW made the ball faster. Sixty degrees of the eight zones are
|
|
|
|
|
REM shallow enough to do it.
|
|
|
|
|
LABEL RESCALE
|
|
|
|
|
IF BSPD% < 0.1 THEN BSPD% = SPD%
|
|
|
|
|
RAT% = SPD% / BSPD%
|
|
|
|
|
BSPD% = SPD%
|
|
|
|
|
FOR B# = 0 TO 2
|
|
|
|
|
IF BLON#(B#) = 1 THEN BEGIN
|
|
|
|
|
BLVX%(B#) = BLVX%(B#) * RAT%
|
|
|
|
|
BLVY%(B#) = BLVY%(B#) * RAT%
|
|
|
|
|
BEND
|
|
|
|
|
NEXT B#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Timers
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL TIMERS
|
|
|
|
|
IF BANT# > 0 THEN BEGIN
|
|
|
|
|
BANT# = BANT# - 1
|
|
|
|
|
IF BANT# = 0 THEN BEGIN
|
|
|
|
|
BAN$ = ""
|
|
|
|
|
GOSUB SETBANNER
|
|
|
|
|
BEND
|
|
|
|
|
BEND
|
|
|
|
|
IF PDEXP# > 0 THEN BEGIN
|
|
|
|
|
PDEXP# = PDEXP# - 1
|
|
|
|
|
IF PDEXP# = 0 THEN BEGIN
|
|
|
|
|
PDW# = 104
|
|
|
|
|
SPRITE 3, 1, 2, 0, 0, 0
|
|
|
|
|
BEND
|
|
|
|
|
BEND
|
|
|
|
|
IF SLOWT# > 0 THEN BEGIN
|
|
|
|
|
SLOWT# = SLOWT# - 1
|
|
|
|
|
IF SLOWT# = 0 THEN BEGIN
|
|
|
|
|
GOSUB LEVELSPEED
|
|
|
|
|
GOSUB RESCALE
|
|
|
|
|
BEND
|
|
|
|
|
BEND
|
|
|
|
|
IF STKT# > 0 THEN BEGIN
|
|
|
|
|
STKT# = STKT# - 1
|
|
|
|
|
IF STKT# = 0 THEN BEGIN
|
|
|
|
|
STKON# = 0
|
|
|
|
|
GOSUB UNSTICK
|
|
|
|
|
BEND
|
|
|
|
|
BEND
|
|
|
|
|
IF CTTM# > 0 THEN BEGIN
|
|
|
|
|
CTTM# = CTTM# - 1
|
|
|
|
|
IF CTTM# = 0 THEN BEGIN
|
|
|
|
|
CTON# = 0
|
|
|
|
|
SPRITE 4, 0
|
|
|
|
|
BEND
|
|
|
|
|
BEND
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Losing a ball, clearing a level, starting and ending a game
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL BALLGONE
|
|
|
|
|
SOUND 2, 7218, 30, 2, 1804, 220
|
|
|
|
|
LIVES# = LIVES# - 1
|
|
|
|
|
STATE# = 3
|
|
|
|
|
STIMER# = 45
|
|
|
|
|
GMTYP# = 0
|
|
|
|
|
BAN$ = "MISS"
|
|
|
|
|
GOSUB SETBANNER
|
|
|
|
|
GOSUB HUDTEXT
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL SERVEAGAIN
|
|
|
|
|
BAN$ = ""
|
|
|
|
|
GOSUB SETBANNER
|
|
|
|
|
GOSUB CLEARPOWERS
|
|
|
|
|
GOSUB RESETBALL
|
|
|
|
|
STATE# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL GAMEISOVER
|
|
|
|
|
PLAY "V3 T1 U8 O4 QG QE QC O3 HG"
|
|
|
|
|
IF SCORE# > HISCORE# THEN HISCORE# = SCORE#
|
|
|
|
|
STATE# = 5
|
|
|
|
|
GOSUB CLEARPOWERS
|
|
|
|
|
BRN# = 0
|
|
|
|
|
FOR I# = 0 TO 59
|
|
|
|
|
BRK#(I#) = 0
|
|
|
|
|
NEXT I#
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
SOLID
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
SPRITE 3, 0
|
|
|
|
|
SPRITE 5, 0
|
|
|
|
|
PN# = 0
|
|
|
|
|
TXS# = 9 : TXC# = 4 : TXX# = 148 : TXY# = 230
|
|
|
|
|
TX$ = "GAME OVER"
|
|
|
|
|
BUILDH# = 0
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXS# = 5 : TXC# = 2 : TXX# = 200 : TXY# = 380
|
|
|
|
|
T4# = SCORE#
|
|
|
|
|
GOSUB FMTNUM
|
|
|
|
|
TX$ = "SCORE " + NUM$
|
|
|
|
|
BUILDH# = 0
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
HN# = 0
|
|
|
|
|
TXS# = 3 : TXC# = 5 : TXY# = 20 : TXX# = 40
|
|
|
|
|
T4# = HISCORE#
|
|
|
|
|
GOSUB FMTNUM
|
|
|
|
|
TX$ = "BEST " + NUM$
|
|
|
|
|
BUILDH# = 1
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXC# = 6 : TXX# = 400
|
|
|
|
|
TX$ = "SPACE FOR TITLE"
|
|
|
|
|
BUILDH# = 1
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
DPLAY# = 1
|
|
|
|
|
DHUD# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL LEVELDONE
|
|
|
|
|
PLAY "V3 T2 U8 O4 QC QE QG O5 HC"
|
|
|
|
|
STATE# = 4
|
|
|
|
|
STIMER# = 60
|
|
|
|
|
GMTYP# = 0
|
|
|
|
|
BAN$ = "CLEAR"
|
|
|
|
|
GOSUB SETBANNER
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL TITLESCREEN
|
|
|
|
|
STATE# = 0
|
|
|
|
|
STIMER# = 0
|
|
|
|
|
GOSUB CLEARPOWERS
|
|
|
|
|
BLN# = 0
|
|
|
|
|
BRN# = 0
|
|
|
|
|
FOR I# = 0 TO 59
|
|
|
|
|
BRK#(I#) = 0
|
|
|
|
|
NEXT I#
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
SOLID
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
SPRITE 3, 0
|
|
|
|
|
SPRITE 5, 0
|
|
|
|
|
SPRITE 6, 0
|
|
|
|
|
SPRITE 7, 0
|
|
|
|
|
PN# = 0
|
|
|
|
|
TXS# = 12 : TXC# = 2 : TXX# = 120 : TXY# = 170
|
|
|
|
|
TX$ = "BREAKOUT"
|
|
|
|
|
BUILDH# = 0
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXS# = 4 : TXC# = 1 : TXX# = 220 : TXY# = 370
|
|
|
|
|
TX$ = "SPACE TO START"
|
|
|
|
|
BUILDH# = 0
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
HN# = 0
|
|
|
|
|
TXS# = 3 : TXC# = 6 : TXX# = 40 : TXY# = 20
|
|
|
|
|
TX$ = "ARROWS MOVE P PAUSE Q QUIT"
|
|
|
|
|
BUILDH# = 1
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
DPLAY# = 1
|
|
|
|
|
DHUD# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL STARTGAME
|
|
|
|
|
PLAY "V3 T2 U8 O4 IC IE IG O5 IC"
|
|
|
|
|
SCORE# = 0
|
|
|
|
|
LIVES# = 3
|
|
|
|
|
LEVEL# = 1
|
|
|
|
|
GOSUB SETUPLEVEL
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------------
|
|
|
|
|
REM Lay a level out. Every third one is a row shorter, so the field
|
|
|
|
|
REM changes shape as well as pace.
|
|
|
|
|
LABEL SETUPLEVEL
|
|
|
|
|
GOSUB CLEARPOWERS
|
|
|
|
|
GOSUB LEVELSPEED
|
|
|
|
|
T3# = 6
|
|
|
|
|
IF MOD(LEVEL#, 3) = 2 THEN T3# = 5
|
|
|
|
|
IF MOD(LEVEL#, 3) = 0 THEN T3# = 4
|
|
|
|
|
BRN# = 0
|
|
|
|
|
FOR I# = 0 TO 59
|
|
|
|
|
BRK#(I#) = 0
|
|
|
|
|
NEXT I#
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM Retire the previous level's rectangles *before* laying this one out, not
|
2026-08-02 13:13:51 -04:00
|
|
|
REM after. Put after FILLROW it throws away all sixty rectangles that were
|
|
|
|
|
REM just registered, which costs nothing visible and simply stops the ball
|
|
|
|
|
REM hitting anything.
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
SOLID
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
FOR R# = 0 TO 5
|
|
|
|
|
IF R# < T3# THEN GOSUB FILLROW
|
|
|
|
|
NEXT R#
|
|
|
|
|
GOSUB RESETBALL
|
|
|
|
|
GOSUB HUDTEXT
|
|
|
|
|
GMTYP# = 0
|
|
|
|
|
BAN$ = ""
|
|
|
|
|
GOSUB SETBANNER
|
|
|
|
|
STATE# = 1
|
|
|
|
|
DPLAY# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM Again a subroutine rather than a block, for the same reason.
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
REM One row of bricks: the array, and the rectangle the interpreter collides
|
|
|
|
|
REM against. The id is the array index plus one, so RCOLLISION hands back a
|
|
|
|
|
REM number that indexes BRK#() with no lookup at all.
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
LABEL FILLROW
|
|
|
|
|
FOR C# = 0 TO 9
|
|
|
|
|
BRK#(R# * 10 + C#) = 1
|
|
|
|
|
BRN# = BRN# + 1
|
Stop the artwork breakout drawing its screen into two sprites
It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.
**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.
**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.
**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.
**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.
**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.
Three mistakes worth recording, because each was silent:
- **The bricks were registered and then immediately thrown away.**
`GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
the list of what is standing"; replaced in place by a bare `SOLID`, it meant
"retire everything" and ran after all sixty had been registered. Nothing
failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
`DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
chapter 17 Step 3 is about, found by printing the handle inside the routine and
again outside it.
Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.
Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
2026-08-02 12:58:08 -04:00
|
|
|
SLX# = BRKX# + C# * 72
|
|
|
|
|
SLY# = BRKY# + R# * 24
|
|
|
|
|
SLI# = (R# * 10 + C#) + 1
|
|
|
|
|
SOLID SLI#, SLX#, SLY#, SLX# + 68, SLY# + 16
|
Add two Breakout examples and the tutorials that build them
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
|
|
|
NEXT C#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL LEVELSPEED
|
|
|
|
|
SPD% = 5.6 + 0.45 * LEVEL#
|
|
|
|
|
IF SPD% > 11.0 THEN SPD% = 11.0
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL RESETBALL
|
|
|
|
|
FOR B# = 0 TO 2
|
|
|
|
|
BLON#(B#) = 0
|
|
|
|
|
BLST#(B#) = 0
|
|
|
|
|
NEXT B#
|
|
|
|
|
SPRITE 6, 0
|
|
|
|
|
SPRITE 7, 0
|
|
|
|
|
BLON#(0) = 1
|
|
|
|
|
BLN# = 1
|
|
|
|
|
BSPD% = SPD%
|
|
|
|
|
PDX% = 348
|
|
|
|
|
PDW# = 104
|
|
|
|
|
BLOFF# = 41
|
|
|
|
|
SPRITE 3, 1, 2, 0, 0, 0
|
|
|
|
|
SPRITE 5, 1, 2
|
|
|
|
|
MOVSPR 3, PDX%, PDY#
|
|
|
|
|
BLX%(0) = PDX% + BLOFF#
|
|
|
|
|
BLY%(0) = PDY# - 23
|
|
|
|
|
MOVSPR 5, BLX%(0), BLY%(0)
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL LAUNCH
|
|
|
|
|
SOUND 1, 6431, 3
|
|
|
|
|
STATE# = 2
|
|
|
|
|
BLST#(0) = 0
|
|
|
|
|
BLVX%(0) = ZVX%(5) * SPD%
|
|
|
|
|
BLVY%(0) = ZVY%(5) * SPD%
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL CLEARPOWERS
|
|
|
|
|
GOSUB LEVELSPEED
|
|
|
|
|
PDW# = 104
|
|
|
|
|
PDEXP# = 0
|
|
|
|
|
SLOWT# = 0
|
|
|
|
|
STKON# = 0
|
|
|
|
|
STKT# = 0
|
|
|
|
|
CTON# = 0
|
|
|
|
|
CTTM# = 0
|
|
|
|
|
GMON# = 0
|
|
|
|
|
BANT# = 0
|
|
|
|
|
BLOFF# = 41
|
|
|
|
|
SPRITE 4, 0
|
|
|
|
|
SPRITE 8, 0
|
|
|
|
|
SPRITE 3, 1, 2, 0, 0, 0
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Text
|
|
|
|
|
REM
|
|
|
|
|
REM The HUD line, rebuilt whenever a number in it changes. Four colours:
|
|
|
|
|
REM labels in cyan, the score in yellow, the lives in light green and the
|
|
|
|
|
REM level in light red. Colour is the whole reason the HUD is drawn
|
|
|
|
|
REM rather than printed -- the interpreter's text layer is white, and it
|
|
|
|
|
REM has no verb that changes that.
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL HUDTEXT
|
|
|
|
|
HN# = 0
|
|
|
|
|
BUILDH# = 1
|
|
|
|
|
TXS# = 4
|
|
|
|
|
TXY# = 14
|
|
|
|
|
TXC# = 1 : TXX# = 24
|
|
|
|
|
TX$ = "SCORE"
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXC# = 2 : TXX# = 144
|
|
|
|
|
T4# = SCORE#
|
|
|
|
|
GOSUB FMTNUM
|
|
|
|
|
TX$ = NUM$
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXC# = 1 : TXX# = 316
|
|
|
|
|
TX$ = "LIVES"
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXC# = 6 : TXX# = 436
|
|
|
|
|
TX$ = "" + LIVES#
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXC# = 1 : TXX# = 500
|
|
|
|
|
TX$ = "LEVEL"
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
TXC# = 4 : TXX# = 620
|
|
|
|
|
TX$ = "" + LEVEL#
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
DHUD# = 1
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM The banner over the field: whatever BAN$ says, centred, in the colour
|
|
|
|
|
REM of the gem that put it there.
|
|
|
|
|
LABEL SETBANNER
|
|
|
|
|
PN# = 0
|
|
|
|
|
DPLAY# = 1
|
|
|
|
|
IF BAN$ = "" THEN RETURN
|
|
|
|
|
BUILDH# = 0
|
|
|
|
|
TXS# = 8
|
|
|
|
|
TXY# = 430
|
|
|
|
|
TXX# = 400 - LEN(BAN$) * 20
|
|
|
|
|
TXC# = 5
|
|
|
|
|
IF GMTYP# = 1 THEN TXC# = 4
|
|
|
|
|
IF GMTYP# = 2 THEN TXC# = 2
|
|
|
|
|
IF GMTYP# = 3 THEN TXC# = 6
|
|
|
|
|
IF GMTYP# = 4 THEN TXC# = 1
|
|
|
|
|
IF GMTYP# = 5 THEN TXC# = 3
|
|
|
|
|
REM Sources 1 to 6 are cyan, yellow, purple, light red, light grey and
|
|
|
|
|
REM green; the five gems are red, yellow, green, blue and purple, so the
|
|
|
|
|
REM banner is as close to the gem as seven colour sources allow.
|
|
|
|
|
TX$ = BAN$
|
|
|
|
|
GOSUB BUILDTEXT
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM ---------------------------------------------------------------------
|
|
|
|
|
REM Turn TX$ into strokes, into the HUD list when BUILDH# is 1 and the
|
|
|
|
|
REM field list when it is 0. This is the expensive routine in the program,
|
|
|
|
|
REM about sixteen lines a character, and it is deliberately nowhere near a
|
|
|
|
|
REM capture: it runs when a value changes, and the drawing that replays
|
|
|
|
|
REM its output happens on some later frame.
|
|
|
|
|
LABEL BUILDTEXT
|
|
|
|
|
TLEN# = LEN(TX$)
|
|
|
|
|
IF TLEN# = 0 THEN RETURN
|
|
|
|
|
TXI# = 0
|
|
|
|
|
DO
|
|
|
|
|
GC# = INSTR(ALPHA$, MID(TX$, TXI#, 1))
|
|
|
|
|
IF GC# > 0 - 1 THEN GOSUB BUILDGLYPH
|
|
|
|
|
TXI# = TXI# + 1
|
|
|
|
|
LOOP UNTIL TXI# >= TLEN#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
LABEL BUILDGLYPH
|
|
|
|
|
GN# = FNC#(GC#)
|
|
|
|
|
IF GN# = 0 THEN RETURN
|
|
|
|
|
GP# = FNI#(GC#)
|
|
|
|
|
GX# = TXX# + TXI# * TXS# * 5
|
|
|
|
|
GK# = 0
|
|
|
|
|
DO
|
|
|
|
|
P1# = FNS#(GP#) : P2# = FNS#(GP# + 1) : GP# = GP# + 2
|
|
|
|
|
IF BUILDH# = 1 AND HN# < 120 THEN BEGIN
|
|
|
|
|
HX1#(HN#) = GX# + (P1# / 10) * TXS#
|
|
|
|
|
HY1#(HN#) = TXY# + MOD(P1#, 10) * TXS#
|
|
|
|
|
HX2#(HN#) = GX# + (P2# / 10) * TXS#
|
|
|
|
|
HY2#(HN#) = TXY# + MOD(P2#, 10) * TXS#
|
|
|
|
|
HC#(HN#) = TXC#
|
|
|
|
|
HN# = HN# + 1
|
|
|
|
|
BEND
|
|
|
|
|
IF BUILDH# = 0 AND PN# < 110 THEN BEGIN
|
|
|
|
|
PX1#(PN#) = GX# + (P1# / 10) * TXS#
|
|
|
|
|
PY1#(PN#) = TXY# + MOD(P1#, 10) * TXS#
|
|
|
|
|
PX2#(PN#) = GX# + (P2# / 10) * TXS#
|
|
|
|
|
PY2#(PN#) = TXY# + MOD(P2#, 10) * TXS#
|
|
|
|
|
PC#(PN#) = TXC#
|
|
|
|
|
PN# = PN# + 1
|
|
|
|
|
BEND
|
|
|
|
|
GK# = GK# + 1
|
|
|
|
|
LOOP UNTIL GK# >= GN#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM T4# as six digits with leading zeros, into NUM$.
|
|
|
|
|
LABEL FMTNUM
|
|
|
|
|
NUM$ = ""
|
|
|
|
|
T2# = T4#
|
|
|
|
|
FOR I# = 1 TO 6
|
|
|
|
|
D# = MOD(T2#, 10)
|
|
|
|
|
NUM$ = MID("0123456789", D#, 1) + NUM$
|
|
|
|
|
T2# = T2# / 10
|
|
|
|
|
NEXT I#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM A generator, because this dialect has no RND
|
|
|
|
|
REM
|
|
|
|
|
REM The usual constants, kept inside 2^31 so the multiply stays inside a
|
|
|
|
|
REM 64 bit integer, and the answer taken from the middle bits because the
|
|
|
|
|
REM low ones of a power-of-two modulus barely move. A GOSUB rather than a
|
|
|
|
|
REM DEF on purpose: a multi-line DEF that assigns to a global spends a
|
|
|
|
|
REM value-pool slot on every call and never gives it back, which empties
|
|
|
|
|
REM the pool after a few thousand throws.
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL NEXTRAND
|
|
|
|
|
SEED# = MOD(SEED# * 1103515245 + 12345, 2147483648)
|
|
|
|
|
RNVAL# = MOD(SHR(SEED#, 16), RNMAX#)
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM Tables
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL LOADTABLES
|
|
|
|
|
FOR I# = 0 TO 5
|
|
|
|
|
READ BRC#(I#)
|
|
|
|
|
NEXT I#
|
|
|
|
|
FOR I# = 0 TO 5
|
|
|
|
|
READ BRV#(I#)
|
|
|
|
|
NEXT I#
|
|
|
|
|
FOR I# = 0 TO 7
|
|
|
|
|
READ ZVX%(I#)
|
|
|
|
|
NEXT I#
|
|
|
|
|
FOR I# = 0 TO 7
|
|
|
|
|
READ ZVY%(I#)
|
|
|
|
|
NEXT I#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM Row colours, top to bottom, and what a brick in that row is worth.
|
|
|
|
|
DATA 3, 9, 8, 6, 4, 5
|
|
|
|
|
DATA 60, 50, 40, 30, 20, 10
|
|
|
|
|
|
|
|
|
|
REM The eight landing zones. Each pair is very nearly a unit vector, so
|
|
|
|
|
REM the ball leaves a bar at the speed it arrived and only the angle
|
|
|
|
|
REM changes.
|
|
|
|
|
DATA -0.85, -0.62, -0.40, -0.18, 0.18, 0.40, 0.62, 0.85
|
|
|
|
|
DATA -0.53, -0.78, -0.92, -0.98, -0.98, -0.92, -0.78, -0.53
|
|
|
|
|
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
REM The stroke font
|
|
|
|
|
REM
|
|
|
|
|
REM One glyph per DATA line: how many strokes, then that many pairs of
|
|
|
|
|
REM points. A point is coded X*10+Y on a grid four wide and seven tall,
|
|
|
|
|
REM so 0 is the top left corner, 30 the top right and 36 the bottom
|
|
|
|
|
REM right. INSTR into ALPHA$ is what turns a character into a glyph
|
|
|
|
|
REM number, which is why the order of these lines matters.
|
|
|
|
|
REM =====================================================================
|
|
|
|
|
LABEL LOADFONT
|
|
|
|
|
GX# = 0
|
|
|
|
|
FOR I# = 0 TO 40
|
|
|
|
|
READ N#
|
|
|
|
|
FNC#(I#) = N#
|
|
|
|
|
FNI#(I#) = GX#
|
|
|
|
|
IF N# > 0 THEN GOSUB READGLYPH
|
|
|
|
|
NEXT I#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM Its own subroutine rather than a BEGIN block: a FOR nested inside a
|
|
|
|
|
REM block that is itself inside a FOR does not find its own NEXT.
|
|
|
|
|
LABEL READGLYPH
|
|
|
|
|
FOR K# = 1 TO N# * 2
|
|
|
|
|
READ D#
|
|
|
|
|
FNS#(GX#) = D#
|
|
|
|
|
GX# = GX# + 1
|
|
|
|
|
NEXT K#
|
|
|
|
|
RETURN
|
|
|
|
|
|
|
|
|
|
REM A
|
|
|
|
|
DATA 4, 0,30, 0,6, 30,36, 3,33
|
|
|
|
|
REM B
|
|
|
|
|
DATA 6, 0,6, 0,30, 3,33, 6,36, 30,33, 33,36
|
|
|
|
|
REM C
|
|
|
|
|
DATA 3, 0,30, 0,6, 6,36
|
|
|
|
|
REM D
|
|
|
|
|
DATA 6, 0,6, 0,20, 20,31, 31,35, 35,26, 26,6
|
|
|
|
|
REM E
|
|
|
|
|
DATA 4, 0,6, 0,30, 3,33, 6,36
|
|
|
|
|
REM F
|
|
|
|
|
DATA 3, 0,6, 0,30, 3,33
|
|
|
|
|
REM G
|
|
|
|
|
DATA 5, 0,30, 0,6, 6,36, 33,36, 13,33
|
|
|
|
|
REM H
|
|
|
|
|
DATA 3, 0,6, 30,36, 3,33
|
|
|
|
|
REM I
|
|
|
|
|
DATA 1, 10,16
|
|
|
|
|
REM J
|
|
|
|
|
DATA 3, 30,36, 6,36, 3,6
|
|
|
|
|
REM K
|
|
|
|
|
DATA 3, 0,6, 30,3, 3,36
|
|
|
|
|
REM L
|
|
|
|
|
DATA 2, 0,6, 6,36
|
|
|
|
|
REM M
|
|
|
|
|
DATA 4, 0,6, 30,36, 0,13, 13,30
|
|
|
|
|
REM N
|
|
|
|
|
DATA 3, 0,6, 30,36, 0,36
|
|
|
|
|
REM O
|
|
|
|
|
DATA 4, 0,30, 6,36, 0,6, 30,36
|
|
|
|
|
REM P
|
|
|
|
|
DATA 4, 0,6, 0,30, 3,33, 30,33
|
|
|
|
|
REM Q
|
|
|
|
|
DATA 5, 0,30, 6,36, 0,6, 30,36, 24,36
|
|
|
|
|
REM R
|
|
|
|
|
DATA 5, 0,6, 0,30, 3,33, 30,33, 13,36
|
|
|
|
|
REM S
|
|
|
|
|
DATA 5, 0,30, 0,3, 3,33, 33,36, 6,36
|
|
|
|
|
REM T
|
|
|
|
|
DATA 2, 0,30, 10,16
|
|
|
|
|
REM U
|
|
|
|
|
DATA 3, 0,6, 30,36, 6,36
|
|
|
|
|
REM V
|
|
|
|
|
DATA 2, 0,16, 16,30
|
|
|
|
|
REM W
|
|
|
|
|
DATA 4, 0,6, 30,36, 6,13, 13,36
|
|
|
|
|
REM X
|
|
|
|
|
DATA 2, 0,36, 30,6
|
|
|
|
|
REM Y
|
|
|
|
|
DATA 3, 0,13, 30,13, 13,16
|
|
|
|
|
REM Z
|
|
|
|
|
DATA 3, 0,30, 30,6, 6,36
|
|
|
|
|
REM 0
|
|
|
|
|
DATA 5, 0,30, 6,36, 0,6, 30,36, 30,6
|
|
|
|
|
REM 1
|
|
|
|
|
DATA 2, 10,16, 1,10
|
|
|
|
|
REM 2
|
|
|
|
|
DATA 5, 0,30, 30,33, 3,33, 3,6, 6,36
|
|
|
|
|
REM 3
|
|
|
|
|
DATA 4, 0,30, 30,36, 3,33, 6,36
|
|
|
|
|
REM 4
|
|
|
|
|
DATA 3, 0,3, 3,33, 30,36
|
|
|
|
|
REM 5
|
|
|
|
|
DATA 5, 0,30, 0,3, 3,33, 33,36, 6,36
|
|
|
|
|
REM 6
|
|
|
|
|
DATA 5, 0,30, 0,6, 3,33, 33,36, 6,36
|
|
|
|
|
REM 7
|
|
|
|
|
DATA 2, 0,30, 30,36
|
|
|
|
|
REM 8
|
|
|
|
|
DATA 5, 0,30, 6,36, 0,6, 30,36, 3,33
|
|
|
|
|
REM 9
|
|
|
|
|
DATA 5, 0,30, 0,3, 3,33, 30,36, 6,36
|
|
|
|
|
REM space
|
|
|
|
|
DATA 0
|
|
|
|
|
REM colon
|
|
|
|
|
DATA 2, 12,13, 14,15
|
|
|
|
|
REM dash
|
|
|
|
|
DATA 1, 3,33
|
|
|
|
|
REM full stop
|
|
|
|
|
DATA 1, 15,16
|
|
|
|
|
REM exclamation mark
|
|
|
|
|
DATA 2, 10,14, 15,16
|