Playing with Blocks
Written 2026-10-01
When writing a computer program, it's often necessary to be able to save and load data- typically in a file, which is then stored on your machine's disk. There's an assumption
Working on "rainbows"
A little while ago I wrote a tiny virtual machine called ok, to serve as a kind of playground for my other programming projects. However, ok doesn't come with much- manipulating values stored in RAM or on its "stacks", basic math/logic operations, and conditional execution. If you wanted to, say, program something like a game or a graphical piece of software (or really any software that seeks to handle input and output), you'd need something additional on top of ok. Think of ok like a CPU or processor, whereas any input/output (like drawing to a screen, listening to a keyboard, or, importantly, working with data in a file), you'd need some external "devices".
rainbows solves this problem. It connects a defined set of specific devices to locations in ok's RAM (i.e. memory-mapped IO) so that manipulating these sections of RAM manipulates the devices themselves. For example, writing a byte at a specific place in RAM could print that byte as a character to a console, or render that byte as a pixel on a screen.
I've only just started working on rainbows- as of right now, it supports input and output to the console, as well as some math extensions making certain computations easier to perform (as ok on its own can't perform things like multiplication and division very efficiently). The next item on my list of things to add to rainbows was some way of manipulating data stored on the disk, like files.
The problem with "files"
For those who may not know, a "file" on your computer isn't the basic building block it appears to be. Your computer provides files as an abstraction, using something called a file system. This is (to keep things short) basically an interface that your operating system provides to make reading, writing, and organizing data on your machine easier for you and for any programmers writing software for your machine. Files don't actually "live" in specific places on your hard drive like they appear to- and don't even get me started on "folders" (or "directories", if you prefer), which are effectively just a table of file names and then pointers referencing the data associated with that file name.
Since files are provided by your operating system, different operating systems could have slightly different notions for what a file actually is, and different ways of interfacing with them- the data in your files don't even have to actually reside on your machine, as anyone using OneDrive has likely found out the hard way.
Thankfully, most mainstream operating systems have fairly compatible file system standards, so programmers don't have to worry too much about the differences between them when writing code. However, this assumes that the code you'll be writing would be running on a mainstream operating system (or an operating system period) in the first place.
A similar project to ok, called Uxn (which was a major influence on ok's design), has a specified set of devices (analogous to rainbows) called Varvara. One of these devices is the file device, which allows you to reach directly into a file or directory on the machine.
This seems like it'd be pretty clearly applicable to ok as well, but looking at versions of Uxn running without an underlying operating system (such as uxnfloppy) it's not clear how exactly interfacing with a "file" would work.
Note: uxnfloppy appears to be incomplete, so perhaps support for the file device is yet to come, but I still don't see how it'd work in the traditional sense, as even if it could read data from the disk in chunks, many features of Varvara's file device wouldn't really apply.
So, in essence, I realized I wanted something roughly compatible with a file abstraction, but a bit "lower level" so that it'd be easy to get up and running without assuming that there's a full underlying operating system.
Past experiments/studies
For a while, I worked on a piece of software called bb (named after the Death Grips song "BB Poison"). It was effectively a key-value database stored entirely within a single file- a key-value database is a way of storing data using a unique "key" (some name or other identifier) associated with a "value" (some sequence of data). The idea was that rainbows would be given one of these .bb files, and the keys/values stored within that file would act as the "files" that rainbows could work with, alongside some additional functions for loading a normal file on your operating system as a key-value pair (using the file's name as the key and its data as the value) in the database, which could be disregarded if you were running rainbows without an operating system.
This ended up becoming really complicated- it was already difficult enough coming up with a system for being able to quickly search for a specific key within the .bb file, and the read/update/delete logic grew even more complicated with it- not to mention the difficulties that came with having to program this in C so that it could properly interface with ok/rainbows (which themselves are written in C). I eventually got burnt out from the project, and it's been on the backburner ever since.
I realized that even something (relatively) simpler or lower-level than a file system, like a basic key-value store, was still a little complicated for my tastes- I wanted something that I could implement a basic prototype for in an afternoon or two, and I also wanted something barebones enough to where I could implement a "proper" file system on top of it, running on ok itself (something that I think would be a fun exercise).
As I learned more about a programming language I've liked for a long time- specifically, FORTH, I learned about its method for interacting with data on the disk. FORTH was designed to run on machines as though it was an operating system, and it used a system of "blocks". The contents of the disk were represented by a number of fixed-size (typically 1024 bytes, a.k.a. a "kibibyte") blocks of data, where you could simply load a single block of data into RAM by referencing the block's number (0 would be the first kibibyte, 1 would be the second kibibyte, and so on) with the BLOCK keyword. From there, you could manipulate this block's data in RAM, and then when the time came to store the data back onto the disk, you'd use UPDATE to "schedule" it to be written.
This seemed rather too barebones at first, but after exploring a bit I found that a conceptually similar virtual machine to ok and Uxn called Konilo used a block-based system, I became interested in giving this a try.
Meet `blocks`
blocks is a tiny, header-only C library that I wrote to provide a "blocks" abstraction that I could use for rainbows, while also being easy enough to port to a machine that lacks an underlying operating system in the future.
The default version (meant for running on an operating system) uses a BlockFile, a single file with a predefined "max blocks" capacity. After creating or opening a .blocks file, you can then easily modify the contents of any particular block:
// create a new BlockFile which holds 100 blocks
BlockFile* bf = blockfile_create("test.blocks", 100);
// if the file exists already, use blockfile_open("test.blocks")
// create a block containing a message
char message[] = "Hello, blocks.h!";
Block* new_block = block_new(message, sizeof(message));
// store that block at position 69 in the BlockFile
new_block->number = 69;
block_update(bf, new_block);
block_free(new_block);
// you can read that block back with block_read(bf, 69)
// free the block and close the file when we're done
block_free(new_block);
blockfile_close(bf);
This library was simple enough to implement in a couple days, and, since it's so "barebones", a more proper file system could be built on top of it. These things come at the cost of being a bit inconvenient- it's not like you can search for specific named blocks or anything, but (at least for me) it's enough to start with.
Feel free to check out blocks yourself to learn more specifics about how the library works.
Conclusions and future goals
Even though the library has enough done to start using it for rainbows, I still have some ideas for ways I could clean up the code or add some additional conveniences to it (like being able to output a sequence of blocks to a "real" file).
I'd also really like to build things separate from rainbows on top of this library- for instance, making a really primitive text (or rather "block") editor could be fun, or perhaps using it to simplify the code for bb if I decide to revisit that in the future.
Either way, I just wanted to share the thought process behind the latest stuff I've been working on- I'm looking forward to playing with blocks more in the future!