starkernel: item 4.3.3 -- Cartesian coordinate machinery, found and fixed a VARIABLE alignment bug

Adds Module 28 (framebuffer_words.c/.h): PLOT ( x y color -- ), FB-WIDTH,
FB-HEIGHT -- raw hardware-boundary C primitives, kernel-only, no-op on
hosted builds, same pattern as every other module.

Adds capsules/fabric.4th (blocks 4900-4902, mkcapsule --lint clean):
COS45/Z->DELTA/PROJECT/CART-Y/CART-PLOT -- the 45-degree cavalier
orthographic projection and Y-flip, in FORTH per the compose-in-FORTH-first
rule (this is policy, not hardware access).

Found and fixed a second real bug while live-testing CART-PLOT over the
serial socket: defining_word_variable() (defining_words.c) captured
vm->here as a VARIABLE's address with no alignment call first, while
vm_load_cell/vm_store_cell require 8-byte-aligned addresses. This capsule's
VARIABLE ZD landed misaligned (945) purely by chance of what preceded it;
other capsules' variables happened to land aligned by luck, not guarantee.
Real deviation from FORTH-83/ANS, which specifies VARIABLE reserves an
aligned cell. Fixed with vm_align(vm) before capturing addr -- ALIGN
already existed as a word but VARIABLE wasn't calling it.

Verified end-to-end on amd64 via manual serial injection + QEMU screendump:
plotted 4 marker points (origin, +100 X, +100 Y, +50 Z) and confirmed all
landed at hand-calculated raster coordinates, including the diagonal
up-right shift for the Z-axis point -- the projection math is correct, not
just non-crashing. fb/fabric-test-cart-plot.png. 4.3.1's corner diagnostic
still renders correctly in the same shot, confirming no regression.

All three architectures (amd64/aarch64/riscv64) boot clean to ok> with the
DoE completing; dict_hash identical across all three
(0xc7f9adf885e306d2), confirming parity is unaffected.

