# MiniForth

**Source:** https://github.com/ttsiodras/MiniForth

GitHub - ttsiodras/MiniForth: A tiny Forth I built in a week. Blog post: https://www.thanassis.space/miniforth.html



[Skip to content](#start-of-content)













## Navigation Menu

Toggle navigation

[Sign in](/login?return_to=https%3A%2F%2Fgithub.com%2Fttsiodras%2FMiniForth)

Appearance settings

* Platform

  + AI CODE CREATION
    - [GitHub CopilotWrite better code with AI](https://github.com/features/copilot)
    - [GitHub SparkBuild and deploy intelligent apps](https://github.com/features/spark)
    - [GitHub ModelsManage and compare prompts](https://github.com/features/models)
    - [MCP RegistryNewIntegrate external tools](https://github.com/mcp)
  + DEVELOPER WORKFLOWS
    - [ActionsAutomate any workflow](https://github.com/features/actions)
    - [CodespacesInstant dev environments](https://github.com/features/codespaces)
    - [IssuesPlan and track work](https://github.com/features/issues)
    - [Code ReviewManage code changes](https://github.com/features/code-review)
  + APPLICATION SECURITY
    - [GitHub Advanced SecurityFind and fix vulnerabilities](https://github.com/security/advanced-security)
    - [Code securitySecure your code as you build](https://github.com/security/advanced-security/code-security)
    - [Secret protectionStop leaks before they start](https://github.com/security/advanced-security/secret-protection)
  + EXPLORE
    - [Why GitHub](https://github.com/why-github)
    - [Documentation](https://docs.github.com)
    - [Blog](https://github.blog)
    - [Changelog](https://github.blog/changelog)
    - [Marketplace](https://github.com/marketplace)

  [View all features](https://github.com/features)
* Solutions

  + BY COMPANY SIZE
    - [Enterprises](https://github.com/enterprise)
    - [Small and medium teams](https://github.com/team)
    - [Startups](https://github.com/enterprise/startups)
    - [Nonprofits](https://github.com/solutions/industry/nonprofits)
  + BY USE CASE
    - [App Modernization](https://github.com/solutions/use-case/app-modernization)
    - [DevSecOps](https://github.com/solutions/use-case/devsecops)
    - [DevOps](https://github.com/solutions/use-case/devops)
    - [CI/CD](https://github.com/solutions/use-case/ci-cd)
    - [View all use cases](https://github.com/solutions/use-case)
  + BY INDUSTRY
    - [Healthcare](https://github.com/solutions/industry/healthcare)
    - [Financial services](https://github.com/solutions/industry/financial-services)
    - [Manufacturing](https://github.com/solutions/industry/manufacturing)
    - [Government](https://github.com/solutions/industry/government)
    - [View all industries](https://github.com/solutions/industry)

  [View all solutions](https://github.com/solutions)
* Resources

  + EXPLORE BY TOPIC
    - [AI](https://github.com/resources/articles?topic=ai)
    - [Software Development](https://github.com/resources/articles?topic=software-development)
    - [DevOps](https://github.com/resources/articles?topic=devops)
    - [Security](https://github.com/resources/articles?topic=security)
    - [View all topics](https://github.com/resources/articles)
  + EXPLORE BY TYPE
    - [Customer stories](https://github.com/customer-stories)
    - [Events & webinars](https://github.com/resources/events)
    - [Ebooks & reports](https://github.com/resources/whitepapers)
    - [Business insights](https://github.com/solutions/executive-insights)
    - [GitHub Skills](https://skills.github.com)
  + SUPPORT & SERVICES
    - [Documentation](https://docs.github.com)
    - [Customer support](https://support.github.com)
    - [Community forum](https://github.com/orgs/community/discussions)
    - [Trust center](https://github.com/trust-center)
    - [Partners](https://github.com/partners)
* Open Source

  + COMMUNITY
    - [GitHub SponsorsFund open source developers](https://github.com/sponsors)
  + PROGRAMS
    - [Security Lab](https://securitylab.github.com)
    - [Maintainer Community](https://maintainers.github.com)
    - [Accelerator](https://github.com/accelerator)
    - [Archive Program](https://archiveprogram.github.com)
  + REPOSITORIES
    - [Topics](https://github.com/topics)
    - [Trending](https://github.com/trending)
    - [Collections](https://github.com/collections)
* Enterprise

  + ENTERPRISE SOLUTIONS
    - [Enterprise platformAI-powered developer platform](https://github.com/enterprise)
  + AVAILABLE ADD-ONS
    - [GitHub Advanced SecurityEnterprise-grade security features](https://github.com/security/advanced-security)
    - [Copilot for BusinessEnterprise-grade AI features](https://github.com/features/copilot/copilot-business)
    - [Premium SupportEnterprise-grade 24/7 support](https://github.com/premium-support)
* [Pricing](https://github.com/pricing)

Search or jump to...


# Search code, repositories, users, issues, pull requests...

Search

Clear

[Search syntax tips](https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax)

# Provide feedback

We read every piece of feedback, and take your input very seriously.


Include my email address so I can be contacted

Cancel
 Submit feedback





# Saved searches

## Use saved searches to filter your results more quickly

Name

Query

To see all available qualifiers, see our [documentation](https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax).

Cancel
 Create saved search

[Sign in](/login?return_to=https%3A%2F%2Fgithub.com%2Fttsiodras%2FMiniForth)

[Sign up](/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E&source=header-repo&source_repo=ttsiodras%2FMiniForth)

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.
You signed out in another tab or window. Reload to refresh your session.
You switched accounts on another tab or window. Reload to refresh your session.
 


Dismiss alert

{{ message }}

[ttsiodras](/ttsiodras) 
/
**[MiniForth](/ttsiodras/MiniForth)**
Public

* [Notifications](/login?return_to=%2Fttsiodras%2FMiniForth) You must be signed in to change notification settings
* [Fork
  7](/login?return_to=%2Fttsiodras%2FMiniForth)
* [Star
   95](/login?return_to=%2Fttsiodras%2FMiniForth)

A tiny Forth I built in a week. Blog post: <https://www.thanassis.space/miniforth.html>

### License

[MIT license](/ttsiodras/MiniForth/blob/master/LICENSE)

[95
stars](/ttsiodras/MiniForth/stargazers) [7
forks](/ttsiodras/MiniForth/forks) [Branches](/ttsiodras/MiniForth/branches) [Tags](/ttsiodras/MiniForth/tags) [Activity](/ttsiodras/MiniForth/activity)

[Star](/login?return_to=%2Fttsiodras%2FMiniForth)

[Notifications](/login?return_to=%2Fttsiodras%2FMiniForth) You must be signed in to change notification settings

* [Code](/ttsiodras/MiniForth)
* [Issues
  0](/ttsiodras/MiniForth/issues)
* [Pull requests
  0](/ttsiodras/MiniForth/pulls)
* [Actions](/ttsiodras/MiniForth/actions)
* [Projects
  0](/ttsiodras/MiniForth/projects)
* [Security
  0](/ttsiodras/MiniForth/security)
* [Insights](/ttsiodras/MiniForth/pulse)

Additional navigation options


* [Code](/ttsiodras/MiniForth)
* [Issues](/ttsiodras/MiniForth/issues)
* [Pull requests](/ttsiodras/MiniForth/pulls)
* [Actions](/ttsiodras/MiniForth/actions)
* [Projects](/ttsiodras/MiniForth/projects)
* [Security](/ttsiodras/MiniForth/security)
* [Insights](/ttsiodras/MiniForth/pulse)

# ttsiodras/MiniForth

master

[Branches](/ttsiodras/MiniForth/branches)[Tags](/ttsiodras/MiniForth/tags)

Go to file

Code

Open more actions menu

## Folders and files

| Name | | Name | Last commit message | Last commit date |
| --- | --- | --- | --- | --- |
| Latest commit   History[41 Commits](/ttsiodras/MiniForth/commits/master/)   41 Commits | | |
| [contrib](/ttsiodras/MiniForth/tree/master/contrib "contrib") | | [contrib](/ttsiodras/MiniForth/tree/master/contrib "contrib") |  |  |
| [simavr @ a56b550](/buserror/simavr/tree/a56b550872906a971ac128002772d90c9e30377d "simavr") | | [simavr @ a56b550](/buserror/simavr/tree/a56b550872906a971ac128002772d90c9e30377d "simavr") |  |  |
| [src](/ttsiodras/MiniForth/tree/master/src "src") | | [src](/ttsiodras/MiniForth/tree/master/src "src") |  |  |
| [src\_x86](/ttsiodras/MiniForth/tree/master/src_x86 "src_x86") | | [src\_x86](/ttsiodras/MiniForth/tree/master/src_x86 "src_x86") |  |  |
| [testing](/ttsiodras/MiniForth/tree/master/testing "testing") | | [testing](/ttsiodras/MiniForth/tree/master/testing "testing") |  |  |
| [.gitignore](/ttsiodras/MiniForth/blob/master/.gitignore ".gitignore") | | [.gitignore](/ttsiodras/MiniForth/blob/master/.gitignore ".gitignore") |  |  |
| [.gitmodules](/ttsiodras/MiniForth/blob/master/.gitmodules ".gitmodules") | | [.gitmodules](/ttsiodras/MiniForth/blob/master/.gitmodules ".gitmodules") |  |  |
| [LICENSE](/ttsiodras/MiniForth/blob/master/LICENSE "LICENSE") | | [LICENSE](/ttsiodras/MiniForth/blob/master/LICENSE "LICENSE") |  |  |
| [Makefile](/ttsiodras/MiniForth/blob/master/Makefile "Makefile") | | [Makefile](/ttsiodras/MiniForth/blob/master/Makefile "Makefile") |  |  |
| [README.md](/ttsiodras/MiniForth/blob/master/README.md "README.md") | | [README.md](/ttsiodras/MiniForth/blob/master/README.md "README.md") |  |  |
| [config.mk](/ttsiodras/MiniForth/blob/master/config.mk "config.mk") | | [config.mk](/ttsiodras/MiniForth/blob/master/config.mk "config.mk") |  |  |
| View all files | | |

## Repository files navigation

* [README](#)
* [MIT license](#)

*( Wrote [a blog post about this here](https://www.thanassis.space/miniforth.html) )*

It was raining hard, a week ago.

And what could you possibly do on a rainy Saturday afternoon?

Well...

You can make a Forth interpreter/compiler from scratch...  
...then put it inside a 1.5$ Blue Pill microcontroller...  
...and finally, inside an Arduino UNO...  
... within its tiny 2K RAM!

Click on the image to watch it blinking the LED of my Arduino:

[![Here's a video of it action, blinking my Arduino :-)](https://camo.githubusercontent.com/10dcc45a92a8eb1fbc79bbcf7127dea99324e316bf5b5ce1bd0515f9c86e5da5/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f7865506f6c6c62437a6f772f302e6a7067)](https://www.youtube.com/watch?v=xePollbCzow)

I haven't done anything even *remotely* close to this in decades...  
I *loved* building it.

The rainy afternoon turned into a week-long hackfest *(was looking
forward every day to the post-work FORTH-tinkering in the afternoon...)*

The result: a tiny, mini, micro Forth. In portable C++ :-)  
It has...

* basic arithmetic
* star-slash (double-word accurate muldiv)
* literals
* constants
* variables
* direct memory access
* string printing
* reseting
* comments
* nested DO/LOOP
* comparisons
* nested IF/ELSE/THEN
* ...and of course, functions (Forth words)

Here's an ascii-cast recording of it in action:

[![Recording of building and uploading on an Arduino UNO](https://camo.githubusercontent.com/429594b830bb91d714d4beb75bd9684a0108521b63c4a5669fa2ce955d2b396d/68747470733a2f2f61736369696e656d612e6f72672f612f3432333634392e737667)](https://asciinema.org/a/423649?autoplay=1)

Read the test scenario below to see my supported Forth constructs.

# Portability, ArduinoSTL and Valgrind/AddressSanitizer checks

I meant it when I said "portable". Part of my reasoning was, that
in addition to targeting multiple platforms (e.g. BluePill and
Arduino) I wanted to be able to use Valgrind and AddressSanitizer
to detect - in the host! - any issues I have with my memory handling.

Since I had embedded targets in mind, I tried ArduinoSTL - but it was too
wasteful memory-wise. It also made the build process significantly slower.
I therefore built my own [memory pool, as well as list, tuple and string-like
C++ templates](https://github.com/ttsiodras/MiniForth/tree/master/src/mini_stl.h). It was a nice challenge, re-inventing a tiny C++ STL...

And I understand STL a lot better now, after building small pieces of it myself :-)

# Simulation / Debugging

I setup simulation via [simavr](https://github.com/buserror/simavr.git).
This tremendously improved my developing speed, since a simulator
spawns and runs much faster than the real board. Due to the code
being portable, debugging took place mostly in the host GDB;
and after Valgrind and AddressSanitizer gave their blessing, I usually
found out that the simulator (and the real board) worked fine as well.

# BluePill vs Arduino UNO

Thanks to ArduinoSTL, I quickly reached the point of running inside the
BluePill. The 1.5$ mini-monster has 10 times more SRAM than an Arduino UNO;
so in a couple of days, I had a [working branch](https://github.com/ttsiodras/MiniForth/tree/BluePill-STM32F103C).

[![The 1.5$ 'Beast'](/ttsiodras/MiniForth/raw/master/contrib/BluePill.jpg "The 1.5$ 'Beast'")](/ttsiodras/MiniForth/blob/master/contrib/BluePill.jpg)

But as said above, that wasn't nearly enough to make it work in my
Arduino UNO. That required far more work *(see below)*.

As for the BluePill, I should note that, as in all my other embedded targets,
I prefer a development workflow that is based on normal bootloaders
*(not on programmers)*. I therefore burned the
[stm32duino](https://github.com/rogerclarkmelbourne/STM32duino-bootloader)
bootloader on the BluePill, which allowed me to easily program it
in subsequent iterations via the USB connection (and a simple `make upload`).

The same USB connection would then function as a serial port immediately
afterwards - allowing me to interact with the newly uploaded Forth in the
BluePill.

The screenshot below is from a `tmux`: on the left, the output from `make upload`;
and on the right, I used `picocom` to interact with my mini-Forth
over the serial port:

[![Compiling, uploading and testing](/ttsiodras/MiniForth/raw/master/contrib/itworks.jpg "Compiling, uploading and testing")](/ttsiodras/MiniForth/blob/master/contrib/itworks.jpg)

# Memory - the final frontier

That covered the first two days.

But when I tried compiling for the Arduino UNO, I realised that the ArduinoSTL
was not enough. I run out of memory...

So I built my own [mini-STL](https://github.com/ttsiodras/MiniForth/tree/master/src/mini_stl.h),
and tightly controlled *all* memory utilisation.

I also used macro-magic to move all strings to Flash at compile-time
(see `dprintf` in the code)... And saved memory everywhere I could,
re-using error messages across various operations - and storing the
entire array of native operations in Flash.

Nothing flexes your coding muscles as much as optimising; whether it is
for speed or for space. See the implementation of ".S" for example,
where the (obvious) stack reversal code is also the most wasteful...
Changing it to a slower but memory-preserving algorithm allowed me
to use ".S" even when almost all my memory is full.

# C++ vs C

I know that many developers hate C++. I even wrote a
[blog post](https://www.thanassis.space/cpp.html) about it.

And I understand why - they see code like this...

```
#include "mini_stl.h"

template<class T>
typename forward_list<T>::box *forward_list<T>::_freeList = NULL;
```

...and they start screaming - "what the hell is that", "incomprehensible
madness", etc.

But there are very important benefits in using C++ - and templates
in particular. You write less code, with no additional run-time or
memory overhead compared to C, and with a lot more compile-time checks
that watch your back (for things that would otherwise blow up in your face).

See my Optional for example, that emulates (badly) the optional
type of Rust/OCaml/F#/Scala/Kotlin etc. It **forces** you to check
your returned error codes:

```
Optional<int> Forth::needs_a_number(const __FlashStringHelper *msg)
{
    if (_stack.empty())
        return error(emptyMsgFlash, msg);
    auto topVal = *_stack.begin();
    if (topVal._kind == StackNode::LIT)
        return topVal._u.intVal;
    else
        return FAILURE;
}
```

You can't "forget" to check the potential for a failure coded inside
your returned value - because your code has to "unwrap" it. I could have
done this better, but I chose to implement it via simple tuples
(this was a one-weeks-afternoons hack, after all :-)

As for the template "magic" incantation above - it *is* true magic: My
`forward_list` template is using free-lists to store the `pop_front`-ed
elements and reuse them in subsequent allocations. I wanted these free-lists to
be global (i.e. static members) because lists of the same type must re-use a
single, commonly-shared free-list. The magic spell tells the compiler I want to
instantiate these globals *once*, for each type T that I use in any
lists in my code.

# My Forth test scenario - including a FizzBuzz!

Yep, FizzBuzz - we are fully Turing complete. And would surely pass
Joel's interview :-)

```
." Reset... " RESET
." Check comments... " \ Yes, we support the new-style comments :-)
." Computing simple addition of 3 + 4... " 3 4 + .
." Is 1 = 2 ?... " 1 2 = .
." Is 1 > 2 ?... " 1 2 > .
." Is 1 < 2 ?... " 1 2 < .
." Define pi at double-word precision... " : pi 355 113 */ ;
." Use definition to compute 10K times PI... " 10000 pi .
." Check: 23 mod 7... " 23 7 MOD .
." Defining 1st level function1... " : x2 2 * ;
." Defining 1st level function2... " : p4 4 + ;
." 2nd level word using both - must print 24... " 10 x2 p4 . 
." Defining a variable with value 123... " 123 variable ot3
." Printing variable's value... " ot3 @ .
." Defining The Constant (TM)... " 42 constant lifeUniverse
." Printing The Constant (TM)... " lifeUniverse .
." Setting the variable to The Constant (TM)... " lifeUniverse ot3 !
." Printing variable's value... " ot3 @ .
." Setting the variable to hex 0x11... " $11 ot3 !
." Printing variable's value... " ot3 @ .
." Setting the variable to binary 10100101... " %10100101 ot3 !
." Printing variable's value... " ot3 @ .
." Defining helper... " : p5 5 U.R . ;
." Defining 3 times loop... " : x3lp 3 0 DO I p5 LOOP ;
." Calling loop... " x3lp
." Defining loop calling loop 2 times... " : x6lp 2 0 DO x3lp LOOP ;
." Nested-looping 2x3 times... " x6lp
." Inline: " : m 3 1 DO 3 1 DO CR J p5 I p5 ." = " J I * p5 LOOP LOOP ;
." Use inline loops with two indexes... " m
." Make multiples of 7 via DUP... " : m7s 10 0 DO DUP I * . LOOP DROP ;
." Print them and DROP the 7... " 7 m7s
." Reset... " RESET
\ Time for Turing completeness...
." Let's do Fizz-Buzz! " \ Turing Completeness check...
\ fizz ( n -- 0_or_1 n )
." Define fizz... " : fizz DUP 3 MOD 0 = IF ." fizz " 1 ELSE 0 THEN SWAP ;
\ buzz ( n -- 0_or_1 n )
." Define buzz... " : buzz DUP 5 MOD 0 = IF ." buzz " 1 ELSE 0 THEN SWAP ;
\ emitNum ( 0_or_1 0_or_1 n -- )
." Define emitNum... " : emitNum ROT ROT + 0 = if . ELSE DROP THEN ;
\ mainloop ( n -- )
." Define mainloop... " : mainloop ." ( " fizz buzz emitNum ." ) " ;
\ fb ( -- )
." Define fizzbuzz... " : fb 37 1 DO I mainloop LOOP ;
." Run it! " fb
." Report memory usage... " .S
." All done! "
```

# Automation

I am a strong believer in automation. The final form of my `Makefile`
therefore has many rules - e.g. `make arduino-sim` - that automate
various parts of the workflow.

Here's what they do:

* **arduino**: Compiles the code for Arduino UNO - builds `src/tmp/myforth.ino.{elf,hex}`
* **arduino-sim**: After building, launches the compiled mini-Forth in `simduino`.
* **upload**: After building, uploads to an Arduino attached to the port
  configured inside `config.mk`.
* **terminal**: After uploading, launches a `picocom` terminal with
  all appropriate settings to interact with my Forth.
* **x86**: Builds for x86. Actually, should easily build for any native target (ARM, etc).
* **test-address-sanitizer**: Uses the x86 binary to test the code, executing
  all steps of the scenario shown above. The binary is built with the
  address sanitizer enabled (to detect memory issues).
* **test-valgrind**: Same, but with Valgrind.
* **test-simulator**: Spawns `simavr` and sends the entire test scenario shown
  above to it - while showing the responses received from it.
* **test-arduino**: Sends the entire test scenario shown above to an
  Arduino Uno connected to the port specified in `config.mk`
  and shows the responses received over that serial port.
* **blink-arduino**: Sends the "hello word" of the HW world: a tiny
  [Forth program](/ttsiodras/MiniForth/blob/master/testing/blinky.fs) blinking the Arduino's LED.

Another example of automation - the complete test scenario shown in the
previous section, is not just an example in the documentation; it is
extracted automatically from this README and fed into the Valgrind and
AddressSanitizer tests... and also into the Python testing script that
sends the data to the board in real-time.

[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), folks.

# Conclusion

I thoroughly enjoyed building this. I know full well that Forths are not
supposed to be built in C++; they are supposed to be built in assembly,
and also, utilise the Flash to store the user-compiled code at run-time.

But that wasn't the point of this - the point was to have fun and learn Forth.  
And what better way to learn a language than to actually implement it! :-)

And... as a child of the 80s... I now know first-hand what
[Jupiter Ace](https://en.wikipedia.org/wiki/Jupiter_Ace) was about :-)

Fork the code, and enjoy tinkering with it!  
Thanassis.

## About

A tiny Forth I built in a week. Blog post: <https://www.thanassis.space/miniforth.html>

### Topics

[arduino](/topics/arduino "Topic: arduino")
[cpp](/topics/cpp "Topic: cpp")
[forth](/topics/forth "Topic: forth")

### Resources

[Readme](#readme-ov-file)

### License

[MIT license](#MIT-1-ov-file)

### Uh oh!

There was an error while loading. Please reload this page.

[Activity](/ttsiodras/MiniForth/activity)

### Stars

[**95**
stars](/ttsiodras/MiniForth/stargazers)

### Watchers

[**6**
watching](/ttsiodras/MiniForth/watchers)

### Forks

[**7**
forks](/ttsiodras/MiniForth/forks)

[Report repository](/contact/report-content?content_url=https%3A%2F%2Fgithub.com%2Fttsiodras%2FMiniForth&report=ttsiodras+%28user%29)

## [Releases 2](/ttsiodras/MiniForth/releases)

[Final release - optimal memory/flash use everywhere.

Latest

Jul 9, 2021](/ttsiodras/MiniForth/releases/tag/v1.1)

[+ 1 release](/ttsiodras/MiniForth/releases)

## [Packages 0](/users/ttsiodras/packages?repo_name=MiniForth)

No packages published

## Languages

* [C++
  80.5%](/ttsiodras/MiniForth/search?l=c%2B%2B)
* [C
  10.9%](/ttsiodras/MiniForth/search?l=c)
* [Makefile
  4.9%](/ttsiodras/MiniForth/search?l=makefile)
* [Python
  1.5%](/ttsiodras/MiniForth/search?l=python)
* [Shell
  1.3%](/ttsiodras/MiniForth/search?l=shell)
* [Forth
  0.9%](/ttsiodras/MiniForth/search?l=forth)

## Footer

© 2026 GitHub, Inc.

### Footer navigation

* [Terms](https://docs.github.com/site-policy/github-terms/github-terms-of-service)
* [Privacy](https://docs.github.com/site-policy/privacy-policies/github-privacy-statement)
* [Security](https://github.com/security)
* [Status](https://www.githubstatus.com/)
* [Community](https://github.community/)
* [Docs](https://docs.github.com/)
* [Contact](https://support.github.com?tags=dotcom-footer)
* Manage cookies
* Do not share my personal information



You can’t perform that action at this time.