Forth: A Frugal and Elegant Programming Language

Spread the love

“Any [one] can make things bigger, more complex, … It takes a touch of genius, and a lot of courage, to move in the opposite direction.” – Ernst F. Schumacher, 1973, from “Small is Beautiful: A Study of Economics As If People Mattered”1


At First Glance

Forth2 is a remarkable computer language born in 1968 that exemplifies the low-fat computing ethos of its inventor Charles ‘Chuck’ Moore (b.1938), for whom simplicity and minimalism were central themes of his life’s work.3 Forth was the first in a series of revolutionary ideas in computing practice4, followed by a simple VLSI design environment written entirely in Forth (OKAD5) and which Chuck used to design Forth-in-hardware chips6 culminating in the GA-144, a 144-core low-power (0.4uW standby power, 7 picojoules per instruction), high-performance (1.4 nanoseconds per instruction, or 700 MIPS per core) parallel computer on a single chip.

Thumbnail History

The first Forth was born in 1968 after a ten year gestation period starting in 1958 when he was an undergraduate at MIT working on an astrophysics program calculating ephemeridae to 1968 during his employment as a programmer at Mohasco Industries. Chuck’s motivation with Forth was to solve the problem of writing application programs efficiently, and after improving Forth at at NRAO (National Radio and Astronomical Observatory) and then at Forth Inc. By 1984, Forth had solved the problem of making the writing of application programs easy, and Chuck moved on to explore machine level efficiencies in hardware and design Forth chips. The Forth language evolved in the hands of its users, from 32 words to over 2500. In the early days, Forth was essentially Chuck Moore’s briefcase of punched cards, evolving to the user groups of the late 70s which led to F77 (European Forth Users Group) and fig-Forth (North American Forth Interest Group) in 1978 and 1980. Forth gradually became larger, leading to F83, then F94 (ANSI draft) and ANSI-1998 the first full standard7
References:

What can you do with it?

I have found Forth to be an excellent language for:

  1. embedded programming of microcontrollers (I wrote a 3-instruction tethered Forth to allow the Arduino to be programmed and run in real-time using GForth from a Windows computer, and used Forth to write TinyPhoto, including video drivers for OLED screen on an AT-TINY chip for which there was insufficient memory for the usual C drivers, as well as 16-key music synthesizer with numerous modes including Indian raga, Chinese tones, and arpeggiators, amongst other projects),
  2. experimenting with algorithms and data structures, mathematical & scientific programming, and exploration (I have written prime number sieves with various congruence properties that allow fast execution in memory constrained environments, propositional logic simulator, and various DFS/BFS ennumerative solvers),
  3. retro graphics programming on Windows using DOSBOX or VDosPlus emulators (I wrote a simplified and engaging graphical Turtle Logo environment to introduce my young kids to algorithmic thinking with a user interface which a 5-year old could use),
  4. introducing children to programming (I have used Forth as a starter language for kids as young as 7 years old).
  5. teaching assembler interactively
  6. simulation (I wrote a supply chain simulation environment in Forth to prove the financial benefits of a different restocking model for expensive, slow-selling items across an estate of 500 stores across the UK)
  7. If there’s one language which I think is a candidate to be a “100-year Language” (cf. Paul Graham’s 2003 article), I believe it is likely to be Forth.

Why Forth?

Forth combines low level thinking and high level design, does not hide abstractions, is elegant/spare, imposes little mental overhead through a simple yet powerful language (as long as one does not use towering stacks), and focuses on semantics (meaning) over syntax. For those who like to understand the essentials of what they do, do it elegantly, and without clutter, Forth is the ultimate in low-fat, high productivity computing.
What I like about Forth is:

  • Forth’s language elements are super simple and there is no superfluous/fiddly syntax. It’s basically variable/memory, math, conditionals, loops, conditionals, strings. A smart person can pick up the basics in an hour and start writing useful programs. A child of 8 can be taught to work in Forth.
  • Forth works from the ground up. It requires you to truly understand the material at the ground level. There is nothing hidden from view. If you wish, you can operate at the machine level. Because the Forth language core is simple, it is possible to hand code a minimal virtual machine for any chip in its own machine code, and then bootstrap the rest of the functionality in Forth from there (e.g. see my 3-Instruction bootstrap Forth for Arduino). And yet Forth is also a high level language (see the next point).
  • Forth is a language oriented program. Used right, you do most of your work in an ideal conceptual language that you have devised to fit your problem domain.
  • To devise this conceptual language, Forth encourages you to seek conceptual/structural simplification. (If you don’t, you end up with a mess.)
  • Forth is interactive. You are in a direct conversation with the computer using the language you have devised.
  • Forth makes it easy to “get it right” and then move on securely approach. The act of checking requires building the tools that later allow innovation.
  • Forth permits all of the software engineering innovations of the last 70 years: object orientation/encapsulation, information hiding, test certificates, literate programming
  • Like any language, if you want libraries, you can have libraries pre-coded, and learn the calling sequences through reference material (there is ANSI Forth just as there is ANSI C). But the neat thing about Forth is that you don’t need it. A dozen words are all you need.

