2026-07-31 21:50:50 -04:00
|
|
|
# 6. Graphics
|
|
|
|
|
|
|
|
|
|
Everything in this chapter needs the SDL build and a graphics device. Without one each
|
|
|
|
|
verb refuses by name:
|
|
|
|
|
|
Execute every documented example as a test
docs/ and README.md carry 85 fenced blocks. Every one was checked by hand
exactly once, when it was written, which is not a standard that survives a
changing interpreter -- and four were already wrong: two transcripts showing a
leading space PRINT does not emit, akbasic_TextSink in README.md missing the
two members it had grown hours earlier, and FILTER's refusal quoted with
wording the code does not use.
tests/docs_examples.sh reads a fence-tag vocabulary and runs what it finds.
BASIC programs and transcripts run and are byte-compared against an `output`
block; C snippets compile with -fsyntax-only against the real include path,
which CMake writes out because it is transitive through akerror, akstdlib and
akgl; shell blocks run in a sandbox. Anything that would reconfigure the build
tree, hit the network or re-enter the suite is tagged norun with the reason in
MAINTENANCE.md, and the two cmake blocks stay hand-maintained by decision.
An untagged block is a failure rather than a default, and the pass line
reports what it executed by kind. Both exist because the way a harness like
this dies is by quietly matching nothing and passing -- which it duly did on
the first CTest run, where a generator expression evaluating to nothing still
contributed an empty argument that the script read as a filename. The count is
what caught it.
The excerpt check earns its own mention: a block tagged
`c excerpt=include/akbasic/sink.h` must still appear in that header, comments
and whitespace ignored. Compiling it would only redefine the type, so a
compile check could not have found the stale struct, and did not.
Registered as the CTest case docs_examples in both configurations. Fixing the
four wrong examples turned up two interpreter defects, fixed in the previous
commit and recorded in TODO.md section 8.
MAINTENANCE.md is new: the fence-tag reference, what to do when the case
fails, and the conventions that until now only existed inside source comments
-- the three test lists and how two of them invert "passed", the sorted verb
table, that a golden file is never edited to suit this interpreter, and that a
fix gets mutation-checked with a file copy rather than git checkout.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:41:36 -04:00
|
|
|
```basic requires=noakgl
|
|
|
|
|
10 DRAW 1, 0, 0 TO 100, 100
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
Execute every documented example as a test
docs/ and README.md carry 85 fenced blocks. Every one was checked by hand
exactly once, when it was written, which is not a standard that survives a
changing interpreter -- and four were already wrong: two transcripts showing a
leading space PRINT does not emit, akbasic_TextSink in README.md missing the
two members it had grown hours earlier, and FILTER's refusal quoted with
wording the code does not use.
tests/docs_examples.sh reads a fence-tag vocabulary and runs what it finds.
BASIC programs and transcripts run and are byte-compared against an `output`
block; C snippets compile with -fsyntax-only against the real include path,
which CMake writes out because it is transitive through akerror, akstdlib and
akgl; shell blocks run in a sandbox. Anything that would reconfigure the build
tree, hit the network or re-enter the suite is tagged norun with the reason in
MAINTENANCE.md, and the two cmake blocks stay hand-maintained by decision.
An untagged block is a failure rather than a default, and the pass line
reports what it executed by kind. Both exist because the way a harness like
this dies is by quietly matching nothing and passing -- which it duly did on
the first CTest run, where a generator expression evaluating to nothing still
contributed an empty argument that the script read as a filename. The count is
what caught it.
The excerpt check earns its own mention: a block tagged
`c excerpt=include/akbasic/sink.h` must still appear in that header, comments
and whitespace ignored. Compiling it would only redefine the type, so a
compile check could not have found the stale struct, and did not.
Registered as the CTest case docs_examples in both configurations. Fixing the
four wrong examples turned up two interpreter defects, fixed in the previous
commit and recorded in TODO.md section 8.
MAINTENANCE.md is new: the fence-tag reference, what to do when the case
fails, and the conventions that until now only existed inside source comments
-- the three test lists and how two of them invert "passed", the sorted verb
table, that a golden file is never edited to suit this interpreter, and that a
fix gets mutation-checked with a file copy rather than git checkout.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:41:36 -04:00
|
|
|
|
|
|
|
|
```output
|
2026-07-31 21:50:50 -04:00
|
|
|
? 10 : RUNTIME ERROR DRAW needs a graphics device and this runtime has none
|
Execute every documented example as a test
docs/ and README.md carry 85 fenced blocks. Every one was checked by hand
exactly once, when it was written, which is not a standard that survives a
changing interpreter -- and four were already wrong: two transcripts showing a
leading space PRINT does not emit, akbasic_TextSink in README.md missing the
two members it had grown hours earlier, and FILTER's refusal quoted with
wording the code does not use.
tests/docs_examples.sh reads a fence-tag vocabulary and runs what it finds.
BASIC programs and transcripts run and are byte-compared against an `output`
block; C snippets compile with -fsyntax-only against the real include path,
which CMake writes out because it is transitive through akerror, akstdlib and
akgl; shell blocks run in a sandbox. Anything that would reconfigure the build
tree, hit the network or re-enter the suite is tagged norun with the reason in
MAINTENANCE.md, and the two cmake blocks stay hand-maintained by decision.
An untagged block is a failure rather than a default, and the pass line
reports what it executed by kind. Both exist because the way a harness like
this dies is by quietly matching nothing and passing -- which it duly did on
the first CTest run, where a generator expression evaluating to nothing still
contributed an empty argument that the script read as a filename. The count is
what caught it.
The excerpt check earns its own mention: a block tagged
`c excerpt=include/akbasic/sink.h` must still appear in that header, comments
and whitespace ignored. Compiling it would only redefine the type, so a
compile check could not have found the stale struct, and did not.
Registered as the CTest case docs_examples in both configurations. Fixing the
four wrong examples turned up two interpreter defects, fixed in the previous
commit and recorded in TODO.md section 8.
MAINTENANCE.md is new: the fence-tag reference, what to do when the case
fails, and the conventions that until now only existed inside source comments
-- the three test lists and how two of them invert "passed", the sorted verb
table, that a golden file is never edited to suit this interpreter, and that a
fix gets mutation-checked with a file copy rather than git checkout.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:41:36 -04:00
|
|
|
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## The coordinate space
|
|
|
|
|
|
Draw into the whole window, not its top-left 320x200
The graphics verbs documented a coordinate transform that did not exist. With
SCALE off a coordinate went straight to akgl_draw_* as a pixel address, so an
800x600 window drew a C128 listing into its corner and left the rest unused --
while the chapter said coordinates were 320x200 and stretching to fit was the
host's business.
akbasic_GraphicsBackend gains a size entry point, require_graphics() asks it
before every verb that draws so a resized window is honoured between two
statements, and 320x200 becomes the fallback for a backend that leaves it NULL.
It is the record's one optional member, so a host written against the old header
keeps the behaviour it had.
SCALE now maps onto the device, and RGR(1)/RGR(2) report the drawing surface so
a program can use a window whose size it did not choose. RGR(0) is BASIC 7.0's
own field, the GRAPHIC mode.
SCALE also mapped xmax onto the width rather than onto the last pixel, so
SCALE 1, 319, 199 followed by DRAW 1, 319, 199 drew nothing at all -- one pixel
past the surface. Fixed in the same line, because it is what makes "SCALE gives
a C128 listing the whole window" true rather than nearly true.
The akgl test renders against a 128x128 target, deliberately smaller than the
old constants: a SCALE still dividing by them misses it entirely rather than
landing somewhere plausible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:35:20 -04:00
|
|
|
**A drawing coordinate is a pixel of the host's window**, with (0, 0) at the top left.
|
|
|
|
|
On the standalone interpreter's 800 by 600 window, `DRAW 1, 799, 599` lands on the
|
|
|
|
|
bottom-right pixel and everything in between is reachable. A game embedding the
|
|
|
|
|
interpreter gets whatever size its own renderer is.
|
|
|
|
|
|
|
|
|
|
`RGR` is how a program finds out:
|
|
|
|
|
|
|
|
|
|
```basic requires=akgl
|
|
|
|
|
10 PRINT "THE SCREEN IS"
|
|
|
|
|
20 PRINT RGR(1)
|
|
|
|
|
30 PRINT "BY"
|
|
|
|
|
40 PRINT RGR(2)
|
|
|
|
|
50 DRAW 1, 0, 0 TO RGR(1) - 1, RGR(2) - 1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Subtracting one is not a wart, it is the last pixel: a window `RGR(1)` wide has
|
|
|
|
|
columns 0 through `RGR(1) - 1`.
|
|
|
|
|
|
|
|
|
|
**A C128 listing assumes 320 by 200 and will draw in the top-left corner.** Give it
|
|
|
|
|
the whole window by naming the space it was written for:
|
|
|
|
|
|
|
|
|
|
```basic requires=akgl
|
|
|
|
|
10 SCALE 1, 319, 199
|
|
|
|
|
20 BOX 1, 0, 0, 319, 199
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
That box is now the border of the window whatever size the window is. When no device
|
|
|
|
|
answers the size question at all, 320 by 200 is what the interpreter assumes — the
|
|
|
|
|
space a C128 listing was written for is the right thing to fall back to.
|
2026-07-31 21:50:50 -04:00
|
|
|
|
|
|
|
|
## Colour
|
|
|
|
|
|
|
|
|
|
`COLOR` binds a *source* to a palette index, and the drawing verbs name the source
|
|
|
|
|
rather than the colour:
|
|
|
|
|
|
Execute every documented example as a test
docs/ and README.md carry 85 fenced blocks. Every one was checked by hand
exactly once, when it was written, which is not a standard that survives a
changing interpreter -- and four were already wrong: two transcripts showing a
leading space PRINT does not emit, akbasic_TextSink in README.md missing the
two members it had grown hours earlier, and FILTER's refusal quoted with
wording the code does not use.
tests/docs_examples.sh reads a fence-tag vocabulary and runs what it finds.
BASIC programs and transcripts run and are byte-compared against an `output`
block; C snippets compile with -fsyntax-only against the real include path,
which CMake writes out because it is transitive through akerror, akstdlib and
akgl; shell blocks run in a sandbox. Anything that would reconfigure the build
tree, hit the network or re-enter the suite is tagged norun with the reason in
MAINTENANCE.md, and the two cmake blocks stay hand-maintained by decision.
An untagged block is a failure rather than a default, and the pass line
reports what it executed by kind. Both exist because the way a harness like
this dies is by quietly matching nothing and passing -- which it duly did on
the first CTest run, where a generator expression evaluating to nothing still
contributed an empty argument that the script read as a filename. The count is
what caught it.
The excerpt check earns its own mention: a block tagged
`c excerpt=include/akbasic/sink.h` must still appear in that header, comments
and whitespace ignored. Compiling it would only redefine the type, so a
compile check could not have found the stale struct, and did not.
Registered as the CTest case docs_examples in both configurations. Fixing the
four wrong examples turned up two interpreter defects, fixed in the previous
commit and recorded in TODO.md section 8.
MAINTENANCE.md is new: the fence-tag reference, what to do when the case
fails, and the conventions that until now only existed inside source comments
-- the three test lists and how two of them invert "passed", the sorted verb
table, that a golden file is never edited to suit this interpreter, and that a
fix gets mutation-checked with a file copy rather than git checkout.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:41:36 -04:00
|
|
|
```basic requires=akgl
|
2026-07-31 21:50:50 -04:00
|
|
|
10 COLOR 1, 3
|
|
|
|
|
20 DRAW 1, 10, 20
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Sources are numbered 0 to 6; palette indices are 1 to 16, as on a C128. That
|
|
|
|
|
indirection is BASIC 7.0's, and it is why every drawing verb's first argument is a
|
|
|
|
|
small number that is not a colour.
|
|
|
|
|
|
|
|
|
|
## The verbs
|
|
|
|
|
|
|
|
|
|
### GRAPHIC
|
|
|
|
|
|
|
|
|
|
`GRAPHIC mode` chooses a screen mode; `GRAPHIC CLR` clears it. Mode 0 is text and
|
|
|
|
|
refuses to draw.
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
Every picture in this chapter is generated by running the listing above it; see
|
|
|
|
|
`MAINTENANCE.md` if you are editing one.
|
|
|
|
|
|
2026-07-31 21:50:50 -04:00
|
|
|
### DRAW
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
```basic requires=akgl screenshot=draw
|
|
|
|
|
10 COLOR 1, 8
|
|
|
|
|
20 DRAW 1, 20, 180 TO 90, 40 TO 160, 150 TO 230, 20 TO 300, 120
|
|
|
|
|
30 COLOR 2, 6
|
|
|
|
|
40 DRAW 2, 20, 190 TO 300, 190
|
|
|
|
|
50 LOCATE 160, 100
|
|
|
|
|
60 COLOR 3, 3
|
|
|
|
|
70 DRAW 3
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|

