Fix snapshot save/load: route both through CPU trap to sync registers
machine_write_snapshot reads maincpu_regs.pc to record the current program counter. When called directly from video_canvas_refresh, maincpu_regs is stale — the CPU loop keeps registers as stack-local variables and only syncs them to maincpu_regs inside DO_INTERRUPT via EXPORT_REGISTERS(). The snapshot therefore recorded the wrong PC. On restore (already trap-based), the wrong PC was faithfully written back into reg_pc via IMPORT_REGISTERS(), causing the CPU to resume at a bad address. The game screen looked correct (memory/VIC/SID state all restored) but the CPU never reached the input-polling code, so button presses had no effect. Fix: route machine_write_snapshot through interrupt_maincpu_trigger_trap so DO_INTERRUPT(IK_TRAP) calls EXPORT_REGISTERS() before the trap function, capturing the true current PC before the snapshot is written.
@@ -32,7 +32,10 @@ if(HAVE_VICE)
|
|||||||
-Wl,--wrap=ui_display_drive_led
|
-Wl,--wrap=ui_display_drive_led
|
||||||
-Wl,--wrap=serial_trap_receive
|
-Wl,--wrap=serial_trap_receive
|
||||||
-Wl,--wrap=event_snapshot_write_module
|
-Wl,--wrap=event_snapshot_write_module
|
||||||
-Wl,--wrap=event_snapshot_read_module)
|
-Wl,--wrap=event_snapshot_read_module
|
||||||
|
-Wl,--wrap=machine_trigger_reset
|
||||||
|
-Wl,--wrap=machine_reset
|
||||||
|
-Wl,--wrap=maincpu_reset)
|
||||||
target_include_directories(vice_jni PRIVATE
|
target_include_directories(vice_jni PRIVATE
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/vice-libs/${ANDROID_ABI}"
|
"${CMAKE_CURRENT_SOURCE_DIR}/vice-libs/${ANDROID_ABI}"
|
||||||
"${VICE_SRC}/src"
|
"${VICE_SRC}/src"
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ extern int __real_console_init(void);
|
|||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "autostart.h"
|
#include "autostart.h"
|
||||||
#include "attach.h"
|
#include "attach.h"
|
||||||
|
#include "interrupt.h"
|
||||||
|
|
||||||
/* Drive LED state.
|
/* Drive LED state.
|
||||||
* g_io_frames: bumped by __wrap_serial_trap_receive; keeps LED lit ~100 ms after
|
* g_io_frames: bumped by __wrap_serial_trap_receive; keeps LED lit ~100 ms after
|
||||||
@@ -302,6 +303,69 @@ int __wrap_event_snapshot_read_module(struct snapshot_s *s) {
|
|||||||
(void)s; return 0;
|
(void)s; return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --wrap=machine_trigger_reset / machine_reset / maincpu_reset: required by the
|
||||||
|
* linker --wrap flags in CMakeLists.txt; kept as simple pass-throughs so the
|
||||||
|
* symbols exist. Previously these suppressed resets during snapshot load, but
|
||||||
|
* the trap-based snapshot load (see load_state_trap below) makes suppression
|
||||||
|
* unnecessary — snapshot load now runs inside DO_INTERRUPT(IK_TRAP) which
|
||||||
|
* provides the EXPORT/IMPORT register sync that makes the CPU resume from the
|
||||||
|
* correct saved PC without any post-load machine reset. */
|
||||||
|
void __real_machine_trigger_reset(unsigned int reset_mode);
|
||||||
|
void __wrap_machine_trigger_reset(unsigned int reset_mode) {
|
||||||
|
__real_machine_trigger_reset(reset_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __real_machine_reset(void);
|
||||||
|
void __wrap_machine_reset(void) {
|
||||||
|
__real_machine_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __real_maincpu_reset(void);
|
||||||
|
void __wrap_maincpu_reset(void) {
|
||||||
|
__real_maincpu_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Snapshot load via VICE CPU trap.
|
||||||
|
*
|
||||||
|
* machine_read_snapshot() restores CPU registers into maincpu_regs (a global
|
||||||
|
* struct), but maincpu_mainloop keeps its own stack-local copies (reg_pc,
|
||||||
|
* reg_a, etc.). If we call machine_read_snapshot() directly from
|
||||||
|
* video_canvas_refresh(), the loop never re-imports maincpu_regs, so the CPU
|
||||||
|
* continues from the *old* reg_pc in the *new* (restored) memory — wrong.
|
||||||
|
*
|
||||||
|
* The fix: schedule a VICE CPU trap. DO_INTERRUPT(IK_TRAP) in 6510core.c
|
||||||
|
* calls EXPORT_REGISTERS() before the trap function and IMPORT_REGISTERS()
|
||||||
|
* after. machine_read_snapshot() inside the trap overwrites maincpu_regs
|
||||||
|
* with the saved state; IMPORT_REGISTERS() then propagates those values
|
||||||
|
* (including the saved PC) back into the CPU loop's stack locals. Result:
|
||||||
|
* the game resumes from exactly the saved execution point. */
|
||||||
|
/* Both save and load run inside a CPU trap so DO_INTERRUPT(IK_TRAP) calls
|
||||||
|
* EXPORT_REGISTERS() before the trap function and IMPORT_REGISTERS() after.
|
||||||
|
*
|
||||||
|
* For save: EXPORT_REGISTERS() syncs the CPU loop's stack-local reg_pc into
|
||||||
|
* maincpu_regs before machine_write_snapshot reads it. Without the trap,
|
||||||
|
* maincpu_regs.pc is stale (last synced at the previous DO_INTERRUPT), so the
|
||||||
|
* snapshot would record the wrong PC and restore would resume at the wrong
|
||||||
|
* address — input polling code never reached, buttons appear dead.
|
||||||
|
*
|
||||||
|
* For load: machine_read_snapshot overwrites maincpu_regs with the saved state;
|
||||||
|
* IMPORT_REGISTERS() then propagates the saved PC back into reg_pc so the CPU
|
||||||
|
* resumes from the correct saved execution point. */
|
||||||
|
static char g_save_trap_path[512] = {0};
|
||||||
|
static char g_load_trap_path[512] = {0};
|
||||||
|
|
||||||
|
static void save_state_trap(uint16_t address, void *data) {
|
||||||
|
(void)address;
|
||||||
|
int r = machine_write_snapshot((const char *)data, 0, 0, 0);
|
||||||
|
LOGI("machine_write_snapshot → %d", r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_state_trap(uint16_t address, void *data) {
|
||||||
|
(void)address;
|
||||||
|
int r = machine_read_snapshot((const char *)data, 0);
|
||||||
|
LOGI("machine_read_snapshot → %d", r);
|
||||||
|
}
|
||||||
|
|
||||||
/* All functions below replace arch/headless/video.c (excluded from libvice.a). */
|
/* All functions below replace arch/headless/video.c (excluded from libvice.a). */
|
||||||
|
|
||||||
int video_arch_get_active_chip(void) { return 0; /* VIDEO_CHIP_VICII */ }
|
int video_arch_get_active_chip(void) { return 0; /* VIDEO_CHIP_VICII */ }
|
||||||
@@ -425,19 +489,19 @@ void video_canvas_refresh(video_canvas_t *canvas,
|
|||||||
}
|
}
|
||||||
pthread_mutex_unlock(&g_pending_lock);
|
pthread_mutex_unlock(&g_pending_lock);
|
||||||
if (save_state_path[0] != '\0') {
|
if (save_state_path[0] != '\0') {
|
||||||
/* save_roms=0, save_disks=0: disk management is handled by our UI;
|
strncpy(g_save_trap_path, save_state_path, sizeof(g_save_trap_path) - 1);
|
||||||
* skipping the drive module avoids path-resolution failures on restore. */
|
g_save_trap_path[sizeof(g_save_trap_path) - 1] = '\0';
|
||||||
int r = machine_write_snapshot(save_state_path, 0, 0, 0);
|
interrupt_maincpu_trigger_trap(save_state_trap, g_save_trap_path);
|
||||||
LOGI("machine_write_snapshot → %d", r);
|
|
||||||
}
|
}
|
||||||
if (load_state_path[0] != '\0') {
|
if (load_state_path[0] != '\0') {
|
||||||
int r = machine_read_snapshot(load_state_path, 0);
|
/* Schedule a VICE CPU trap so machine_read_snapshot runs inside
|
||||||
LOGI("machine_read_snapshot → %d", r);
|
* DO_INTERRUPT(IK_TRAP). That macro calls EXPORT_REGISTERS() before
|
||||||
/* After snapshot restore the virtual device loses its D64 attachment
|
* the trap and IMPORT_REGISTERS() after, so the saved PC written into
|
||||||
* and falls back to host-filesystem mode. Re-attach the same disk so
|
* maincpu_regs by machine_read_snapshot is picked up by the CPU loop
|
||||||
* the C64 program can continue reading from the image it expected. */
|
* immediately — the game resumes from the correct saved execution point. */
|
||||||
if (r == 0 && g_current_disk_path[0] != '\0')
|
strncpy(g_load_trap_path, load_state_path, sizeof(g_load_trap_path) - 1);
|
||||||
file_system_attach_disk(8, 0, g_current_disk_path);
|
g_load_trap_path[sizeof(g_load_trap_path) - 1] = '\0';
|
||||||
|
interrupt_maincpu_trigger_trap(load_state_trap, g_load_trap_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drive LED.
|
/* Drive LED.
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path
|
|
||||||
android:fillColor="#3DDC84"
|
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M9,0L9,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,0L19,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,0L29,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,0L39,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,0L49,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,0L59,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,0L69,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,0L79,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M89,0L89,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M99,0L99,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,9L108,9"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,19L108,19"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,29L108,29"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,39L108,39"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,49L108,49"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,59L108,59"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,69L108,69"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,79L108,79"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,89L108,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,99L108,99"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,29L89,29"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,39L89,39"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,49L89,49"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,59L89,59"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,69L89,69"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,79L89,79"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,19L29,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,19L39,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,19L49,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,19L59,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,19L69,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,19L79,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="85.84757"
|
|
||||||
android:endY="92.4963"
|
|
||||||
android:startX="42.9492"
|
|
||||||
android:startY="49.59793"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@android:color/transparent"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
</adaptive-icon>
|
||||||
</adaptive-icon>
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@android:color/transparent"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
</adaptive-icon>
|
||||||
</adaptive-icon>
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 454 KiB |
|
After Width: | Height: | Size: 405 KiB |