UnoDOS pc64

Building & tooling

Building UnoDOS pc64 from source, running it under QEMU, packing a bootable USB image, building the flashers, and regenerating this manual.

Toolchain

  • Build: x86_64-w64-mingw32-gcc (UEFI apps are PE32+ images, the mingw target's native output, so no gnu-efi or EDK2) and python3.
  • USB image: sgdisk (gptfdisk), mtools, python3.
  • Emulator: qemu-system-x86 + OVMF.

On Ubuntu: sudo apt install gcc-mingw-w64-x86-64 qemu-system-x86 ovmf mtools gdisk python3

Build & run

From the pc64/ directory:

./build.sh                 # build the unoui desktop shell -> build/esp/
./build.sh run             # build, then boot it in QEMU + OVMF
./build.sh legacy          # build the older 14-app "legacy" core
UNO_DEBUG=1 ./build.sh     # the debug/test harness build (adds unoautomate)
UNO_PYRT=0 ./build.sh      # skip PYRT.UNO (no Python runtime; smaller image)
python3 tools/mkuefi.py 512   # pack build/esp/ into build/unodos-uefi.img (512 MiB)
python3 harness.py boot    # scripted QEMU boot + screenshot
python3 nettest.py         # headless network + TLS verification

The build compiles the platform, framebuffer, toolkit, ten themes, apps and drivers freestanding, and links a single BOOTX64.EFI into a bootable ESP tree in build/esp/. That kernel is about 3.8 MB and the whole tree about 14 MB: the rest is the .UNO apps, the four TrueType fonts, the sample media and the SDK beside it. Two local extras grow it if you have them, and neither is in the repository: the Wi-Fi firmware blobs in fw-blobs/ add about 7 MB, and a Doom WAD in wads/ adds its own size again. UNO_DEBUG=1 links a bigger kernel, about 4.4 MB.

Keyboard-firstThe desktop is fully keyboard-driven, so QEMU needs no special input setup. QEMU is also scriptable over QMP (send-key + screendump), which is how the screenshots in this manual are made.

Pack a real USB disk image

QEMU fakes a disk from the build/esp/ directory, but real firmware needs a partition table. tools/mkuefi.py turns the ESP tree into a raw disk image: GPT with one FAT32 EFI System Partition holding the whole tree. This is the image the flashers embed.

Build variants

Two environment toggles to build.sh choose what the image is:

ToggleEffect
UNO_DEBUG=1The debug / test harness: crash reports, the watchdog, the conformance suites, and the whole unoautomate remote channel and on-device automation. Off by default - a production image has none of it.
UNO_PYRT=0Skip PYRT.UNO, the vendored-MicroPython Python runtime, for a smaller image. On by default; without it, Python apps will not run.

Feature flags

FlagEffect
-DUNO_XHCICompile the xHCI USB host + USB-Ethernet stack (inert stubs otherwise).
-DUNO_I2C_TRACKPADEnable the native I2C-HID trackpad driver (inert stubs otherwise).
-DUNO_DBGCONEnable the port-0x402 debug console. Off by default: it SMM-traps and hangs some real hardware.
-DUNO_APP_SYM=...Name a bridged app's entry symbol so multiple apps link into one binary.

Gotchas worth knowing

  • LLP64: long is 32-bit under mingw. Use unsigned long long / uintptr_t for addresses.
  • Freestanding: no host libc or CRT; use pc64_libc.c / pc64_math.c.
  • Resolution: some laptop panels black out on a real SetMode. pc64 keeps the GOP mode and scales the desktop framebuffer to fit instead.
  • Watchdog: disable it at startup or UEFI resets the machine after about five minutes.
  • Present-on-change: only changed framebuffer rows are written to video memory; full-frame rewrites every frame ruin input smoothness on real hardware.

Build the flashers

The flashers each embed a gzipped copy of the USB image and stream it to the raw device. Source: pc64/flash/.

TargetBuild onCommandOutput
WindowsWindows + WSL (in-box csc.exe, no SDK)pc64\flash\build-flasher.ps1UnoDosFlasher.exe
macOSa Mac (Swift toolchain)pc64/flash/mac/build-app.shUnoDosFlasher.app (universal)

Pass --skip-build to reuse an existing build/unodos-uefi.img (useful when the image is built on another machine). The built binaries are published on the project's GitHub Releases.

Regenerate the manual screenshots

The desktop screenshots in this manual are produced headlessly by pc64/docs_shots.py: it boots the real image under QEMU + OVMF and drives the desktop over QMP, capturing each scene to pc64/shots/manual/. Because the emulated pointer does not reach the shell, every scene is driven from the keyboard.

python3 docs_shots.py                               # all scenes
python3 docs_shots.py themes editor browser_docs    # selected scenes
UNO_NIC=1 python3 docs_shots.py browser_http        # networking scenes

Copy the PNGs you need into docs/assets/img/, then rebuild and commit.

Scenes name apps by id - A("uocalc"), never a row number - because the Start-menu order moves whenever an app is added and a scene that counts keystrokes does not fail when it does: it opens the next app along and files the picture under the old name. The order comes from pc64/build/apps_roster.txt, written by a run that opened every app by id over URC and checked the window that appeared:

UNO_DEBUG=1 ./build.sh && python3 harness.py unoapps   # measure the order
./build.sh && python3 docs_shots.py                     # capture the figures

The figures come from a production build, where URC would want a token typed at the console, so the order is measured on a debug build of the same tree. Before it captures anything, docs_shots.py asks the live menu how many rows it has and stops the run if that disagrees with the roster, which is what makes borrowing the order from the other build safe. If the Control Panel's tab order changes, that is still a hand edit.