Forth vs. C
Noteworthy differences (from a Forth preference standpoint):

  • C is verbose, with a lot of punctuation (parentheses, braces), grammar, syntax
  • The compiler is finicky, requiring casting and other hoops to jump through to demonstrate that e.g. you really do intend to use the LSB of a 16-bit word…
  • Factorizing C into small definitions is inefficient because each function call creates a new nested stack and copies current parameters to it. (Workaround this is to use #define in the pre-processor as short-hand for unpacking a small definition without dropping into a function)
  • C requires a compile/test cycle and is not interactive (exception is Tiny C Compiler – TCC which is interpreted)
  • Using C we no longer know how our code will be commpiled down, i.e. we are at the mercy of the moving feast that is gcc.
  • C’s processor independence is illusory. It comes from a highly complex GCC software that maps down to the processors syntactially. By comparison, Forth uses a simple virtual machine that can readily be coded natively for any particular processor.
  • Forth has indirection (pointers), double indirection, and function pointers. But it does so naturally (like assembly language), without syntactic fuss.

Here are what others have said about why they have chosen to work in Forth.

  1. Chuck Moore (creator of Forth) – Forth the Language, and Language Design (an interview), from Masterminds of Programming, O’Reilly, 2009, by F. Biancuzi & S. Warden
  2. Bernd Paysan – Why Forth?
  3. Brad Rodriguez – Why Forth?
  4. Marc Petremann – Forth for embedded computing For those wanting to use FlashForth on Arduino, read on: (Part 2) (Part 3) (Part 4) (All Articles)
  5. Elliot Williams – Forth: The Hacker’s Language
  6. Sam Gentle: Why Less is Moore (2015)
  7. Stephen Pelc, Modern Forth and Extreme Forth, 2008, Dr. Dobbs Journal
  8. Jeff Fox: Essential Forth (2002), Thoughtful Programming & Forth (1999), Low Fat Computing (1998), Forth – The Lego of Programming Languages (1998)
  9. Chuck Moore himself: Programming a Program Oriented Language, 1970 and Forth: A Language for Interactive Computing (1970)
  10. Yossi Kreinin – My history with Forth and Stack Machines

For LISP’s influence on Forth and Forth’s influence on other languages, see Appendix 1 (in progress).

Quotes by Chuck Moore

  1. “My contention is that every application that I have seen that I didn’t code has ten times as much code in it as it needs. How big should a program be? About a thousand instructions seems about right to me to do about anything.8 How do you get there? What is the magic? How can you make applications small? (1) No Hooks. (2) Don’t Complexify. (3) Factor. You factor. You factor, you factor, you factor and you throw away everything that isn’t being used, that isn’t justified. The whole point of Forth was that you didn’t write programs in Forth you wrote vocabularies in Forth. When you devised an application you wrote a hundred words or so that discussed the application and you used those hundred words to write a one line definition to solve the application. It is not easy to find those hundred words, but they exist, they always exist.”- From a 1999 talk by Chuck Moore to the Silicon Valley FIG, titled “1x Forth”
  2. “Forth = Small Definitions that can accept parameters + Stacks. I don’t know anything else to say except that if you have a lot of small definitions you are writing Forth. That is in my mind one of the keystones of Forth, you factor and you factor and you factor until most of your definitions are one or two lines long. In order to write a lot of small definitions you have to have a stack. But as to stack parameters, the stacks should be shallow. A Forth word should not have more than one or two arguments. This stack which people have so much trouble manipulating should never be more than three or four deep.”9 – Chuck Moore
  3. To be a good Forth programmer, there is something more than the formalism and syntax of Forth that has got to be embedded in your brain before you’re going to be effective at what you do.” 10

Moore on Hardware

  1. “It was easy to write applications, trivial to write applications. All the problems lay in the hardware.”
    [See Chuck’s personal history of Forth, and the official HOPL article by collaborator Elizabeth Rather]

  2. “The goal was very simple: to minimize the complexity of the hardware software combination. As far as I can see no-one else is doing that. Some lip service perhaps, but no-one is trying to minimize the complexity of anything and that is a great concern to me.” – Chuck Moore, 1999, 1x Forth, an interview by Jeff Fox (Online)
  3. “We are building a culture which can not survive as trivial an incident as Y2K. Once we lose the billion dollar fabrication plants and once we lose the thousand man programming teams how do we rebuild them? Would we bother to rebuild them? Are computers worth enough to demand the social investment that we put into them. It could be a lot simpler. If it were a lot simpler I would have a lot more confidence that the technology would endure into the indefinite future.” – Chuck Moore, 1999, 1x Forth, an interview by Jeff Fox (Online)

Getting Started

Forths of Note

  • GForth – a modern Forth for Windows and Linux (B. Paysan & A. Ertl). Download GForth (v.0.7.0 Portable for Windows)
  • F-PC – an excellent Forth for DOS, that runs in vDosPlus (emulator) and allows programming Windows as though able to access chip functions (assembler, graphics, sound card, devices, directly).
  • eForth – one of the original FIG-Forths. e-Forth is a collaboration between Bill Muench and C.H. Ting. Notes (Bill Muench)
  • Forth for Arduino – a Forth for embedded microcontroller programming, by C.H. Ting
  • Win32Forth (or here) – an easy to learn on Forth with ability to drop easily into assembler and program the Windows API.
  • JonesForth – an exploration in writing your own Forth for a chip.
  • Three-instruction Forth by Frank Sergeant (1991) Synopsis: Fetch Byte from Address, Store Byte to Address, Call Address. Everything else gets done in the host system. Only takes 32 bytes on the 68HC11!. It doesn’t get any more streamlined than this Three-Instruction Forth…
  • Public Domain Forths
  • Documentation

Learning the Language

  1. A Beginner’s Guide to Forth (Noble) – excellent first tutorial, used for his physics students,
  2. Starting Forth (Brodie) – outstanding introduction to Forth for total newbies to programming (e.g. children),
  3. Thinking Forth (Brodie)
    Word Lists

  4. Forth 79 quick reference (PDF) – core words from long ago.
  5. Forth 200x Cheat Sheet – ANSified Forth quick reference
  6. answords.fs – ANS Forth has approx. 400 words defined across 13 wordsets – bloated word set.
  7. Gforth Word Glossary and Notation for understanding it – bloated word set

Good Forth Style

Every coder has their own preferred style
Below are my preferences in Forth to bring ensure code is clear, readable, and easy to comprehend.

  1. (GForth) all programs should start with
    marker clear-dictionary

    . This allows cleanly clearing the dictionary before reloading.

  2. (All Forths) programs should start with a : help ; word, and end with help command (so upon loading user sees the help display at the interactive terminal). You should fill it in with the main words of your program – this is where you provide the ready-reference to the language you have built for your exploring/solving your problem.
  3. (All Forths) gather all your problem-independent, generally useful support words at the top of the file. Keep these minimal. Separate your application language from the support words.
  4. (All Forths) name variables
    (name)

    . Suggests a container. Makes it clear why @ and ! follow (fetch, store respectively). Encourages creating get/set words.

  5. (All Forths) Group the words that work with a variable near that variable. Makes comprehension easier.
  6. (All Forths) use literate programming word naming and hyphens to separate words
    word-word

    e.g.

    init-array
  7. (All Forths) use stack notation at the start of each word ( input — output). This is essential for documentation, and identifies the parameters being passed on the stack. If you modify the register stack, indicate that with ( r: input — output )
  8. (All Forths) prefix test words with
    test-
  9. (All Forths) code in readable expanded format, using tabs for easier comprehension. E.g.
    Conditionals:

    condition
       if
       else
       then
    

    For nested conditionals:

    condition
       if
       else condition
           if 
           else
           then
       then
    
  10. (All Forths) Loops – try to conceptualize your loops using [begin…condition until] (simplest kind). Use [do…loop] only for numerical iteration. Use [begin..condition while…repeat] only when it is imperative that the first pass should only occur after a successful condition test. Otherwise, refactor to use [begin…condition until]
  11. (All Forths) NO STACK JUGGLING! If you are fiddling with more than 3 parameters on the stack, you are probably not simplifying enough. REFACTOR!
  12. (All Forths) Refactor till your words are clear in intent and easy to grok and test. String words together like pearls. This is the art of Forth. It is not required, but neither is writing incomprehensively… in any language bad writing is on the writer, not the language.
  13. (All Forths) Unless you are working in a character constrained environment, do NOT adopt highly abbreviated word names. It does nothing to code words like a b c d e f g or g1 g2. You have simply written a terrible assembly like language. The power of Forth is creating an inviting domain language for solving your problem (and allowing others to explore it further and solve related problems).
  14. (All Forths) separate display from content. prefix display words with
    show-

Useful Forth Links & References

  1. Forth Interest Group (FIG)
  2. Comp.Lang.Forth: on Google Groups: quick, knowledgable answers to good questions
  3. Taygeta Scientific Inc (TSI): Repository for the Forth Community…
  4. Forth Dimensions: Archive of PDFs
  5. JFAR: Journal of Forth Application & Research
  6. JFAR downloads from Forth Inc.
  7. Julian Noble’s Quick Primer on Win32Forth
  8. Forth Scientific Library: see also Julian Noble’s Scientific Forth…
  9. Chuck Moore’s thoughts on Forth
  10. UltraTechnology: Forth Chips
  11. Chuck Moore: ColorForth
  12. Programming a Problem Oriented Language (PDF) and Excerpts
  13. Brian Foley – Why Forth?
  14. Paul Tomblin – Why Forth?
  15. Forth on Reddit
  16. Forth for Algorithms
  17. Ganssle: I Hate Forth (2001); (while I disagree with the perspective, the comments to the article are interesting, including that the average Forth definition is no more than 7 words long)
  18. Phil Koopman’s Bibliography on Stack-Based Computers (he designed the Harris RTX)
  19. Bolo’s Forth Page
  20. Peter Knaggs – Forth papers
  21. Forth Library
  22. Brad Rodriguez Publications
  23. EuroForth Papers (1985 to present, 35 years!)archives of papers starting from 1998
  24. Chuck Moore’s interviews: interview 1x Forth (1999)), and interview 1
    Jump into the language

  25. Application Programming: Forth & Domain Specific Languages Forth, Lisp, & Ruby
  26. Forth program examples
  27. Turtle Logo in Forth (Portable for Windows)
  28. My core words file: 3iCore.fs

