Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure

Source tree reorganization:
- Move StarForth v3 engine to v3/ (src/, include/, Makefile)
- Move kernel to kernel/ (src/, include/, linker/, Makefile)
- Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md)
- Move FABRIC-0..4.md to docs/fabric/
- Move ONTOLOGY.md and ROADMAP.md to docs/

Board infrastructure:
- Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/
- Each board has board.mk (ISA, CPU flags, boot recipe) and README.md
- Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET
- make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board
- ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet)
- scripts/mkdiskimage.sh builds disk images for all boards

Docs pipeline:
- docs/book/ with LaTeX master (main.tex) and Makefile
- pandoc converts Markdown to LaTeX at build time
- Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks)
- make docs builds single PDF (754 pages, 0 missing characters)
- make docs TARGET=<board> adds board appendix
- build/docs/<book|board>/meta.tex stamps git commit into PDF

Bug fixes:
- 42 include paths that only worked by accident now use correct relative paths
- clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build)
- Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved
- Doxyfile, .clang-tidy, README.md, Kconfig paths updated

Verified:
- Hosted v3 build passes 1012 tests, 0 failures
- SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE)
- Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes
- make clean TARGET=<board> removes only that board and its ISA objects
- make all builds all boards, hosted v3, and docs in one run

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
rajames
2026-10-01 15:40:09 -04:00
co-authored by Junie
parent 3c709c115b
commit a8b70e88d3
630 changed files with 2646 additions and 2036 deletions
+168
View File
@@ -0,0 +1,168 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/* ttf_words.c — TrueType text entry point (FABRIC-0.md item 4.3.7e).
* Kernel-only; no-op on hosted builds. */
#include "include/ttf_words.h"
#include "../../include/log.h"
#include "../../include/word_registry.h"
#ifdef __STARKERNEL__
#include "starkernel/framebuffer.h"
#include "starkernel/ttf.h"
#include "starkernel/capsule_generated.h"
#define TTF_WORDS_CACHE_SLOTS 32
#define TTF_WORDS_DEFAULT_FONT "fonts:JetBrainsMono-Regular.ttf"
static ttf_font_t g_ttf_font;
static int g_ttf_font_ready = 0;
static ttf_raster_cache_slot_t g_ttf_cache_slots[TTF_WORDS_CACHE_SLOTS];
static ttf_raster_cache_t g_ttf_cache;
static int ttf_words_ensure_font(void) {
if (g_ttf_font_ready) return 1;
if (ttf_load_from_capsule(
capsule_get_directory(), capsule_get_descriptors(),
capsule_get_names(), capsule_get_arena(),
TTF_WORDS_DEFAULT_FONT, &g_ttf_font) != TTF_OK)
return 0;
ttf_raster_cache_init(&g_ttf_cache, g_ttf_cache_slots, TTF_WORDS_CACHE_SLOTS);
g_ttf_font_ready = 1;
return 1;
}
/* Decode one UTF-8 codepoint from VM memory at *a (VM-space address),
* with *len bytes known remaining. Mirrors capsules/fabric.4th's
* DECODE-UTF8 exactly: same lead-byte-length table, same U+FFFD
* fallback (advancing exactly one byte) on an invalid or truncated
* sequence. Advances *a and *len past the consumed bytes. */
static uint32_t ttf_words_decode_utf8(VM *vm, vaddr_t *a, cell_t *len) {
uint8_t lead, b[4];
uint32_t seqlen, i, cp;
if (*len <= 0 || !vm_addr_ok(vm, *a, 1)) {
*len = 0;
return 0xFFFD;
}
lead = vm_load_u8(vm, *a);
if (lead < 0x80) seqlen = 1;
else if ((lead & 0xE0) == 0xC0) seqlen = 2;
else if ((lead & 0xF0) == 0xE0) seqlen = 3;
else if ((lead & 0xF8) == 0xF0) seqlen = 4;
else seqlen = 0;
if (seqlen == 0 || (cell_t) seqlen > *len || !vm_addr_ok(vm, *a, seqlen)) {
*a += 1;
*len -= 1;
return 0xFFFD;
}
for (i = 0; i < seqlen; i++) b[i] = vm_load_u8(vm, *a + i);
switch (seqlen) {
case 1:
cp = b[0];
break;
case 2:
cp = ((uint32_t) (b[0] & 0x1F) << 6) | (b[1] & 0x3F);
break;
case 3:
cp = ((uint32_t) (b[0] & 0x0F) << 12) | ((uint32_t) (b[1] & 0x3F) << 6) | (b[2] & 0x3F);
break;
default:
cp = ((uint32_t) (b[0] & 0x07) << 18) | ((uint32_t) (b[1] & 0x3F) << 12)
| ((uint32_t) (b[2] & 0x3F) << 6) | (b[3] & 0x3F);
break;
}
*a += seqlen;
*len -= (cell_t) seqlen;
return cp;
}
/* TTF-TEXT ( c-addr u x y size color -- ) */
static void ttf_word_text(VM *vm) {
cell_t color, size, y, x, u, addr_cell;
vaddr_t a;
cell_t remaining;
uint32_t pen_x, baseline_y;
q48_16_t scale;
if (vm->dsp < 5) {
log_message(LOG_ERROR, "TTF-TEXT: Stack underflow");
vm->error = 1;
return;
}
color = vm_pop(vm);
size = vm_pop(vm);
y = vm_pop(vm);
x = vm_pop(vm);
u = vm_pop(vm);
addr_cell = vm_pop(vm);
if (!ttf_words_ensure_font()) {
log_message(LOG_ERROR, "TTF-TEXT: font capsule load failed");
vm->error = 1;
return;
}
if (size <= 0 || size > TTF_CACHE_MAX_SIZE_PX) {
log_message(LOG_ERROR, "TTF-TEXT: size out of range");
vm->error = 1;
return;
}
a = VM_ADDR(addr_cell);
remaining = u;
pen_x = (uint32_t) x;
/* Caller-facing (x,y) is Cartesian: origin at the bottom-left of the physical
* framebuffer, y increasing upward. fb_put_pixel() takes framebuffer coordinates
* (origin top-left, y increasing downward), so flip once here per-call rather
* than at every blit site. */
baseline_y = fb_height() - (uint32_t) y;
scale = q48_div(q48_from_u64((uint32_t) size), q48_from_u64(g_ttf_font.units_per_em));
while (remaining > 0) {
uint32_t cp = ttf_words_decode_utf8(vm, &a, &remaining);
ttf_bitmap_t bmp;
int rc, was_hit;
uint32_t gid, adv_units, px, py;
q48_16_t adv_q;
rc = ttf_raster_cache_get(&g_ttf_cache, &g_ttf_font, cp, (uint32_t) size, &bmp, &was_hit);
if (rc == TTF_OK) {
for (py = 0; py < bmp.height; py++) {
for (px = 0; px < bmp.width; px++) {
if (bmp.pixels[py * bmp.width + px]) {
fb_put_pixel(pen_x + px - TTF_CACHE_MARGIN,
baseline_y + py - (uint32_t) size - TTF_CACHE_MARGIN,
(uint32_t) color);
}
}
}
}
gid = ttf_codepoint_to_glyph(&g_ttf_font, cp);
adv_units = ttf_glyph_advance_width(&g_ttf_font, gid);
adv_q = q48_mul(q48_from_u64(adv_units), scale);
pen_x += (uint32_t) q48_to_u64(adv_q);
}
}
#endif /* __STARKERNEL__ */
void register_ttf_words(VM *vm)
{
#ifdef __STARKERNEL__
register_word(vm, "TTF-TEXT", ttf_word_text);
#else
(void) vm;
#endif
}