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 at the device_id location 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.
We can crib the right values from the firmware source code.
Putting it all together
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 an adjunct to the BDOS GET_VERSION call in CP/M. Watch this space!
