Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 06:29 10 Nov 2026 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : zeptoforth on the Colour Maximite 2

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 12152
Posted: 04:35pm 07 Nov 2026
Copy link to clipboard 
Print this post

zeptoforth on the Colour Maximite 2

A complete Forth for both the G1 and the G2, in the RC3 programs zip. It needs firmware 7.00.00RC3 or later.

https://github.com/UKTailwind/CMM2V1.5/releases/tag/v7.00.00RC3

Travis Bemann's zeptoforth (https://github.com/tabemann/zeptoforth) now runs on the CMM2. It has preemptive multitasking, a full-screen editor, double-precision floating point on the H743's FPU, and words for the screen, sound, files, the keyboard and mouse, gamepads and the I/O pins. The screen and USB keyboard are its console, so you need no PC.

The firmware has not changed. If you already have RC3, download the programs zip again, as zeptoforth was added to it on 7 November, and copy the zeptoforth folder to your card.


Why Forth?
If you have only used BASIC, Forth looks odd at first. It is worth the effort, because it works in a very different way.

A Forth program is a set of words. You build each new word from the words you already have, and from then on it is part of the language, no different from the words that came with it. Numbers pass between words on a stack, so a word needs no declarations and no argument lists:

: square  dup * ;
7 square .


The first line defines a word that squares a number. The second puts 7 on the stack, squares it, and prints 49.

That simple idea is where its power comes from:

• It is compiled and interactive at once. Each word becomes ARM machine code the moment you finish typing it, on the CMM2 itself, with no PC and no build step, and you can try it at the prompt straight away.
• You build from the bottom up. You test each small word as you write it, so a fault is usually in the last thing you typed.
• The language is yours to change. The compiler is made of words too, so you can add control structures of your own, or change how numbers are read. Double-precision numbers like 1.5de0 were added here with about 30 lines of Forth, by hooking the way numbers are read.
• Nothing stands between you and the machine. Words read and write any address, and there is an assembler when you need one. If you get an address wrong, the fault usually comes back to the prompt as an error.
• It is small. The whole system here, with the library and both editors, takes about 510KB.

zeptoforth adds real multitasking to all that. Tasks are preemptive, so one that never yields cannot hold up the others, and there are semaphores, locks and channels between them. This runs beside the prompt, which stays in use:

task import
: hello 3 0 do ." tick " 100 ms loop cr ;
0 ' hello 320 256 1024 spawn run



Getting started
Copy the zeptoforth folder from CMM2_Programs_7.00.00RC3.zip to A:/zeptoforth. Then on a G2:

CHDIR "A:/zeptoforth"
RUN "zeptoforth.app"


On a G1 the program is zeptoforth_g1.app. Type bye to go back to MMBasic. Ctrl-C stops a word that is running and restarts Forth: what you saved is kept, and what is only in RAM is lost.

To draw on the screen:

cmm2 import
600 100 60 3 1de0 cyan orange circle


To edit a file on the card:

s" A:/zeptoforth/mycode.fs" zed


In the editor Ctrl-W saves, Ctrl-V leaves, and Ctrl-? or Ctrl-/ shows all the keys. To load a program, type include mycode.fs. To keep your words for next time, compile them to the flash dictionary and save it:

compile-to-flash
include mycode.fs
compile-to-ram
save-image



What it has
• The console is the VT100 terminal on the screen, so ANSI colours and cursor control work, with 500 lines of scrollback on Shift with Page Up and Page Down. It works in any 8 or 16-bit mode, with the font chosen to suit, and your graphics keep their colours beside it. The serial port gets the same output, and you can type at either.
• Typing enable-line gives you line editing and history on the arrow keys.
• zeptoed is a full-screen editor with several files open, selection, cut and paste, and undo. It has been tried on a 62KB program.
• Forth's blocks are kept on the card, with the block editor: n edit, n list and n load.
• Double precision uses dv words, such as dv+, dvsin and dv., with numbers like 1.5de0.
• zeptoforth's own file words work on the firmware's files, with MMBasic's names. The date and time are the CMM2's, and random comes from the hardware.
• The module cmm2 has the firmware's services as Forth words, with arguments in MMBasic's order.


