diff --git a/FABRIC.md b/FABRIC.md index 1c29c4a..dd2925e 100644 --- a/FABRIC.md +++ b/FABRIC.md @@ -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 4900–4902, 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 diff --git a/capsules/BLOCK_MAP.md b/capsules/BLOCK_MAP.md index 7057457..7b73e60 100644 --- a/capsules/BLOCK_MAP.md +++ b/capsules/BLOCK_MAP.md @@ -1,5 +1,5 @@ # Capsule Block Manifest — Auto-generated - + @@ -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 ` to refresh.* +*27 capsule(s) scanned. Re-run `mkcapsule --manifest ` to refresh.* diff --git a/capsules/fabric.4th b/capsules/fabric.4th new file mode 100644 index 0000000..8616b80 --- /dev/null +++ b/capsules/fabric.4th @@ -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 ; diff --git a/fb/fabric-test-cart-plot.png b/fb/fabric-test-cart-plot.png new file mode 100644 index 0000000..ce81d4b Binary files /dev/null and b/fb/fabric-test-cart-plot.png differ diff --git a/lfs/amd64/starforth b/lfs/amd64/starforth index 2fe467a..ac22db8 100755 Binary files a/lfs/amd64/starforth and b/lfs/amd64/starforth differ diff --git a/src/word_registry.c b/src/word_registry.c index 2d98c09..f0260f8 100644 --- a/src/word_registry.c +++ b/src/word_registry.c @@ -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"); } \ No newline at end of file diff --git a/src/word_source/defining_words.c b/src/word_source/defining_words.c index 45b271e..71a8328 100644 --- a/src/word_source/defining_words.c +++ b/src/word_source/defining_words.c @@ -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) { diff --git a/src/word_source/framebuffer_words.c b/src/word_source/framebuffer_words.c new file mode 100644 index 0000000..d11de04 --- /dev/null +++ b/src/word_source/framebuffer_words.c @@ -0,0 +1,65 @@ +/* + StarForth — Steady-State Virtual Machine Runtime + + Copyright (c) 2023–2025 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); +} diff --git a/src/word_source/include/framebuffer_words.h b/src/word_source/include/framebuffer_words.h new file mode 100644 index 0000000..c3d32a0 --- /dev/null +++ b/src/word_source/include/framebuffer_words.h @@ -0,0 +1,38 @@ +/* + StarForth — Steady-State Virtual Machine Runtime + + Copyright (c) 2023–2025 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 */