PC3 Fuzix v0.16 - the machine plays PETSCII Robots
The machine plays PETSCII Robots — the largest MMBasic program anyone has written, with music, sound effects and a keyboard or a switch array.
Getting it there found a defect in nearly every layer of this port, and each fix is worth more than the game.
Write the card
pc3-sd-cc.img.gz is the whole card; fuzix.uf2 is the kernel. Both are needed — this release changes the contract between them in two places, so an old kernel with a new card (or the reverse) will misbehave rather than fail cleanly.
The game and everything it needs — levels, music, pictures, the sprite library — are in /root/MMBasic/robots. Run it from that directory: it looks for its data relative to wherever it is started.
cd /root/MMBasic/robots
mmbc robots.bas
cc robots.c
./robots.bc
It takes a few minutes to compile. Arrow keys and space, or a switch array on GP34–GP41.
The other 51 example programs are in /root/MMBasic, each described in the README there.
What was wrong, and is not now
LOAD IMAGE drew to the screen, whatever FRAMEBUFFER WRITE had selected, because the draw target is per-process and loadimage is a separate binary. A child of the framebuffer's owner now draws where the owner draws — which also fixes SAVE IMAGE, and LOAD JPG/LOAD PNG when they arrive.
DIM s$(n) LENGTH m sets the spacing of the elements, not just a cap on each. The firmware puts element k at base + k*(m+1), and Robots reads its 128×64 world map straight out of that with PEEK. Documented in the manual.
The blitter was touching every pixel three times — expanding a row to one byte per pixel, editing it, squeezing it back — when source and destination are both already packed two pixels to a byte. Now nibble to nibble: a screen of 77 tiles went from 64 ms to 10 ms. A 720-shape harness sweep guards it.
swapin() panicked when a program could not be swapped back in beside a newly exec'd neighbour. It refuses the exec now and says what it could not fit beside — a feature fails instead of the machine dying, and the message names the sizes.
The MOD player stuttered, and not for the reason it looked like. usleep cannot sleep for less than a decisecond, and a timeslice here is half a second, so the player was ready and not running while its queue emptied. SNDIOC_PCMWAIT sleeps in the kernel until the ring drains and lets the woken player run. The queue is 93 ms — which is also the delay before a sound effect is heard — where 557 ms had not been enough.
GFXIOC_VSYNC held the processor for up to a frame inside a system call, where this kernel cannot preempt, so compositing stopped everything else. GFXIOC_VSYNCTRY does the same wait in slices, in user mode.
Keyboard auto-repeat was 600/150 — MMBasic's, and a typewriter's rate. A keyboard-played game crawled beside the same game on switches. 250/50 now.
Headroom
A 292 K program in a 336 K pool leaves nothing for the helpers it spawns, so:
- 8,376 bytes off every BASIC process — the shared-libm redirection completed (three functions had crept back into
bcrun), andMM_BATCH512→128, which measured identical on the board. loadimagelost its dithering — nine raster-wide error planes, 18,504 bytes of bss — taking it from 12 blocks to 7. The quantiser stays, so colours still land where the interpreter would put them.
Coverage
111 statements, 98 functions, 11 MATH sub-functions (was 104 and 91). SPRITE MEMORY, FRAMEBUFFER LAYER <colour>, CALL by name in both forms, and N-dimensional array initialisers are new.
Gates
make check 61 · cgate byte-identical · fcctests 60 · qemu 61 · ctest 165 · cpptest clean · ioctlcheck 62 codes, no duplicates · kbdsync clean · relcheck agrees.
Accepted from the written card: it boots, MM.VER answers 0.16 compiled by the card's own mmbc and cc, and Robots translates, compiles and runs on the machine itself.