- Integrated Lua 5.4 engine (32-bit mode for embedded ARM) - Created LuaGame wrapper class implementing Game interface - Added C++ bindings exposing renderer, game state, and input to Lua - Implemented SD card loader for automatic .lua game discovery - Updated GameLauncher to support std::function for lambda captures - Made Game class members public for Lua bindings access - Added example Lua games: counter, snake, bouncing ball - Included comprehensive API documentation Games can now be written as .lua text files on SD card and loaded without recompilation. Build size: 747KB UF2, Lua VM uses ~50-80KB RAM.
72 lines
3.3 KiB
Markdown
72 lines
3.3 KiB
Markdown
# Game Save System Implementation Plan (RP2350/RP2040)
|
|
|
|
This document outlines the strategy for implementing non-volatile game save states using the onboard Flash memory.
|
|
|
|
## 1. Objectives
|
|
* **Persistence:** Save game state so progress is kept after power-off.
|
|
* **Multi-game Support:** Allow multiple games to have independent saves without overwriting each other.
|
|
* **Safety:** Ensure flash writes don't interfere with program execution or interrupt handlers.
|
|
* **Wear Leveling:** Minimize unnecessary writes to prolong flash lifespan.
|
|
|
|
## 2. Flash Memory Mapping
|
|
The RP2350 on this board has 8MB of Flash memory mapped starting at `XIP_BASE` (`0x10000000`). We will reserve the top section for data.
|
|
|
|
| Memory Range | Purpose | Size |
|
|
| :--- | :--- | :--- |
|
|
| `0x10000000 - 0x106FFFFF` | Program Code, Fonts, Assets | 7MB |
|
|
| `0x10700000 - 0x107EFFFF` | Reserved/Future | 960KB |
|
|
| `0x107F0000 - 0x107F0FFF` | **Save Registry** | 4KB (1 Sector) |
|
|
| `0x107F1000 - 0x107FFFFF` | **Game Save Slots** | 60KB (15 Slots) |
|
|
|
|
## 3. Architecture Components
|
|
|
|
### A. `PersistentStorage` Manager (`lib/persistent_storage.h`)
|
|
A singleton or static helper class that handles raw flash operations.
|
|
* `bool save_exists(const char* game_id)`
|
|
* `bool save_data(const char* game_id, void* data, size_t size)`
|
|
* `bool load_data(const char* game_id, void* buffer, size_t size)`
|
|
|
|
### B. Updated `Game` Interface (`lib/game.h`)
|
|
Add methods to the base class to standardize how state is handled.
|
|
```cpp
|
|
virtual const char* get_game_id() const = 0;
|
|
virtual size_t get_save_data(void* buffer, size_t max_size) = 0;
|
|
virtual void load_save_data(const void* buffer, size_t size) = 0;
|
|
```
|
|
|
|
### C. Save Registry
|
|
To avoid collisions, the first sector (`0x107F0000`) will store a lookup table:
|
|
```cpp
|
|
struct RegistryEntry {
|
|
char game_id[16];
|
|
uint32_t sector_index;
|
|
uint32_t data_size;
|
|
uint32_t checksum;
|
|
};
|
|
```
|
|
|
|
## 4. Implementation Steps
|
|
|
|
1. **Create `PersistentStorage`:**
|
|
* Implement flash sector erasing and programming using `pico/flash.h`.
|
|
* Include interrupt protection using `save_and_disable_interrupts()`.
|
|
2. **Update `Game` Base Class:**
|
|
* Modify `lib/game.h` to include the virtual persistence methods.
|
|
3. **Implement in Monopoly:**
|
|
* Create a `MonopolySaveState` struct (POD - Plain Old Data).
|
|
* Populate it from game variables in `get_save_data`.
|
|
4. **Integrate with Launcher:**
|
|
* Modify `GameLauncher` to check for existing saves when a game is selected.
|
|
* Modify the main loop to trigger `save_data` at logical points (e.g., end of turn/exit).
|
|
|
|
## 5. Collision Avoidance Logic
|
|
* When a game requests a save for the first time, the `PersistentStorage` manager scans the Registry.
|
|
* If the `game_id` isn't found, it allocates the next available 4KB sector.
|
|
* It writes the specific `game_id` and its assigned `sector_index` back to the Registry sector.
|
|
* Sequential saves for that ID will always use that specific sector.
|
|
|
|
## 6. Important Notes
|
|
* **Flash Wear:** Each sector supports ~100k erase cycles. Total lifespan depends on frequency of saves.
|
|
* **Alignment:** Data must be programmed in multiples of `FLASH_PAGE_SIZE` (256 bytes).
|
|
* **Recursion:** Never call flash functions from within an interrupt or callback that might be triggered during a write.
|