LISP’s influence on Forth and Forth’s on other languages: Some reflections

In the history of ideas, it is often the case that the fundamental essence of a brilliant idea lives on, inspiring variations that ripple on through the creations of others.
LISP, was inspired by the work of logician Alonzo Church who was investigating the limits of computability in the early days of computing, and following the discoveries of logician Godel who demonstrated that a purely formal approach to mathematics, computability, or linguistics is incomplete as soon as the system is strong enough to include arithmetic. Put another way, as long as a formal system can represent arithmetic (and is consistent), then the system is also capable of formulating statements which are provably undecidable, i.e. the system is fundamentally incomplete. This story leads back to the foundations of mathematics, the birth of formal systems in algebra, and investigations into logic and meta-mathematics, and has interesting implications.

In the 1950s, John McCarthy at MIT was investigating artificial intelligence and computing, and was led through these investigations to formulate in 1958 the specification of LISP, or List Processing. Chuck Moore was a student of McCarthy’s at MIT in the 1950s, and by 1968 Chuck had formulated his own version of a list based language, equally spare, equally spartan, and equally powerful, based on definitions just as mathematics is. His language was Forth.

Looking at dynamic languages, there are similarities shared with Forth.

Perl was designed with the mentality of “how can we make programming work like language”. Larry Wall was a linguist, so that primarily drove his decisions when designing the language. This gives it a ton of flexibility, but makes it difficult to read others (or even your own) code. Sounds a bit like Forth. And indeed, Larry Wall on Forth: “Larry Wall, the creator of the Perl programming language, has expressed admiration for the Forth programming language. In his book “The Perl Programming Language”, he mentions Forth as one of the influences on the design of Perl. He notes that Forth’s approach to lexical parsing and its use of a simple, extensible syntax were among the ideas that inspired him in the development of Perl. However, he also acknowledges that Forth has some limitations, such as its lack of support for high-level abstractions and its reliance on the stack for data structure management. Overall, it seems that Larry Wall views Forth as an important precursor to Perl and a valuable influence on the development of the Perl language.” Q: What did Larry Wall think of Forth? Source: Brave Leo AI, Jan 11, 2024

