Building a Tiny Unix-Style Operating System for the Elektronika MK-90

MU90 stands for Mad Unix 90. It is the name I have chosen for the small Unix-style operating system I have been developing for the Elektronika MK-90, the Soviet handheld computer built around a PDP-11-compatible architecture.

The first implementation is therefore MU90/MK90. The name deliberately does not bind the project forever to one machine: the number 90 comes from the MK-90 and identifies the family, while future ports can use target suffixes such as MU90/BK, MU90/UKNC or MU90/PDP11.

The current hardware-proven release is based on what was internally called MK90/Unix 0.7. For reproducibility I have not changed the strings inside that binary yet, so the existing uname command still prints the historical name. The next release can adopt the MU90 name inside the firmware itself.

Why write an operating system for the MK-90?

The Elektronika MK-90 is a fascinating machine. It combines a PDP-11-compatible processor architecture with only 16 KiB of RAM, a 120 x 64 monochrome LCD, a calculator-style keyboard, a real-time clock and removable SMP memory modules.

That hardware is powerful enough to do much more than run the original calculator environment, but it is also constrained enough that every design decision matters. There is no MMU, no large RAM pool, no disk controller in the modern sense and no room for a conventional Unix implementation.

MU90 is therefore not an attempt to squeeze historical UNIX source code into the MK-90: it is a new, compact operating system written specifically for this class of hardware, borrowing the parts of the Unix user model that make sense: hierarchical paths, familiar command names, files, directories, pipes, processes, a shell, a text editor and small command-line tools.

The name: MU90, MBC and MBX

The naming now forms a coherent family:

  • MU90 — Mad Unix 90, the operating system;
  • MU90/MK90 — the Elektronika MK-90 port;
  • MBC — Mad BASIC Compiler;
  • MBX — Mad BASIC eXecutable, the bytecode executable format used by MBC and EXEC.

Booting from a 27C256 cartridge

The current MK-90 port is distributed as a complete 32 KiB image for a 27C256 EPROM. The ROM is not copied wholesale into RAM. A small bootstrap talks directly to the SMP cartridge interface, transfers the resident kernel to RAM, checks it and starts the system.

The current hardware-fixed build has the following layout:

ROM image             32768 bytes
Bootstrap               476 bytes
Resident payload        8750 bytes
Physical MK-90 RAM     16384 bytes
Resident code limit   0x2A40
Main overlay window   0x3300 ...


The resident kernel is extremely tight: in this build only 18 bytes remain below the resident allocation ceiling. Larger applications therefore live in cartridge ROM as verified overlays and are loaded into fixed RAM windows only when needed.

The shell

MU90 has a small command shell with 33 registered commands. The command vocabulary intentionally resembles a compact Unix environment:

help      ls        cd        pwd
mkdir     rmdir     cat       cp
rm        edit      mount     umount
mkfs      clear     uname     mem
time      date      beep      devices
romtest   echo      uart      run
ps        kill      wait      sleep
mv        less      pipe      mbc
exec

The shell supports absolute and relative paths, . and .., a virtual root and fixed mount points such as /mnt/smp0 and /mnt/smp1.

The naming is intentionally familiar, but the implementation does not pretend to be POSIX. MU90 implements the useful subset that fits the machine and documents its limits explicitly.

MKS2: a small filesystem for removable modules

The filesystem used by the current release is MKS2. It is designed around very small removable cartridges rather than modern disks.

The current implementation provides sixteen inode-like nodes with parent links, files and directories, mirrored metadata and copy-on-write updates. The file layer validates data with checksums and verifies written sectors by reading them back.

Supported filesystem sizes are:

8 KiB
10 KiB
16 KiB
32 KiB
64 KiB

A writable module can be mounted, formatted, used as a directory tree and accessed through the same path layer used by shell commands and applications.

The maximum regular-file length in this release is 64,256 bytes, subject of course to the actual capacity and free space of the mounted module.

Cooperative multiprogramming on 16 KiB

