How best to map the core components of Forth onto the Mill?

From Mill Computing Wiki
Revision as of 18:17, 5 January 2015 by LarryP (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

In his post [[1]] forums, Ivan wrote:

The interesting part of doing Forth on a Mill is not the ops, which are straightforward, but in figuring out how to map the Forth dual-stack program model onto the belt

{LarryP opinion}: I'd generalize Ivan's statement to, "How best to map the Forth's key components (including the two stacks) onto the Mill architecture."

For the moment, let's put input/output functions and structures aside (including the outer interpreter, which is basically an input line editing/buffering device.) What's left that Forth needs:

  • A data stack
  • A return stack (or functional equivalent)
  • The inner interpreter
  • The dictionary, a contiguous chunk of memory used for both compiled words and static variables.

Let's dispose of the easy ones first:

The inner interpreter and any helper functions it needs must be in memory that has execute permission. This probably means that the interpreter and other directly executable code needs to be in a separate chunk of memory from the dictionary and stacks (though they may be adjacent.)

The dictionary is big (way too big to fit on the belt and/or the spiller) and needs both read and write permissions. Although words in the dictionary will be "executed," that execution is really just interpretation by the inner interpreter and any helper functions it calls.

The data stack is also very likely too big to fit on the belt, and will also need to be in read/write memory.


Now the less obvious Forth-to-Mill mappings

Unless we want to do painful, low-level management of call and return addresses (e.g. and use branches instead of calls), I think the best option for the return stack is to use the Mill's native call and return operations. Note that using Mill call/return ops will hide the actual CPU/spiller state from us. However, so long as call and return behave; I think that's probably OK.

However, the Mill's call/return ops impose something -- an a priori known number of results, that Forth doesn't itself enforce. Relatively few Forth words produce variable numbers of results, because those make stack management even trickier than otherwise. I think we can use either Nones or argc/argv for variable numbers of results.

Now using the Mill's call/return machinery means that to execute a word, the interpreter will have to:

  • Find the dictionary entry for the Forth "word" to be executed.
  • Extract from that dictionary entry the word's execution address, number of input arguments and number of results.
  • Get the input args onto its own belt
  • Call the function (probably using an indirect call)
  • Once the function returns, copy the results from the interpreter's belt onto the data stack, including handling possible variable number of results.


/{LarryP opinion}