This is legit a text that goes the an x86 Forth implementation. Actually, it's just an implementation with really extensive comments. That said, including whitespace and comments, it's just 2000 lines and the pedagogy is excellent. Highly recommended for anyone who would rather see behind the curtain before picking up a larger text.
So, Smith decided to hand-write a Forth directly in x86-64 opcodes (well, the corresponding ascii hex bytes). It's incredibly slim and enlightening how you can bootstrap a language in just a couple hundred bytes or so.
This project actually inspired me to really learn the x86-64 architecture, so I ended up hand-decompiling the SmithForth binary instead of going through his commented implementation. Hand-decompilation is an absolutely fascinating exercise. You learn all about ELF structure, opcode encodings, and actually start to see the gaps where microarchitectural details shine through. Highly recommended for any hacker that really wants to grok low level details.
Don't forget eForth, which I believe is a better, and more "Forthy" implementation than JonesForth is, though not as easy to understand for a newcomer.
Another outstanding resources are the post from Bradford J. Rodriguez. Can't write a Forth implementation without going through the wisdom that's condensed in those links (see the first section):
This is what got me interested in Forth using ARM STM32 devices a couple years ago.
Specifically, I put Mecrisp-Stellaris Forth on an STM32F401. It was fun! You can experiment and quickly learn (fail fast) trying different features -- could try features of the chip as I read the datasheet.
I was able to translate those learnings to a C implementation as well.
"In fact, you shouldn’t write any serious application in Forth; as a lan-
guage it’s simply not powerful enough. What you should do is write your own
language in Forth (lexicons) to model your understanding of the problem, in
which you can elegantly describe its solution."
I've enjoyed reading about FORTH and LISP over the years and I think LISP advocates have a similar philosophy. What Leo Brodie states so nicely is very hard for me to actually do. I think that's what separates the people who "get" computing from those (like me) who glue bits of various libraries together to get past the problem of the moment.
As an HP calculator lover (I wore the ENTER > = t-shirt 40 years ago) I understand the stack and RPN but have no idea how I'd write a trig or stat function if it weren't already built in!
> As an HP calculator lover (I wore the ENTER > = t-shirt 40 years ago) I understand the stack and RPN but have no idea how I'd write a trig or stat function if it weren't already built in!
From talking to several people who've implemented things like this, I think they would've said the same thing before.
Nobody walks around knowing how to implement these kinds of things. If you need it, you just start trying, learning, reading, etc, until eventually you do it.
Anecdote about people at different stages of that learning:
I worked at FORTH, Inc. one summer in the 80s before starting college. They sold a Forth system which they were converting to a new language standard, which (among other things) had changed the canonical booleans from 1-for-true to -1. One day I noticed the trig functions were giving wrong answers.
So I bring up the code with my boss, and it's pretty hard for us to figure out -- compact, no explanation, tables of random-looking numbers. Someone stops by (Dean Sanderson? iirc), we tell him what's up, he looks at the screen, and almost right away he's like "Oh, this is easy! All you need to do is swap the lines in the tables."
FWIW, I did try. Many languages now have websites where one can experiment with an interpreter or compiler. I've used them for playing with JavaScript, APL, Python, etc. Strangely, there does not seem to be one for FORTH. SwiftForth from forth.com does not work with 64-bit Intel Mac OS-es like Catalina which is what I have [1].
I tried gForth as well but during the installation, it invoked HomeBrew which gave a series of hostile messages that my system is obsolete and to not bother asking for any help or support. See [2] and the output provided by the poster - it is what I saw in my case.
> I've enjoyed reading about FORTH and LISP over the years and I think LISP advocates have a similar philosophy.
Want to make a hacker laugh? Say "Forth is the dual of Lisp" to someone familiar with both languages. It seems they reach the same goal of a totally programmable programming language through completely opposite approaches.
The spec for a language is harder to define than the compiler for it. The spec for Lisp is written in Lisp. And it's about half a page of Lisp code. And it's executable, which is not usually true with specs.
This is why reading the Lisp spec feels like reading "the Maxwell's equations of software" (to quote Alan Kay). It's a whole other fundamental model of computation, every bit as fundamental as a Turing machine, but much more practical. Nobody builds serious computing engines out of pure Turing machines, but people do it all the time with McCarthy machines.
The LISP 1.5 Programmers manual! Love this one. Your 2) is what Alan Kay called "Maxwell's Equations of Software".
And you're right, there's absolute magic in old computing papers. They were pioneers. I wish we could see more of this pioneering spirit combined with todays silicon and ease of international communication.
In my mind, Forth is a "reverse Lisp". Not sure why that's funny though :)
They're both the same basic idea, just that Forth is a much more radical low-level implementation of that idea (e.g. it was just perfect for minimal 8-bit home computers).
And creating a DSL for your problem, and user functions being identical to build in functions, and being able to fully extend the language yourself, to the point of adding custom conditionals or whatever, and several other things....
Aside from the functional vs concatenative paradigms, they share a lot of development philosophy, and give a lot of the same superpowers to the developers.
PostScript is kind of like a cross between Forth and Lisp. But it's actually a lot more like Lisp than Forth: high level, polymorphic, homoiconic, etc.
on: What are the enduring innovations of Lisp? (2022):
>but have no idea how I'd write a trig or stat function if it weren't already built in!
Well, that's not really about programmings skills though. That's math.
You could write a decent approximate SIN using the taylor series formula [1]. It's pretty easy, you don't even need to know math, can just look the formula up and translate it to an algorithm. Real world language SIN methods use more efficient and accurate methods, but it's still about knowing the math or looking up common such algorithms.
Or you could just invoke the libc (standard C library) SIN from Forth or whatever language you're using. That's what Python and other languages do.
The quote is more about the approach of writing an application by writing a DSL for the application, than about starting from zero and re-doing all the low level libraries like trig yourself.
Fair enough. I certainly understand Taylor series. 40+ years ago I experimented with series approximations using the programmable HP calculators of the day to see how my functions compared to the builtins. I intended to say that I would struggle to write something worth using. Poor wording to say the least.
I had no idea FORTH could invoke libraries from C. I got the impression it would be used on systems where space is at a premium precluding the installation of other higher-level languages.
There are even bindings to C/C++ GUI libraries and so on. Running at constrained systems is just a common deployment scenario, but nothing in Forth necessitates it. You can even run it under Windows.
The DSL approach to Lisp programming was mainstream in the 80's. It comes natural to any system that has enough frictionless syntactic plasticity and expressiveness.
These days it still exists in Racket (and possibly others), see language oriented programming ideas.
I would like to add though, that "language" does not have to mean macros and DSL. It can also simply mean what kind of things one considers primitives for the problem to solve. Those primitives can be built in already, they can be user defined functions (or words) or macros or whatever else makes an identifier or thing one can reference later when solving the actual problem.
Agreed that the semantic of language-oriented is sometimes hard to disentangle from the aesthetic of "parsing and compiling". In a majority of cases you can go part of the way in that direction by overlaying a FSM, stack machine or a trivial string parser onto whatever feels cluttered and obnoxious to keep track of.
If you want to do the parsed exterior DSL thing today, the magic wand that probably does it the best is Raku and its souped-up grammar system. I actually am exploring that now and I think it has a good toolkit for prototyping anything resembling a textual interface.
If you want to solve computing problems more directly, you will want to apply the approaches of Thinking Forth, regardless of the language you use. A lot of software and features are there to communicate with other people and their software. Forth is syntax for talking with the machine, and you can learn to distinguish between the two by engaging with Forth and trying to do more things with it.
Forth is also a pretty good development target, if you need a "fully featured" stack VM with a repl. There's never been a requirement that using Forth means "writing the entire system in a Forth that you bootstrapped yourself". Sometimes you want to have a little build step on top, and sometimes you want to target a system that has more than the minimalistic or standards-driven stuff, or you want to have a small interpreter over library code that has implemented the fast algorithms already. Forth systems tend to be personalized, quirky, and under-documented, but most are also easy to strip down and examine closely.
When I recommend this book to people I like to say: "it was talking about TDD, modularity, iterative development, and all your _cool things_ back in the 80s ...".
The next time a thread about "what should I give a kid today instead of BASIC" rolls around, a Forth with Starting Forth and Thinking Forth is a probable answer.
Forth gives an even more direct experience than the microcomputer BASICs did: you wrote a word, it was a number so it went on a stack, then you wrote a different word, and it popped the number from the stack. You allocated memory with ALLOT. Now when you run HERE(the address of the bump allocator), you see that it moved up by the amount that you passed to ALLOT. You can use SEE to explore word definitions and get a sense of how much of the language is implemented in itself. You can explore the double number arithmetic to learn fixed point maths.
It just poses a certain clarity and lack of restriction that is only limited by how much the programmer can reason through their abstractions. Thinking Forth helps with that, in that it shows that you can often solve things in a simpler way than you thought.
I keep wanting to do a project in FORTH and even have started a couple, but always end up punting and going to something else (usually assembly, they're vintage computer projects). I do have a Rockwell R65F11 FORTH microcontroller-on-a-chip I'm working on turning into a clock that I've tried to promise myself not to just do in 6502 ASM :P
Back when those came out I used a dozen of them on a serial network to run a custom robot. As part of that project I designed a floppy disk controller, paged memory expansion boards, motor control and encoder interface boards, control panel interface and more.
In case the context isn’t clear to some, no Internet, no Arduino…you couldn’t buy a bunch of little boards to solve problems. The entire project consisted of a couple dozen wire-wrapped STD-44 boards. Like these:
I came across the R65F11 I have from Burgess Macneal, who was using FORTH in the early 1980s to build a digitally controlled audio mastering equalizer. For various reasons it never became a commercial product, but he'd saved all of his prototyping material.
Burgess had been using FORTH on his Ohio Scientific, so I suppose the transition to another 6502-core was probably pretty easy!
I went looking and found an old storage device (binder) with long-term storage technology elements (sheets of paper) for my old R65F11 designs. Not the entire thing, but a couple hundred pages of H-CAD (hand drawn pencil schematics), printed code listings and manuals. Here's the main processor board:
In the documentation set I have a full printed listing for some version of the code editor I wrote as well as a pile of utilities and drivers. This is dating back almost exactly 40 years.
Also have the schematics for various interface boards, including a floppy disk interface board and, I believe, the driver I wrote to run screen-based code storage and retrieval from it. This was a very long time ago. I can't remember how closely these schematics and code matched working hardware.
If any of this could help you I might be able to scan and post somewhere. It will take some work because I have to use a flat-bed scanner. It's amazing how well paper and vellum survive four decades inside a binder.
Thanks for the uploads! I will check today but I believe I've got hardcopy of the datasheets/appnotes you have -- Burgess also grouped design docs into binders, so I have his originals. It's a habit I started carrying over to my own projects a number of years ago, never have to worry about something being deleted by accident, a format becoming hard to read (switched EAGLE CAD -> KiCAD 9 years ago), etc. Plus, I find it easier to read dead tree reference material when working with something on the bench!
We did a R6501Q based SBC a number of years ago, and eventually got RSC FORTH kernel and development ROMs running on it. We'd found a copy of the ROM object code in Burgess's archives and got it going from that. I want to eventually add a floppy controller to it, a friend had been working on replacing some of the floppy-based screen storage with CompactFlash backed storage, but got busy on other things.
What was the intended purpose for your R65F11 hacking?
I've done some mostly trivial FORTH development [^0] and love it! That being said... there's absolutely zero guard rails. You'll find just as many, if not more segfaults as you would in C and there's no concepts of types really.
GForth is pretty good, the docs and tutorial are a great way to get started and the implementation is still actively developed.
- Why is shell script more succinct than JavaScript at some things
- How would I do something like unix pipes in JS
- The JS pipe proposal F# syntax
- Tacit programming
- Concatenative languages
I find for a lot of these esoteric programming languages and paradigms, most people just don't have the right motivation to explore them. So we are just stuck with OO everywhere. But once you pull on the right threads and start questioning the status-quo, you really start to gain a deeper understanding and realize why things are so complex.
Forth is a charming little language, and I highly recommend spending a few evenings implementing one on the processor of your choice. You'll be astonished at how much functionality will fit in just a few kilobytes. FORTH will teach you no-foolin' hard core pragmatism, and a few dirty tricks that will make you smile.
(That said, I don't like FORTH much at all. The amount of stack-thinking is just agony).
There was always something I liked about Forth when I didn't have to do much stack-thinking. One of the things that felt forth-y to me when I first encountered it was threaded code in Clojure (or any other point-free kind of idiom for that matter).
I'll sometimes abuse Python's compose to do similar things I won't send out for code review. Maybe I really should just try to live in Coconut instead of silly stuff like:
Forth is still my goto language for a few things. A lot of my work these days involves doing board bringup on unique hardware so there's not much for docs on how the _system_ works. Just a schematic and data sheets on the parts. Nothing quite like dropping a Forth REPL on a bare board and seeing how all the SoC bits behave. :)
I still have an original copy of this from when it came out in 1984. The same author, Leo Brodie wrote an earlier book "Starting Forth" that I still have as well.
Top-posting in case this is of interest to others:
Link to my post on this thread about a Forth-based robot controller I designed back in 1984 using a dozen R65F11 Rockwell CPU's that came out of the box running Forth:
Played a little with Forth back at the time. Even wrote a simple interpreter. Thought it was a great language.
However I quickly realised why it was callled a WOL (write-only language - was a pun on RAM / ROM acronyms), as it was hard to understand my own code sometimes only days later.
Never looked back. Actually i did look back, at the beauty of the language, but never been tempted to actually use it.
DonHopkins on Jan 13, 2023 | parent | context | favorite | on: What the hell is Forth? (2019)
This is a great article! I love his description of Forth as "a weird backwards lisp with no parentheses".
Reading the source code of "WAForth" (Forth for WebAssembly) really helped me learn about how WebAssembly works deep down, from the ground up.
It demonstrates the first step of what the article says about bootstrapping a Forth system, and it has some beautiful hand written WebAssembly code implementing the primitives and even the compiler and JavaScript interop plumbing. We discussed the possibility of developing a metacompiler in the reddit discussion.
I posted this stuff about WAForth and a link to a reddit discussion with its author in the hn discussion of "Ten influential programming languages (2020)":
It's a lovingly crafted and hand written in well commented WebAssembly code, using Racket as a WebAssembly macro pre-processor.
I learned so much about WebAssembly by reading this and the supporting JavaScript plumbing.
The amazing thing is that the FORTH compiler dynamically compiles FORTH words into WebAssembly byte codes, and creates lots of tiny little WebAssembly modules dynamically that can call each other, by calling back to JavaScript to dynamically create and link modules, which it links together in the same memory and symbol address space on the fly! A real eye opener to me that it was possible to do that kind of stuff with dynamically generated WebAssembly code! It has many exciting and useful applications in other languages than FORTH, too.
Lots more discussion and links in the reddit article.
If you can't be bothered to install VS Code, you can have a look at a standalone version of the example notebook (in a 26kB self-contained page).
And if you're planning to go to FOSDEM 2023, come say hi: I'll be giving a talk there on WebAssembly and Forth in the Declarative and Minimalistic Computing devroom.
DonHopkins:
I really love your tour-de-force design and implementation of WAForth, and I have learned a lot about WebAssembly by reading it. Never before have I seen such beautiful meticulously hand written and commented WebAssembly code.
Especially the compiler and runtime plumbing you've implemented that dynamically assembles bytecode and creates WebAssembly modules for every FORTH word definition, by calling back to JavaScript code that pulls the binary bytecode of compiled FORTH words out of memory and creates a new module with it pointing to the same function table and memory.
WebAssembly is a well designed open standard that's taking over the world in a good way, and it also runs efficiently not just in most browsers and mobile smartphones and pads, but also on the desktop, servers, cloud edge nodes, and embedded devices. And those are perfect target environments for FORTH!
What you've done with FORTH and WebAssembly is original, brilliant, audacious, and eye-opening!
I'd read the WebAssembly spec before, and used and studied the Unity3D WebAssembly runtime and compiler to integrate Unity3D with JavaScript, and I also studied the AssemblyScript subset of TypeScript targeting WebAssembly and its runtime, and also Aaron Turner's awesome wasmboy WebAssembly GameBoy emulator .
I first saw your project a few years ago and linked to it in this Hacker News discussion about Thoughts on Forth Programming because I thought it was cool, but it's come a long way in three years, and I'm glad I finally took the time to read some of your code, which was well worth the investment of time.
Until reading your code, I didn't grasp that it was possible to integrate WebAssembly with JavaScript like that, and use it to dynamically generate code the way you have!
Also, the way you used Racket as a macro assembler for WebAssembly was a practical and beautiful solution to the difficult problem of writing maintainable WebAssembly code by hand.
Even for people not planning on using FORTH, WAForth is an enlightening and useful example for learning about WebAssembly and its runtime, and a solid proof of concept that it's possible to dynamically generate and run WebAssembly code on the fly, and integrate a whole bunch of tiny little WebAssembly modules together.
Playing with and reading through your well commented code has really helped me understand WebAssembly and TypeScript and the surface between them at a much deeper level. Thank you for implementing and sharing it, and continuing to improve it too!
remco:
Wow, thanks a lot, I really appreciate that! It makes me very happy that I was able to get someone to learn something about WebAssembly by reading the source code, which is exactly what I was going for.
[More links and discussion of WAForth, WebAssembly, and Forth Metacompilers:]
- JonesForth: https://github.com/nornagon/jonesforth/blob/master/jonesfort...
This is legit a text that goes the an x86 Forth implementation. Actually, it's just an implementation with really extensive comments. That said, including whitespace and comments, it's just 2000 lines and the pedagogy is excellent. Highly recommended for anyone who would rather see behind the curtain before picking up a larger text.
- SmithForth: https://dacvs.neocities.org/SF/
So, Smith decided to hand-write a Forth directly in x86-64 opcodes (well, the corresponding ascii hex bytes). It's incredibly slim and enlightening how you can bootstrap a language in just a couple hundred bytes or so.
This project actually inspired me to really learn the x86-64 architecture, so I ended up hand-decompiling the SmithForth binary instead of going through his commented implementation. Hand-decompilation is an absolutely fascinating exercise. You learn all about ELF structure, opcode encodings, and actually start to see the gaps where microarchitectural details shine through. Highly recommended for any hacker that really wants to grok low level details.
- Mecrisp: https://mecrisp.sourceforge.net/
An amazingly fast Forth implementation for MSP430, ARM, RISC-V, MIPS, and some FPGAs. This gave me one really nice understanding of Forth as
- ##forth on libera.chatReally active and helpful members there. Definitely worth dropping by.