Releases: UKTailwind/FUZIX
Release list
Fuzix for the Pico Computer - v0.6
Fuzix for the Pico Computer — v0.6
Artefacts: fuzix.uf2 (kernel), pc3-sd-cc.img.gz (SD card),
FUZIX-PC3-MANUAL.pdf. BOTH the kernel and the card are needed — the
card's binaries are statically linked and this release fixes a C
library bug, so an old card with a new kernel still carries it.
This release is about memory. A translated BASIC program could not hold
a framebuffer-sized array and a large one could not be loaded at all;
both are fixed, and the same work made everything 15% faster.
BASIC arrays and strings live in the PSRAM heap
They used to sit in the 48K of address space bcrun gives a program,
of which about 36K was usable, so a 38,400 byte array - one MODE 1
framebuffer - did not fit at all and failed before the first
statement ran. Globals become members of one generated struct taken
once; LOCAL arrays and strings get a block per invocation, released
on every path out, which is what makes recursion correct. Simple
variables stay in the process image: they are the hot ones and SRAM
is 3.7x faster to reach than PSRAM (44MB/s against 12, measured).
Sized by sizeof, so nothing has to be kept in step by hand.
One kind of address, and 15%
A program address was an offset into mem[], so every access was
base+index. That model could not survive a program holding memory the
VM does not own, and teaching the C paths to spot a real address was
not enough: bcrun has a native Thumb backend, and generated code
reaches memory as "ldr r3, [r6, r2]" with r6 = mem, in hardware,
where no test can be inserted. So the offset goes - the loader
relocates the program to where mem[] actually is, once, and an
address is an address. The backend needed no change at all, because
r6 becomes 0. The eclipse went 3.307 -> 2.782 s and bcrun got 2.4K
smaller; the heap split above costs a further 0.5%.
The program heap is PSRAM
heap_init asks the kernel for PSRAM once at load, so malloc serves
megabytes at the cost it served kilobytes. Per-call allocation
through the kernel was measured and rejected: an ioctl alloc/free
pair is 5142ns against 350ns in-process, which over the eclipse's
219,063 routine calls would have been 40% of its running time.
BCRUN_HEAP sets the size in KB.
Three allocator bugs
pagemap_alloc claimed blocks one at a time and returned ENOMEM still
holding them, and newproc abandons the slot without freeing it: one
failed fork cost 132K of a 312K machine for the rest of the boot.
fork needed the process resident twice, so nothing bigger than half
of memory could fork - bcrun with a program loaded is ~172K, which is
why every SAVE IMAGE died with "cannot start a program"; the parent
is now staged into PSRAM instead. And PROGSIZE was still 262144, the
size of a fixed swap slot, which stopped a 140K translated program
loading on a machine with 8MB of PSRAM.
A C library bug, not specific to this machine
fread reads a large request straight into the caller's buffer, which
advances the descriptor without touching bufread; fseek's fast path
works out where the buffer starts as fpos + (bufstart - bufread),
which is only true while those agree. After a direct read it claims a
window it does not own and returns real, well-formed, wrong data - no
error, no short count. It needs a file small enough to sit in one
buffer, which is why weeks of 100K objects were fine and the first
305-byte one took the machine down inside the loader. Reported
upstream (UPSTREAM-fread-fseek-report.md); the whole userland is
relinked against the fix.
Verified on the hardware, on a freshly written card
All 29 C samples compiled by the board's own cc produce output
byte-identical to the gcc references. The 3,200-line eclipse
translates in 1.0 s, compiles in 6.0 s and runs in 2.803 s against
12.5 under MMBasic and 8.8 under MicroPython, every digit identical.
varroom.bas claims its 38,408 byte array and forks free and ps.
imgloop.bas runs five SAVE/LOAD IMAGE rounds and memory returns to
its boot figure. localheap.bas gets recursion, STATIC and 2000 calls
of a routine with a 1608-byte LOCAL array right - 3.2MB through a
512K heap, so the frees are real.
Host gates: c-testsuite 165/175 (unchanged, the ten known failures),
31 samples against gcc, qemudiff 10/10, and for the translators cgate
byte-identical over the suite, tokgate 31/31, fcctests 11/11,
qemutests 12/12 native.
Manual
The memory sections rewritten - specification, arrays, the C heap,
the device table - and a page break before each chapter.
Fuzix for the Pico Computer - v0.5
Artefacts: fuzix.uf2 (kernel), pc3-sd-cc.img.gz (SD card),
FUZIX-PC3-MANUAL.pdf. BOTH the kernel and the card are needed.
MMBasic draws
MODE, COLOUR, PIXEL, LINE, CIRCLE and RGB() translate and run on the
HDMI display. MODE 1 is 640x480 mono, MODE 2 is 320x240 in MMBasic's
RGB121 palette, matching the VGA builds and the first two HDMI modes.
A whole shape crosses into the kernel in one call - a 640 point line
is 71us against 433us for the naive version. The primitives are
static functions in mmb_gfx.h and cc discards the ones a program
never calls, so a program that does not draw circles does not carry
the circle code.
SAVE IMAGE, LOAD IMAGE and SYSTEM
SYSTEM runs a program and waits for it, each argument passed
separately so no shell quoting is involved. SAVE IMAGE and LOAD
IMAGE are built on it, so a program that touches no image pays
nothing for them. The decoder is MMBasic's: 1/4/8/16/24/32-bit BMPs,
BI_BITFIELDS, RLE4/RLE8, and dithering only when asked for.
mmedit
MMBasic's full-screen editor, ported. BASIC is now written,
translated, compiled and run without leaving the machine.
TIMER and RND
TIMER is a float off a 64-bit microsecond clock. RND returns 53 bits
from splitmix64 rather than 15 from the C library.
THE FILESYSTEM CORRUPTION IS FIXED
Repeatedly rewriting a large file destroyed the card. f_trunc_blocks
had two tests of opposite sense: it freed a file's double indirect
block, root included, and then left i_addr[19] still pointing at it.
The next write reused a freed block as the double indirect root - by
then reallocated, often as data of the same file - so bmap read
picture data as block pointers. It bit any file over 273 blocks,
which is why it survived upstream on machines with small discs.
Verified with a 640x480 BMP: 1800 blocks, about 1526 double indirect
entries over six level-2 blocks, allocated and truncated ten times,
fsck clean.
Panics now reach the serial port
plt_monitor allowed sleep_ms(1) for an interrupt-driven UART to
drain - about eleven characters at 115200, against panic lines of
well over a hundred. A panic naming the fault was indistinguishable
from a silent hang. It now drains by polling.
The SD root is built from source
It had no build recipe: the Makefile's diskimage rule was empty and
the root was an artefact nobody could reproduce. mksdimage.sh builds
it from the tree at 64000 blocks rather than the 65535 that sits on
blkno_t's ceiling, where the "no such block" sentinel also lives.
Building it exposed two gaps the old artefact had been hiding: no
/usr/bin, so the compiler was installed into the wrong directory by
a cd that failed silently, and no /dev/hdc, so swapon failed and the
8MB of swap was never enabled.
Manual
New chapters on mmedit and on moving files over the serial port with
uue/uud in both directions, with the tools to use on Windows and
Linux and the flow-control trap that catches everyone. Graphics,
SAVE/LOAD IMAGE and the image commands documented; Appendix C
updated.
pc3-v0.4: MMBasic self-hosts on the Pico Computer 3
Fuzix for the Pico Computer — pc3-v0.4
The machine now compiles MMBasic on its own. mmbc translates a BASIC
program to C, cc compiles it to native ARM, and the result runs as an
ordinary program — all on the PC3, no PC involved.
MMBasic self-hosting
# mmbc bench.bas
wrote bench.c
# cc bench.c
# ./bench.bc
Performance: 29649 grains
MMBasic itself scores about 12,000 grains on the same board at the same
clock.
The 3,200-line solar eclipse program — Bessel elements, lunar and solar
series — translates, compiles and runs on the machine, digit-identical
to MMBasic and MicroPython, in 3.24 s against MMBasic's 12.5 s and
MicroPython's 8.8 s.
mmbc on the board writes C for the board's own compiler by default
(--gcc for the host form). Coverage: 59 statements and 69 functions,
listed in Appendix C of the manual, generated from the translator's own
tables. It grows with each release and will never be complete.
Native code generation
cc2 translates each function to Thumb-2 for the Cortex-M33 and stores
it in the object; bcrun executes native and keeps the interpreter for
whatever did not translate. Dhrystone 2.1, compiled on the machine,
runs at 90,021/s — a quarter of the same benchmark cross-compiled by
gcc -O2 for the same chip.
New this release: push/op fusion (the left operand of a binary operator
stays in a register instead of round-tripping through memory), direct
DCP arithmetic, and the statement-seam rewrite — the last of which
shipped broken in development and is now correct, with a native gate to
keep it that way.
Compiled programs run directly: ./prog.bc.
Fixes worth naming
- Compiling a large program crashed the machine. Both compiler
passes are recursive descent and overflowed the 8 KB stack, silently,
into their own BSS. Binaries can now ask for what they need through
PT_GNU_STACK; cc1 and cc2 ask for 32 KB. Nothing else pays. cc1matched index lists without comparing their lengths — an
out-of-bounds read that could hand back another type's array
dimensions.- Multidimensional array parameters were wrong.
int a[5][7]passed
to a function decayed to the element pointer, so every store through
it went elsewhere. Dhrystone had been failing its own self-check since
the port began. - The USB keyboard. The LED report is a control transfer on EP0 —
the endpoint enumeration also needs — and was issued inline from
wherever a lock key was decoded. That wedged EP0: the keyboard worked
until the next lock key, and from then on a device could attach but
never enumerate. It now goes out from the poll loop, one transfer at a
time. - One stray Return after answering the boot prompt: auto-repeat firing
on a key whose release was never fetched. - The PSRAM disc refuses an out-of-range block instead of memcpy'ing
into unmapped memory, and the fault dump prints the faulting address
before the registers.
Also
The manual gains a chapter on mmbc with worked examples taken from
the machine, and PC3-IRQ-REVIEW.md records what this port's interrupt
handling actually does — the SDK's mechanisms work here, and hand-hooked
vectors should give way to them for peripherals.
Installing
fuzix.uf2— flash as usual (BOOT + RESET, copy to the drive).pc3-sd-cc.img.gz— write to an SD card of 128 MB or larger.FUZIX-PC3-MANUAL.pdf— the manual.
Both the kernel and the card image are needed: the card carries the
compiler, and the kernel carries the loader that honours its stack
request.
Fuzix for Pico Computer 2/3 v0.3 - native code generation
The compiler now generates native ARM code — and its output runs as ./prog.
# cc hello.c
# ./hello.bc
hello from Fuzix
cc still reads the same C89-plus dialect and still validates against gcc byte-for-byte (165 of 175 applicable c-testsuite tests, unchanged), but what it emits is no longer interpreted bytecode alone: every function the translator covers is compiled to ARM Thumb-2 machine code and runs on the metal, with the bytecode retained as a per-function fallback. Coverage is complete enough that whole real programs go fully native.
Measured on the Pico Computer 3, same programs, before → after:
| benchmark | v0.2 (interpreted) | v0.3 (native) |
|---|---|---|
| sieve of Eratosthenes | 3.85 s | 47 ms |
| fib(27) | 2.64 s | 71 ms |
| shellsort | 3.66 s | 74 ms |
| xorshift PRNG | 8.18 s | 57 ms |
| Dhrystone 2.1 | 3,900/s | 16,961/s |
| solar eclipse (3,200-line BASIC → C) | 9.19 s | 3.47 s |
For calibration, the same Dhrystone cross-compiled by arm-none-eabi-gcc as a native Fuzix binary runs at 379,000/s — the bytecode machine's compiler now stands within ~22× of a first-class optimizing compiler, from ~100× in v0.2. The eclipse figure beats MicroPython on the same silicon (8.77 s) and MMBasic (12.5 s) — a BASIC program machine-translated to C, compiled on this machine or off it, running faster than both interpreters.
Under the hood, in the order it happened: double-precision arithmetic moved to the RP2350's DCP coprocessor (benefits everything, BBC BASIC included); the code generator grew 64-bit and floating-point coverage, switch, struct copy, direct calls between native functions, and compare/branch fusion; the runtime binds every library call once instead of string-matching per call; and dead bytecode is reclaimed so full-native programs fit a 256 KiB process.
New: a PSRAM arena. A process can ask the kernel for a region of the 8 MiB PSRAM outside its own image — not counted against the process size, never copied on context switch, never swapped (PSRAMIOC_ALLOC/FREE/STAT on /dev/sys, 1 MiB pool by default, psram=<n>[K|M] at the bootdev prompt to retune). The compiler is its first client: cc2's tables live there, which is what lifted the on-board compiler to host-class capacity limits. Fork leaves an arena with the parent; exec and exit reclaim it.
New: #! execution. The kernel execs #!-marked files through /usr/bin/bcrun; cc marks and chmods its output, so compiled programs launch like any other binary. Older .bc files still run via bcrun prog.bc.
Also fixed along the way: a block-device bounds check that only covered the first sector of a transfer, warm-reset SD/flash/boot-state bugs that could brick a reset until reflash (three separate causes, all found and fixed), a latent truthiness bug on denormal comparison results, and %lld printing through a libc that reads long for ll.
Flash fuzix.uf2, write pc3-sd.img.gz (decompressed) to an SD card, and the samples in /root/cc are waiting. The manual is unchanged from v0.2; the new features are documented in the tree (PC3-PSRAM-ARENA.md, Applications/CC/PLAN-arm-backend.md).
🤖 Generated with Claude Code
Fuzix for Pico Computer 2/3 v0.2 - self-hosted C compiler
The machine now compiles C on its own.
cc is Alan Cox's Fuzix Compiler Kit retargeted to a bytecode machine, running on the hardware — not a cross-compiler. Write a C file on the Pico Computer, cc prog.c, bcrun prog.bc, and that is it.
# cc hello.c
# bcrun hello.bc
hello from Fuzix
Dialect is C89 plus declarations after statements: the full type system, structs and unions by value, enums, typedefs, function pointers, switch, goto, 64-bit long long, double-precision floating point, and a runtime library with stdio, string, memory and file I/O. #include <stdio.h> works.
Correctness is measured rather than asserted: 165 of the 175 applicable tests in the public c-testsuite conformance suite pass with output byte-identical to gcc, and a set of samples in the tree is diffed against gcc on every change — on the board as well as on the host. Bitfields are the only part of C89 missing, and are refused with a diagnostic rather than half-implemented. The manual's The C compiler chapter lists the limitations.
The samples are on the card in /root/cc if you want something to compile.
Two long-standing crash bugs fixed
Both were shared Fuzix code that is correct where int is 16 bits and wrong where it is 32 — a class of fault worth knowing about on any 32-bit Fuzix target.
Filesystem corruption. blk_alloc refilled the block free list with a copy length of sizeof(int) for a uint16_t field, running two bytes past s_free[] onto s_ninode, the inode free-list count. Every refill restored a stale inode count and resurrected already-allocated inodes naming live files.
panic: no free buffers with the pool empty. freebuf() computed a buffer's age from uint16_t operands against an int16_t; at 32-bit int those promote to signed, so once the LRU counter wrapped every buffer looked negatively aged and the kernel panicked with all twenty buffers free. It had been hunted as a leak for some time — the dump that settled it showed 18 of 20 buffers reading busy 0.
Also timer_expired(), whose magnitude comparison read a freshly set timer as already expired whenever its deadline wrapped — a source of occasional spurious SD errors. That one is wrong at either int width.
Also since v0.1
- Flash writes made safe for PSRAM and the XIP cache, taking MMBasic's QMI save/restore approach
- Interrupt-driven console in both directions — serial file transfers went from lossy to 14K in about four seconds
- Console line editor and command history restored, after the user-memory corruption that had forced their removal was traced to an unaligned initial user stack pointer in
_execve - Process pool pinned to its own linker region, so growth is a link error rather than a silent relocation of userland
bufsreports the kernel buffer cache;devtools/bufwatch.pysamples it between steps of a workload
Files
| File | Purpose |
|---|---|
fuzix.uf2 |
Kernel image — flash via BOOTSEL drag-and-drop |
pc3-sd.img.gz |
SD card image, now with the compiler (gunzip, then write to a card ≥ 128 MB) |
FUZIX-PC3-MANUAL.pdf |
User manual: installation, SD setup, BBC BASIC, the C compiler, pin usage |
SHA256SUMS |
Checksums for the above |
One kernel image serves both the Pico Computer 2 and 3; the board is detected at boot. See the manual for installation. Sources: branch pc3; the SD image is built by Applications/CC/mkccimage.sh.
Note on the tag name
The tag is pc3-v0.2, not v0.2 — upstream Fuzix already uses v0.2, v0.2.1, v0.3 and v0.4 for Alan Cox's own releases. v0.1 was free only because upstream's numbering happens to start at 0.2, so PC3 releases are namespaced from here on.
Fuzix for Pico Computer 2/3 v0.1
First release of Fuzix for the Pico Computer 2 and Pico Computer 3 (RP2350B).
One kernel image serves both boards, detected at boot via the DS3231 32 kHz signature on GP27.
Highlights
- Fuzix 0.5 kernel on the RP2350B at 375 MHz: 320 KiB process RAM, 30 processes, preemptive multitasking with async signal delivery, 256 KB per-process ceiling backed by 8 MB PSRAM swap
- Standalone operation: HSTX/DVI display (80×40 ANSI console, 640×480) with USB host keyboard (6 layouts), or headless over the CH340 USB console at 115200
- BBC BASIC (R. T. Russell's BBCSDL console edition): IEEE-754 double maths, MODE 0–5 graphics upscaled to 1024×768 XGA (5:8 coverage-blend in MODE 0/3), 4-channel SOUND/ENVELOPE synth on the PCM5102 I2S DAC, ADVAL joystick (GP34–37) + ADC (GP41–44), POINT, serial I/O on GP0/GP1 via
OPENUP "/dev/tty2", ASCII program LOAD - Storage: SD root filesystem (hardware SPI1 on the PC3, bit-banged on the PC2), NAND flash fallback root, PSRAM RAM-disc, DS3231 RTC
- File interchange: FAT partition on the SD card readable from Fuzix with the
fatcommand, plus XMODEMrx/sxover the serial ports - Full application set: shells, editors (levee/vi), fforth, games, development tools
Updates since first publication
Assets are rebuilt in place from branch pc3 (currently 49e6eec9):
- DS3231: I2C bus recovery (a jammed battery-backed chip survives power cycles), interrupt-safe transactions with retries, hourly drift resync;
setdate -wnow works — see the manual's "Setting the clock" - Unattended boot-time filesystem repair:
fsck -a -y /honoured all the way down - 13 libc fixes pulled from upstream Codeberg (strtol ×4, fread, mntent, getpass, curses ×4, cfmakeraw); all binaries in the SD image rebuilt against the fixed library
- XMODEM
rx/sxutilities added (upstream contribution) - BBC BASIC no longer emits absolute cursor motion on the mirrored console — the serial terminal stays in sync with the HDMI display
Files
| File | Purpose |
|---|---|
fuzix.uf2 |
Kernel image — flash via BOOTSEL drag-and-drop |
pc3-sd.img.gz |
SD card image (gunzip, then write to a card ≥ 128 MB) |
FUZIX-PC3-MANUAL.pdf |
User manual: installation, SD setup, BBC BASIC, pin usage |
See the manual for installation details. Sources: branch pc3.