|
|
|
|
|
|
2026-07-31 21:50:50 -04:00
|
|
|
One coordinate pair plots a point. Two or more, separated by `TO`, draw a polyline. A
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
bare `DRAW 3` — the last line above, and the single red pixel in the middle of the
|
|
|
|
|
picture — plots wherever `LOCATE` left the pixel cursor.
|
2026-07-31 21:50:50 -04:00
|
|
|
|
|
|
|
|
### BOX
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
```basic requires=akgl screenshot=box
|
|
|
|
|
10 COLOR 1, 8
|
|
|
|
|
20 BOX 1, 20, 30, 130, 140
|
|
|
|
|
30 COLOR 2, 6
|
|
|
|
|
40 BOX 2, 180, 30, 290, 140, 30
|
|
|
|
|
50 COLOR 3, 3
|
|
|
|
|
60 LOCATE 300, 190
|
|
|
|
|
70 BOX 3, 20, 160
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|

|
|
|
|
|
|
|
|
|
|
Corners, and an optional rotation angle — the green box is the same box turned 30
|
|
|
|
|
degrees about its own centre. Two coordinates instead of four take the other corner
|
|
|
|
|
from the pixel cursor, which is the red box.
|
|
|
|
|
|
|
|
|
|
**`BOX` always outlines; it cannot fill.** BASIC 7.0 selects fill with a seventh
|
|
|
|
|
argument and that is not implemented here — see Chapter 13. `PAINT` is the fill you
|
|
|
|
|
have.
|
2026-07-31 21:50:50 -04:00
|
|
|
|
|
|
|
|
### CIRCLE
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
```basic requires=akgl screenshot=circle
|
|
|
|
|
10 COLOR 1, 8
|
|
|
|
|
20 CIRCLE 1, 80, 70, 60, 60
|
|
|
|
|
30 COLOR 2, 6
|
|
|
|
|
40 CIRCLE 2, 230, 70, 75, 45
|
|
|
|
|
50 COLOR 3, 3
|
|
|
|
|
60 CIRCLE 3, 160, 140, 130, 50, 90, 270
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|