FABRIC.md item 4.3.3 marked done with full acceptance evidence.
This commit is contained in:
Robert Allan James
2026-08-07 13:20:52 -04:00
parent fbf0625317
commit ef9806977a
9 changed files with 173 additions and 3 deletions
+29 -1
View File
@@ -3692,7 +3692,7 @@ document and committing that amendment as its own item.*
> a Makefile target, per direction — run directly for now. aarch64/riscv64 not covered by
> this script; not needed for 4.3.1's amd64-only diagnostic.
- [ ] **4.3.3 — Cartesian coordinate machinery.** Origin bottom-left `(0, 0)`, Y-up, plus a
- [x] **4.3.3 — Cartesian coordinate machinery.** Origin bottom-left `(0, 0)`, Y-up, plus a
new Z axis (depth-into-screen, not height) and a fixed orthographic projection as a
placeholder — not the final projection, no perspective/camera work yet. **Angle settled
2026-08-07: true 45° cavalier.** New C primitives `PLOT ( x y color -- )`, `FB-WIDTH`,
@@ -3700,6 +3700,34 @@ document and committing that amendment as its own item.*
`capsules/fabric.4th` (blocks 4900+) for `PROJECT`/`CART-Y`/`CART-PLOT`, per the
compose-in-FORTH-first rule — the transform is policy, not hardware access. *Refs:* §27.3.
> **Done, 2026-08-07.** `register_framebuffer_words()` (Module 28) adds `PLOT`/`FB-WIDTH`/
> `FB-HEIGHT` — kernel-only, no-op on hosted builds, same pattern as every other module.
> `capsules/fabric.4th` (blocks 49004902, lint-clean per `mkcapsule --lint`) defines
> `COS45`/`Z->DELTA`/`PROJECT`/`CART-Y`/`CART-PLOT`.
>
> **A second real bug found and fixed, not scope creep.** Live-tested `CART-PLOT` over the
> serial socket (same injection technique the DoE machinery uses) and hit a silent `ERROR`
> on the capsule's own `VARIABLE ZD`, while an identical `VARIABLE` typed live at the REPL
> worked fine. Traced to `defining_word_variable()` (`defining_words.c:471`): it captures
> `vm->here` as the variable's address with no alignment call first, and `vm_load_cell`/
> `vm_store_cell` require 8-byte-aligned addresses. `ZD` landed at `945` (misaligned) purely
> because of what preceded it in the capsule; `TESTV`/`ZD2` defined live happened to land on
> aligned addresses by luck. This is a real deviation from FORTH-83/ANS, which specifies
> `VARIABLE` reserves an *aligned* cell. Fixed with one line (`vm_align(vm)` before capturing
> `addr`) — `ALIGN` already existed as a word (`dictionary_words.c`) but `VARIABLE` wasn't
> calling it. Fixes every `VARIABLE` in the system, not just this capsule's — other capsules
> (`doe.4th`, `init-4.4th`) were landing aligned by luck, not by guarantee. Both hosted and
> kernel builds recompiled clean after the fix.
>
> **Verified end-to-end**, amd64, via the same manual-injection + `screendump` technique:
> plotted 4 marker points (origin, +100 X, +100 Y, +50 Z; a 3×3 cluster each for visibility)
> and confirmed all four landed at hand-calculated raster coordinates — including the
> diagonal up-right shift for the Z-axis point, confirming the 45° cavalier projection math
> is correct, not just non-crashing. Screenshot: `fb/fabric-test-cart-plot.png`. 4.3.1's
> corner diagnostic still renders correctly in the same shot — no regression.
>
> Not wired into `init.4th`'s boot chain, per plan — Console isn't a fleet VM yet.
- [ ] **4.3.3a — Q48.16 trigonometry.** `Q.SIN`/`Q.COS` (radian input) added to `q48_16.c`,
Taylor series after range-reducing into `[-π, π]` — same pattern as this file's existing
`Q.LOG`/`Q.EXP`/`Q.SQRT`, not a new precedent. Raised 2026-08-07 while scoping 4.3.3: needed
+6 -2
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-07T14:56:31Z -->
<!-- Generated by mkcapsule --manifest 2026-08-07T16:05:06Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
@@ -13,6 +13,7 @@
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
| `fabric.4th` | 4900, 4901, 4902 | `0xb7db7380ec4a42a0` |
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4175, 4176 | `0x85c7b311d1e5bf97` |
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
@@ -224,10 +225,13 @@
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4851 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4852 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4900 | `fabric.4th` | `0xb7db7380ec4a42a0` | ok |
| 4901 | `fabric.4th` | `0xb7db7380ec4a42a0` | ok |
| 4902 | `fabric.4th` | `0xb7db7380ec4a42a0` | ok |
## Conflicts
None.
---
*26 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
*27 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
+32
View File
@@ -0,0 +1,32 @@
Block 4900
( fabric.4th -- Console drawing-fabric coordinate machinery )
( FABRIC.md item 4.3.3. 45-degree cavalier orthographic )
( projection. Z is depth-into-screen, not height. Q48.16 )
( throughout. Raw pixel write (PLOT/FB-WIDTH/FB-HEIGHT) is )
( C; this capsule is the FORTH-side policy on top of it. )
46341 CONSTANT COS45 ( Q48.16 cos(45)=sin(45) ~= 0.70710678 )
VARIABLE ZD
: Z->DELTA ( z -- delta )
Q.FROM-INT COS45 Q.* Q.TO-INT ;
Block 4901
( PROJECT: 3D Cartesian (x y z) -> 2D Cartesian (sx sy). )
( Cavalier projection: depth pushes diagonally up-right. )
: PROJECT ( x y z -- sx sy )
Z->DELTA ZD !
ZD @ +
SWAP ZD @ +
SWAP ;
( CART-Y: Cartesian Y (origin bottom, up positive) -> )
( raster Y (origin top, down positive). The Y-flip. )
: CART-Y ( cart-y -- raster-y )
FB-HEIGHT 1- SWAP - ;
Block 4902
( CART-PLOT: full pipeline -- 3D Cartesian point to screen. )
: CART-PLOT ( x y z color -- )
>R
PROJECT
CART-Y
R>
PLOT ;
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.
+2
View File
@@ -73,6 +73,7 @@
#include "word_source/include/q48_words.h"
#include "word_source/include/inference_words.h"
#include "word_source/include/defer_words.h"
#include "word_source/include/framebuffer_words.h"
/**
* @brief Registers a single FORTH word in the virtual machine
@@ -128,6 +129,7 @@ void register_forth79_words(VM *vm) {
register_q48_words(vm); /* Module 25: Q48.16 Fixed-Point Math */
register_inference_words(vm); /* Module 26: SSM Inference + Jacquard */
register_defer_words(vm); /* Module 27: DEFER / IS late binding */
register_framebuffer_words(vm); /* Module 28: Console fabric -- raw framebuffer primitives */
log_message(LOG_INFO, "FORTH-79 Standard word set registration complete");
}
+1
View File
@@ -478,6 +478,7 @@ static void defining_word_variable(VM *vm) {
return;
}
vm_align(vm);
vaddr_t addr = (vaddr_t) vm->here;
void *p = vm_allot(vm, sizeof(cell_t));
if (!p) {
+65
View File
@@ -0,0 +1,65 @@
/*
StarForth Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/* framebuffer_words.c — raw framebuffer hardware-boundary FORTH words
* (FABRIC.md item 4.3.3). Kernel-only; no-op on hosted builds. */
#include "include/framebuffer_words.h"
#include "../../include/log.h"
#include "../../include/word_registry.h"
#ifdef __STARKERNEL__
#include "starkernel/framebuffer.h"
#endif
/* PLOT ( x y color -- ) : raw raster pixel write, top-left origin, Y-down.
* No Cartesian awareness -- that is capsules/fabric.4th's job. */
static void fbw_plot(VM *vm)
{
if (vm->dsp < 2) {
log_message(LOG_ERROR, "PLOT: Stack underflow");
vm->error = 1;
return;
}
cell_t color = vm_pop(vm);
cell_t y = vm_pop(vm);
cell_t x = vm_pop(vm);
#ifdef __STARKERNEL__
fb_put_pixel((uint32_t)x, (uint32_t)y, (uint32_t)color);
#else
(void)x; (void)y; (void)color;
#endif
}
/* FB-WIDTH ( -- n ) */
static void fbw_width(VM *vm)
{
#ifdef __STARKERNEL__
vm_push(vm, (cell_t)fb_width());
#else
vm_push(vm, 0);
#endif
}
/* FB-HEIGHT ( -- n ) */
static void fbw_height(VM *vm)
{
#ifdef __STARKERNEL__
vm_push(vm, (cell_t)fb_height());
#else
vm_push(vm, 0);
#endif
}
void register_framebuffer_words(VM *vm)
{
register_word(vm, "PLOT", fbw_plot);
register_word(vm, "FB-WIDTH", fbw_width);
register_word(vm, "FB-HEIGHT", fbw_height);
}
@@ -0,0 +1,38 @@
/*
StarForth Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
#ifndef FRAMEBUFFER_WORDS_H
#define FRAMEBUFFER_WORDS_H
#include "vm.h"
/**
* @defgroup framebuffer_words Framebuffer Words
* @{
*
* @brief Raw hardware-boundary FORTH words for the Console drawing fabric
* (FABRIC.md item 4.3.3). Deliberately raster-native -- no Cartesian
* awareness, no policy. That lives in capsules/fabric.4th instead, per the
* compose-in-FORTH-first rule.
*
* Kernel-only: no-op on hosted builds (no framebuffer exists there).
*
* @par PLOT ( x y color -- )
* Write one pixel at raster coordinates (top-left origin, Y-down).
* Out-of-bounds writes are silently ignored (inherited from fb_put_pixel).
*
* @par FB-WIDTH ( -- n )
* @par FB-HEIGHT ( -- n )
* Push the framebuffer's pixel width / height.
* @}
*/
void register_framebuffer_words(VM *vm);
#endif /* FRAMEBUFFER_WORDS_H */