Skip to content

Fuzix for the Pico Computer - v0.6

Latest

Choose a tag to compare

@UKTailwind UKTailwind released this 03 Aug 16:37

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.