So, why not C? While my computers sometimes share an architecture or an operating system, cross-platform audio and graphical development is unlikely to work between them, and is subject to bitrot.
A virtual machine without registers
Emulation is the reproduction of the behavior of a physical computer's circuitry with software. Given that an emulator can translate the actions of one computer onto an other, the same program can sometimes be used on both.
Virtual Machines are software emulating the actions of real and imaginary computers. Someone could devise a fictional computer that is not necessarily based on existing hardware, write software for this fantastical computer, implement an emulator for it, and use the same program across vastly different systems via an emulator.
Over the years, I wrote software for a multitude of peripherals and frameworks, the vast majority is now defunct due to either falling behind on the ever-changing toolchains or simply the hardware being discontinued. Perhaps it's just a matter of time until people build emulators to make these projects usable again, otherwise these projects were never truly mine, and my learning of these languages only ever belonged to the platforms.
I. An Adequate Number Of Bits
During my research into portability, I kept thinking about how frictionless it is to play classic console games today. Pulling on that thread led me to projects designed explicitly for virtual machines, such as Another World which is equally easy to play today due to its targeting of a portable virtual machine, instead of any ever-changing physical hardware.
For a time, I thought I ought to be building software for the NES to ensure its survival against the wave of disposable modern platforms — So, I did. Unfortunately, most of the software that I care to write and use require slightly more than an 8-button controller, namely a keyboard and a pointing device.
So, why not the Commodore 64? Having implemented a NES emulator I found that, in comparison, implementing a c64 emulator is a monumental project.
II. Tarpits & Houses Of Cards
If the focus of this experiment is to ensure the support of a piece of code by writing emulation software for each new platform, the specifications should be painless to implement.
Let's use the time one would need to write a passable emulator as a limit in complexity for this system. Could a computer science student implement an emulation of the 6502 instructions in an afternoon, a week, a month? Could that design be simplified, changed in some way to make it more approachable for would-be implementers?
- Smalltalk is a complete computing environment and virtual machine, that was said to take about a year for one person to implement.
- Lisp(tal) is a fully featured symbolic functional architecture that takes about a month to implement. It comes with the complexities of garbage collection, extensive memory usage, and endless edge cases.
- Brainfuck(tal) is a 8-Instructions architecture taking at most an afternoon for one person to implement. What it does away in emulation complexity, it offloads onto the toolchain needed to make programs fast and writing them a pleasant experience.
- Subleq(tal) is a 1-Instruction architecture taking at most an hour for one person to implement. Similar tarpit to brainfuck, only moreso.
So, let's set a limit to the complexity of the system to a week, since it would be an equally Herculean task to build an emulator and assembler for a machine with thousands of instructions; or a single instruction machine building abstract logic from thousands of primitive parts.
So, why not the Chifir? Because of its 16-bytes long instructions, incomplete specification and unspecified behaviors.
III. Things Betwixt
In 1977, a programmer created the CHIP-8 virtual machine with 36 instructions, 16 registers and 4096 bytes of memory. It had no mouse device, its controller is 16 keys organized in a square, the screen is barely capable of displaying readable text, but I was able to write an implementation in a weekend.
In 1964, a computer scientist proposed the SECD abstract machine with 10 instructions and 4 stacks. The superficially documented implementation specifies a list processing system capable or hosting functional languages. The system was later expanded with arithmetic and IO operations, but rests on an intricate and inefficient garbage collected system. I was able to write an implementation in about two weeks.
So, why not Nock? While it is supposedly a clean slate computing stack(turned webapp delivery system somehow), its fascists, blockchain and technocratic ties points in the opposite direction of where I want to go, to say nothing of the challenge of making the system runs reliably and fast.
Somewhere along this voyage into finding a suitable host for my programs, I began thinking about electronic waste, and I couldn't justify surrounding myself with yet more electronics. This dream platform would therefore be designed to be emulated, its complexity would be designed around the complexity of software and not that of hardware, so I do no consider FPGAs.
So, why not Pico-8? The comparison comes from people conflating Uxn with Varvara. A better comparison would be Uxn and the LuaVM, which lives at the core of Pico-8, but isn't intended to be targeted directly and is subject to change.
IV. Back & Forth
The balancing act of virtual machine instructions, assembler, emulator and the resulting capabilities of its language eventually brought me back to stack machines.
Concatenative languages consist of breaking a program into a list of words, and to interpret each word, words are often combinations of other words, combined to create more complex words. Brackets and parentheses are unnecessary, there is no requirement for precedence rules, the program merely performs calculations in the order that is required, letting the stack store intermediate results on the fly for later use.
| operation | 3 | 10 | 5 | + | * |
|---|---|---|---|---|---|
| stack | 3 | 10 | 5 | 15 | 45 |
| 3 | 10 | 3 | |||
| 3 |
For this specific imaginary system,
I wanted the memory, program and stacks to consist of 8-bit cells. For
example, the 12 / (34 - 12) sequence is equivalent to the following six
bytes:
uxntal | # 12 34 OVR SUB DIV binary | a0 12 34 07 19 1b
In order words to keep the assembly programming pleasant and fend off the need to abstract computation to a high-level language, I've combined a few stack-machine operations, arithmetic, bitwise functions and reached an expressive virtual machine that can be implemented in a weekend running at a reasonable speed. An emulator capable of running the self-hosted assembler is about 150 lines of C.
For it to work as an Archival Computer, it must remain completely detached from dependencies, and highly discoverable. It operates on bytes as to remain portable on small systems, abstracting I/O entirely to the host system via dedicated opcodes and, hopefully, its design will encourage re-implementation instead of adoption.
- See the napkin definition