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>
224 lines
6.6 KiB
C
224 lines
6.6 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
*/
|
||
|
||
#include "include/dictionary_words.h"
|
||
#include "../../include/word_registry.h"
|
||
#include "vm.h"
|
||
#include "../../include/log.h"
|
||
|
||
/* === SAFE DICTIONARY WORDS IMPLEMENTATION (addresses are VM offsets) === */
|
||
|
||
/**
|
||
* @brief HERE ( -- addr ) Returns the dictionary pointer
|
||
* @param vm Pointer to the VM structure
|
||
*/
|
||
void dictionary_word_here(VM *vm) {
|
||
vm_push(vm, vm->here);
|
||
}
|
||
|
||
static void dictionary_word_align(VM *vm) {
|
||
vm_align(vm);
|
||
}
|
||
|
||
/**
|
||
* @brief ALLOT ( n -- ) Allocate n bytes in dictionary
|
||
* @param vm Pointer to the VM structure
|
||
* @details Allocates n bytes in the dictionary. n can be negative to deallocate space
|
||
*/
|
||
void dictionary_word_allot(VM *vm) {
|
||
if (vm->dsp < 0) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
cell_t n = vm_pop(vm);
|
||
cell_t new_here = vm->here + n;
|
||
/* Dictionary growth is bounded to DICTIONARY_MEMORY_SIZE, not the full
|
||
* VM_MEMORY_SIZE: blocks >= USER_BLOCKS_START live in the same flat
|
||
* vm->memory array (see vm_get_block_addr), so letting `here` grow past
|
||
* the dictionary region would silently corrupt live block/user data. */
|
||
if (new_here < 0 || new_here > (cell_t) DICTIONARY_MEMORY_SIZE) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vm->here = new_here;
|
||
}
|
||
|
||
/**
|
||
* @brief , ( n -- ) Compile cell into dictionary
|
||
* @param vm Pointer to the VM structure
|
||
* @details Compiles a single cell value into the dictionary at HERE
|
||
*/
|
||
void dictionary_word_comma(VM *vm) {
|
||
if (vm->dsp < 0) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
cell_t n = vm_pop(vm);
|
||
vm_align(vm);
|
||
if (vm->here + (cell_t) sizeof(cell_t) > DICTIONARY_MEMORY_SIZE) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vaddr_t addr = VM_ADDR(vm->here);
|
||
if (!vm_addr_ok(vm, addr, sizeof(cell_t))) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vm_store_cell(vm, addr, n);
|
||
vm->here += sizeof(cell_t);
|
||
}
|
||
|
||
/**
|
||
* @brief C, ( c -- ) Compile byte into dictionary
|
||
* @param vm Pointer to the VM structure
|
||
* @details Compiles a single byte value into the dictionary at HERE
|
||
*/
|
||
void dictionary_word_c_comma(VM *vm) {
|
||
if (vm->dsp < 0) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
cell_t c = vm_pop(vm);
|
||
if (vm->here + 1 > DICTIONARY_MEMORY_SIZE) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vaddr_t a = VM_ADDR(vm->here);
|
||
if (!vm_addr_ok(vm, a, 1)) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vm_store_u8(vm, a, (uint8_t)(c & 0xFF));
|
||
vm->here += 1;
|
||
}
|
||
|
||
/**
|
||
* @brief 2, ( d -- ) Compile double-cell into dictionary
|
||
* @param vm Pointer to the VM structure
|
||
* @details Compiles a double cell value into the dictionary at HERE, low cell first
|
||
*/
|
||
void dictionary_word_2comma(VM *vm) {
|
||
if (vm->dsp < 1) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
cell_t high = vm_pop(vm);
|
||
cell_t low = vm_pop(vm);
|
||
vm_align(vm);
|
||
if (vm->here + (cell_t)(2 * sizeof(cell_t)) > DICTIONARY_MEMORY_SIZE) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vaddr_t a = VM_ADDR(vm->here);
|
||
if (!vm_addr_ok(vm, a, 2 * sizeof(cell_t))) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
vm_store_cell(vm, a, low);
|
||
vm_store_cell(vm, a + sizeof(cell_t), high);
|
||
vm->here += 2 * sizeof(cell_t);
|
||
}
|
||
|
||
/**
|
||
* @brief PAD ( -- addr ) Get scratch pad buffer address
|
||
* @param vm Pointer to the VM structure
|
||
* @details Returns VM address of 512-byte temporary text buffer at top of memory
|
||
*/
|
||
void dictionary_word_pad(VM *vm) {
|
||
size_t pad_offset = VM_MEMORY_SIZE - 512;
|
||
vm_push(vm, (cell_t) pad_offset);
|
||
}
|
||
|
||
/**
|
||
* @brief SP@ ( -- sp ) Get current stack pointer
|
||
* @param vm Pointer to the VM structure
|
||
* @details Returns the current stack-pointer index (top is 0)
|
||
*/
|
||
void dictionary_word_sp_fetch(VM *vm) {
|
||
/* No stack args required */
|
||
vm_push(vm, (cell_t) vm->dsp);
|
||
}
|
||
|
||
/**
|
||
* @brief SP! ( sp -- ) Set stack pointer
|
||
* @param vm Pointer to the VM structure
|
||
* @details Restores the stack-pointer, but never allows growing the stack
|
||
*/
|
||
void dictionary_word_sp_store(VM *vm) {
|
||
if (vm->dsp < 0) {
|
||
vm->error = 1;
|
||
return;
|
||
} /* need one arg */
|
||
cell_t new_sp = vm_pop(vm);
|
||
|
||
/* Valid range: -1 (empty stack) up to current dsp (can shrink, not grow) */
|
||
if (new_sp < -1 || new_sp > vm->dsp) {
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
vm->dsp = new_sp;
|
||
/* We don’t need to scrub values above dsp; they’re considered garbage/unused. */
|
||
}
|
||
|
||
/**
|
||
* @brief LATEST ( -- addr ) Get latest definition address
|
||
* @param vm Pointer to the VM structure
|
||
* @details Returns VM address near most recent compiled definition (end of dictionary)
|
||
*/
|
||
void dictionary_word_latest(VM *vm) {
|
||
vm_push(vm, vm->here);
|
||
}
|
||
|
||
/* Registration */
|
||
void register_dictionary_words(VM *vm) {
|
||
register_word(vm, "HERE", dictionary_word_here);
|
||
register_word(vm, "ALIGN", dictionary_word_align);
|
||
register_word(vm, "ALLOT", dictionary_word_allot);
|
||
register_word(vm, ",", dictionary_word_comma);
|
||
register_word(vm, "C,", dictionary_word_c_comma);
|
||
register_word(vm, "2,", dictionary_word_2comma);
|
||
register_word(vm, "PAD", dictionary_word_pad);
|
||
register_word(vm, "SP!", dictionary_word_sp_store);
|
||
register_word(vm, "SP@", dictionary_word_sp_fetch);
|
||
register_word(vm, "LATEST", dictionary_word_latest);
|
||
} |