Ruby asks “what makes programming fun?” “I believe that the purpose of life is, at least in part, to be happy. Based on this belief, Ruby is designed to make programming not only easy but also fun. It allows you to concentrate on the creative side of programming, with less stress.” – Yukihiro Matsumoto. Yukihiro Matsumoto see himself first and foremost as a programmer, and his goals with Ruby is to make coding in it as enjoyable as possible. Sounds like Forth.

Python followed a similar but opposite path “what if programming worked like math”. Guido van Rossum sees himself as a mathematician. That decision makes python a great language for researchers, especially in the data science field. It makes the language straight forward, but often verbose. Sounds like mathematics.

There are commonalities in terms of traps as well. Perl has been in a 20 year tailspin where Perl 5 was stable and highly usable with a massive following. Sure it was quirky, but it was known, used, loved, and productive. But Larry Wall wanted to push the language, to go further, and in pursuit of Perl 6 broke the community and the language.

Ruby suffered from the “it’s too much fun syndrome and its community was more about getting the work done and being highly creative and elegant, and not about the nuts and bolts of standardization and documentation.

Forth’s history is similar. Chuck Moore has never stopped improving the language as it works best for what he wants to create. In his view Forth is a problem oriented language, and its gift is its ability to constantly evolve and be fluid. But that does not lead to widespread adoption or collaboration at scale, which require standards and standardization. The highly divisive issue of standardization and the ISO ANSI adoption of a Forth standard impacted the community, and without strong central leadership on the language, the was also the push between Forth Inc commercially and the highly individualistic FIG community

Python has managed to avoid the worst excesses of the others. Guido van Rossum adopted the governance model of benevolent dictator. He enforces direction, and ensures that efforts are made to always keep the language highly clear and well-documented, making it easy to learn, with clear standards, and stable. This is helped by the “only one way to do any particular thing” approach.

Forth lessons learned – reflections for the community, by Peter Knagg


Some (Raw) Lecture Notes on the Language

In Forth, you create your own words (definitions). A word is a function/subroutine

: hello ." Hello World!" ;

Comments

\	is an inline comment
( ... ) is an inline comment

Stack Effect Diagram

( -- )  is a stack effect diagram, LHS is before, RHS is after, right on both sides is TOS
    example: /mod ( a b -- r q )   r = remainder a b mod   q = integer quotient a b / 

Note that GForth is an ANSI Forth with a lot of definitions. You need only know the minimal Forth subset.

Loading a file:
Gforth:
include file.fs
s” file.fs” included

F-PC: fload file.fs

The stack: creation, inspection, manipulation
every number entered pushes onto the top of the stack
.s (inspect the stack)
drop 2drop (remove items from the stack)
2drop
. hex. u. (print top of stack, removing it, in decimal, hex, or unsigned integer)
stack diagrams ( before state — after state ) document the stack effects. Top of stack (TOS) is to the right hand side. (GForth) (Primer)
Examples
: + ( a b — a+b) … ;
: ! ( n adr — ) ;
: @ ( adr — n ) ;

Stack manipulation:
There are 4 fundamental stack operations (dup drop swap rot) and 2 convenient ones (-rot, over) which you need to know:

: -rot ( a b c -- c b a )  swap rot swap ;
: over ( a b -- a b a )    swap dup -rot ;

Exercise: define reverse ( a b c — c b a )

: rev ( a b c -- c b a ) -rot swap ;

Exercise: define reverseN ( a1 a2 … aN N — aN aN-1 … a1 )
11

Return Stack: a 2nd stack
Using the return stack as a temporary second location within the application
>R from parameter to return stack
R> from return stack to parameter stack
R@ copy TOS of return stack to parameter stack
This last is very useful, because it means you can continually have a base pointer available in a routine.

Number Bases
. displays top of stack in signed decimal
u. displays top of stack in unsigned decimal
hex. displays top of stack in hex
$FF inputs hex number
hex changes input mode into hex
decimal changes input mode into decimal

To see the natural word length of your Forth: -1 u.
For GForth, this is: 4,294,967,295 = 2^32 -1

Basic Operations (Arithmetic/Logical):

+ - * / and or xor = < > 0= negate invert
: not ( 0/x -- 1/0 ) if 0 else 1 then ;