MU90 includes a deliberately small cooperative scheduler. There are four task-control blocks. Only one context executes at a time and tasks switch at explicit yield or blocking points; there is no timer-driven preemption.

The scheduler saves the PDP-11 register state, checks stack guard words and rotates among READY tasks. Background task slots can sleep, terminate, fault and be reused.

This is enough to make commands such as the following meaningful:

run pulse
ps
sleep 3
wait 1
kill 2

The implementation also distinguishes the shell, background workers and a foreground child process. It is intentionally small and deterministic rather than trying to imitate a large multi-user Unix kernel.

Pipes

The system contains a real bounded pipe mechanism with a 256-byte FIFO and separate reader and writer endpoints. A writer blocks when the FIFO is full and a reader blocks when it is empty while the writer is still alive.

At shell level the current syntax supports a two-stage pipeline, for example:

ls | less
cat notes | less
help | cat

The implementation is deliberately limited to one bounded two-process pipeline. That is enough to demonstrate the Unix idea without consuming RAM on a machine that only has 16 KiB total.

The text editor and pager

MU90 includes its own full-screen text editor and a less-style pager. The editor works directly with the MK-90 keyboard and LCD and uses a fixed RAM workspace rather than dynamic allocation.

This makes it possible to create and modify BASIC source files on the machine itself, compile them and execute them without another computer.

MBC Mad BASIC Compiler

One of the largest parts of the project is MBC, the Mad BASIC Compiler. MBC runs directly on the MK-90 and compiles the MBC-16 BASIC dialect into an intermediate executable format called MBX1.

MBC-16 uses signed 16-bit arithmetic and supports both classic BASIC constructs and structured extensions. Among the implemented features are:

  • LET, PRINT and INPUT;
  • IF, GOTO, GOSUB and RETURN;
  • FOR/NEXT;
  • IF/ELSE/ENDIF structured blocks;
  • WHILE/WEND;
  • REPEAT/UNTIL;
  • arrays with DIM;
  • DATA, READ and RESTORE;
  • file I/O;
  • keyboard, clock and timing functions;
  • sound and simple graphics for the 120 x 64 LCD.

The resulting .mbx file is not PDP-11 machine code. It is portable bytecode interpreted by the native EXEC runtime.

EXEC and a portable application layer

This distinction between the operating system and MBX bytecode is one of the most useful architectural decisions in the project.

A BASIC program can be compiled once into MBX1. To move that program to another machine, the target does not necessarily need a PDP-11 processor: it only needs an EXEC-compatible interpreter implementing the same bytecode ABI, 16-bit arithmetic semantics and device-service contracts.

I have already ported MBC and the EXEC interpreter to ANSI C on Linux. On a PC I can now do:

./mbc program.bas
./mbexec program.mbx

The host compiler has been checked against the MK-90 compiler using the supplied examples, producing identical MBX files byte for byte in those tests.

This creates a practical cross-development environment while retaining the ability to compile programs directly on the MK-90.

Real-hardware MBC and EXEC test

After solving the bootstrap regression I tested MBC and EXEC on the real machine with the following program:

10 A=2+3*4
20 B=-17/5
30 C=-17 MOD 5
40 PRINT A;",";B;",";C
50 GOSUB 80
60 PRINT A
70 END
80 A=A+1
90 RETURN

The real MK-90 produced:

14,-3,-2
15

This is a small test, but it crosses several layers of the real system: source-file handling, MBC parsing and code generation, creation of MBX bytecode, EXEC loading, signed arithmetic, expression precedence and subroutine control flow.

Overlay architecture

The operating system cannot keep every command and language component resident at the same time. Instead it uses a fixed overlay architecture.

The resident kernel contains the parts that must always remain available:

  • startup and shell loop;
  • keyboard and RTC interrupt handling;
  • LCD text services;
  • SMP block-device access;
  • MKS2 filesystem core;
  • file streams;
  • path resolution;
  • scheduler and pipe services;
  • resident strings and dispatch data;
  • the verified overlay loader.

