"To master riding bicycles you have do ride bicycles"
started at 23/07/2025, agsb@
first version at 12/10/2025, @agsb
minimal dictionary compiled words at 04/12/2025, @agsb
Please, vide Changes and Notes
Any Forth system depends on the I/O functions and the executable linkable format (ELF) of the host system.
The problem is reach a functional minimal code forth engine for RISCV ISA.
This is an implementation of MilliForth (sector-forth) concept for RISCV ISA, using Minimal Indirect Thread Code.
Milliforth uses a minimal set of functions and primitives for make a Forth.
This version with minimal code (.text), uses only 454 bytes, 388 bytes for Forth engine and 66 bytes for linux system I/O. Not counting ELF headers. Used 56 bytes to load ELF PIC address and 44 bytes for word headers.
No human WORDS. It uses DJB2 hash in headers.
No Terminal Input Buffer, just an token-to-hash stream ascii parser.
Only use a IMMEDIATE flag, at MSBit (31) of hash, it also is NaN, used to indicate errors.
There are a file with more core words in native code to use.
How shink to a minimal compiled size in a Risc-V ?
1. do not need align, the size of opcodes is always 2 or 4 bytes;
2. choose registers to maximize use of compressed riscv opcodes;
3. warn the user about possible errors but abandon error checking;
4. Use streams, no buffers;
5. do not speculate;
The sector-riscv.S is working, also the extra-milliforth.S,
could test by:
**cat t0.f t1.f t2.f - | sh doit.sh | tee output**
t0.f is a minimal set of words, same as test0-riscv.f;
t1.f is a complement with hash and more words;
the hiphen refers to terminal (/dev/tty)
Could test by:
cat t0.f | sh doit.sh | tee z1
cat t0.f t1.f | sh doit.sh | tee z2
t1.f includes <builds create variable constant does> (_STUB_)
PS.
Add a hyphen at end of cat files list to allow terminal I/O
cat t0.f t1.f t2.f - > sh doit.sh
Some esoteric bug makes the first word to have hash error.
The memory management is done by extend the dictionary
into .bss, by reserve .skip bytes, defaults to 64k * 4
no linux calls for memory allocation. (Anyone ?)
The source could be compiled with 'missed' hack and
more extensive native code word set.
This version uses DJB2 hash for dictionary entries, and includes:
minimal primitives:
u@ return the address of user structure
0# if top of data stack is not zero returns -1 (0xFFFFFFFF)
+ adds two values at top of data stack
nand logic not-and the two values at top of data stack
@ fetch a value of cell wich address at top of data stack
! store a value into a cell wich address at top of data stack
: starts compiling a new word
; stops compiling a new word
exit ends a word
key get a char from default terminal (stdin)
emit put a char into default terminal (stdout)
only internals:
main, cold, warm, miss, warp, abort, quit,
token, skip, hash, scan, mask,
find, eval, compile, execute, immediate, comma,
unnest, next, pick, jump, nest, move
ps. exit is unnest, nest is enter/docol,
next is not the NEXT of FOR loop !
with externals, ecall to linux:
_getc, _putc, _exit, ( _fcntl, _init )
extras: (selectable)
;CODE execute native code at instruction pointer (IP), vide Notes
ABORT restart the Forth interpreter
BYE ends the Forth, return to system
. show the cell at top of data stack in hexadecimal
% next token is a signed integer hexadecimal number to TOS
debug:
%S list cells in data stack
%R list cells in return stack
WORDS list all compiled words in dictionary order
DUMP list contents of dictionary in memory order
SEE list compiled hash contents of last word
For Forth language primer see Starting Forth
For Forth from inside howto see JonasForth
For A Problem Oriented Language see POL