|
|
|
|
|
|
|
|
|
|
Source, centre, then the two radii — equal radii give a circle and unequal ones an
|
|
|
|
|
ellipse. Two further arguments are a start and an end angle, which is what makes the
|
|
|
|
|
red arc: 90 to 270 is the bottom half, because angles here are degrees clockwise from
|
|
|
|
|
straight up, the same convention `MOVSPR` uses. Beyond those come a rotation and the
|
|
|
|
|
degree increment, and a large increment is what turns a circle into a polygon.
|
2026-07-31 21:50:50 -04:00
|
|
|
|
|
|
|
|
### PAINT
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
```basic requires=akgl screenshot=paint
|
|
|
|
|
10 COLOR 1, 8
|
|
|
|
|
20 CIRCLE 1, 100, 100, 70, 70
|
|
|
|
|
30 BOX 1, 180, 50, 290, 150
|
|
|
|
|
40 COLOR 2, 6
|
|
|
|
|
50 PAINT 2, 100, 100
|
|
|
|
|
60 COLOR 3, 3
|
|
|
|
|
70 PAINT 3, 230, 100
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|

|
|
|
|
|
|
|
|
|
|
Flood-fills the region containing a point, stopping at whatever is already drawn — so
|
|
|
|
|
the outline you fill inside can come from any verb. If the region is too large for the
|
|
|
|
|
fill's own working space it stops and reports rather than leaving a half-painted screen
|
|
|
|
|
with no explanation.
|
2026-07-31 21:50:50 -04:00
|
|
|
|
|
|
|
|
### LOCATE
|
|
|
|
|
|
|
|
|
|
Moves the pixel cursor, which is where a bare `DRAW` plots and where a `BOX` with two
|
|
|
|
|
coordinates finishes.
|
|
|
|
|
|
|
|
|
|
### SCALE
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
```basic requires=akgl screenshot=scale size=640x400
|
|
|
|
|
10 COLOR 1, 3
|
|
|
|
|
20 BOX 1, 0, 0, 319, 199
|
|
|
|
|
30 SCALE 1, 319, 199
|
|
|
|
|
40 COLOR 2, 6
|
|
|
|
|
50 BOX 2, 0, 0, 319, 199
|
|
|
|
|
60 DRAW 2, 0, 0 TO 319, 199
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|

