You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
wc64 is a 64-bit implementation of Forth inspired by Tachyon, written for the FASM assembler.
The executable is currently at about 5528 bytes.
wc64 has a bunch of primitives and it:
has a minimal "inner loop"
can create colon-definitions,
knows about immediate words,
can make any syscall (with 0-6 arguments), and
automatically loads a 'boot-file' named 'wc64-boot.fth' if it exists.
Architecture Highlights
Direct-threaded interpreter with tail-call dispatch optimization (31.5% faster than traditional CALL/RET)
Bit 63 tagging for numeric literals and constants: enables unified dispatch through the interpreter
Tagged constants in primTable: (lit), (jmp), (jmpz), (jmpnz), (njmpz), (njmpnz), (h), mem, cell, (l), base, state are compile-time constants generated via the TAGGED_NUM macro
Tagged Constants Explanation
The interpreter uses bit 63 as a marker to distinguish between different value types:
Bit 63 = 0: Code address (primitive or colon definition) → execute via jmp
Bit 63 = 1: Numeric literal or constant → extract value with btr and push to stack
This unified approach eliminates special casing in the dictionary lookup and dispatch logic. Constants like cell (8) and address references like (h) are stored in primTable with bit 63 set, so they behave identically to numeric literals in code.
TAGGED_NUM Macro
Tagged constants are generated using the TAGGED_NUM macro:
macro TAGGED_NUM name, val { name = (val) + xNum}TAGGED_NUM PLIT_ADDR, p_LITTAGGED_NUM PJMP_ADDR, p_JMP
This approach keeps the constant definitions DRY and ensures all tagged values are consistently marked with bit 63.
Building wc64
On Linux, use make. Requires that fasm is installed.
I actually directed Claude (Haiku 4.5) to code most of it (with guidance from me), so I am hoping that it "understands" it well enough that it might be able to migrate it to another CPU/platform without too much trouble.
Built-in primitives:
Stack Manipulation
Word
Stack
Description
dup
(n -- n n)
Duplicate top of stack
drop
(n --)
Remove top of stack
swap
(n1 n2 -- n2 n1)
Swap top two stack items
over
(n1 n2 -- n1 n2 n1)
Copy second item to top
Arithmetic
Word
Stack
Description
+
(n1 n2 -- n3)
Add
-
(n1 n2 -- n3)
Subtract (n1 - n2)
*
(n1 n2 -- n3)
Multiply
/mod
(n1 n2 -- q r)
Quotient and remainder
1+
(n -- n')
Increment
1-
(n -- n')
Decrement
negate
(n -- -n)
Negate
Logical/Bitwise
Word
Stack
Description
and
(n1 n2 -- n3)
Bitwise AND
or
(n1 n2 -- n3)
Bitwise OR
xor
(n1 n2 -- n3)
Bitwise XOR
invert
(n -- ~n)
Bitwise NOT
Comparison
Word
Stack
Description
=
(n1 n2 -- f)
Equal (0 or -1)
<
(n1 n2 -- f)
Less than (0 or -1)
>
(n1 n2 -- f)
Greater than (0 or -1)
Memory Access
Word
Stack
Description
@
(addr -- n)
Fetch 64-bit cell from memory
!
(n addr --)
Store 64-bit cell to memory
c@
(addr -- c)
Fetch byte from memory
c!
(c addr --)
Store byte to memory
Return Stack
Word
Stack
Description
>r
(n --) (R: -- n)
Push to return stack
r@
(-- n) (R: n -- n )
Copy return stack top
r>
(-- n) (R: n --)
Pop from return stack
Literals & Code
Word
Stack
Description
lit
(-- n)
Push next cell as literal
(lit)*
(-- addr)
Address of lit primitive (tagged constant)
lit,
(n --)
Compile n as literal
(h)*
(-- addr)
Address of HERE variable (tagged constant)
mem*
(-- addr)
Base address of code memory (tagged constant)
cell*
(-- n)
Size of a cell in bytes (tagged constant)
,
(n --)
Compile cell to HERE
* Indicates tagged constant: value is pushed directly via bit 63 tagging
I/O
Word
Stack
Description
emit
(c --)
Output character
type
(addr len --)
Output string
key
(-- c)
Read character from input
cr
(--)
Output newline
Dictionary
Word
Stack
Description
(l)*
(-- addr)
Address of LAST variable (tagged constant)
base*
(-- addr)
Address of current number base variable (tagged constant)
state*
(-- addr)
Address of compile/interpret state variable (tagged constant)
find
(cs -- addr)
Find word in dictionary (0 if not found)
add-word
(--)
Add the next word to dictionary
* Indicates tagged constant: value is pushed directly via bit 63 tagging