A FORTH in 336 bytes — the smallest real programming language ever, as of yet.
The code in the above gif, that of [an older version of] hello_world.FORTH
, is a modified version of the hello world program used by sectorFORTH (see below)
Yes, bytes. This is a FORTH so small it fits in a 512-byte boot sector. This isn't new — sectorFORTH[^1] successfully fit a FORTH within the boot sector. However, milliFORTH appears to be the smallest "real" programming language implementation ever, beating out sectorLISP[^2], a mind-blowing 436 byte implementation of LISP, by 96 bytes. ("real" excludes esolangs and other non-production languages - for example, the sectorLISP author's implementation of BF is just 99 bytes, but it clearly isn't used to any serious capacity.) It's now even smaller thanks to some really neat suggestions!
Yes! This project now includes bf.FORTH
, a compliant brainfuck interpreter, to illlustrate that this is truly a real language.
sectorFORTH[^1] was an extensive guide throughout the process of implementing milliFORTH, and milliFORTH's design actually converged on sectorFORTH unintentionally in a few areas. That said, the language implemented is intentionally very similar, being the 'minimal FORTH'.
FORTH itself will not be explained here (prior understanding assumed). Being so small, milliFORTH contains just a handful of words:
Word | Signature | Function |
---|---|---|
@ |
( addr -- value ) |
Get a value at an address |
! |
( value addr -- ) |
Store a value at an address |
sp@ |
( -- sp ) |
Get pointer to top of the data stack |
rp@ |
( -- rp ) |
Get pointer to top of the return stack |
0# |
( value -- flag ) |
Check if a value does not equal zero (-1 = TRUE, 0 = FALSE) |
+ |
( a b -- a+b ) |
Sum two numbers |
nand |
( a b -- aNANDb ) |
NAND two numbers |
exit |
( r:addr -- ) |
Pop from the return stack, resume execution at the popped address |
key |
( -- key ) |
Read a keystroke |
emit |
( char -- ) |
Print out an ASCII character |
s@ |
( -- s@ ) |
The "state struct" pointer. The cells of this struct are, in order:
|
On a fundamental level, milliFORTH the same FORTH as implemented by sectorFORTH, with a few modifications:
s@
).tib
(terminal input buffer) word, because tib
always starts at 0x0000
, so you can just use >in
and don't need to add anything to it.ok.
). Erroneous input prints an extra blank line between the previous input and the next prompt.sector.bin is an assembled binary of sector.asm. You can run it using make emulate
, which invokes (and thus requires) qemu-system-i386
, or by using any emulator of your choice.
Alternatively, make
will reassemble sector.asm, then run the above qemu emulator.
Additionally, you can run an example file easily by running make runfile file=SOURCE_CODE
. Try out make runfile file=hello_world.FORTH
or make runfile file=bf.FORTH
! NOTE: Files run this way currently do not accept user input from stdin, as the file itself is being piped to qemu. Fix coming shortly.
make sizecheck
is a utility which assembles sector.asm into sector.bin and then lists out the size of sector.bin for you. Note that this automatically removes the padding from the .bin (as a working bootloader must be exactly 512 bytes).
[^1]: The immensely inspirational sectorForth, to which much credit is due: https://github.com/cesarblum/sectorforth/. [^2]: Mind-blowing sectorLISP: https://justine.lol/sectorlisp2/, https://github.com/jart/sectorlisp.