Arithmetic
2 1 +
2 1 –
2 3 *
6 3 / this is integer division, returns quotient
5 3 mod returns remainder
3 6 /mod ( a b — q r ) returns quotient and remainder
1 2 max
1 2 min
1 negate returns negative
-1 abs returns positive

Fixed Point Arithmetic
There are times when one needs to use double-width arithmetic, when dealing with large numbers, etc.

Logic and Arithmetic Comparison
true this is -1
false this is 0
1 2 < 1 2 >
1 1 =
1 2 <>
true invert this gives false
1 0 and .
1 0 or .
$5 $F xor .s
xor .s
Flips between 0101 and 1010

viewing binary encodings (see logic.fs)

(   viewing bitfield of a number  - determines number of bits from T )
:  T   $FF  ;  \ 1111b  Determines whether calculations are 4-, 8-, or 16-bits
:  ~    ( p -- ~p ) T xor ;          \ negation op takes bitfield size from T
: .bit  ( n -- ) 0> if 1 else 0 then . ;
: init-mask  T 2 / ~  ;  \ initial mask is $8=1000b, $80=1000000b, or $8000 depending on T
: bits  (  bitfield  --  )  
    init-mask begin      \  loop through bits, pick them off, and print
        2dup and .bit    
        1 rshift dup 0= until 
    2drop ." b" ;
: .. ( bf -- bf ) dup bits 2 spaces ;

: exbit ( bf --  bf>>1   bit )  \ extract bit from bitfield
  dup 1 rshift swap 1 and ;  

characters
char ,
44 emit
‘A’ emit

ascii facilities

: |. '|' emit ;
: ascii-table
    cr $FF 0 do 
        i dup . dup hex. emit |. cr 
    loop ;

Observe:
printable characters are from $20 (32d) = SPACE to $7E (~)
numbers are from $30 to $39
letters are from $41 to $5A (capitals) and from $61 to $7A (smalls). Note that letter case is controlled by ASCII bit 6 (bitmask $20) — set for smalls, unset for capitals.

ASCII letter case is controlled by bit 6 (bitmask 0x20). Set for small letters, unset for capitals.

: tolower ( char -- low-char ) $20 or ; \ make lowercase
: toupper ( char -- upp-case ) $20 invert and ; \ make uppercase
: toggle_case ( char -- tog-case ) $20 xor ; \ switch case

: is_between? ( n a b -- t/f ) rot dup rot > -rot > or invert ;  \ not ( n>b or n<a )
: is_char?    ( asc -- t/f ) $20  $7E  is_between? ;  \ standard char range: space to ~
: is_number?  ( asc -- t/f ) '0' '9' is_between? ;
: is_cap_ltr?  $41  $5A  is_between? ;
: is_small_ltr?  $61  $7A  is_between? ;
: is_letter?  ( asc -- t/f ) dup is_cap_ltr? swap is_small_ltr?  or ;
: is_letter?  ( asc -- t/f ) tolower 'a' 'z' is_between? ;

: .comma ( -- ) $2c emit ; \ comma: 
: .del ( -- ) $8 emit ;	\ backspace
: .. ( n -- ) . .del ;  \ print with no trailing space
: ., ( n -- ) . .del .comma ;  \ print with trailing comma (CSV)
: a2d         ( asc -- digit )  '0' - ;                           \ ascii to digit
: d2a         ( digit -- asc )  '0' + ;                           \ digit to ascii

strings:
.” hello world!”

keyboard input, controlled output

: pause ( -- )     ." PRESS ANY KEY TO CONTINUE... " key drop ;
  $1B  constant  esc-key   
: pause-esc  ( -- t/f ) ." PRESS any key to continue...; ESC to exit... " key esc-key =  ;
: loop-till-esc   begin  ." ." key? until ." Done!" ;
: ?continue ( -- )  ." Continue? (y/n) " key tolower [char] n = if cr ." Stop." quit then ;  \ stop if no
: 10-row? ( i -- ) 10 mod   9 = if pause then ;  \ newline after X entries
\ Scroll Screen code - code for maintaining page status for scrolling
variable (lines) 0 (lines) !  \ number of lines emitted
: (cr) newline type (lines) @ dup 29 > if drop 0 (lines) ! ( reset ) ." -----------(continue?)" ?continue cr else 1+ (lines) ! then ;

  $C8  constant  fwd-arrow
  $CD  constant  right-arrow
  $CB  constant  left-arrow
  $8   constant  backspace
  $27  constant  tick            

Memory: Store (!) and Load (@)

Built-in Memory Operators
C, W, S, D prefixes denote character (byte), unsigned word (wyde), signed word, and double wyde data lengths, e.g. c@, w@, s@, d@
c!, ! ( val addr — ) \ STORE value (NOS) into address (TOS)
c@, @ ( addr — val ) \ LOAD value from address on TOS (replaces address with value)
+! ( val addr — ) \ ADD val to value already stored in address

Memory iterators / array words

: c!+  \ store value and increment address 
    ( val adr -- adr++ ) 
    swap over c! 1+ ;

: c@+  \ retrieve value and increment address
    ( adr -- adr++ val )
    dup 1+ swap c@ ;

Creating memory
6 constant id \ creates a constant
id ( — value ) \ returns value (dereferenced address)
variable id 6 id ! id @ . \ creates and allocates one cell of memory (4 bytes)
id ( — adr ) returns address (pointer) of variable

