First code for StarForth v4 (JUSTIFICATION.md section 10, step 1): one node of the 32-instruction core as a C99 model, with cell width as a build parameter. - Node: P, A, B, F18 circular stacks (10 and 9 deep, D-2), word-addressed memory (D-1), 5% guard bands on every bounded list. - Instruction word: six 5-bit slots in 32 bits at every cell width. - Executor: all 32 opcodes of DECOMPOSITION.md 1.3. Cell arithmetic wraps explicitly; no signed overflow or implementation-defined shift. - Heat: per-opcode and per-call-target counters and the anti-clock, driven by instruction retirement (1.4, D-6 interim). - Slot packer and runner for tests, and a reference unsigned multiply in plain C99 with no 128-bit type. Tests run at 32- and 64-bit cells, and under ASan and UBSan. They cover every opcode and execute the first section 4 definitions (NIP SWAP OR NEGATE ROT 0< 0= 2DUP - U<) against the C operation each stands for. UM* as written in section 4 is exact only while u1 <= 2^(n-2). Two known failing cases are pinned in test_foundation.c until it is rewritten. DECOMPOSITION.md: record D-9, the instruction word is 32 bits at every cell width (ruled 2026-10-02). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
4.8 KiB
C
102 lines
4.8 KiB
C
/* iword.h -- the 32-bit instruction word and its decode.
|
|
*
|
|
* DECOMPOSITION.md section 1.2:
|
|
*
|
|
* 31 27 26 22 21 17 16 12 11 7 6 2 1 0
|
|
* +--------+--------+--------+--------+--------+--------+----+
|
|
* | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 | xx |
|
|
* +--------+--------+--------+--------+--------+--------+----+
|
|
*
|
|
* "Slots execute left to right. A branch (jump, call, next, if, -if) takes
|
|
* its target from all bits to the right of its slot, and that target replaces
|
|
* the same low bits of P (page-relative, as in the F18). Branches are
|
|
* therefore legal in slots 0-3 only", with reach 27 / 22 / 17 / 12 bits.
|
|
*
|
|
* That reach column is not a separate rule, it falls out of the layout: slot k
|
|
* sits at bits [31-5k : 27-5k], so the bits to its right are [26-5k : 0],
|
|
* which is 27-5k bits -- 27, 22, 17, 12 for slots 0..3. This header derives
|
|
* the masks from that one formula rather than hard-coding four of them, so the
|
|
* decode cannot drift from the reach table.
|
|
*
|
|
* THE SPARE BITS ARE ADDRESS BITS. "Spare" means only that they are never
|
|
* *executed*: they are below every slot, so "all bits to the right of its
|
|
* slot" includes them, and they are the low bits of the address of a branch in
|
|
* any slot. They are inert for opcode decode and live in a branch target.
|
|
* This is easy to assume otherwise and get wrong -- an earlier revision of
|
|
* this header subtracted them from the branch width, which silently cost two
|
|
* address bits off every branch and was caught by tests/test_iword.c. They
|
|
* exist because 6*5 = 30 does not fill a 32-bit word, and on the F18 those low
|
|
* bits are simply the bottom of the address space.
|
|
*
|
|
* The consequence for the compiler is worth stating: a non-branch slot's low
|
|
* bits are free to carry a compressed argument, but a branch slot's are not,
|
|
* because they are address.
|
|
*
|
|
* NOTE ON WIDTH. The word format is 32 bits regardless of V4_CELL_BITS: the
|
|
* diagram is 32 bits wide and 6*5+2 = 32 exactly. D-5 makes the *cell* width
|
|
* a build parameter, but the instruction word is not a payload and does not
|
|
* follow the cell. At 32-bit cells a word is a cell. At 64-bit cells it is
|
|
* the low 32 bits of the cell and the high half is ignored (D-9), so compiled
|
|
* code is identical at both widths. The decode below operates on a distinct
|
|
* 32-bit type; the fetch in exec.c does the narrowing.
|
|
*/
|
|
#ifndef V4_IWORD_H
|
|
#define V4_IWORD_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "v4/cell.h"
|
|
#include "v4/opcode.h"
|
|
|
|
/* The instruction word is 32 bits at every cell width. */
|
|
typedef uint32_t v4_iword;
|
|
|
|
#define V4_SLOT_COUNT 6u /* six 5-bit slots, DECOMPOSITION.md 1.1 */
|
|
#define V4_SPARE_BITS 2u /* "plus 2 spare bits" */
|
|
|
|
/* C99 has no _Static_assert, so the "6*5+2 == 32" identity is enforced with
|
|
* the negative-array-size idiom. If the slot count, the spare-bit count or the
|
|
* word width ever stop adding up to 32, this fails to compile. */
|
|
typedef char v4_iword_layout_is_exact
|
|
[((V4_SLOT_COUNT * 5u + V4_SPARE_BITS) == 32u) ? 1 : -1];
|
|
|
|
/* Bit range of slot k: high bit 31-5k, low bit 27-5k. */
|
|
#define V4_SLOT_HIGH_BIT(k) (31u - 5u * (k))
|
|
#define V4_SLOT_LOW_BIT(k) (27u - 5u * (k))
|
|
|
|
/* Number of address bits a branch in slot k carries: all bits to the right of
|
|
* the slot. Slot k's low bit is 27-5k, so the bits below it are 0..(26-5k),
|
|
* which is 27-5k bits -- equal to the slot's own low bit. This is the single
|
|
* source of the reach column, and it is why the spare bits are *not* subtracted
|
|
* here: see the note on the spare bits below. */
|
|
#define V4_BRANCH_ADDR_BITS(k) (V4_SLOT_LOW_BIT(k))
|
|
|
|
/* Mask of the low address bits a branch in slot k replaces in P. */
|
|
uint32_t v4_iword_slot_mask(unsigned slot);
|
|
|
|
/* The 5-bit opcode in slot k, 0..31. */
|
|
unsigned v4_iword_op(v4_iword w, unsigned slot);
|
|
|
|
/* The 2 spare low bits. Never executed. */
|
|
unsigned v4_iword_spare(v4_iword w);
|
|
|
|
/* The branch target carried by a branch in slot k: all bits to the right of
|
|
* the slot, zero-extended. Meaningful only for the five branch opcodes. */
|
|
v4_iword v4_iword_target(v4_iword w, unsigned slot);
|
|
|
|
/* Assemble a word from six 5-bit opcodes and the 2 spare bits. Opcode values
|
|
* above 31 or spare bits above 3 are rejected, because a golden model must not
|
|
* paper over an out-of-range encoding. */
|
|
v4_iword v4_iword_assemble(const unsigned op[V4_SLOT_COUNT], unsigned spare);
|
|
|
|
/* Apply a branch: replace the low V4_BRANCH_ADDR_BITS(slot) bits of P with the
|
|
* target, preserving the bits above (page-relative, F18-style). */
|
|
v4_cell v4_iword_branch(v4_cell p, v4_iword w, unsigned slot);
|
|
|
|
/* Branches are legal in slots 0-3 only (section 1.2). Slots 4 and 5 have 7 and
|
|
* 2 address bits respectively, which is not a useful reach and is not
|
|
* sanctioned by the table. */
|
|
int v4_iword_branch_legal(unsigned slot);
|
|
|
|
#endif /* V4_IWORD_H */
|