Learning Forth
If Forth is new to you, these are good places to start:

• Easy Forth, a short tutorial that runs in your browser: https://skilldrick.github.io/easyforth/
• Starting Forth by Leo Brodie, the classic introduction, free to read online: https://www.forth.com/starting-forth/
• Thinking Forth, also by Leo Brodie, on how to design programs in Forth: https://thinking-forth.sourceforge.net
• The tutorial in the Gforth manual: https://www.complang.tuwien.ac.at/forth/gforth/Docs-html/Tutorial.html
• zeptoforth's wiki: https://github.com/tabemann/zeptoforth/wiki

Forths differ in their details, so a word in a book may have another name here, or need a module imported.

For reference, WORDS.md in the zeptoforth folder describes every word added for the CMM2: the screen, sound, files and I/O, double precision, blocks and the rest. zeptoforth's own reference has everything else: https://github.com/tabemann/zeptoforth/tree/master/docs/words


Known limits
• A G2 has 3MB of RAM dictionary and 2MB of flash dictionary. A G1 has 1MB and 768KB.
• What zeptoforth calls flash is memory here, saved to zeptoforth.img or zeptoforth_g1.img by save-image. Nothing is written to the CMM2's own flash. Keep a copy of the image before you save over it.
• list-dir cuts names to 8.3. The word files in the module cmm2 gives them whole.
• Forth asks the terminal on the screen how big it is, so a terminal on a PC should be set to the same size, or use console-size.
• zeptoforth's manual covers words for other boards, such as pin and uart. Here those are in the module cmm2.


What has and has not been tried
On both a G1 and a G2: 313 checks of the words for the screen, sound, files and I/O, 108 checks of double precision against IEEE's exact results, and a 1,900-line solar eclipse program whose output is identical to MMBasic's, byte for byte. On a G2 it also ran beside three tasks that never yield. A G1 was tried with OPTION COMPILE ON as well. A real USB keyboard has been tried on a G2.

Not tried yet: a USB keyboard on a G1, the mouse wheel, a gamepad, and I2C, SPI, ADC and serial receive with real devices. Please report what you find.

The solar eclipse program in forth
0.94 seconds on the G2, 1.16 seconds on the G1
solar_eclipse.zip

Licences
zeptoforth is under the MIT licence, and my thanks go to Travis Bemann for it. Its console has libvterm, under the MIT licence, and the Spleen fonts, under the BSD 2-Clause licence. The licences are in the licences folder of the zip. The source is in apps/zeptoforth of the firmware's repository, and the guide is in Colour_Maximite_2_App_User_Guide.pdf, posted with the release.
Edited 2026-11-08 02:47 by matherp
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 12152
Posted: 05:28pm 07 Nov 2026
Copy link to clipboard 
Print this post

Black-Scholes on the CMM2
One of my first jobs, back in around 1983, was implementing a new options reporting system for the London International Financial Futures exchange LIFFE and this involved moving an implementation of Black-Scholes from Forth to Pascal. Now that the CMM2 has a Forth with real double-precision floating point, it seemed a good time to see it working in Forth again.

Black-Scholes gives the fair value of a stock option from five numbers: the stock price, the strike price, the interest rate, the volatility and the time to expiry. A call is the right to buy at the strike price, and a put is the right to sell at it.

This is the whole of the maths:

\ 1 - N(x) for x >= 0, N being the cumulative normal distribution. This is
\ Abramowitz and Stegun 26.2.17, which is good to 7.5e-8
: normal-tail { D: x -- D: tail }
 dv1 0.2316419de0 x dv* dv1 dv+ dv/ { D: t }
 1.330274429de0 t dv* -1.821255978de0 dv+
 t dv* 1.781477937de0 dv+
 t dv* -0.356563782de0 dv+
 t dv* 0.319381530de0 dv+
 t dv*
 x x dv* dv-half dv* dvexp dv*
 dv2 dvpi dv* dvsqrt dv/