here ( — addr ) shows the NEXT unallocated mememory address on the heap
here dump4 \ shows 0 0 0 0 (dump4 is from ake.f)
create id 5 allot creates a POINTER to an address, associated with the symbol x. Which address? NEXT memory cell on the heap,
allot ( n — ) advances here by the bytes specified
create id 6 , 7 , 8 ,
create id 6 c, 7 c, 8 c, assign a value immediately to here and increment

Advanced Memory Work
Lookup tables in Forth
create id 6 c, 7 c, 8 c, assign a value immediately to here and increment

Function pointers (in Forth these are called executable tokens)
‘ definition-name
[‘] definition-name (from within a definition)
execute

Control / Looping operations:
if-else-then, begin-until, do-loop, begin-while-repeat

if-else-then
: construct start test? if else then rest ;

begin-until
: test 5 begin dup . 1 – dup 0 = until .” Done!” ;
test 5 4 3 2 1 ok
PREFERRED. REPEATS UNTIL TRUE. FRUGAL

do-loop
: test 5 0 do i . loop ;
test 0 1 2 3 4 ok
iterates from 0 upwards stops when implicit index i reaches 5
ATTENTION POINT: do-loop implicitly uses top of register stack to carry index i through the loop.
Therefore any use of >r r@ r> must be completed without interruption by do, i, or loop words.
Otherwise unexpected interactions results will happen.

Illustration (implementing do-loop using begin-until and without using the register stack)
: test 5 0 begin dup . 1 + 2dup = until ;

Nested do-loops
: nestedloop
cr 5 0 do i . .” :” cr \ note: i refers to the loop index within the current loop
10 0 do j . i . .” ” loop cr \ note: j refers to the outer loop index within the current loop
loop ;

How to run a simple do loop where there is a need to carry parameters on the stack throughout the loop?
Use case: pattern matching: given a variable set of key-tokens and a user-provided entry, test this against the key tokens and indicate if there’s a match against any. For instance:

This should return true when any of yYzZ are pressed, otherwise false:

     'y' 'z' 2 key check-keyset .  

This should return true when any of aAbBxXyYzZ are pressed, otherwise false:

     'a' 'b' 'x' 'y' 'z' 5 key check-keyset . 

Solution: encapsulate the operation cleanly in a T1 (check-key) without referencing the return stack.
Then T1 can be safely used in a simple do loop.
Care must be taken in managing the stack in check-keyset so that it matches the T1 stack-structure at start and end of the do loop.

\ pattern match against acceptable values, lowercase keys, uppercase automatically checked
: T1 ( keyn keyp -- keyp t/f )    \ tests if key matches lower/upper case keyn
    swap tolower dup toupper rot swap over = -rot swap over = rot or ;
: check-keyset ( k1 .. kn n keyp -- keyp t/f ) \ testing through the list of n patterns k1 k2 ... (cumulative or)
    swap 0 swap over do ( k1 k2 keyp t/f ) 
         -rot T1 rot or 
    loop swap drop ;

begin-while-repeat
: test < init > begin < test > while < loop code > repeat;
: test 5 begin dup 0 > while dup . 1 – repeat ;
REPEATS WHILE TRUE. MORE COMPLEX THAN UNTIL. Better not used.

File Facilities

Files Tutorial in F-PC:


: q2c-string ( c-adr nlen -- cadr ) \ quoted string to counted string
    here -rot dup c, here swap dup bytes allot cmove ;   
handle myfile                       \ create file handle
s" assad"                           \ filename
q2c-string myfile $>handle          \ store filename in handle
myfile hcreate .                    \ create file 0 = success
s" hello world!" myfile hwrite .    \ write 12 characters
create msg                          \ prepare binary data array
  1 c, 1 c, 1 c, 2 c, 3 c, 0 c,         \ 3iForth: xcall pinmode ( out pin2 -- )
  1 c, 1 c, 1 c, 2 c, 3 c, 1 c,         \ 3iForth: xcall pinset  ( hi  pin2 -- )
msg 12 myfile hwrite .              \ write 12 binary characters
myfile hclose                       \ flush buffers and release file

myfile hopen .
create fbuf 50 bytes allot 
fbuf 12 myfile hread .

handle myfile2
s" assad2" q2c-string myfile2 $>handle

myfile myfile2 hrename .

myfile hdelete .

In GForth:


\ FILE PROCESSING  (GFORTH)
constants: r/w, r/o, w/o 
cr
\ : throw ( err -- ) if abort then ;  \ 0 means ok, nonz means error 
\ : create-file ( s-str fmode -- err ) ;   \ usage: s" test.txt" r/w create-file throw constant fh
\ : close-file ( fhandle -- err )  ;  \ usage:  fh close-file throw
TEXT PROCESSING
\ : write-file ( s-str fh -- err ) ;  \ writes string to file, no newline.  flushes with close-file.  Usage: write-file throw
\ : write-line ( s-str fh -- err ) ;  \ writes string to file with newline.  Usage: write-line throw
: .file ( val fh -- )  \ converts number to formatted string and dumps to file, one number per line.  Won't flush to file until close-output
  	swap s>d tuck dabs <<# #s rot sign #> rot write-line throw #>>  ;
: export-array ( adr fh -- ) >r begin @+ dup while r@ .file repeat r> 2drop ;  \ write null-terminated array to file (already opened)
: export-carray ( adr fh -- ) >r begin c@+ dup while r@ .file repeat r> 2drop ; \ write null-terminated byte array to file

BINARY FILE PROCESSING EXAMPLE
s" test.bin" r/w bin create-file constant fh
create data $10 c, $20 c, $30 c, $FF c, $FE c,
data 5 fh write-file fh flush-file
The data is now there --- look at it in a binary file viewer e.g. HxD.

