mirror of
https://github.com/pelgraine/Meck.git
synced 2026-10-30 19:52:01 +01:00
GBCEmulatorScreen.cpp → examples/companion_radio/ui-new/, fingerprint 1093 (build 28 was 1071). Replaces build 28; nothing else changes.
What changed Quiet period 2 s → 20 s. A background save now only happens after the cart RAM has been untouched for 20 seconds, so ordinary in-game writes during active play no longer trip it. A real menu save is followed by a pause, so it still gets written. Checksum guard. Before any save, background or on-quit, the live cart RAM is checksummed against what was last written; if the bytes are identical, nothing is written to the card. The game replaying identical state, which is what caused the save-just-after-opening, no longer touches the SD card at all. Seeded from the loaded .sav at launch, so opening a game and idling never writes.
This commit is contained in:
1 parent
37b3d343e7
commit
e1964d00cb
2 files changed
+30
-7
No files matched your search
Vendored
+2
-1
@@ -6,6 +6,7 @@
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
"ms-vscode.cpptools-extension-pack",
|
||||
"pioarduino.pioarduino-ide"
|
||||
]
|
||||
}
|
||||
@@ -67,9 +67,11 @@
|
||||
// after gb_init. Written in the background: every cart-RAM write by
|
||||
// the game marks the save dirty, and poll() (UI task, so it owns the
|
||||
// SD card) writes the file once the game has left its cart RAM alone
|
||||
// for GBC_SAVE_QUIET_MS -- in practice a couple of seconds after the
|
||||
// user saves in-game. Quit writes only if something is still dirty,
|
||||
// so quitting is normally instant. (Build 9 wrote only on quit.)
|
||||
// for GBC_SAVE_QUIET_MS (20 s) AND the bytes have actually changed
|
||||
// since the last write (a checksum gates out the frequent identical
|
||||
// rewrites the game makes). A 32 KB SD write blocks the UI loop on the
|
||||
// shared SPI bus, so writes are kept rare. Quit writes only if the
|
||||
// save still differs from disk.
|
||||
//
|
||||
// - Memory: cart RAM (128 KB), ROM arena (reserved at 2 MB on first launch
|
||||
// and retained for the life of the boot, so a big game late in a long
|
||||
@@ -175,7 +177,7 @@ extern void otaResumeRadio(); // main.cpp
|
||||
#define GBC_ROM_RESERVE 0x200000 // 2 MB arena on first launch
|
||||
#define GBC_FRAME_US 16742 // 59.73 Hz
|
||||
#define GBC_MAX_DEFICIT_US 250000 // behind by more than this = stall, resync
|
||||
#define GBC_SAVE_QUIET_MS 2000 // cart RAM untouched this long -> write .sav
|
||||
#define GBC_SAVE_QUIET_MS 20000 // cart RAM untouched this long -> consider a save
|
||||
#define GBC_STOP_WAIT_MS 200 // max wait for the task to park on quit
|
||||
#define GBC_TASK_STACK 8192 // bytes, static, reserved at boot
|
||||
// The LoRa radio and mesh loop keep running while a game plays (they live on
|
||||
@@ -244,6 +246,7 @@ static StaticTask_t s_task_tcb;
|
||||
static size_t s_save_size = 0;
|
||||
static volatile bool s_cram_dirty = false; // set by the game's cart-RAM writes (emulator task)
|
||||
static volatile uint32_t s_cram_dirty_ms = 0; // millis() of the last such write
|
||||
static uint32_t s_saved_sum = 0; // checksum of cart RAM as last written to .sav
|
||||
static char s_save_path[96];
|
||||
|
||||
// ---- Core callbacks ---------------------------------------------------------
|
||||
@@ -495,6 +498,19 @@ static void print_heaps(const char *when) {
|
||||
(unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM));
|
||||
}
|
||||
|
||||
// Fast additive-rotate checksum over the live save region. Two different
|
||||
// save states practically never collide, which is all this needs: it
|
||||
// gates out writes when the bytes have not changed since the last one.
|
||||
static uint32_t gbc_cram_sum(void) {
|
||||
uint32_t h = 2166136261u;
|
||||
for (size_t i = 0; i < s_save_size; i++) {
|
||||
h ^= s_cram[i];
|
||||
h = (h << 1) | (h >> 31);
|
||||
h += 0x9E3779B9u;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// Write cart RAM to the .sav file. UI task only (the SD card is the UI
|
||||
// task's). Returns true if the whole file was written.
|
||||
static bool gbc_write_save(void) {
|
||||
@@ -518,6 +534,7 @@ static bool gbc_write_save(void) {
|
||||
Serial.printf("[GBC] save written: %u bytes to %s (remove %lu, open %lu, write %lu, close %lu ms)\n",
|
||||
(unsigned)put, s_save_path, (unsigned long)(t1 - t0), (unsigned long)(t2 - t1),
|
||||
(unsigned long)(t3 - t2), (unsigned long)(t4 - t3));
|
||||
if (put == s_save_size) s_saved_sum = gbc_cram_sum();
|
||||
return put == s_save_size;
|
||||
}
|
||||
|
||||
@@ -679,6 +696,7 @@ bool GBCEmulatorScreen::launch(int idx) {
|
||||
}
|
||||
}
|
||||
s_cram_dirty = false; // the load above is not the game writing
|
||||
s_saved_sum = gbc_cram_sum(); // matches the .sav on disk; no write until it changes
|
||||
gb_init_lcd(s_gb, draw_line);
|
||||
s_gb->direct.frame_skip = 1;
|
||||
memset(s_fb, 0, (size_t)GB_W * GB_H * sizeof(uint16_t));
|
||||
@@ -778,7 +796,8 @@ void GBCEmulatorScreen::finishStop() {
|
||||
bool saved = false;
|
||||
if (s_cram_dirty) { // normally already written in the background
|
||||
s_cram_dirty = false;
|
||||
saved = gbc_write_save();
|
||||
if (gbc_cram_sum() != s_saved_sum) saved = gbc_write_save();
|
||||
else saved = true; // already on disk, nothing to write
|
||||
}
|
||||
// The keyboard stays in raw joypad mode, and keeps being drained by the
|
||||
// busy poll, until the ROM list has actually been drawn (poll() releases
|
||||
@@ -834,7 +853,10 @@ void GBCEmulatorScreen::poll() {
|
||||
// game keeps running on core 0 meanwhile).
|
||||
if (s_cram_dirty && (uint32_t)(millis() - s_cram_dirty_ms) >= GBC_SAVE_QUIET_MS) {
|
||||
s_cram_dirty = false;
|
||||
gbc_write_save();
|
||||
// Only touch the card if the save actually differs from what is on
|
||||
// it: the game rewrites cart RAM with identical bytes often, and a
|
||||
// 32 KB SD write blocks the UI loop for the length of the write.
|
||||
if (gbc_cram_sum() != s_saved_sum) gbc_write_save();
|
||||
}
|
||||
#if GBC_SOUND
|
||||
if (keyboard.rawMutePressed() && s_audio_on) {
|
||||
|
||||
Reference in new issue
Block a user