;

\ N(x)
: cum-normal { D: x -- D: n }
 x dv0< if x dvnegate normal-tail else dv1 x normal-tail dv- then
;

\ The values of a call and a put, from the stock price, the strike price, the
\ interest rate, the volatility and the time to expiry
: black-scholes { D: s D: k D: r D: vol D: t -- D: call D: put }
 vol t dvsqrt dv* { D: sd }
 s k dv/ dvln  vol vol dv* dv2 dv/ r dv+ t dv*  dv+  sd dv/ { D: d1 }
 d1 sd dv- { D: d2 }
 r t dv* dvnegate dvexp k dv* { D: pv }
 d1 cum-normal s dv*  d2 cum-normal pv dv*  dv-
 d2 dvnegate cum-normal pv dv*  d1 dvnegate cum-normal s dv*  dv-
;


The { ... } at the start of a word names its inputs as local variables, and D: means the variable holds a double. cum-normal uses a polynomial from Abramowitz and Stegun's Handbook of Mathematical Functions, published in 1964. It is the one part that isn't full double precision, and its error doesn't reach the fourth decimal place in the results below.


The results
Black-Scholes in double precision

Stock 42, strike 40, interest 10%, volatility 20%, 6 months
Call   4.7594    Put   0.8086

Stock 31.55, strike 22.75, interest 5%, volatility 50%, 3.5 years
Call  16.5039    Put   4.0516

Stock 100, interest 10%, volatility 20%

        Calls, by months to expiry     Puts, by months to expiry
Strike       1       3       6      12         1       3       6      12
   80 20.6639 21.9939 24.0270 27.9927    0.0000  0.0187  0.1254  0.3797
   90 10.7971 12.6450 15.2883 19.9886    0.0503  0.4229  0.8990  1.4239
  100  2.7322  5.2954  8.2778 13.2697    1.9023  2.8264  3.4007  3.7534
  110  0.1741  1.4711  3.7432  8.1831    9.2612  8.7552  8.3784  7.7152
  120  0.0023  0.2675  1.4186  4.7082   19.0064 17.3047 15.5662 13.2887

0 checks failed


The second example agrees with the 16.504 from a Black-Scholes calculator, and the whole output is identical, byte for byte, to the same arithmetic done in Python. The program also checks put-call parity across the table.

On a G2 it values a call and a put in 14.7 microseconds, which is about 68,000 a second.


Try it
Download black_scholes.fs from here and copy it to A:/zeptoforth:
https://github.com/UKTailwind/CMM2V1.5/blob/main/apps/zeptoforth/test/black_scholes.fs

Start zeptoforth, then type:

include black_scholes.fs
option-table


To value an option of your own, give the stock price, strike price, interest rate, volatility and years to expiry. Rates are fractions, so 5% is 0.05de0. This prints the put, then the call:

31.55de0 22.75de0 0.05de0 0.5de0 3.5de0 black-scholes dv. dv.

Edited 2026-11-08 03:30 by matherp
 
tabemann
Newbie

Joined: 21/07/2025
Location: United States
Posts: 11
Posted: 10:41pm 07 Nov 2026
Copy link to clipboard 
Print this post

This is Travis here. It's really cool that you got zeptoforth working on the CMM2, which makes me wish I owned one so I could try it out.

I can just imagine the amount of work needed to get it to work with the CMM2 firmware, since zeptoforth was originally intended to be completely bare-metal only and has a lot of dependencies on the underlying hardware. (This has slowed down work on making a compatible port of zeptoforth to RISC-V.)

The way your port handles writing to flash reminds me of the way Mecrisp-Stellaris on the RP2040 and Mecrisp-Quintus on the RP2350 do, except that instead of reading images from and writing images to actual flash they are stored in files. Of course, the fact that the CMM2 firmware has the built-in assumption of a filesystem being available makes this possible, whereas mainline zeptoforth, Mecrisp-Stellaris, and Mecrisp-Quintus all do not assume a filesystem (even though 'full*' builds of zeptoforth include FAT32 drivers). Even in zeptoforth on the PicoCalc, which requires an SD card to boot, FAT32 is treated as a sort of bolted-on extra.

