Implement NUFLI delta encoding with scanlines

- Add tools/apply_scanlines.py for CRT scanline effect
- Add tools/nufli_delta.py for delta encoding (base + bitmask deltas)
- Delta format: 2880 byte bitmask + N byte values per screen
- 40.1% size reduction vs raw NUFLI (69KB vs 115KB for 5 screens)
- Base (23KB) at 000-FFF, title delta (7KB) at -
- Other deltas use title as fallback (TODO: disk loading)
- Update tasks.md with scanline and delta encoding documentation
This commit is contained in:
2026-07-19 00:12:37 +02:00
parent 1d7554cd39
commit f4b344cccd
22 changed files with 426 additions and 125 deletions
+1 -21
View File
@@ -13,13 +13,6 @@
// and calls game_step() once per frame.
// 6. while (1) {} — idle. All per-frame work happens in
// the IRQ handler.
//
// In Phase 4 step 6 was `while (1) game_step();` — a busy-wait that
// called game_step as fast as the CPU could, so the "frame counter"
// was CPU-bound. In Phase 5 the busy-wait is gone: game_step is
// called from the raster IRQ at exactly 50 Hz, so state durations
// are wall-clock-bound (60 frames = 1.2 sec, etc.) regardless of
// what the CPU is doing between IRQs.
#include "memmap.h"
#include "audio.h"
@@ -27,18 +20,6 @@
#include "score.h"
#include "tick.h"
// We don't malloc, so the heap is unused. Setting it to 0 frees the
// space for the screen data in the main region.
//
// The stack is set to 0x400 (1 KB) — this is the oscar64 default,
// pinned explicitly so the layout is predictable across `-O1` /
// `-O3` builds. With `-O3` the code section is larger, so the
// data section's spillover into the default stack/heap gap region
// leaves only ~0x400 for both. Anything larger (0x600+) makes
// `-O3` fail to link with "Cannot place stack section". 1 KB is
// plenty because the oscar64 software stack lives in zero-page
// (0xF7-0xFF, 9 bytes per the -O3 default); the spillover area
// only needs to hold the few locals/params that don't fit in ZP.
#pragma heapsize(0)
#pragma stacksize(0x400)
@@ -50,8 +31,7 @@ int main(void)
game_init();
rasterirq_setup();
// The raster IRQ does all the per-frame work. The main loop
// is a deliberate spin: nothing to do between IRQs.
// The raster IRQ does all the per-frame work.
for (;;)
;