KnightOS was an interesting operating system

Published 2020-01-27 on Drew DeVault's blog

KnightOS is an operating system I started writing about 10 years ago, for Texas Instruments line of z80 calculators — the TI-73, TI-83+, TI-84+, and similar calculators are supported. It still gets the rare improvements, but these days myself and most of the major contributors are just left with starry eyed empty promises to themselves that one day they’ll do one of those big refactorings we’ve been planning… for 4 or 5 years now.

Still, it was a really interesting operating system which was working under some challenging constraints, and overcame them to offer a rather nice Unix-like environment, with a filesystem, preemptive multiprocessing and multithreading, assembly and C programming environments, and more. The entire system was written in handwritten z80 assembly, almost 50,000 lines of it, on a compiler toolchain we built from scratch.

There was only 64 KiB of usable RAM. The kernel stored all of its state in 1024 bytes of statically allocated RAM. Many subsystems used overlapping parts of this memory, which was carefully planned for to avoid conflicts. The userspace memory allocator used a simple linked list for tracking allocations, to minimize the overhead of each allocation and maximize the usable space for userspace programs. There was no MMU in the sense that we have on modern computers, so any program could freely overwrite any other programs. In fact, the “userspace” task switching GUI would read the kernel’s process table directly to make a list of running programs.

The non-volatile storage was NOR Flash, which presents some interesting constraints. In the worst case we only had 512 KiB of storage, and even in the best case just 4MiB (this for a device released in 2013). This space was shared with the kernel, whose core code was less than 4KiB, and including high-address subsystems still clocked in at less than 16KiB. Due to the constraints of NOR Flash, a custom filesystem was designed which did all daily operations by only resetting bits in the underlying storage. In order to set any bits, we had to set the entire 64 KiB sector to 1. Overhead was also kept to a bare minimum here to maximize storage space available to users.

Writing to Flash storage also renders it unreadable while the operation is in progress. The kernel normally executes directly from Flash, resident at the bottom of the memory. Therefore, in order to modify Flash, the kernel’s Flash driver copies part of itself to RAM, jumps to it, and then jumps back after the operation is complete. Recall that all of the kernel’s memory is statically allocated, and there’s not much of it — we used only 128 bytes for the code which runs in RAM, and it’s shared with some other stuff that we had to plan around. In order to meet these constraints, we employ self modifying code — the Flash driver copies some of itself into RAM, then pre-computes some information and modifies that machine code in-place before jumping to it.

We also had some basic networking support. The calculator has a 2.5mm jack, similar to headphone jacks — if you had a 3.5mm adapter, we had a music player which would play MIDI or WAV files. The kernel had direct control of the voltages on the ring and tip, and had to bitbang them directly in software1. Based on this we built some basic networking support, which supported calculator-to-calculator and calculator-to-PC information exchange. Later models had a mini-USB controller (which, funnily enough, can also be bitbanged in software), but we never ended up writing a driver for it.

The KnightOS kernel also includes some code which is the first time I ever wrote “here be dragons” into a comment, and I don’t think I’ve topped it since.

Despite these constraints, KnightOS is completely booted up to a useful Unix-like (with a graphical interface) faster than you can lift your finger off of the power button. The battery could last the entire semester, if you’re lucky. Can the device you’re reading this on claim the same?2

  1. Newer hardware revisions had some support hardware which was capable of transferring a single byte without software intervention. 

  2. The device I’m writing this blog post with is 3500× faster than my calculator, has 262,144× more RAM, and 2.1×106 times more storage space. 

Have a comment on one of my posts? Start a discussion in my public inbox by sending an email to ~sircmpwn/public-inbox@lists.sr.ht [mailing list etiquette]

Are you a free software maintainer who is struggling with stress, demanding users, overwork, or any other social problems in the course of your work? Please email me — I know how you feel, and I can lend a sympathetic ear and share some veteran advice.


Articles from blogs I follow around the net

Updates in March 2020

This post gives an overview of the recent updates to the Writing an OS in Rust blog and the corresponding libraries and tools. I focused my time this month on finishing the long-planned post about Async/Await. In addition to that, there were a few updates …

via Writing an OS in Rust April 1, 2020

Wayland clipboard and drag & drop

Clipboard and drag & drop are arguably one of the most complicated parts of the core Wayland protocol. They involve a lot of back-and-forth communication between three processes: the application where some content has been copied, the compositor, and …

via emersion March 26, 2020

Go, the Go Community, and the Pandemic

Go always comes second to more basic concerns like personal and family health and safety. Around the world, the past couple months have been terrible, and we are still at the start of this awful pandemic. There are days when it seems like wo…

via The Go Programming Language Blog March 25, 2020

Generated by openring