Smalltalk on a small computer
In my previous post, I mentioned the Smalltalk system developed at Xerox' PARC research center starting in the early 1970s.
Smalltalk is a system that pioneered one of the first object-oriented languages (Objective C by NeXT/Apple inherited Smalltalk's ideas of object orientation and using messages to communicate between objects) and also one of the first graphical user interfaces, starting with the highly influential Xerox Alto computer system. In addition, Smalltalk programs are compiled to virtual machine bytecodes, which are in turn interpreted or JIT translated by the VM system (you didn't think Java invented this, did you?).
I was recently exchanging emails with Michael Haupt, an old friend of mine since our time as students in Siegen. Michael mentioned that my work on getting students interested in Plan 9, Inferno and Oberon was incomplete without also taking a closer look at the Smalltalk system. Specifically, the Smalltalk-80 system, which has excellent documentation in a series of books (collected by Stéphane Ducasse, thanks a lot!) which nowadays are freely available. The Blue Book (Smalltalk-80: The Language and its Implementation by Adele Goldberg and David Robson) is especially relevant, since it describes the details of the VM operation and bytecodes.
Of course, Michael was completely right!
By coincidence, Dan Banay had published a version of the Smalltalk-80 VM implemented in C++ from information in the Blue Book a couple of days prior. His version of the VM uses SDL for graphical output and input management and usually runs on several different Unix systems as well as Windows.
So I thought that might be an interesting project for a rainy weekend to look at. But, of course, simply compiling and running the system on a Unix host is boring, so what else could I do?
Some of my students will be working on porting Plan 9, Inferno and Oberon to RISC V systems. Porting Smalltalk to RISC V was a bit too much for one weekend, so I looked for a challenge which was a bit less complex. I was already playing around with Plan 9 and Inferno on several Raspberry Pis here, so running Smalltalk bare-metal (without a supporting operating system) on the Raspberry sounded like a nice challenge.
I got lucky and found a bare-metal environment that supports running SDL on the Raspberry Pi GPU and also USB input devices using the uspi library. The most recent commit to the bare metal environment has been six years ago, so the platform it supports is the original Raspberry Pi (model 1B) - more recent Raspberry models have quite different processor cores. Luckily, the popular Raspberry Pi Zero models use the same system-on-chip (an ARM11 based Broadcom BCM2835) as the original Raspberry Pi, so these are supported by the library, too.
So I did not have to write too much code here - I combined the bare metal environment with the Smalltalk implementation and... nothing worked. Of course. A number of problems were still to solve:
- Debug output was definitely required. I didn't have a JTAG interface set up on the Raspberry (it would be so nice if Raspberrys included a standard JTAG connector...), so I had to set up good old printf-style debugging on the Raspberry's serial interface.
- The Smalltalk snapshot image (Smalltalk uses a persistent memory image, you don't have to boot the system from scratch every time you power up the system) could not be loaded, as there was no FAT filesystem support in the Smalltalk VM implementation (it usually uses standard POSIX calls). So I had to adapt the Smalltalk VM file system interface to use the FatFs module by Chan which is included in the bare metal environment.
- Of course, this would have caused (and did...) lots of frustration right at the start of the project, so I employed a trick and converted the Smalltalk snapshot file to a nice, large C header file which was compiled with the VM program. Of course, this required changing all routines to access the image. But this worked well (Hint: xxd --include is really useful for this!).
- The Raspberry Pi Zero has only one (OTG) USB port. Smalltalk, of course, needs a keyboard and a mouse, so my options were to either use a USB hub or a combined USB transceiver for a wireless keyboard/mouse set. Unfortunately, the ancient version of the uspi library only recognized one device on the wireless transceiver and none connected to the hub. So I spent more than a day integrating the most recent version of uspi. This one detects keyboard and mouse nicely but I was unable to get any data from the devices. After about a day of bug hunting I found out that I had inserted a debug printf in one of the interrupt handlers. Bad idea... the UART runs at 115200 bps, so outputting a 20 character string already takes about 2 milliseconds. No wonder that this completely messes up interrupt handling. Ouch. Now keyboard and mouse work nicely.
So I was able to stand on the proverbial shoulders of giants and add my little bit of hacking to it. But it runs, as you can see in this video and in the picture below.
However, there are still some problems:
- The system hangs when switching to a different file in the editor without saving ("accepting") the changes to the current file first. A yes/no requester comes up (implemented as a BinaryChoiceView in the Smalltalk system), the cursor changes to a nice "thumbs up/down" cursor (long before facebook...) and after moving the mouse a bit the buttons are inverted and the system no longer accepts mouse clicks (as shown in the picture below). However, the mouse still moves and the VM is executing instructions. I am currently reading the Smalltalk-80 sources to figure out what is happening there.
- The current version only runs on systems using the original ARM11-based BCM2835 SoC, but not the more recent multicore Cortex-A based SoCs in the Raspberry Pi 2/3/4. So the only supported Raspberry Pi versions are the original Raspberry Pi 1B (thanks for borrowing me yours, Joseph!), the Raspberry Pi Zero and the Zero W (with WLAN and Bluetooth, which are both unsupported in Smalltalk as of now). All these systems have been tested successfully. It should also work on the original Raspberry Pi Compute Module (CM1), but I don't have one to test it on. Porting to more recent Raspberrys will at least require new startup code and probably new code for interfacing with the GPU.
- The system is quite slow and takes about 20 seconds to boot. This is probably caused by the fact that the VM does a separate seek and read for every object of four bytes or so. Here, caching should obviously help.
- Drawing the GUI is also slow, I will need to take a closer look at the bitblit routines and the SDL implementation. In the long run, getting rid of SDL and running directly on the framebuffer is an interesting option.
So, this is all a wild hack right now and the source code definitely needs some cleanup and tuning in addition to fixing the bugs. But it definitely was a very fun project already and it's great to have a system which enables the user to read the code and understand what's happening under the hood!
It will also serve as a nice basis for upcoming student projects, e.g. to implement a JIT, to work on multiprocessor support and to port to a completely open source (hard- and software) RISC V-based FPGA system.
So, stay tuned for updates and the publication of the code on github... this will take a bit, since the weather here in Trondheim is really nice right now...
- Dan Banay's Smalltalk-80 implementation
- Marco Maccaferri's Raspberry bare-metal environment
- Rene Stange's uspi USB library
- Smalltalk-80 Blue Book
- Smalltalk on a multiprocessor
Tags: Smalltalk, Raspberry, bare-metal