|
Forum Index : Microcontroller and PC projects : zeptoforth on the Colour Maximite 2
| Author | Message | ||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 12152 |
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 KingdomPosts: 12152 |
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 StatesPosts: 11 |
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 StatesPosts: 11 |
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 StatesPosts: 11 |
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: AustraliaPosts: 1140 |
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 StatesPosts: 11 |
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 StatesPosts: 11 |
@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. |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |