After a very active month of development since the 0.5 announcement, it is time to welcome EndBASIC 0.6!

This new 0.6 release is super-exciting for three reasons:

  • preliminary GPIO support in the standard library specifically tailored to the Raspberry Pi;
  • multidimensional array support in the language; and
  • availability of binary releases for the most common platforms.

You can dive right in by:

But stick around and continue reading if you are interested in many more details about these major changes 😉

GPIO support for the Raspbery Pi

Let’s begin this release walk-through with the flashy stuff:

How it started on Feb 1st: a Monk Makes Project Box 1 and no experience with GPIO nor connecting hardware to the Raspberry Pi.
How it was going on Feb 15th: a prerelease build of EndBASIC 0.6 showing its GPIO interface on this same machine.

I started this month of February with no clue on how to program the Raspberry Pi’s hardware. I had had this little machine for years now, mostly for RetroPie, but it wasn’t until last month that I got the idea of using EndBASIC to leverage its hardware features. After all, quick rewards is what EndBASIC is all about, and flashing LEDs qualifies as such. I quickly ordered the first beginner’s kit I could find with good reviews (a Monk Makes Project Box 1) and followed some of its sample circuits. Soon after, I was on my way to prototyping GPIO access from EndBASIC.

Adding GPIO support was way more frustrating than originally anticipated. At first, I started using a crate that relies on sysfs to manipulate the GPIO pins, but then found out that this crate is ancient (wouldn’t run as non-root), and that GPIO via sysfs is deprecated and going away. I picked up the next best crate for GPIO, which uses the character device interface… and things worked, but I then discovered that a trivial button wouldn’t function as easily as it did with the Python libraries.

As it turns out, the trivial button push examples shown in the official projects rely on the Raspberry Pi’s built-in pull-up and pull-down resistors. Their use is abstracted away in the gpiozero Python libraries so, even though you think you are operating with GPIO alone, you are actually doing much more. This extra hardware is “out of band” of GPIO and requires dedicated handling, which this generic crate I picked didn’t have. So I ended up having to find an alternative specifically for the Raspberry Pi and RPPAL came along the way. Unfortunately, this crate is not portable so I had to add build-time Cargo features to make its use conditional, which in turn makes installation more fragile. Sigh.

But it all now finally works and controlling an LED attached to, let’s say, pin 18 is as simple as doing something like:

GPIO_SETUP 18, "OUT"
state = TRUE
FOR i = 1 to 10
    GPIO_WRITE 18, state
    SLEEP 1
    state = NOT state
NEXT
GPIO_CLEAR

And equally easy is waiting for a button press connected between, say, pin 8 and ground:

GPIO_SETUP 8, "IN-PULL-DOWN"
pressed = FALSE
WHILE NOT pressed:
    pressed = GPIO_READ(8)
    SLEEP 0.01
WEND

The Raspberry Pi has many more hardware features than GPIO, and even GPIO on its own can be more complicated than what I showed above. So the next challenge for EndBASIC is: getting SPI support in place so that I can drive this little LCD display and get some scrolling banner on it:

Raspberry Pi 3 with a Waveshare LCD hat showing its builtin demo program. Future EndBASIC versions should have the necessary features to program this type of hardware.

Multidimensional array support

Another core feature in this release is support for multidimensional arrays. I had previously envisioned that I would need this to support GPIO by exposing the pins as a memory-mapped array, so I did invest the time to write this feature upfront. In the end I took a very different path for GPIO but arrays are interesting and necessary anyway.

The primary difficulty in adding arrays was that the syntax to index arrays and to call functions is ambiguous. You see: BASIC uses a notation like values(x, y) to express what you’d write as values[x][y] in pretty much any other language (note the use of parenthesis, not brackets). This, combined with the way functions were previously handled in the expression parser, made it very hard to properly handle this syntax without a general rewrite. Things were even worse when trying to disambiguate array assignments from builtin calls. Compare the array assignment values(x) = y vs. the PRINT (x) call: the parser has to wait until it sees the equal sign to decide what type of statement this is.

This all made me wonder: why did the designers of the language put up with these difficulties when computing resources were more limited? Why didn’t they choose an easier syntax to process? Well, I think they did, and it’s me who has made things difficult for myself. You see: if you make command names such as PRINT be first-class citizens in the lexer, then it’s trivial to disambiguate array assignments from statements. All you have to do is look at the first token of each line to know what to do (and I guess that’s why LET exists, although it’s optional). But remember: I’m explicitly keeping the core of EndBASIC separate from the standard library, which means that the lexer cannot know anything about command names. The core doesn’t know that PRINT is special. This required some… interesting changes to the parser, but these were still doable with just one lookahead token. And these changes will be useful anyway to implement nastier things like MID$(str, 2, 3) = "foo", which QuickBASIC offers to modify strings in-place.

And with that, arrays work as you might expect:

DIM squares(10) AS INTEGER

' Populate the array...
FOR i = 0 TO 9
    squares(i) = i * i
NEXT

' And print its contents...
FOR i = 0 TO 9
    PRINT "The square of"; i; "is"; squares(i)
NEXT

Overcoming these difficulties was tedious but resulted in a nicer and more-robust parser. Unit tests, as usual, were critically important; I cannot stress this enough. Furthermore, and as part of this work, I extended the internal symbols table to handle more than just variables, which required a mechanism to unify all possible symbols (variables, arrays, functions, and commands) within it. This will come in handy once I add the ability to define custom functions and maybe even commands.

Binary releases

The last major change in this release, which isn’t a change in the code per se, is that this is the first release to ship with prebuilt binaries for the most common platforms. These binaries are automatically built on GitHub Actions and are automatically published to the release page.

Why add binary releases now? In fact, very few people have installed EndBASIC on their machine as far as I can tell: the online web interface is so much more convenient. But GPIO changes the rules of the game. If you want to use this feature, you now need to have EndBASIC on a Raspberry Pi, and building it there can be too slow. Plus you need to remember to use --features=rpi. Downloading a prebuilt binary for a casual look (which is what most people are doing right now anyway) is much easier.

As you can deduce from the rest of this post, this wasn’t trivial either. GitHub Actions are great and all, but configuring any remote service like this, without the ability to iterate locally, is painful:

Common process of setting up a remote service without being able to test changes locally. If you have ever found yourself in a similar situation, know that you are not alone.

It doesn’t help at all that a lot of the GitHub Actions involved are scarcely documented, have interfaces that try to do too much at once (thus being bad for composability), and that the officially-looking ones I started with are unmaintained and deprecated.

Anyhow. Things are working now and it’s incredibly exciting to see automation that builds the release binaries and drafts a GitHub release entry as soon as I push the right Git tag.

Snapshot of the first successful run of the release publishing workflow.

Feedback, please!

EndBASIC’s primary goal continues to be to offer a simplified, self-contained, retro-looking environment with which to learn programming concepts. Features that offer quick visual reward are a priority, and this new area of controlling hardware seems to fit the bill well. (Be aware that I’m currently favoring breath of features over depth to see what sticks and what doesn’t, so my plans might change…)

As for the future, think about a “boot to EndBASIC” image for the Raspberry Pi that drops you into the interpreter within a few seconds and that includes built-in instructions and sample code for cool projects. Think about this environment being able to connect to a common server so that you can share your creations with other people, and maybe host “email-like” messages—all within the same textual interface.

What do you think? I’d love to hear from you. And please use any of the links below to spread the word!