diff --git a/include/core/rom.hpp b/include/core/rom.hpp index c54031ec..37e399b4 100644 --- a/include/core/rom.hpp +++ b/include/core/rom.hpp @@ -37,6 +37,10 @@ struct loaded_rom */ void load(std::map& settings, uint64_t rtc_sec, uint64_t rtc_subsec) throw(std::bad_alloc, std::runtime_error); +/** + * Reset the emulation state to state just before last load. + */ + void reset_to_load() { return rtype().reset_to_load(); } /** * Saves core state into buffer. WARNING: This takes emulated time. * diff --git a/include/interface/c-interface-translate.hpp b/include/interface/c-interface-translate.hpp index c1cbaa72..25113502 100644 --- a/include/interface/c-interface-translate.hpp +++ b/include/interface/c-interface-translate.hpp @@ -166,6 +166,10 @@ template<> struct e2t { typedef lsnes_core_get_vma_list* t; typedef lsnes_core_get_vma_list& r; }; +template<> struct e2t { + typedef lsnes_core_reinit* t; + typedef lsnes_core_reinit& r; +}; template class t2e {}; template<> struct t2e { const static int e = LSNES_CORE_ENUMERATE_CORES; }; @@ -202,6 +206,7 @@ template<> struct t2e { const static int e = LSNES_CORE_D template<> struct t2e { const static int e = LSNES_CORE_PRE_EMULATE; }; template<> struct t2e { const static int e = LSNES_CORE_GET_DEVICE_REGS; }; template<> struct t2e { const static int e = LSNES_CORE_GET_VMA_LIST; }; +template<> struct t2e { const static int e = LSNES_CORE_REINIT; }; } #endif diff --git a/include/interface/c-interface.h b/include/interface/c-interface.h index e4aa2462..51bbd399 100644 --- a/include/interface/c-interface.h +++ b/include/interface/c-interface.h @@ -109,8 +109,9 @@ extern "C" { #define LSNES_CORE_CAP1_MEMWATCH 0x00010000U //Core supports lightguns (By setting lightgun_height/lightgun_width in LSNES_CORE_GET_AV_STATE). #define LSNES_CORE_CAP1_LIGHTGUN 0x00020000U +//Core supports fast reinit (By supporting LSNES_CORE_REINIT). +#define LSNES_CORE_CAP1_REINIT 0x00040000U //Reserved capabilities. -#define LSNES_CORE_CAP1_RESERVED18 0x00040000U #define LSNES_CORE_CAP1_RESERVED19 0x00080000U #define LSNES_CORE_CAP1_RESERVED20 0x00100000U #define LSNES_CORE_CAP1_RESERVED21 0x00200000U @@ -838,6 +839,16 @@ struct lsnes_core_get_vma_list struct lsnes_core_get_vma_list_vma** vmas; }; +//Request 34: Reinit core to last loaded state. +//Item id: Core ID. +//Default action: Emulate using loadstate. +//Signals that the core state should be reset to state just after last load (load savestate at moment of initial +//poweron). +#define LSNES_CORE_REINIT 27 +struct lsnes_core_reinit +{ +}; + #ifdef LSNES_BUILD_AS_BUILTIN_CORE void lsnes_register_builtin_core(lsnes_core_func_t fn); diff --git a/include/interface/romtype.hpp b/include/interface/romtype.hpp index 83b6122c..eb5c86fd 100644 --- a/include/interface/romtype.hpp +++ b/include/interface/romtype.hpp @@ -362,6 +362,7 @@ struct core_core std::vector get_trace_cpus(); void debug_reset(); bool isnull() const; + void reset_to_load() { c_reset_to_load(); } bool safe_to_unload(loadlib::module& mod) { return !mod.is_marked(this); } protected: /** @@ -530,6 +531,10 @@ protected: * Reset all debug hooks. */ virtual void c_debug_reset() = 0; +/** + * Reset to state equivalent to ROM load. + */ + virtual void c_reset_to_load() = 0; /** * Is null core (only NULL core should define this). */ @@ -618,6 +623,7 @@ public: std::vector get_trace_cpus() { return core->get_trace_cpus(); } void debug_reset() { core->debug_reset(); } bool isnull() const { return core->isnull(); } + void reset_to_load() { return core->reset_to_load(); } bool safe_to_unload(loadlib::module& mod) const { return core->safe_to_unload(mod); } protected: /** diff --git a/src/core/moviedata.cpp b/src/core/moviedata.cpp index 28052aa1..1e936e08 100644 --- a/src/core/moviedata.cpp +++ b/src/core/moviedata.cpp @@ -427,8 +427,11 @@ namespace core.rom->set_pflag(_movie.dyn.poll_flag); core.controls->set_macro_frames(_movie.dyn.active_macros); } else { - //Reload the ROM in order to rewind to the beginning. - core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond); + //If settings possibly change, reload the ROM. Otherwise rewind to beginning. + if(!*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid) + core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond); + else + core.rom->reset_to_load(); //Load the SRAM and volatile RAM. Or anchor savestate if any. core.controls->set_ports(portset); _movie.dyn.rtc_second = _movie.movie_rtc_second; diff --git a/src/core/nullcore.cpp b/src/core/nullcore.cpp index 0f281726..eaa17e39 100644 --- a/src/core/nullcore.cpp +++ b/src/core/nullcore.cpp @@ -96,6 +96,7 @@ namespace { return std::vector(); } + void c_reset_to_load() {} } core_null; } diff --git a/src/emulation/bsnes-legacy/core.cpp b/src/emulation/bsnes-legacy/core.cpp index f68f8934..8f93e7ed 100644 --- a/src/emulation/bsnes-legacy/core.cpp +++ b/src/emulation/bsnes-legacy/core.cpp @@ -89,6 +89,7 @@ namespace bool video_refresh_done; bool forced_hook = false; std::map> ptrmap; + std::vector init_savestate; uint32_t cover_fbmem[512 * 448]; //Delay reset. unsigned long long delayreset_cycles_run; @@ -668,6 +669,10 @@ namespace do_reset_flag = -1; if(ecore_callbacks) ecore_callbacks->action_state_updated(); + //Save initial state, so we can restore it later. + serializer s = SNES::system.serialize(); + init_savestate.resize(s.size()); + memcpy(&init_savestate[0], s.data(), s.size()); } return r ? 0 : -1; } @@ -1539,6 +1544,16 @@ again2: //TODO: Trace various chips. return r; } + void c_reset_to_load() + { + serializer s(&init_savestate[0], init_savestate.size()); + if(!SNES::system.unserialize(s)) + throw std::runtime_error("SNES core rejected initial state?"); + have_saved_this_frame = false; + do_reset_flag = -1; + if(ecore_callbacks) + ecore_callbacks->action_state_updated(); + } } bsnes_core; struct _type_snes : public core_type diff --git a/src/emulation/gambatte/core.cpp b/src/emulation/gambatte/core.cpp index 9704e866..328d49de 100644 --- a/src/emulation/gambatte/core.cpp +++ b/src/emulation/gambatte/core.cpp @@ -71,6 +71,7 @@ namespace #endif unsigned frame_overflow = 0; std::vector romdata; + std::vector init_savestate; uint32_t cover_fbmem[480 * 432]; uint32_t primary_framebuffer[160*144]; uint32_t accumulator_l = 0; @@ -407,7 +408,8 @@ namespace for(unsigned i = 0; i < 12; i++) if(!palette_colors_default[i >> 2]) instance->setDmgPaletteColor(i >> 2, i & 3, palette_colors[i]); - + //Save initial savestate. + instance->saveState(init_savestate); return 1; } @@ -845,6 +847,13 @@ namespace r.push_back("cpu"); return r; } + void c_reset_to_load() + { + instance->loadState(init_savestate); + memset(primary_framebuffer, 0, sizeof(primary_framebuffer)); + frame_overflow = 0; //frame_overflow is always 0 at the beginning. + do_reset_flag = false; + } } gambatte_core; std::vector boolean_values = {{"0", "False", 0}, {"1", "True", 1}}; diff --git a/src/emulation/sky/sky.cpp b/src/emulation/sky/sky.cpp index e535e79f..42824fbe 100644 --- a/src/emulation/sky/sky.cpp +++ b/src/emulation/sky/sky.cpp @@ -420,5 +420,11 @@ namespace sky std::vector r; return r; } + void c_reset_to_load() + { + //Clear the RAM and jump to boot vector. + memset(corei.state.as_ram().first, 0, corei.state.as_ram().second); + rom_boot_vector(corei); + } } sky_core; } diff --git a/src/emulation/test/test.cpp b/src/emulation/test/test.cpp index fe2c232a..8cb46d4f 100644 --- a/src/emulation/test/test.cpp +++ b/src/emulation/test/test.cpp @@ -203,5 +203,8 @@ namespace { return std::vector(); } + void c_reset_to_load() + { + } } test_core; } diff --git a/src/interface/c-interface.cpp b/src/interface/c-interface.cpp index cd58c1e0..7c324e3b 100644 --- a/src/interface/c-interface.cpp +++ b/src/interface/c-interface.cpp @@ -50,6 +50,7 @@ template<> int ccore_call_param_map::id = LSNES_CORE_DRAW template<> int ccore_call_param_map::id = LSNES_CORE_PRE_EMULATE; template<> int ccore_call_param_map::id = LSNES_CORE_GET_DEVICE_REGS; template<> int ccore_call_param_map::id = LSNES_CORE_GET_VMA_LIST; +template<> int ccore_call_param_map::id = LSNES_CORE_REINIT; template<> const char* ccore_call_param_map::name = "LSNES_CORE_ENUMERATE_CORES"; template<> const char* ccore_call_param_map::name = "LSNES_CORE_GET_CORE_INFO"; @@ -86,6 +87,7 @@ template<> const char* ccore_call_param_map::name = "LSNE template<> const char* ccore_call_param_map::name = "LSNES_CORE_PRE_EMULATE"; template<> const char* ccore_call_param_map::name = "LSNES_CORE_GET_DEVICE_REGS"; template<> const char* ccore_call_param_map::name = "LSNES_CORE_GET_VMA_LIST"; +template<> const char* ccore_call_param_map::name = "LSNES_CORE_REINIT"; namespace { @@ -660,6 +662,18 @@ failed: failed: return std::list(); } + void c_reset_to_load() + { + lsnes_core_reinit r; + if(caps1 & LSNES_CORE_CAP1_REINIT) { + entrypoint(id, r, [](const char* name, const char* err) { + throw std::runtime_error("Resetting state failed: " + std::string(err)); + }); + return; + } + //Emulate by loadstate. + c_unserialize(&init_savestate[0], init_savestate.size()); + } std::map get_ports() { return ports; @@ -668,6 +682,11 @@ failed: { internal_pflag = true; } + void update_initial_savestate() + { + if((caps1 & LSNES_CORE_CAP1_REINIT) == 0) + c_serialize(init_savestate); + } private: std::string fullname; std::string shortname; @@ -680,6 +699,7 @@ failed: std::vector actions; framebuffer::raw cover; std::vector covermem; + std::vector init_savestate; entrypoint_fn entrypoint; c_lib_init* plugin; }; @@ -721,6 +741,7 @@ failed: entrypoint(_id, r, [_id](const char* name, const char* err) { (stringfmt() << "LSNES_CORE_LOAD_ROM(" << _id << ") failed: " << err).throwex(); }); + dynamic_cast(get_core())->update_initial_savestate(); return 0; } controller_set t_controllerconfig(std::map& settings)