6 ms·
I wrote a Forth interpreter for a SUBLEQ machine (https://github.com/howerj/subleq https://github.com/howerj/subleq), and for a bit-serial machine (https://gith
by howerj 3y ago
I wrote a Forth interpreter for a SUBLEQ machine (https://github.com/howerj/subleq https://github.com/howerj/subleq), and for a bit-serial machine (https://github.com/howerj/bit-serial https://github.com/howerj/bit-serial), both of which do not have a function call stack which is a requirement of Forth. SUBLEQ also does not allow indirect loading and stores as well and requires self-modifying code to do anything non-trivial. The approach I took for both machines was to build a virtual machine that could do those things, along with cooperative multithreading. The heap, if required, is written in Forth, along with a floating point word-set (various MCUs not having instructions for floating point numbers is still fairly common, and can be implemented as calls to software functions that implement them instead).
I would imagine that other compilers took a similar approach which wasn't mentioned.
EDIT: There were some BASIC interpreters which did this as well, implementing a VM and then targetting that instead. P-Code is a similar thing.
- anthk 3y agoI was about to talk about subleq, but it's damn difficult even to write a "Hello world".
- howerj 3y agoYeah, it really is a pain to do anything in it. I got around that by writing in another language instead as soon as possible. You can paper over many issues with a VM, although it will slow things down on such limited computers.
- anthk 3y agoHow does muxleq work? It looks like a 'concurrent' subleq...
- howerj 3y agoI am not sure what you mean by concurrent in this case, muxleq is just subleq with one extra instruction. The extra instruction is based off of multiplexing (see https://en.wikipedia.org/wiki/Multiplexer https://en.wikipedia.org/wiki/Multiplexer). Subleq as you know is incredibly inefficient, muxleq is an experiment to add a single instruction to Subleq in order to greatly improve its efficiency. The `mux` instruction added computes: m[operand2] = (m[operand1] & ~selector) | (m[operand2] & selector) As multiplexing is a universal gate (so long as you have access to true and false as constants) you can use this to calculate AND/OR/XOR, which are very expensive to do in a pure Subleq machine. It also means you can do a MOV in a single instruction instead of four (set the `selector` to zero in this case). This in turns speeds up indirect loads and stores.
- parlortricks 3y agoI came across your works learning and consuming all things Forth and Subleq. It was great to read over how you approach things. I wanted to purchase your book but Amazon says no...will there be another run?
- howerj 3y agoThanks! You should still be able buy it, you might have to either click on "Paperback" or "Hardcover", if you want to get the kindle edition you have to go to the Amazon webpage that serves you country (e.g. if you are in the UK and you are on amazon.com you get "This title is not currently available for purchase", but if you go to amazon.co.uk, you get buy it). That's an odd user interface issue...
- parlortricks 3y agoAhhhh Amazon weirdness...all good, i managed to sort it out, paperback on its way!
- bitwize 3y ago> EDIT: There were some BASIC interpreters which did this as well, implementing a VM and then targetting that instead. The TI-99/4A had 256 bytes (128 words) of main, CPU-accessible RAM. Most of the base system's memory was video RAM, accessible by a relatively cumbersome process of poking and peeking registers on the system's video chip. The video chip maintained an autoincrementing current memory pointer, so successive reads (or writes) would bump the pointer by one allowing straightforward transfers of data, but the very fact that most of the system's memory was only accessible in this way made significant programs difficult to write. So, TI's solution was to create an abstract machine called GPL in which memory accesses to this video RAM were more natural. It was interpreted on the TMS9900 and therefore slower than native code, though -- especially given that the CPU can only access the video chip's RAM while the chip itself is not doing scanout to the display, so during horizontal and vertical retrace. And since all the BASIC code and variables lived in this video memory, guess what the TI-99/4A's BASIC interpreter was written in! Yeah, it wasn't very fast, like at all. The neat part, apropos of the article's topic, is that there were no actual general-purpose registers on the TMS9900: workspace registers WR0 through WR15 were instead located somewhere in memory, pointed to by the WP (workspace pointer) register. The CPU only had three physical registers: PC (program counter), WP, and a status register. What this amounted to was you could do a very primitive form of register windowing: by using the BLWP (Branch and Load WP) instruction, you can branch to a subroutine in which a new set of "registers" will be active elsewhere in memory -- and the return address will be saved in the new workspace. If I'm going on about the TI-99/4A a lot recently, it's because I'm writing an assembler for it as a personal project.
- snowAbstraction 3y agoInteresting. For other readers, this is about an early 80s Texas Instruments computer and not a graphing calculator as I first thought.