LINKING TO BINARY LIBRARIES
GForth allows linking to C libraries (*.so) using add-lib command



Appendix 0: Forth Utilities

  1. Notepad++ has Forth syntax highlighting built in from 2017 version (verified present from v7.7.1, build 2019) – see NPP article, Appendix E to enable
  2. for Notepad++: Forth Language definition file (xml)


Appendix 1: My Forth Programs

How I came to Forth

I started programming in Forth 10 years ago (in 2013), a few years after hearing it mentioned in 2010 as a force multiplier language as we were writing the embedded software for a fleet of autonomous underwater robots. It turns out the chief engineer, Harry Gosling (formerly BAE systems), was not kidding. Forth is remarkable in this respect. I had initially planned to use it to build a simulation tool for supply chain, but shelved the idea in favour of an SQL based implementation that could handle massive data sets from a commercial retailer. I played with Forth on the side, exploring various mathematical areas in number theory using GForth (2014). Over the next few years, Forth became the enabler for some super interesting projects: binary visualizer (2015), an assembly language emulator using F-PC and DosBox (2015), a graphical Turtle Logo application using F-PC and VDosPlus (2016), a Symbolic Logic simulator using GForth (2020), a Forth random number generator (2020), a Forth for Tethered real-time Arduino embedded development for ATmega328P chip (Arduino Nano/Uno) over a serial line based on 3 instructions using GForth (2020), Forth for graphics conversion for TinyPhoto product using GForth (2021).

    Graphics / DOS Programming / Games / for Children

  • turtle.fs – graphical drawing program Turtle Logo for teaching children algorithmic thinking (2016-2020). NOTE: The turtle.fs source code will run on F-PC inside a DOS emulator (vDosPlus recommneded or DOSBox). The compiled program has been packaged to be standalone and run on Windows 10 (Download Turtle Logo portable version). Article
  • Number Theory

  • prime.fs – (2014) – primality testing. Example: 16807 is-prime?
  • factor.fs (2014) – Factoring integers. Example: 16807 factor
  • seive4thin.f (2014) – Sieve of Eratosthenes, optimized for memory to be able to produce more primes without windowing

    Randomness

  • rand.fs – (2020) – GForth 32-bit LCM (linear congruence method) generator using m=2^31-1, a=16807, c=0
  • rand.r – (2020) – Validation example in R for 32-bit LCM above

    Mathematical Logic

  • Symbolic Logic Simulator (2020) – evaluates sentences in propositional logic using truth tables
    Program length is 28 lines-of-code in total (=16 + 4×3) written in Forth and runs on GForth (0.7.0, Windows, Linux, Mac), which can be downloaded here).
    Source code:
    Definition files: Each definition file is 4 lines defining T,F,p,q,r,s according to the number of logical variables (4 bits per variable): logic2.fs logic3.fs logic4.fs
    Symbolic Logic Simulator: Core program is 16 lines-of-code and defines the logical operations and a bitfield viewer. logic.fs

    Algorithms and Complexity

  • gray.fs – (2020) – illustrates combinatorial search using search trees (breath first search in this case) on the problem of finding gray codes

    An interesting project would be implementing Knuth’s The Art of Computer Programming (TAOCP) in Forth, or the algorithm and data structure in such classics as [Cormen/2009] Introduction to Algorithms-3e (“the Fern Book”), [Sedgewick/2011] Algorithms-4e, online free, [Aho/1983] Data Structures and Algorithms, [Bhargava/2015] Grokking Algorithms.



    Appendix 2: Windows Programming in Forth
    GForth has useful facilities for programming on Windows:

    page    \  clear screen
    page 40 12 at-xy ." Hello World!" cr 1 20 at-xy   \  print wherever you want to on the terminal
    s" OS" getenv cr type     \  get Windows environment variable (run SET in cmd), note must be all caps
    s" APPDATA" getenv cr type
    s" PATH" getenv cr type
    history-file cr type    \ show where history file is kept
    environment-wordlist >order words previous   \ show words recognized by environment?
    s" gforth" environment? . type
    s" os-type" environment? . type    \  if CYGWIN then will have some problems with running on windows
    s" os-class" environment? . type   \ if UNIX then expect problems on Windows
    

    Can set Gforth’s search path on the command line for both image and source files – note: no need for drive, paths separated by :
    gforth.exe –path=\totalcmd\:\totalcmd\gforth\:\totalake\__scratch
    Invoking Gforth

    Code for inspecting working directory:
    create tmp 255 cells allot
    tmp 255 get-dir type

    Using blocks

    load “serial.fs”


    Footnotes

    1. This quote by Ernst F. Schumacher is often incorrectly attributed to Einstein
    2. Forth on Wikipedia
    3. Chuck Moore Interview: The Lost Art of Keeping it Simple
    4. Language Oriented Programming, also called Problem Oriented Language, or Domain specific Language
    5. OKAD was built on top of OK, which was Chuck Moore’s One Kilobyte operating system. OKAD2 was built within ColorForth and benefited from 10 years of Chuck Moore’s experience designing chips.
    6. The history is interesting: 1) Jeff Fox’s history, 2) Chuck Moore’s history
    7. Whether or not to pursue an ANSI standard was hotly debated. See Jeff Fox’s opposition viewpoint).
    8. At one instruction per byte, that means most programs need not be more than 1000 bytes or 1KB.
    9. AE: you know when your stack is too long because you start stack juggling (rot, swap, -rot, over, …). At this point factor, simplify, and if you really need three arguments, maybe one of them is a more slowly varying parameter and can therefore be kept in a variable removing the stack juggling noise.
    10. “A problem with all programmers is too many input parameters to a routine. Look at some ‘C’ programs and it gets ludicrous. Everything in the program is passed through the calling sequence and that is dumb.
    11. Below are algorithmic answers – they don’t actually work in Forth because do..loop construction uses the register stack for counted loops, so cannot also use the register stack for the rearrangements.
      First solution is QUADRATIC COMPLEXITY. Solves N=5 in 50 ops, solves N=10 in 250 ops, solves N=100 in 29,500 ops. Formulas: 3N^2-5N.
      Second solution is LINEAR COMPLEXITY. Solves N=5 in 12 ops, solves N=10 in 27 ops, solve N=100 in 297 ops. Formula: 3N-3.

      variable N-iter
      : reverseN ( a1 .. aN N -- aN .. a1 )  \ Using only >r, swap, and r>.  solve N=5 in 50 operations = 5*(3 + 3*2 + 1).  Solves N in N*(N-2 + 2*(N-2) + 1) = N*(3N-5) = 3N^2 -5N.   Solves N=5 in 50 ops.  Solves N=10 in 250 ops.  Solves N=100 in 29,500 ops.
          N-iter !
          N-iter @ 0 do 
              N-iter @ 2 - 0 do >r loop
              swap 
              N-iter @ 2 - 0 do r> swap loop
          loop ;
      
      : reverseN ( a1 .. aN N -- aN .. a1 ) \ Using only swap and -rot, >r, r>.  Solves N=5 in 12 operations.  Solves N in 2+ N-2 + 1 + 2*(N-2) = N-1+2N-4 = 3N-3 = 3(N-1).  Solves N=5 in 12 ops.  Solves N=10 in 27 ops.  Solves N=100 in 297 ops.
          2 - !
          swap rot 
          N-iter @ 0 do >r >r >r loop
          swap
          N-iter @ 0 do r> -rot loop ;
      
      

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

  

  

  