One note is that a good amount of zeptoforth FAT32-using code assumes 8.3 filenames at the present, as you alluded to with regard to FAT32-TOOLS::LIST-DIR . This is something to keep in mind when dealing with files and especially directory entries ─ there may be bugs when dealing with LFN's in code that was not written for them. I had done some work to make zeptoforth well-behaved in the face of Long File Names, e.g. deleting LFN's when their files are deleted, but fundamentally I chose to stick with 8.3 for the sake of minimizing the complications of things like renaming files (as renaming a file with an LFN is a non-trivial operation, especially when faced with things like concurrent file operations).

To other people here who might want to try out zeptoforth on the CMM2, one thing that matherp made me aware of is that the single-precision floating-point math routines have some bugs, which are largely caused by instability of mathematical algorithms which ought to work in a world in which one has unlimited precision when faced with the limited precision of single-precision floating-point mantissas combined with accumulating error across multiple iterations. In particular, don't try FLOAT32::VEXP with negative inputs ─ it just doesn't work.

I plan on revising the single-precision floating-point math routines in an upcoming release, but this may take some time as it will involve fundamental algorithmic changes and not simple bugfixes. As a temporarily fix for FLOAT32::VEXP I will have it use the reciprocal when negative arguments are passed in.

Note that this does not impact matherp's double-precision floating-point routines, which are implemented separately from zeptoforth and are merely called from zeptoforth.
 
tabemann
Newbie

Joined: 21/07/2025
Location: United States
Posts: 11
Posted: 03:48am 08 Nov 2026
Copy link to clipboard 
Print this post

I've implemented an improved version of FLOAT32::VEXP that uses an entirely different approach, that calculates 2**(x * log2(e)) by breaking x * log2(e) into k + r where k is an integral portion rounded to the nearest even number and r is the difference, and then calculates 2**(k + r) by breaking it into 2**k * 2**r, and then uses a purely binary floating point approach to calculate 2**k and a fixed polynomial to calculate 2**r.
 
tabemann
Newbie

Joined: 21/07/2025
Location: United States
Posts: 11
Posted: 04:15am 08 Nov 2026
Copy link to clipboard 
Print this post

I've also fixed FLOAT32::V>F64 , which had a truly stupid bug in it that should have been obvious.
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1140
Posted: 06:59am 08 Nov 2026
Copy link to clipboard 
Print this post

Wow!    
Found Leo Brodie's book in my shed library - hope to install over the next few days and play. Thanks Peter.

While delving in my shed library, I also found "Programming in Modula-2" by Nicholas Wirth (Modula-2) "Programming in Prolog" by Clocksin and Mellish ( see GNU version by Daniel Diaz here  )

Doug
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
tabemann
Newbie

Joined: 21/07/2025
Location: United States
Posts: 11
Posted: 05:42pm 08 Nov 2026
Copy link to clipboard 
Print this post

Hope you have fun with this!

I should note that this is a much fuller-featured Forth than many out there. It provides many amenities that many Forths lack which are largely outside the scope of Leo Brodie's books.

Of course, this means that it has larger binaries than most Forths, especially since its code generator is optimized for code speed over code size.

Let me and matherp know if you have any questions. I keep track of discussions and issues on the zeptoforth GitHub (https://github.com/tabemann/zeptoforth) and have subscribed to this thread.

Of course, for matherp's port here may be a better place because he's put a lot of his own work into it independent of my work, and because I currently do not own a CMM2.
 
tabemann
Newbie

Joined: 21/07/2025
Location: United States
Posts: 11
Posted: 03:54pm 09 Nov 2026
Copy link to clipboard 
Print this post

@matherp

I was going to say that you should bring your zeptoforth port in sync with 1.17.2, but I see you've already done it.
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2026