MilliForth-6502, A Forth For The 6502 CPU

2 days ago 1

"A Forth in 328 bytes — the smallest real programming language ever, as of yet."

The milliForth1 is a review of sectorForth2, and smaller than sector Lisp3

The miniForth4 is another Forth to use in a boot sector of less than 512 bytes.

we are in hackaday

we are alive

( this repo needs a review )

Yes, bytes. But those are for a x86 16-bit CPU. How minimal could be it for a classic 6502 8-bit CPU ?

Two essentially different CPUs, a 16-bit x86 based on complex registers and opcodes, and a 8-bit 6502 using page zero as common registers and page one as hardware stack.

The FIG-Forth (1980) for 6502 uses Indirect Thread Code (ITC) as inner interpreter.

The miniForth, sectorforth and milliForth use Direct Thread Code (DTC)

This Forth for 6502, will be was done using two models:

with classic Direct Thread Code (DTC) and with Minimal Thread Code (MTC)

(later we will compare both, but DTC will win for less size)

This project is also used to verify standart Direct Thread Code against variations of Minimum Thread Code.

Minimal Thread Code is an alternative model for inner interpreter, where the dictionary is organized with the primitives words grouped together before the compound words, defining a "tipping point", from where could decide if the reference of a word will be to executed or be pushed into return stack.

PS. MTC is a reduced form of TachyonForth inner interpreter.

Focus in size not performance.

The compilation wqas done with ca65 V2.19 - Git 7979f8a41.

The emulation of 6502 CPU was done with run6502

The way at 6502 is use a page zero and lots of lda/sta bytes.

Both DTC and MTC runs with my_hello_world.FORTH, with these numbers.

MTC, Instructions: 35796948, Cycles: 148802145, SIZE: 582 DTC, Instructions: 35211469, Cycles: 148450585, SIZE: 596 compiling my_hello_world.FORTH, overhead MTC / DTC Instructions: 1.66% Cycles: 0.24%

(*) Thanks to wise changes from peterferrie !

  • as Forth-19945: FALSE is $0000 and TRUE is $FFFF ;

  • all tib (80 bytes), pic (16 cells), data (36 cells) and return (36 cells) stacks are in page $200 ;

  • tib and pic grows forward, stacks grows backwards ;

  • only immediate flag used as $80, no more flags ;

  • no line editor, no backspace, no cancel line, no low ASCII verify ;

  • no stacks overflow or underflow checks ;

  • 6502 is a byte processor, no need 'pad' at end of even names ;
  • hardware stack (page $100) not used as forth stack, free for use ;
  • uses 32 bytes of page zero ;
  • no multiuser, no multitask, no faster ;
  • only update latest at end of word definition ;
  • redefine a word does not change previous uses ;
  • stacks moves like the hardware stack ;
  • words must be between spaces, before and after ever ;
  • better use 7-bit ASCII characters ;
  • approuch as ANSI X3.215-19945

Look up at Notes6 for more.

24/08/2024 merge of dtc and mtc code into a flagged sector-6502.s file

16/08/2024: both models DTC and MTC, works with my_hello_world.FORTH;

11/08/2024: return to direct thread code;

11/06/2024: adapt for minimal thread indirect code;

24/01/2024: write using standart direct thread code;

14/11/2023 code for 6502 sized to 624 bytes, no ascii-7, no key, no emit, no 2/, many errors

This version includes:

primitives: s@ return the address of user structure + 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 0# test if top of data stack is not zero : starts compilng a new word ; stops compiling a new word exit ends a word key get a char from default terminal (system dependent) emit put a char into default terminal (system dependent) internals: spush, spull, rpull, rpush, (stack code) copyfrom, copyinto, (heap code) incr, decr, add, etc (register mimics) cold, warm, quit, token, skip, scan, getline, (boot and terminal) parse, find, compile, execute, (outer interpreter) unnest, next, nest, (dtc inner) unnest, next, nest, pick, jump, (mtc inner) ps. next is not the FOR NEXT loop externals: getch, putch, byes (depends on system, used minimal for emulator ) extensions: (selectable) 2/ shift right one bit exec jump to address at top of spt :$ jump to (ipt) ;$ jump to next extras: (selectable) bye ends the Forth, return to system abort restart the Forth .S list cells in data stack .R list cells in return stack . show cell at top of data stack words extended list the words in dictionary dump list contents of dictionary in binary A my_hello_world.FORTH alternate version with dictionary for use; The sp@ and rp@ are now derived from s@ in the my_hello_world.FORTH
$000 page zero ; cpu reserved $100 hardware stack ; cpu reserved $200 TIB ; terminal input buffer, 80 bytes $298* data stack ; data stack, 36 cells, backwards $2E0* return stack ; return stack, 36 cells, backwards $2E0 PIC ; reserved for scratch, 16 cells $300 _main_ ; start of Forth $??? _ends_ ; end of code and primitives of Forth $??? _init_ ; start of compound dictionary _init_ is the page (MSB) of _ends_ + 1

" When heap moves forward, move the stack backward "

Push is 'store and decrease'. Pull is 'increas and fetch'.

A common memory model organization of Forth:

tib->...<-spt, user forth dictionary, here->pad...<-rpt,

then backward stacks allow to use the slack space ...

This 6502 Forth memory model is blocked in pages of 256 bytes:

page0, page1, page2, core ... forth dictionary ...here...

at page2, without 'rush over'

|tib 40 cells> <spt 36 cells| <rpt 36 cells|pic 16 cells> | .

"SectorFORTH 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'."

For Forth language primer see Starting Forth

For Forth from inside howto see JonasForth

** 16/08/2024 DTC and MTC models operational, using lib6502 for emulation and tests **

A crude small script for compile with ca65 is included.

; for make it sh mk a sector-6502 ; for clear it sh mk x sector-6502

the size is take from main: to ends: using the sector-6502.lbl file

the originals files are edited for lines with less than 80 bytes

the bf.FORTH and hello_world.FORTH are from original milliForth1

the my_hello_world.FORTH is adapted for miiliforth-6502

  1. The original milliForth: https://github.com/fuzzballcat/milliForth 2

  2. The inspirational sectorForth: https://github.com/cesarblum/sectorforth/

  3. Mind-blowing sectorLISP: https://justine.lol/sectorlisp2/, https://github.com/jart/sectorlisp

  4. The miniforth: https://github.com/meithecatte/miniforth

  5. Forth standart ANSI X3.215-1994: http://www.forth.org/svfig/Win32Forth/DPANS94.txt 2

  6. Notes and Times: https://github.com/agsb/milliForth-6502/blob/acc2f8ddc6aafb2dec6346e90f5372ee16b38c8c/docs/Notes.md

Read Entire Article