|
|
|
|
|
|
|
|
|
|
That picture is 640 by 400, and both boxes name the same four numbers. The red one is
|
|
|
|
|
drawn with `SCALE` off, so its coordinates are pixels and it covers exactly the
|
|
|
|
|
top-left 320 by 200 of the window — which is what a C128 listing does here. The green
|
|
|
|
|
one is drawn after `SCALE 1, 319, 199` and fills the window, and the diagonal confirms
|
|
|
|
|
that 319, 199 reaches the last pixel rather than stopping one short of it.
|
|
|
|
|
|
2026-07-31 21:50:50 -04:00
|
|
|
Turns on user coordinates and gives their maxima. With it on, your coordinates are
|
Draw into the whole window, not its top-left 320x200
The graphics verbs documented a coordinate transform that did not exist. With
SCALE off a coordinate went straight to akgl_draw_* as a pixel address, so an
800x600 window drew a C128 listing into its corner and left the rest unused --
while the chapter said coordinates were 320x200 and stretching to fit was the
host's business.
akbasic_GraphicsBackend gains a size entry point, require_graphics() asks it
before every verb that draws so a resized window is honoured between two
statements, and 320x200 becomes the fallback for a backend that leaves it NULL.
It is the record's one optional member, so a host written against the old header
keeps the behaviour it had.
SCALE now maps onto the device, and RGR(1)/RGR(2) report the drawing surface so
a program can use a window whose size it did not choose. RGR(0) is BASIC 7.0's
own field, the GRAPHIC mode.
SCALE also mapped xmax onto the width rather than onto the last pixel, so
SCALE 1, 319, 199 followed by DRAW 1, 319, 199 drew nothing at all -- one pixel
past the surface. Fixed in the same line, because it is what makes "SCALE gives
a C128 listing the whole window" true rather than nearly true.
The akgl test renders against a 128x128 target, deliberately smaller than the
old constants: a SCALE still dividing by them misses it entirely rather than
landing somewhere plausible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:35:20 -04:00
|
|
|
mapped onto the drawing surface: 0 is the first pixel and the maximum you gave is the
|
|
|
|
|
*last* one, so `DRAW 1, 1023, 1023` above reaches the bottom-right corner rather than
|
|
|
|
|
missing it by a pixel. `SCALE 1` on its own uses 7.0's 1023 by 1023. `SCALE 0` turns
|
|
|
|
|
it off and coordinates go back to being window pixels.
|
|
|
|
|
|
|
|
|
|
### RGR
|
|
|
|
|
|
|
|
|
|
`RGR(0)` is the current `GRAPHIC` mode. `RGR(1)` and `RGR(2)` are the drawing
|
|
|
|
|
surface's width and height in pixels — those two are ours rather than 7.0's, and they
|
Let a program ask how big the text grid is
`RGR(1)` and `RGR(2)` gave the window in pixels and nothing gave columns, rows
or the cell size -- so anything placing a character *and* a sprite at the same
spot had to hardcode a number measured by hand against whatever font the host
loaded. The Breakout in examples/ does exactly that, `CW# = 16`, and it is the
one thing in that listing that breaks on a different font or window.
**`RWINDOW` is BASIC 7.0's own answer and had never been implemented here.**
`RWINDOW(0)` is the current text window's rows and `RWINDOW(1)` its columns.
`RWINDOW(2)` reports a C128's 40 or 80 column screen mode, and this interpreter
has neither -- refused by name, because answering 0 would be a plausible lie,
which is worse than a refusal that says why.
The cell size in pixels is `RGR(3)` and `RGR(4)`, beside the surface's own
dimensions rather than on `RWINDOW`. Two reasons: a cell size is a fact about
the surface, and `RWINDOW` reports the *window*, so dividing `RGR(1)` by a
column count stops being right the moment a program calls `WINDOW`.
Both read a new optional `grid` entry point on `akbasic_TextSink` -- columns,
rows, cell width, cell height -- implemented by the akgl sink and forwarded by
the tee, in the shape `moveto` and `window` already had. NULL everywhere else,
so both verbs refuse by name against a sink with no grid. `akbasic_sink_init_
stdio()` clears it for the same reason it now clears the other two.
Measured on the standalone build: `RGR(3)` answers 16 and `RWINDOW` answers 50
columns by 37 rows -- the three numbers the Breakout listing had written out as
constants -- and `RWINDOW` follows a `WINDOW` call while `RGR(3)` does not.
tests/console_verbs.c drives the answers through a stand-in sink with a grid,
since the harness sink is stdio and has none; tests/graphics_verbs.c covers the
new `RGR` fields, their refusal, and the moved range bound. The `c excerpt=`
block in docs/10-embedding.md moves with the header, which is `docs_examples`
doing its job.
TODO.md section 6 item 31's second half, struck.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 00:05:17 -04:00
|
|
|
are what a program needs to use a window whose size it did not choose. They
|
|
|
|
|
refuse when there is no graphics device, unlike `RGR(0)`, which is a mode this
|
Draw into the whole window, not its top-left 320x200
The graphics verbs documented a coordinate transform that did not exist. With
SCALE off a coordinate went straight to akgl_draw_* as a pixel address, so an
800x600 window drew a C128 listing into its corner and left the rest unused --
while the chapter said coordinates were 320x200 and stretching to fit was the
host's business.
akbasic_GraphicsBackend gains a size entry point, require_graphics() asks it
before every verb that draws so a resized window is honoured between two
statements, and 320x200 becomes the fallback for a backend that leaves it NULL.
It is the record's one optional member, so a host written against the old header
keeps the behaviour it had.
SCALE now maps onto the device, and RGR(1)/RGR(2) report the drawing surface so
a program can use a window whose size it did not choose. RGR(0) is BASIC 7.0's
own field, the GRAPHIC mode.
SCALE also mapped xmax onto the width rather than onto the last pixel, so
SCALE 1, 319, 199 followed by DRAW 1, 319, 199 drew nothing at all -- one pixel
past the surface. Fixed in the same line, because it is what makes "SCALE gives
a C128 listing the whole window" true rather than nearly true.
The akgl test renders against a 128x128 target, deliberately smaller than the
old constants: a SCALE still dividing by them misses it entirely rather than
landing somewhere plausible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:35:20 -04:00
|
|
|
interpreter recorded rather than a screen it has to go and measure.
|
2026-07-31 21:50:50 -04:00
|
|
|
|
Let a program ask how big the text grid is
`RGR(1)` and `RGR(2)` gave the window in pixels and nothing gave columns, rows
or the cell size -- so anything placing a character *and* a sprite at the same
spot had to hardcode a number measured by hand against whatever font the host
loaded. The Breakout in examples/ does exactly that, `CW# = 16`, and it is the
one thing in that listing that breaks on a different font or window.
**`RWINDOW` is BASIC 7.0's own answer and had never been implemented here.**
`RWINDOW(0)` is the current text window's rows and `RWINDOW(1)` its columns.
`RWINDOW(2)` reports a C128's 40 or 80 column screen mode, and this interpreter
has neither -- refused by name, because answering 0 would be a plausible lie,
which is worse than a refusal that says why.
The cell size in pixels is `RGR(3)` and `RGR(4)`, beside the surface's own
dimensions rather than on `RWINDOW`. Two reasons: a cell size is a fact about
the surface, and `RWINDOW` reports the *window*, so dividing `RGR(1)` by a
column count stops being right the moment a program calls `WINDOW`.
Both read a new optional `grid` entry point on `akbasic_TextSink` -- columns,
rows, cell width, cell height -- implemented by the akgl sink and forwarded by
the tee, in the shape `moveto` and `window` already had. NULL everywhere else,
so both verbs refuse by name against a sink with no grid. `akbasic_sink_init_
stdio()` clears it for the same reason it now clears the other two.
Measured on the standalone build: `RGR(3)` answers 16 and `RWINDOW` answers 50
columns by 37 rows -- the three numbers the Breakout listing had written out as
constants -- and `RWINDOW` follows a `WINDOW` call while `RGR(3)` does not.
tests/console_verbs.c drives the answers through a stand-in sink with a grid,
since the harness sink is stdio and has none; tests/graphics_verbs.c covers the
new `RGR` fields, their refusal, and the moved range bound. The `c excerpt=`
block in docs/10-embedding.md moves with the header, which is `docs_examples`
doing its job.
TODO.md section 6 item 31's second half, struck.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 00:05:17 -04:00
|
|
|
**`RGR(3)` and `RGR(4)` are a character cell's width and height**, also ours. They come
|
|
|
|
|
from the *text* device rather than the graphics one — a character grid belongs to the
|
|
|
|
|
sink — so they refuse by naming that instead. Between them and
|
|
|
|
|
[`RWINDOW`](12-function-reference.md), which gives the current text window in columns
|
|
|
|
|
and rows, a program can place a character and a sprite at the same spot without
|
|
|
|
|
hardcoding a number measured against whatever font the host loaded:
|
|
|
|
|
|
|
|
|
|
```basic requires=akgl
|
|
|
|
|
10 CW# = RGR(3)
|
|
|
|
|
20 CH# = RGR(4)
|
|
|
|
|
30 COL# = 12
|
|
|
|
|
40 ROW# = 3
|
|
|
|
|
50 CHAR 1, COL#, ROW#, "X"
|
|
|
|
|
60 MOVSPR 1, COL# * CW#, ROW# * CH#
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`RWINDOW` follows a `WINDOW` call, because it reports the window. `RGR(3)` and `RGR(4)`
|
|
|
|
|
do not, because windowing does not change how big a character is.
|
|
|
|
|
|
2026-07-31 21:50:50 -04:00
|
|
|
### WIDTH
|
|
|
|
|
|
|
|
|
|
`WIDTH 1` or `WIDTH 2` sets how thick a drawn line is. A thick line is drawn as
|
|
|
|
|
parallel passes; see Chapter 13.
|
|
|
|
|
|
|
|
|
|
## Saving and stamping regions
|
|
|
|
|
|
|
|
|
|
`SSHAPE` copies a rectangle off the screen and `GSHAPE` stamps it back:
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|
```basic requires=akgl screenshot=shapes
|
|
|
|
|
10 COLOR 1, 8
|
|
|
|
|
20 CIRCLE 1, 40, 40, 30, 30
|
|
|
|
|
30 COLOR 2, 6
|
|
|
|
|
40 PAINT 2, 40, 40
|
|
|
|
|
50 SSHAPE A$, 8, 8, 72, 72
|
|
|
|
|
60 GSHAPE A$, 120, 20
|
|
|
|
|
70 GSHAPE A$, 200, 60
|
|
|
|
|
80 GSHAPE A$, 120, 120
|
2026-07-31 21:50:50 -04:00
|
|
|
```
|
|
|
|
|
|
Generate the documentation's figures from the listings they illustrate
Chapters 6 and 8 described what a verb draws in prose. Eight figures now show
it, and each one is produced by running the BASIC listing printed immediately
above it -- so a picture cannot drift away from the code beside it, which is the
way a screenshot goes wrong and the way nothing notices.
tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy
video driver, software renderer, run to completion, read the target back, write
a PNG. It draws no text layer on purpose, so a READY in the corner is not noise
in a figure about BOX and no font has to be resolved.
tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out
of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the
point being made is a 320x200 listing filling a larger window, which cannot be
made on a 320x200 surface.
Two gates, answering different questions. docs_examples fails a tagged block
with no image, in both configurations, so a figure cannot be added and
forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every
figure and compares byte for byte, so a listing edited without regenerating
fails. Only the second catches a stale picture.
The PNGs are checked in because a reader on the forge has no build tree, and
docs/images/README.md says loudly that they are generated. Regenerating is never
part of a build: the target is run deliberately, so a make cannot put eight
binary diffs in front of whoever ran it.
Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed
in bold that BOX fills on a negative angle while its own paragraph said the fill
was filed rather than implemented. BOX cannot fill, and filled_rect is reached
by no verb as a result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 07:37:04 -04:00
|
|
|

|
|
|
|
|
|
|
|
|
|
One disc drawn, captured, and stamped three times.
|
|
|
|
|
|
Honour a subscript in SSHAPE and GSHAPE
`shape_variable()` took the identifier off the leaf and looked the variable up
without ever evaluating the subscript, and both verbs then addressed element
zero with a literal. So `SSHAPE SH$(2), ...` wrote the handle into `SH$(0)` and
`GSHAPE SH$(2)` stamped whatever was in `SH$(0)`.
Ordinary assignment and `PRINT` honour the subscript, which is what made this
expensive: a program keeping several saved shapes in an array got every one of
them resolving to the same element, silently, and the only symptom was that
every stamp came out as the last shape captured. The Breakout in examples/ keeps
its six brick stamps in six separate scalars for exactly this reason.
`SPRSAV` was the counter-example and is the model -- it evaluates its argument
and handles an array element correctly. The subscript resolution itself is now
shared: `collect_subscripts()` comes out of src/environment.c as
`akbasic_environment_collect_subscripts()`, so a verb taking a variable by name
resolves a subscript the same way assignment does rather than each verb deciding
for itself.
tests/graphics_verbs.c covers TODO.md's reduction -- which used to print
"[SHAPE:0] []" and now prints "[] [SHAPE:0]" -- and the case a program actually
wants: two shapes captured into two elements, each stamped back through its own,
asserted against the device log so a fix that merely made the strings look right
would not pass.
Chapter 18's trap 4 becomes history, and Chapter 6 says an array works.
TODO.md section 9 item 6, struck.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 00:10:24 -04:00
|
|
|
A string array works: `SSHAPE SH$(2), ...` writes into element 2 and `GSHAPE SH$(2)`
|
|
|
|
|
stamps what element 2 names, so a program can keep a set of shapes in one array.
|
|
|
|
|
|
2026-07-31 21:50:50 -04:00
|
|
|
**`A$` holds a handle, not the pixels.** On a C128 the string holds the bitmap, so a
|
|
|
|
|
program could save it to disk or take its `LEN`. Here a string is a fixed 255 bytes and
|
|
|
|
|
the region is a device surface, so what goes in the string is a reference to it —
|
|
|
|
|
`SHAPE:0`. You can pass it to `GSHAPE` and to `SPRSAV`, which is everything BASIC ever
|
|
|
|
|
does with one, but you cannot store it or measure it.
|
|
|
|
|
|
|
|
|
|
## What is not here
|
|
|
|
|
|
|
|
|
|
`FILTER` parses and then refuses: there is no filter stage to configure. See Chapter 7.
|
|
|
|
|
|
|
|
|
|
The graphics verbs draw straight to the renderer rather than into a display list, so
|
|
|
|
|
anything drawn is overwritten by the text layer on the next frame. A program that wants
|
|
|
|
|
its drawing to persist has to redraw it. This is recorded as a defect rather than a
|
|
|
|
|
design; see Chapter 13.
|
Write down that a redraw has to fit inside one batch
"Drawing does not persist across frames; redraw it every frame" was the advice,
and it is not sufficient on its own. The host runs a fixed number of source
lines and then presents, and presenting discards the drawing buffer -- so a run
of drawing verbs longer than one budget is **torn**, not merely transient, and
an `SSHAPE` at the end of it captures only what was issued since the present,
over whatever the frame before left behind.
Measured against the standalone frontend's 256: after synchronising to a jiffy
edge, 220 lines of drawing survive a capture and 250 do not.
There is no fix available that does not change what a host owns -- the budget is
the host's and so is the present -- so this is documentation, in the three
places it belongs. Chapter 13 beside the note it qualifies, with the numbers.
Chapter 6 where `SSHAPE` is introduced, which is where a program meets it.
Chapter 14 from the step loop's side, naming `AKBASIC_FRONTEND_STEPS_PER_FRAME`
and explaining the one thing a script can do about it: `settime()` is called once
a frame, so `TI#` changes on the first step of a batch and nowhere else, and
spinning until it changes is the only frame synchronisation this dialect has.
**Untested, deliberately.** Reproducing it needs the real frontend, a clock and
a timing window, and a test that reproduced it would be reproducing a race.
Recorded in TODO.md rather than left looking like an oversight.
TODO.md section 9 item 5, struck as a documentation outcome.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 00:23:36 -04:00
|
|
|
|
|
|
|
|
**A redraw also has to fit inside one batch.** The host runs a fixed number of source
|
|
|
|
|
lines and then presents, and presenting throws the drawing buffer away — so a run of
|
|
|
|
|
drawing verbs longer than one batch is torn rather than merely transient, and an
|
|
|
|
|
`SSHAPE` at the end of it captures only the part issued since the present. Watching
|
|
|
|
|
`TI#` change is how a program finds the boundary; Chapter 13 has the measured numbers and
|
|
|
|
|
[Chapter 18](18-tutorial-breakout-artwork.md#step-4-find-the-frame-boundary) has a
|
|
|
|
|
routine that uses them.
|