Commands and larger subsystems are loaded into controlled RAM windows. The current build contains overlays for storage commands, directory commands, file commands, jobs, editor, clock, pager, the MBC compiler stages and the different portions of the MBX virtual machine.

Each module carries a checksum and an ABI version. The linker checks size limits and prevents unsafe direct calls between overlays whose lifetime would not be valid.

A small native kernel API

Native PDP-11 programs can use a documented kernel API. The current build exports seventeen resident services covering files, tasks, block I/O and pipes:

file_open       file_read       file_write
file_close      file_abort      block_io

task_spawn      task_yield      task_sleep
task_self       task_data       task_beep
task_exit

pipe_read       pipe_write
pipe_close_read pipe_close_write

The API is deliberately versioned and build-specific. MU90 has no memory protection, so native applications are expected to obey the documented calling and lifetime contracts.

What is not implemented

I prefer documenting missing features instead of presenting reserved names as completed drivers. In the current release:

  • there is no timer-preemptive multitasking;
  • there is no MMU or process memory protection;
  • pipes are deliberately limited to a two-process pipeline;
  • the XP1 UART driver is not yet implemented;
  • FD and CF mount points are reserved but do not yet have working drivers;
  • date and time currently read the RTC but do not set it;
  • there is no general heap allocator;
  • the current shell is Unix-style, not POSIX-complete.

There is also a physical-display detail still to investigate: during refresh I have observed the last two horizontal lines of the upper half of the real MK-90 LCD flicker. It does not prevent the operating system from running, but it is now on the hardware bug list rather than being hidden by a simulator.

Why MU90 can become a family

The MK-90 implementation is still tied to its hardware in several places, so moving the complete kernel to another machine is not simply a recompile. The bootstrap, memory map, LCD/console, keyboard, clock and storage driver must be replaced or adapted for each target.

However, most of the higher-level design is already separable:

arch/mk90/      bootstrap, LCD, keyboard/RTC, SMP, memory map
kernel/         filesystem, streams, shell, paths, scheduler, pipes
apps/           editor, pager and command overlays
mbc/            Mad BASIC Compiler
mbx/            Mad BASIC eXecutable runtime

For another PDP-11-compatible machine, the instruction set is already familiar and much of the higher-level assembly can potentially be reused after replacing the machine-dependent layer. For a completely different CPU, MBX1 provides an even cleaner migration path: implement EXEC for the new target and existing compiled BASIC applications can move with it.

This is the main reason I like the name MU90. The first machine is the MK-90, but the project does not have to end there.

Source organization

The current source tree is almost ten thousand lines of PDP-11 assembly before build scripts and documentation. The executable code is split into resident kernel modules and overlay modules rather than being generated as opaque machine code by Python.

Python is used only as the build-time assembler/linker, ROM packer, resource generator and CPU-model test environment. The MK-90 itself does not need Python, a C compiler or an external assembler.

The full annotated reference listing for this release contains more than ten thousand lines because it expands the resident include tree and places every executable assembly module into one readable document, while the actual build continues to use the modular source tree.

Where the project stands now

MU90/MK90 has moved beyond being only a simulator experiment. The corrected release boots on a physical MK-90, presents its shell and can compile and execute MBC-16 programs on the real machine.

There is still plenty to do: RTC setting, serial communications through XP1, additional storage hardware, improved display refresh and eventually the first second-machine port. But the architectural core is now substantial enough to deserve its own name.

MU90 — Mad Unix 90 is that name.

MU90 0.7 / MK90 port
Former development name: MK90/Unix 0.7 Download full source and dev-env: MU90_v0.7_MK90
MBC — Mad BASIC Compiler & MBX — Mad BASIC eXecutable  are now part of “MBC16 suite”
Download source code for cross platform development: MBC16_Linux_0.1_FULL

Everything (C) 2026 Papadopol Lucian-Ioan All rights reserved. Source and bin are free to use and modify but keep my author note.
l.i.papadopol@gmail.com

MU90 is an independent hobby operating system with a Unix-style user interface. The name does not imply certification or compatibility with any standardized UNIX specification.