Your comments are valued! (Please indulge the gatekeeping question as spam-bots cannot (yet) do simple arithmetic...) - required

Optionally add an image (JPEG only)

 

Stats: 1,089,379 article views since 2010 (Aug '24 update)

Dear Readers:

Welcome to the conversation!  We publish long-form pieces as well as a curated collection of spotlighted articles covering a broader range of topics.   Notifications for new long-form articles are through the feeds (you can join below).  We love hearing from you.  Feel free to leave your thoughts in comments, or use the contact information to reach us!

Reading List…

Looking for the best long-form articles on this site? Below is a curated list by the main topics covered.

Mathematics History & Philosophy

  1. What is Mathematics?
  2. Prehistoric Origins of Mathematics
  3. The Mathematics of Uruk & Susa (3500-3000 BCE)
  4. How Algebra Became Abstract: George Peacock & the Birth of Modern Algebra (England, 1830)
  5. The Rise of Mathematical Logic: from Laws of Thoughts to Foundations for Mathematics
  6. Mathematical Finance and The Rise of the Modern Financial Marketplace
  7. A Course in the Philosophy and Foundations of Mathematics
  8. The Development of Mathematics
  9. Catalysts in the Development of Mathematics
  10. Characteristics of Modern Mathematics

Topics in Mathematics: Pure & Applied Mathematics

  1. Fuzzy Classifiers & Quantile Statistics Techniques in Continuous Data Monitoring
  2. LOGIC in a Nutshell: Theory & Applications (including a FORTH simulator and digital circuit design)
  3. Finite Summation of Integer Powers: (Part 1 | Part 2 | Part 3)
  4. The Mathematics of Duelling
  5. A Radar Tracking Approach to Data Mining
  6. Analysis of Visitor Statistics: Data Mining in-the-Small
  7. Why Zero Raised to the Zero Power IS One

Technology: Electronics & Embedded Computing

  1. Electronics in the Junior School - Gateway to Technology
  2. Coding for Pre-Schoolers - A Turtle Logo in Forth
  3. Experimenting with Microcontrollers - an Arduino development kit for under £12
  4. Making Sensors Talk for under £5, and Voice Controlled Hardware
  5. Computer Programming: A brief survey from the 1940s to the present
  6. Forth, Lisp, & Ruby: languages that make it easy to write your own domain specific language (DSL)
  7. Programming Microcontrollers: Low Power, Small Footprints & Fast Prototypes
  8. Building a 13-key pure analog electronic piano.
  9. TinyPhoto: Embedded Graphics and Low-Fat Computing
  10. Computing / Software Toolkits
  11. Assembly Language programming (Part 1 | Part 2 | Part 3)
  12. Bare Bones Programming: The C Language

Technology: Sensors & Intelligent Systems

  1. Knowledge Engineering & the Emerging Technologies of the Next Decade
  2. Sensors and Systems
  3. Unmanned Autonomous Systems & Networks of Sensors
  4. The Advance of Marine Micro-ROVs

Maths Education

  1. Maxima: A Computer Algebra System for Advanced Mathematics & Physics
  2. Teaching Enriched Mathematics, Part 1
  3. Teaching Enriched Mathematics, Part 2: Levelling Student Success Factors
  4. A Course in the Philosophy and Foundations of Mathematics
  5. Logic, Proof, and Professional Communication: five reflections
  6. Good mathematical technique and the case for mathematical insight

Explore…

Timeline