Saving & memory
These helpers persist small values, reserve reusable memory, and stream animation frames from flash. See /reference/ for the signatures and /memory/ for the memory model behind the last two modules.
picogame_save
Section titled “picogame_save”This structured key-value store uses microcontroller.nvm, a reserved flash region writable from code.py. Use it for a high score, unlocked level, or settings that must survive power loss. The region is 4 KiB on the supported RP2040 builds; the class checks the actual available length when it is constructed.
NVM is a single region shared by every program on the device, so each game passes its own key. The key is hashed into the header and checked on load - if another game or stale data wrote the slot, load() returns your defaults instead of misreading foreign bytes.
You describe your data as a schema: an ordered dict of name -> (struct format char, default). Common chars: "B" 0-255, "H" 0-65535, "I" 0 to 2^32-1; lowercase b/h/i are signed.
Save(key, schema, *, offset=0)- create a store.keyis your game’s name (str or bytes).offsetis keyword-only; bump it only if two coexisting games must use different NVM regions. RaisesRuntimeErrorif NVM is unavailable, orValueErrorif the schema does not fit NVM.load()- return a dict of the stored values, or a fresh copy of the defaults if the slot is blank, corrupt, or written by a different game (key mismatch). Never raises on bad data.save(values)- persist a dict. Missing keys fall back to their schema default. Writes a checksum so a laterload()can detect corruption.reset()- write the defaults back under this game’s key.defaults()- a fresh dict of just the default values, no NVM read.
import picogame_save
# persist the best lap time (seconds) across rebootsstore = picogame_save.Save("ghostrace", {"best_t": ("H", 0)})best_t = store.load()["best_t"] # 0 = no record yet
# ...later, on a new best run:if best_t == 0 or secs < best_t: best_t = secs store.save({"best_t": best_t}) # survives power-offpicogame_arena
Section titled “picogame_arena”A buffer arena reserves one contiguous block early and hands out slices of it later. Use it when several scenes or modes need large Canvas buffers at different times. MicroPython’s garbage collector does not compact the heap, so repeatedly creating and discarding large buffers can leave enough free memory in total but no sufficiently large contiguous block.
Arena(pixels)- allocate the arena. Size is in pixels; it reservespixels * 2bytes (RGB565). Do this early, before the heap fragments.canvas(w, h, transparent=None)- apg.Canvasbacked by the next slice (no per-canvas heap alloc); 16-bit aligned automatically. Returns theCanvas.alloc(nbytes, align=1)- a genericmemoryviewslice ofnbytes: reuse it as a file/network read buffer, parse scratch, audio block, etc.alignrounds the slice start up (usealign=2for 16-bit data,align=4for word access). RaisesMemoryErrorif the arena is full. Valid until the nextreset().mark()- return the current allocation offset. Keep it when entering a temporary mode or scene.release(mark)- rewind the arena to a previous mark and make every later slice available again. Marks should be released in reverse order. Objects backed by released slices must no longer be used.reset()- free all slices handed out so far. Call at the start of each scene that reuses the arena. AnyCanvasfrom before the reset must no longer be drawn.free()- bytes still available in the arena.
import picogame_arena
# one arena for the big canvases, grabbed once while the heap is contiguous;# scenes that never run at the same time share the bytes (reset each).ARENA = picogame_arena.Arena(320 * 80) # 320x80 px = 51 200 bytes
def big_canvas(w, h, transparent=None, first=False): if first: ARENA.reset() # reuse the arena for this scene return ARENA.canvas(w, h, transparent=transparent)picogame_stream
Section titled “picogame_stream”StreamSheet keeps one PAL8 animation frame in RAM and reads the requested frame from a file on flash. A 64x100 sheet with 11 frames therefore needs a 6,400-byte pixel buffer instead of 70,400 bytes for all frame pixels. The file must be frame-major, with each frame’s w*h bytes contiguous. Create it with tools/pack_sheet.py.
StreamSheet(pg, path, w, h, frames, palette, transparent=None)- openpath, allocate one frame buffer, build apg.Bitmap(PAL8) over it, and load frame 0.paletteis the color table for the PAL8 data..bitmap- the singlepg.Bitmapwhose pixels get overwritten in place. Build yourpg.Spritefrom this.use(i)- load framei(wrapped moduloframes) into the shared buffer and return the bitmap. Cached: re-reads from flash only wheniactually changes.close()- close the underlying file.
import picogame_stream
sheet = picogame_stream.StreamSheet(pg, "jill.bin", 64, 100, 11, PAL, transparent=0)player = pg.Sprite(sheet.bitmap, x, y)# ...each frame the animation advances:sheet.use(frame_index) # stream that frame into the shared bufferplayer.touch() # tell the scene to repaint it (pixels changed in place)