Twelve new words, stack-underflow checks on every word that takes arguments,
and fixes for banked memory. Tested on real MicroBeast hardware.
New words
Stack
NIP ( a b -- b ), TUCK ( a b -- b a b ) (Core Extensions)
-ROT ( a b c -- c a b ), the reverse of ROT (antforth extension)
? ( addr -- ) prints the value stored at an address, the same as @ . (Tools)
Comparisons
<>, 0<>, 0>, U> (Core Extensions)
<=, >=, signed (antforth extensions)
Loops
BEGIN … AGAIN for loops that never end on their own, such as task bodies
and event loops. Leave with EXIT.
?DO runs zero times when the limit equals the start (DO would run 65,536
times). It works with LOOP, +LOOP, LEAVE, UNLOOP, I and J.
Core Extensions coverage goes from 15 to 23 of the 46 words.
Stack underflow is caught everywhere
Every kernel word and assembler mnemonic that takes stack arguments now raises -4 stack underflow when they're missing. The check runs before the word
changes anything. Before, DUP, 0=, 1+, TYPE, FILL, MOVE, ACTIVATE and about 45 others silently worked on whatever was left in a
register.
PICK and ROLL check that the stack holds u items.
SET-ORDER and RESTORE-INPUT count their arguments correctly.
An assembler instruction missing an operand raises -4 before it writes any
code bytes, so a caught error leaves HERE where it was.
Fixes
>NUMBER swapped the two halves of its starting value. With a nonzero
start, 1 0 S" 0" >NUMBER 2DROP D. printed 655360 instead of 10.
Starting from 0 0, as most code does, wasn't affected.
Banked words could hide or hang kernel words. Giving +BANK the same
physical page twice made two banks share one piece of RAM, so defining words
in one bank overwrote dictionary entries in the other. A lookup could then
miss CR or BANK!, or hang. +BANK now refuses a page that is already
listed (dup?, error -2), and so does the boot page list.
Parsing near the top of a bank could write into fixed RAM. When a bank
above 0 was nearly full, the parse buffer and PAD ran past $C000 and
overwrote bank 0's dictionary. Banks above 0 now end at $BF58, so .BANKS
shows 16,216 bytes free per bank instead of 16,384.
WORD refuses words over 128 characters with error -18, so even EVALUATE can't overflow the parse buffer.
Things to know when upgrading
Room for only two tasks. Every task's control block (TCB) must sit below $8000, and this kernel leaves room for two named tasks
(TASK CONSTANT name), with 25 bytes to spare. A third TASK raises -8.
Create your tasks before anything else. TRAFFIC.FTH now does this, so LIGHTS is task 1. To see the free room, type DECIMAL 32768 HERE - . at a
fresh prompt. See "How many tasks fit" in docs/phase6-multitasker.md.
Scripts that add the same page twice ($22 +BANK $22 +BANK) now fail.
Use a different page for each bank.
The underflow checks cost some speed: INCLUDE is about 10–14% slower in
CPU cycles. Disk I/O is unchanged, so the real-world difference is smaller.
Banked code must leave 168 bytes below $C000. Code that filled a bank to
the brim before now raises -8 sooner.
Size
antforth.com is 31,395 bytes, 641 more than v3.2.1.
AntForth 3.3.0 released
I made a new release of AntForth, because there are a couple of words I need for another project. There were a few digressions and bugfixes along the way!
Get it here.
Release notes:
antforth v3.3.0
Twelve new words, stack-underflow checks on every word that takes arguments,
and
The new Feersum Technology NanoBeast is nearly with us! It shares a lot of DNA with the MicroBeast, but there are some subtle differences that might call for special-case handling in any generic cross-beast software. How do we go about that?
Examining the firmware
The easiest approach is to look at the device_idlocation in the firmware. This will return 0x1 if you're currently running on a MicroBeast, and 0x2 if you're running on a NanoBeast, as per these defined constants.
The downside to this approach is that this magic memory location isn't at a fixed address: the next firmware released could move it to a different address. And it doesn't exist at all for firmware versions prior to 1.8.
This means that to be truly portable we'd have to detect the firmware version first, and then detect the hardware platform, which is a tad irksome, but not insurmountable.
What we need is a more portable approach.
Detecting the hardware
We can start by taking a look at how the firmware does it: what's writing 0x1 or 0x2 into the device_id location?
This code is writing values to the PIO_A_CTRL and PIO_B_CTRL IO ports. These ports map to the Z84C20 PIO controller chip. This is the chip that provides the two 8-bit bidirectional I/O ports (with handshaking) that's used on the MicroBeast.
And that's the crucial part: the NanoBeast does not use the same chip, it has custom PIO logic in its own CPLD IC.
Here are the PIO-related IO ports for both devices, side by side:
IO address
MicroBeast
NanoBeast
0x10
Port A data
Port A data
0x11
Port B data
Port A direction
0x12
Port A control
LCD Translate (low)
0x13
Port B control
LCD Translate (high)
0x14
Port B data
0x15
Audio
0x16
Interrupt control
0x17
I2C data
So we can see that on a NanoBeast, ports 0x12 and 0x13 are no longer PIO_A_CTRL and PIO_B_CTRL but rather something new and mysterious called LCD Translate. I surmise this is glue logic in the NanoBeast's CPLD to support the NanoExplorer peripheral, which has a single line LCD display.
Crucially, these registers have an ID function, in that if we write the value NIO_TEST_BITS to both LCD control registers, the lower one will respond with NIO_VALID_LOWER and the upper one with NIO_VALID_UPPER.
If we pull the same trick on a MicroBeast we will be writing to PIO_A_CTRL and PIO_B_CTRL. The value chosen (NO_TEST_BITS, 0x4f) has the effect of setting a PIO port into input mode (Mode 1), so it will affect the MicroBeast's PIO ports, but does so in an electrically safe way.
When we attempt to read back PIO_A_CTRL and PIO_B_CTRL on the MicroBeast we will get nonsense, because those registers are write-only in a real PIO chip.
Tidying up after ourselves
The BeastOS firmware does hardware detection before it sets up the PIO ports to support system functionality. If we emulate how it works in our own code, we'll have clobbered the system PIO port configuration on a MicroBeast. Therefore it is crucial to restore the ports to a known, working state on the MicroBeast, otherwise we might find that the Real Time Clock, I2C, and UART no longer function.
Unfortunately it's not as simple as reading the incumbent values of PIO_A_CTRL and PIO_B_CTRL before we start messing about and restoring them afterwards: remember that real PIO control registers are write-only.
Here's what it looks like in Forth, the World's Greatest Language:
( microbeast ports)
0x10 constant M_A_DATA
0x12 constant M_A_CTRL
0x11 constant M_B_DATA
0x13 constant M_B_CTRL
( nanobeast ports)
0x10 constant N_A_DATA
0x11 constant N_A_DIR
0x14 constant N_B_DATA
( nanobeast detection)
0x4f constant NIO_TEST_BITS
0xd4 constant NIO_VALID_LOWER
0xfa constant NIO_VALID_UPPER
0xcf constant M_B_MODE
0xff constant M_B_IOMASK
: is-nanobeast
NIO_TEST_BITS M_A_CTRL OUT
NIO_TEST_BITS M_B_CTRL OUT
M_A_CTRL IN NIO_VALID_LOWER =
M_B_CTRL IN NIO_VALID_UPPER =
and
( restore state)
M_B_MODE M_B_CTRL OUT
M_B_IOMASK M_B_CTRL OUT
;
: wotbeast
is-nanobeast IF ." nanobeast" ELSE ." microbeast" THEN CR ;
is-nanobeast is a new word that does the actual checking, and wotbeast uses it to print a human-readable identifier.
And here's that code running on a NanoBeast and a MicroBeast, side by side:
If you'd like to watch a video of me doing this from scratch then you're in luck:
Future directions
The 1.8 firmware's built-in device_id facility is supremely useful, as it is a cached version of the above hardware check, done at the right time (i.e. before the PIO ports got set up by the system for its own use).
If we can use that instead of probing the hardware ourselves then we don't perturb the hardware and we don't have to restore it afterwards.
But the forwards and backwards compatibility remains a problem. For this reason, it is highly likely that the NanoBeast release firmware will include the same functionality in a more portable form: possibly as part of the existing MBB_GET_VERSION BIOS call.
Sexing your Beasts
The new Feersum Technology NanoBeast is nearly with us! It shares a lot of DNA with the MicroBeast, but there are some subtle differences that might call for special-case handling in any generic cross-beast software. How do we go about that?
Examining the firmware
The easiest approach is to look
This version of SLIDE brings it up to date with the latest version of the SLIDE Protocol spec. The headline item is the introduction of a "command channel" which allows your main computer to to send simple commands to the MicroBeast and get responses back, during a file transfer session. Currently those commands are:
VOLS - what volumes (drives) are present on the 'Beast
DIR - what files are in a particular volume?
REN - rename a file
DEL - delete a file
I know what you're thinking: all MicroBeasts have two drives but that might not always be the case!
These commands are mainly intended for use by impending BeasTTY features, but they're available right now in the command line helper tools:
SLIDE — command-channel probe (wire v0.3 §2)
Port: /dev/ttyUSB4 @ 19200 baud
Probe: ENQ 0x05, 3 attempt(s), 500ms echo timeout
Typing slide r at the CP/M prompt...
✓ Z80 connected. (46 stray byte(s) skipped, wakeup signature seen)
--- Probe result (after handshake) ---
Outcome: supported
Attempts: 1
Queued before probe: (none)
Stray bytes during probe window: (none)
VER raw: 30 30 30 32 ('0002')
Version: major 0, minor 2
We speak: major 0, minor 2
--- CMD_REN (current drive SLIDE.COM -> SLIDE2.COM) ---
Status: 0x00 — ST_OK
(no records — probe-only exchange completed)
--- FIN exchange (session-intact check) ---
FIN echoed. The peer was still in its file loop and exited
cleanly — the probe did not disturb the session.
VERDICT: probe outcome 'supported', session intact.
DEL
./slide probe /dev/ttyUSB4 --start-cmd "slide2 r" --cmd del --match=RESET.COM
gets you:
SLIDE — command-channel probe (wire v0.3 §2)
Port: /dev/ttyUSB4 @ 19200 baud
Probe: ENQ 0x05, 3 attempt(s), 500ms echo timeout
Typing slide2 r at the CP/M prompt...
✓ Z80 connected. (47 stray byte(s) skipped, wakeup signature seen)
--- Probe result (after handshake) ---
Outcome: supported
Attempts: 1
Queued before probe: (none)
Stray bytes during probe window: (none)
VER raw: 30 30 30 32 ('0002')
Version: major 0, minor 2
We speak: major 0, minor 2
--- CMD_DEL (current drive RESET.COM) ---
Status: 0x00 — ST_OK
(no records — probe-only exchange completed)
--- FIN exchange (session-intact check) ---
FIN echoed. The peer was still in its file loop and exited
cleanly — the probe did not disturb the session.
VERDICT: probe outcome 'supported', session intact.
As you can see, it's a little verbose: there's a lot of diagnostic information in there that we used during backwards compatibility testing.
We did a lot of backwards compatibility testing...but there are always gremlins lurking. If you're feeling helpful please adopt slide v0.6.1 as your daily driver and give it a whirl while we wait for the new BeasTTY features - let me know of any issues!
SLIDE v0.6.1 released
I just released version 0.6.1 of SLIDE, the PC<-->Beast serial file transfer tool.
This version of SLIDE brings it up to date with the latest version of the SLIDE Protocol spec. The headline item is the introduction of a "command channel" which