Scraped on: Sun 12 Apr 2020 03:55:32 AM PDT mikekchar 3 months ago [-] In FORTH there is a distinction between the compiler and the interpreter. Each function in FORTH is called a "word". You define a word by giving it a name (historically only the first X characters counted -- often 8). The compiled words are stored in a "dictionary" which is essentially a key value pair with the name of a compiled word and a pointer to its compiled code. When you are compiling a word, you add a new entry to the dictionary. Then for each word contained in the new word you are defining, you look it up in the dictionary and store the pointer to its compiled code. So essentially, the compiled code consists of a list of pointers -- one for each function call you are making. It's a little more complicated than that, but not much. Numerical literals need to be added to the stack rather than being treated as a pointer to a function, but there are a variety of ways you can tag the information you are putting in the list. The interpreter works by taking a pointer to the function, and running it. This will essentially jump you to another pointer to a function which you will run. That will jump you to another pointer to a function which you will run. Eventually you will end up pointing to a function that was hand implemented in assembly language (part of the kernel for the language). This technique is known as a "threaded interpreted language" (TIL). I haven't really explained it very well, but essentially it's just either pushing a literal onto the stack or jumping to a subroutine. The actual code for the interpreter is insanely small because it does virtually nothing (either jump to a subroutine or push a value onto the stack). While the entire thing is not 2 or 3 machine instructions, the actual running functionality would just be looping through something of that size. One of the nice things about this setup is that it's pretty easy to write an entire FORTH kernel in 16 or 32K. So all of the actually executing code will fit in the cache of even a small processor. The rest of the code is literally lists of addresses and integer literals. They are super easy to fetch and you can also be tricky about optimising how you fetch them. The end result is that you barely ever hit main memory when talking about the code part of the system. And since you prefer working on the stack to working on the heap, you get really good locality on the working memory as well. This can give you insanely good performance with very little cognitive overhead as a programmer. mycall 3 months ago [-] It the kernel and interpreter typically single threaded? I'm curious how it handles UARTs, IRQs and atomics. tdeck 3 months ago [-] Here's a good doc on multitasking in FORTH: https://www.bradrodriguez.com/papers/mtasking.html