After an unsatisfactory period of employment in March this year I took some time to reflect on the technologies I use, trying to learn about more systems and languages that I had only superficially explored. In the period immediately after leaving employment I wanted to try and get back into technologies like Inferno with the idea, amongst others, of porting it to an old netbook-style device I had acquired at the end of 2017. The problem was that I was in a different country to my development system, so any serious work would have to wait until I could access it again. In the meantime I thought about my brief exposure to the world of Qt in yet another work environment – specialisation can be an advantage when finding work but can also limit your opportunities to expand your horizons – and I wondered about other technologies I had missed out on over the years. I had previously explored Android development in an attempt to see what was interesting about it, but it was time to try something different.
Being very comfortable with Python as a development language during the late 1990s and 2000s had probably made me a bit lazy when it came to learning other programming languages. However, having experimented with my own flavour of Python, and being unimpressed by the evolution of the Python language, I considered making a serious attempt to learn Go with the idea that it might be a desirable skill to have. Many years ago I was asked to be a proofreader for Mark Summerfield's Programming in Go textbook but found it difficult to find the time outside work to do that, so I didn't manage to learn much of the language. This time, I worked through A Tour of Go, which I found quite rewarding even if I wasn't quite sure how to express what I wanted in some of the exercises — I often felt that I was guessing rather than figuring out exactly how a type should be defined, for example.
When the time came to pack up and return to Norway I considered whether I wanted to continue writing small examples in Go and porting some of my Python modules. It didn't feel all that comfortable or intuitive to write in Go, though I realise that it simply takes practice to gain familiarity. Despite this, it was worth taking some time to get an overview of the basics of Go for reasons that I'll get to later.
As mentioned earlier, I was interested in setting up Inferno on an old netbook – an Efika MX Smartbook – and had already experimented with running the system in its hosted form on top of Debian GNU/Linux. Running hosted Inferno is a nice way to get some experience using the system and seems to be the main way it is used these days. Running the system natively requires porting it to the specific hardware in use, and I knew that I could use the existing code for U-Boot, FreeBSD and Linux as a reference at the very least. So, the task would be to take hardware-specific code for the i.MX51 platform and adapt it to use the conventions of the Inferno porting layer. Building from the ground up, there are a few ports of Inferno to other ARM devices that could be used as foundations for a new port.
One of the things that made it possible for me to consider starting a port of Inferno to the Smartbook was the existing work that had gone into porting FreeBSD to the device. This included a port of U-Boot that enabled the LCD screen to be used to show the boot console instead of requiring a debug cable that is no longer available. This made it much easier to test “bare metal” programs and gain experience with modifying U-Boot, as well as using its API to access the keyboard and screen. Slowly, I built up a set of programs to work out how I might boot into Inferno while using the U-Boot API to allow the operating system to access the framebuffer console.
As you might expect, booting Inferno involves a lot of C code and some assembly language. It also involves modules written in Limbo, a language from the C programming language family that is an ancestor of Go. At a certain point in the boot process – see lecture 10 of this course for details – Inferno runs a Limbo module to perform some system-specific initialisation, and it's useful to know how to write Limbo code beyond simply copying and pasting lines of code from other ports.
At this point the time spent learning the basics of Go proved to be useful, though maybe only in the sense that aspects of Limbo seemed more familiar to me than they might have done if I had never looked at Go. I wrote a few lines of code to help set up devices for the booting system and check that some simple features worked. By this time I had started looking at Limbo for its own sake, not just as something I had to learn to get Inferno working.
There are a lot of existing programs written in Limbo, though it's not always obvious where to find them. The standard introductions, A Descent into Limbo by Brian Kernighan and The Limbo Programming Language by Dennis Ritchie contain example programs, but many practical programs can be found in the Inferno repository itself inside the appl directory, which is where the sources reside for Limbo applications and libraries. I linked to some other resources in my first article about Inferno.
While there are a few resources already available, I wanted to experiment a bit on my own and get a feel for the language. As a result I started to write a collection of small example programs that tested my intuitions about how certain features of the language worked. Over time it became more natural to write programs in Limbo, especially if they used features like threads and channels to delegate work and coordinate how it is performed. Since Limbo was inspired by communicating sequential processes and designed with threading in mind, threads are a built-in feature of the language, so using them is fairly painless compared to their counterparts in languages like Python and C. Using channels to communicate between threads is fairly intuitive, though it can take time to become accustomed to the syntax and control structures.
While Limbo's natural environment is Inferno, the ability to run Inferno as a hosted environment in another operating system makes it possible to experiment with the language fairly conveniently. However, if you want to edit programs in Inferno's graphical environment then you might find it takes some effort to adapt to it, especially if you already have a favourite editor or integrated development environment. As a result, it might be desirable to edit programs in the host operating system and copy them into the hosted Inferno environment. To enable more rapid prototyping I created a tool to automate the process of transferring and compiling a Limbo file to a standalone application, but the way it works is a bit more complicated than that sounds.
Inspired by Chris Double's article on Bundling Inferno Applications and the resources he drew from, the tool I wrote automates the process of building a custom hosted Inferno installation. This seems excessive for the purpose of building a standalone application, though it is important to realise that Limbo programs are relying on features of the Inferno platform, such as the Dis virtual machine, even when they are running on another operating system. By controlling exactly what is built we can ensure that we only include features that a standalone application will need — we can also run strip on the executable afterwards to reduce its size even further.
With a tool to make standalone executables that can run on the host system, it becomes interesting to think about how we might combine the interesting features of Limbo (and its own packaged Inferno) with the libraries and services provided by the host operating system. However, that's a topic for another article.
Categories: Limbo, Inferno, Free Software
Copyright © 2020 David Boddie
Published: 2018-09-12 15:57:51 UTC
Last updated: 2020-10-24 22:33:44 UTC