diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/emu/debug/debugcmd.cpp | 25 | ||||
-rw-r--r-- | src/emu/debug/debughlp.cpp | 2 | ||||
-rw-r--r-- | src/emu/diexec.cpp | 3 | ||||
-rw-r--r-- | src/emu/emuopts.cpp | 2 | ||||
-rw-r--r-- | src/emu/inpttype.h | 2 | ||||
-rw-r--r-- | src/emu/machine.cpp | 16 | ||||
-rw-r--r-- | src/emu/machine.h | 4 | ||||
-rw-r--r-- | src/emu/save.cpp | 410 | ||||
-rw-r--r-- | src/emu/save.h | 41 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/debugwininfo.cpp | 20 | ||||
-rw-r--r-- | src/osd/modules/debugger/win/debugwininfo.h | 1 |
11 files changed, 286 insertions, 240 deletions
diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp index c3b794fd525..c14d74a4299 100644 --- a/src/emu/debug/debugcmd.cpp +++ b/src/emu/debug/debugcmd.cpp @@ -1646,21 +1646,16 @@ void debugger_commands::execute_stateload(int ref, const std::vector<std::string void debugger_commands::execute_rewind(int ref, const std::vector<std::string> ¶ms) { - if (!m_machine.save().rewind()->enabled()) - { - m_console.printf("Rewind not enabled\n"); - return; - } - - m_machine.rewind_step(); - - // clear all PC & memory tracks - for (device_t &device : device_iterator(m_machine.root_device())) - { - device.debug()->track_pc_data_clear(); - device.debug()->track_mem_data_clear(); - } - m_console.printf("Rewind step attempted. Please refer to window message popup for results.\n"); + bool success = m_machine.rewind_step(); + if (success) + // clear all PC & memory tracks + for (device_t &device : device_iterator(m_machine.root_device())) + { + device.debug()->track_pc_data_clear(); + device.debug()->track_mem_data_clear(); + } + else + m_console.printf("Rewind error occured. See error.log for details.\n"); } diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp index f25d52b3ae7..4eff7e4e8e4 100644 --- a/src/emu/debug/debughlp.cpp +++ b/src/emu/debug/debughlp.cpp @@ -463,7 +463,7 @@ static const help_item static_help_list[] = "The rewind command loads the most recent RAM-based state. Rewind states, when enabled, are " "saved when \"step\", \"over\", or \"out\" command gets executed, storing the machine state as " "of the moment before actually stepping. Consecutively loading rewind states can work like " - "reverse execution. Depending on which steps forward were taken previously, the bahavior can " + "reverse execution. Depending on which steps forward were taken previously, the behavior can " "be similar to GDB's \"reverse-stepi\" or \"reverse-next\". All output for this command is " "currently echoed into the running machine window. Previous memory and PC tracking statistics " "are cleared, actual reverse execution does not occur.\n" diff --git a/src/emu/diexec.cpp b/src/emu/diexec.cpp index d84f3b8f4be..0958108fe42 100644 --- a/src/emu/diexec.cpp +++ b/src/emu/diexec.cpp @@ -499,9 +499,6 @@ void device_execute_interface::interface_post_start() assert_always(m_icountptr != nullptr, "m_icountptr never initialized!"); // register for save states - device().save_pointer(NAME(m_icountptr), 1); - device().save_item(NAME(m_cycles_running)); - device().save_item(NAME(m_cycles_stolen)); device().save_item(NAME(m_suspend)); device().save_item(NAME(m_nextsuspend)); device().save_item(NAME(m_eatcycles)); diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp index 4fd91155fd2..0cb37e6895e 100644 --- a/src/emu/emuopts.cpp +++ b/src/emu/emuopts.cpp @@ -63,7 +63,7 @@ const options_entry emu_options::s_option_entries[] = { OPTION_STATE, nullptr, OPTION_STRING, "saved state to load" }, { OPTION_AUTOSAVE, "0", OPTION_BOOLEAN, "enable automatic restore at startup, and automatic save at exit time" }, { OPTION_REWIND, "0", OPTION_BOOLEAN, "enable rewind savestates" }, - { OPTION_REWIND_CAPACITY "(1-500)", "100", OPTION_INTEGER, "quantity of states rewind buffer can contain" }, + { OPTION_REWIND_CAPACITY "(1-2048)", "100", OPTION_INTEGER, "rewind buffer size in megabytes" }, { OPTION_PLAYBACK ";pb", nullptr, OPTION_STRING, "playback an input file" }, { OPTION_RECORD ";rec", nullptr, OPTION_STRING, "record an input file" }, { OPTION_RECORD_TIMECODE, "0", OPTION_BOOLEAN, "record an input timecode file (requires -record option)" }, diff --git a/src/emu/inpttype.h b/src/emu/inpttype.h index a1e5bef133b..cf7cef9150d 100644 --- a/src/emu/inpttype.h +++ b/src/emu/inpttype.h @@ -825,7 +825,7 @@ inline void construct_core_types_UI(simple_list<input_type_entry> &typelist) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_CONFIGURE, "Config Menu", input_seq(KEYCODE_TAB) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE, "Pause", input_seq(KEYCODE_P, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE_SINGLE, "Pause - Single Step", input_seq(KEYCODE_P, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_REWIND_SINGLE, "Rewind - Single Step", input_seq(KEYCODE_TILDE, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_TILDE, KEYCODE_RSHIFT) ) + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_REWIND_SINGLE, "Rewind - Single Step", input_seq(KEYCODE_TILDE, KEYCODE_LSHIFT) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RESET_MACHINE, "Reset Machine", input_seq(KEYCODE_F3, KEYCODE_LSHIFT) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SOFT_RESET, "Soft Reset", input_seq(KEYCODE_F3, input_seq::not_code, KEYCODE_LSHIFT) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_GFX, "Show Gfx", input_seq(KEYCODE_F4) ) diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp index 48edd645ac1..c8ed052ad98 100644 --- a/src/emu/machine.cpp +++ b/src/emu/machine.cpp @@ -687,10 +687,9 @@ void running_machine::immediate_load(const char *filename) // state to the rewind list //------------------------------------------------- -void running_machine::rewind_capture() +bool running_machine::rewind_capture() { - if (m_save.rewind()->enabled()) - m_save.rewind()->capture(); + return m_save.rewind()->capture(); } @@ -699,10 +698,9 @@ void running_machine::rewind_capture() // rewind states //------------------------------------------------- -void running_machine::rewind_step() +bool running_machine::rewind_step() { - if (m_save.rewind()->enabled()) - m_save.rewind()->step(); + return m_save.rewind()->step(); } @@ -713,8 +711,7 @@ void running_machine::rewind_step() void running_machine::rewind_invalidate() { - if (m_save.rewind()->enabled()) - m_save.rewind()->invalidate(); + m_save.rewind()->invalidate(); } @@ -960,6 +957,9 @@ void running_machine::handle_saveload() if (saverr != STATERR_NONE && m_saveload_schedule == saveload_schedule::SAVE) file.remove_on_close(); } + else if (openflags == OPEN_FLAG_READ && filerr == osd_file::error::NOT_FOUND) + // attempt to load a non-existent savestate, report empty slot + popmessage("Error: No savestate file to load.", opname); else popmessage("Error: Failed to open file for %s operation.", opname); } diff --git a/src/emu/machine.h b/src/emu/machine.h index efe464983db..098d677b077 100644 --- a/src/emu/machine.h +++ b/src/emu/machine.h @@ -230,8 +230,8 @@ public: void immediate_load(const char *filename); // rewind operations - void rewind_capture(); - void rewind_step(); + bool rewind_capture(); + bool rewind_step(); void rewind_invalidate(); // scheduled operations diff --git a/src/emu/save.cpp b/src/emu/save.cpp index eff71cd6870..eb1fd704571 100644 --- a/src/emu/save.cpp +++ b/src/emu/save.cpp @@ -79,7 +79,12 @@ void save_manager::allow_registration(bool allowed) // allow/deny registration m_reg_allowed = allowed; if (!allowed) + { dump_registry(); + + // everything is registered by now, evaluate the savestate size + m_rewind->clamp_capacity(); + } } @@ -567,279 +572,304 @@ rewinder::rewinder(save_manager &save) : m_save(save) , m_enabled(save.machine().options().rewind()) , m_capacity(save.machine().options().rewind_capacity()) + , m_current_index(REWIND_INDEX_NONE) + , m_first_invalid_index(REWIND_INDEX_NONE) + , m_first_time_warning(true) + , m_first_time_note(true) { } //------------------------------------------------- -// check_size - shrink the state list if it is -// about to hit the capacity +// clamp_capacity - safety checks for commandline +// override //------------------------------------------------- -void rewinder::check_size() +void rewinder::clamp_capacity() { - // safety check that shouldn't be allowed to trigger - if (m_state_list.size() > m_capacity) - { - // drop all states beyond capacity - uint32_t count = m_state_list.size() - m_capacity; - m_state_list.erase(m_state_list.begin(), m_state_list.begin() + count); - } + if (!m_enabled) + return; - // check if we're about to hit capacity - if (m_state_list.size() == m_capacity) - { - // check the last state - ram_state *last = m_state_list.back().get(); + const size_t total = m_capacity * 1024 * 1024; + const size_t single = ram_state::get_size(m_save); - // if we're not on top of the list, no need to move states around - if (!last->m_valid) - return; + // can't set below zero, but allow commandline to override options' upper limit + if (total < 0) + m_capacity = 0; - // we can now get the first state and invalidate it - std::unique_ptr<ram_state> first(std::move(m_state_list.front())); - first->m_valid = false; - - // move it to the end for future use - m_state_list.push_back(std::move(first)); - m_state_list.erase(m_state_list.begin()); + // if capacity is below savestate size, can't save anything + if (total < single) + { + m_enabled = false; + m_save.machine().logerror("Rewind has been disabled, because rewind capacity is smaller than savestate size.\n"); + m_save.machine().logerror("Rewind buffer size: %d bytes. Savestate size: %d bytes.\n", total, single); + m_save.machine().popmessage("Rewind has been disabled. See error.log for details"); } } //------------------------------------------------- -// report_error - report rewind success or -// error type +// invalidate - mark all the future states as +// invalid to prevent loading them, as the +// current input might have changed //------------------------------------------------- -void rewinder::report_error(save_error error, rewind_operation operation, int index) +void rewinder::invalidate() { - const char *const opname = (operation == rewind_operation::LOAD) ? "load" : "save"; + if (!m_enabled) + return; - switch (error) + // is there anything to invalidate? + if (!current_index_is_last()) { - // internal saveload failures - case STATERR_ILLEGAL_REGISTRATIONS: - m_save.machine().popmessage("Error: Unable to %s state due to illegal registrations. See error.log for details.", opname); - break; + // all states starting from the current one will be invalid + m_first_invalid_index = m_current_index; - case STATERR_INVALID_HEADER: - m_save.machine().popmessage("Error: Unable to %s state due to an invalid header. Make sure the save state is correct for this machine.", opname); - break; + // actually invalidate + for (auto it = m_state_list.begin() + m_first_invalid_index; it < m_state_list.end(); ++it) + it->get()->m_valid = false; + } +} - case STATERR_READ_ERROR: - m_save.machine().popmessage("Error: Unable to %s state due to a read error.", opname); - break; - case STATERR_WRITE_ERROR: - m_save.machine().popmessage("Error: Unable to %s state due to a write error.", opname); - break; +//------------------------------------------------- +// capture - record a single state, returns true +// on success +//------------------------------------------------- - // external saveload failures - case STATERR_NOT_FOUND: - if (operation == rewind_operation::LOAD) - m_save.machine().popmessage("No rewind state to load."); - break; +bool rewinder::capture() +{ + if (!m_enabled) + { + report_error(STATERR_DISABLED, rewind_operation::SAVE); + return false; + } - // success - case STATERR_NONE: + if (current_index_is_last()) + { + // we need to create a new state + std::unique_ptr<ram_state> state = std::make_unique<ram_state>(m_save); + const save_error error = state->save(); + + // validate the state + if (error == STATERR_NONE) + // it's safe to append + m_state_list.push_back(std::move(state)); + else { - const char *const warning = (m_save.machine().system().flags & MACHINE_SUPPORTS_SAVE) ? "" - : "\nWarning: Save states are not officially supported for this machine."; + // internal error, complain and evacuate + report_error(error, rewind_operation::SAVE); + return false; + } + } + else + { + // invalidate the future states + invalidate(); - // figure out the qantity of valid states - int invalid = get_first_invalid_index(); + // update the existing state + ram_state *state = m_state_list.at(m_current_index).get(); + const save_error error = state->save(); + + // validate the state + if (error != STATERR_NONE) + { + // internal error, complain and evacuate + report_error(error, rewind_operation::SAVE); + return false; + } + } - // all states are valid - if (invalid == REWIND_INDEX_NONE) - invalid = m_state_list.size(); + // make sure we will fit in + if (!check_size()) + // the list keeps growing + m_current_index++; - if (operation == rewind_operation::SAVE) - m_save.machine().popmessage("Rewind state %i captured.\nRewind state count: %i.%s", invalid - 1, invalid, warning); - else - m_save.machine().popmessage("Rewind state %i loaded.\nRewind state count: %i.%s", index, invalid, warning); - } - break; + // update first invalid index + if (current_index_is_last()) + m_first_invalid_index = REWIND_INDEX_NONE; + else + m_first_invalid_index = m_current_index + 1; - // something that shouldn't be allowed to happen - default: - m_save.machine().popmessage("Error: Unknown error during state %s.", opname); - break; - } + // success + report_error(STATERR_NONE, rewind_operation::SAVE); + return true; } //------------------------------------------------- -// get_current_index - get the index of the -// state to assume as current +// step - single step back in time, returns true +// on success //------------------------------------------------- -int rewinder::get_current_index() +bool rewinder::step() { - // nowhere to search - if (m_state_list.empty()) - return REWIND_INDEX_NONE; + if (!m_enabled) + { + report_error(STATERR_DISABLED, rewind_operation::LOAD); + return false; + } - // fetch the current machine time - attotime curtime = m_save.machine().time(); + // do we have states to load? + if (m_current_index <= REWIND_INDEX_FIRST || m_first_invalid_index == REWIND_INDEX_FIRST) + { + // no valid states, complain and evacuate + report_error(STATERR_NOT_FOUND, rewind_operation::LOAD); + return false; + } - // find the state at the current time, or at least the first one after - for (auto it = m_state_list.begin(); it < m_state_list.end(); ++it) - if (it->get()->m_time >= curtime) - return it - m_state_list.begin(); + // prepare to load the last valid index if we're too far ahead + if (m_first_invalid_index > REWIND_INDEX_NONE && m_current_index > m_first_invalid_index) + m_current_index = m_first_invalid_index; - // all states are older - return REWIND_INDEX_NONE; -} + // step back and obtain the state pointer + ram_state *state = m_state_list.at(--m_current_index).get(); + // try to load and report the result + const save_error error = state->load(); + report_error(error, rewind_operation::LOAD); -//------------------------------------------------- -// get_first_invalid_index - get the index of the -// first invalid state -//------------------------------------------------- - -int rewinder::get_first_invalid_index() -{ - for (auto it = m_state_list.begin(); it < m_state_list.end(); ++it) - if (!it->get()->m_valid) - return it - m_state_list.begin(); + if (error == save_error::STATERR_NONE) + return true; - // all states are valid - return REWIND_INDEX_NONE; + return false; } //------------------------------------------------- -// invalidate - mark all the future states as -// invalid +// check_size - shrink the state list if it is +// about to hit the capacity. returns true if +// the list got shrank //------------------------------------------------- -int rewinder::invalidate() +bool rewinder::check_size() { - // fetch the current state index - int index = get_current_index(); + if (!m_enabled) + false; - // more invalid states may be farther back, account for them too - int invalid = get_first_invalid_index(); + // state sizes in bytes + const size_t singlesize = ram_state::get_size(m_save); + size_t totalsize = m_state_list.size() * singlesize; - // roll back if we can - if (invalid != REWIND_INDEX_NONE && (index == REWIND_INDEX_NONE || invalid < index)) - index = invalid; + // convert our limit from megabytes + const size_t capsize = m_capacity * 1024 * 1024; - if (index != REWIND_INDEX_NONE) + // safety check that shouldn't be allowed to trigger + if (totalsize > capsize) { - // if it's the last state in the list, skip further invalidation - if (++index >= m_state_list.size()) - return index; + // states to remove + const u32 count = (totalsize - capsize) / singlesize; - // invalidate all the future states, as the current input might have changed - for (auto it = m_state_list.begin() + index; it < m_state_list.end(); ++it) - it->get()->m_valid = false; + // drop everything that's beyond capacity + m_state_list.erase(m_state_list.begin(), m_state_list.begin() + count); } - // index of the first invalid state - return index; -} + // update before new check + totalsize = m_state_list.size() * singlesize; + // check if capacity will be hit by the newly captured state + if (totalsize + singlesize >= capsize) + { + // check if we have spare states ahead + if (!current_index_is_last()) + // no need to move states around + return false; -//------------------------------------------------- -// capture - record a single state -//------------------------------------------------- - -void rewinder::capture() -{ - // fetch the current state index and invalidate the future states - int index = invalidate(); + // we can now get the first state and invalidate it + std::unique_ptr<ram_state> first(std::move(m_state_list.front())); + first->m_valid = false; - if (index == REWIND_INDEX_NONE) - { - // no current state, create one - std::unique_ptr<ram_state> state = std::make_unique<ram_state>(m_save); - save_error error = state->save(); + // move it to the end for future use + m_state_list.push_back(std::move(first)); + m_state_list.erase(m_state_list.begin()); - // validate the state - if (error == STATERR_NONE) + if (m_first_time_note) { - // it's safe to append - m_state_list.push_back(std::move(state)); - } - else - { - // internal error, complain and evacuate - report_error(error, rewind_operation::SAVE); - return; - } - } - else - { - // update the existing state - ram_state *state = m_state_list.at(--index).get(); - save_error error = state->save(); - - // validate the state - if (error != STATERR_NONE) - { - // internal error, complain and evacuate - report_error(error, rewind_operation::SAVE); - return; + m_save.machine().logerror("Rewind note: Capacity has been reached. Old savestates will be erased.\n"); + m_save.machine().logerror("Capacity: %d bytes. Savestate size: %d bytes. Savestate count: %d.\n", + totalsize, singlesize, m_state_list.size()); + m_first_time_note = false; } - } - // make sure we still fit in - check_size(); + return true; + } - // success - report_error(STATERR_NONE, rewind_operation::SAVE); + return false; } //------------------------------------------------- -// step - single step back in time +// report_error - report rewind results //------------------------------------------------- -void rewinder::step() +void rewinder::report_error(save_error error, rewind_operation operation) { - // check presence of states - if (m_state_list.empty()) + const char *const opname = (operation == rewind_operation::LOAD) ? "load" : "save"; + switch (error) { - // no states, complain and evacuate - report_error(STATERR_NOT_FOUND, rewind_operation::LOAD); - return; - } + // internal saveload failures + case STATERR_ILLEGAL_REGISTRATIONS: + m_save.machine().logerror("Rewind error: Unable to %s state due to illegal registrations.", opname); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + break; - // fetch the current state index - int index = get_current_index(); + case STATERR_INVALID_HEADER: + m_save.machine().logerror("Rewind error: Unable to %s state due to an invalid header. " + "Make sure the save state is correct for this machine.\n", opname); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + break; - // if there is room, retreat - if (index != REWIND_INDEX_FIRST) - { - // we may be on top of the list, when all states are older - if (index == REWIND_INDEX_NONE) + case STATERR_READ_ERROR: + m_save.machine().logerror("Rewind error: Unable to %s state due to a read error.\n", opname); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + break; + + case STATERR_WRITE_ERROR: + m_save.machine().logerror("Rewind error: Unable to %s state due to a write error.\n", opname); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + break; + + // external saveload failures + case STATERR_NOT_FOUND: + if (operation == rewind_operation::LOAD) + { + m_save.machine().logerror("Rewind error: No rewind state to load.\n"); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + } + break; + + case STATERR_DISABLED: + if (operation == rewind_operation::LOAD) { - // use the last consecutively valid state, to ensure rewinder integrity - index = get_first_invalid_index(); + m_save.machine().logerror("Rewind error: Rewind is disabled.\n"); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + } + break; - if (index == REWIND_INDEX_NONE) - // all states are valid - index = m_state_list.size(); - else if (index == REWIND_INDEX_FIRST) + // success + case STATERR_NONE: + { + const u64 supported = m_save.machine().system().flags & MACHINE_SUPPORTS_SAVE; + const char *const warning = supported || !m_first_time_warning ? "" : + "Rewind warning: Save states are not officially supported for this machine.\n"; + const char *const opnamed = (operation == rewind_operation::LOAD) ? "loaded" : "captured"; + + // for rewinding outside of debugger, give some indication that rewind has worked, as screen doesn't update + m_save.machine().popmessage("Rewind state %i %s.\n%s", m_current_index + 1, opnamed, warning); + if (m_first_time_warning && operation == rewind_operation::LOAD && !supported) { - // no valid states, complain and evacuate - report_error(STATERR_NOT_FOUND, rewind_operation::LOAD); - return; + m_save.machine().logerror(warning); + m_first_time_warning = false; } } + break; - // obtain the state pointer - ram_state *state = m_state_list.at(--index).get(); - - // try to load and report the result - report_error(state->load(), rewind_operation::LOAD, index); - return; + // something that shouldn't be allowed to happen + default: + m_save.machine().logerror("Error: Unknown error during state %s.\n", opname); + m_save.machine().popmessage("Rewind error occured. See error.log for details."); + break; } - - // no valid states, complain - report_error(STATERR_NOT_FOUND, rewind_operation::LOAD); } diff --git a/src/emu/save.h b/src/emu/save.h index e5c8c2fdbbf..baf18258d09 100644 --- a/src/emu/save.h +++ b/src/emu/save.h @@ -30,7 +30,8 @@ enum save_error STATERR_ILLEGAL_REGISTRATIONS, STATERR_INVALID_HEADER, STATERR_READ_ERROR, - STATERR_WRITE_ERROR + STATERR_WRITE_ERROR, + STATERR_DISABLED }; @@ -181,7 +182,7 @@ private: running_machine & m_machine; // reference to our machine std::unique_ptr<rewinder> m_rewind; // rewinder bool m_reg_allowed; // are registrations allowed? - int m_illegal_regs; // number of illegal registrations + s32 m_illegal_regs; // number of illegal registrations std::vector<std::unique_ptr<state_entry>> m_entry_list; // list of registered entries std::vector<std::unique_ptr<ram_state>> m_ramstate_list; // list of ram states @@ -191,12 +192,12 @@ private: class ram_state { - save_manager & m_save; // reference to save_manager - util::vectorstream m_data; // save data buffer + save_manager & m_save; // reference to save_manager + util::vectorstream m_data; // save data buffer public: - bool m_valid; // can we load this state? - attotime m_time; // machine timestamp + bool m_valid; // can we load this state? + attotime m_time; // machine timestamp ram_state(save_manager &save); static size_t get_size(save_manager &save); @@ -206,9 +207,13 @@ public: class rewinder { - save_manager & m_save; // reference to save_manager - bool m_enabled; // enable rewind savestates - uint32_t m_capacity; // imposed limit of total states (1-500) + save_manager & m_save; // reference to save_manager + bool m_enabled; // enable rewind savestates + size_t m_capacity; // total memory rewind states can occupy (MB, limited to 1-2048 in options) + s32 m_current_index; // where we are in time + s32 m_first_invalid_index; // all states before this one are guarateed to be valid + bool m_first_time_warning; // keep track of warnings we report + bool m_first_time_note; // keep track of notes std::vector<std::unique_ptr<ram_state>> m_state_list; // rewinder's own ram states // load/save management @@ -221,20 +226,20 @@ class rewinder enum { REWIND_INDEX_NONE = -1, - REWIND_INDEX_FIRST = 0 + REWIND_INDEX_FIRST }; - - int get_current_index(); - int get_first_invalid_index(); - void check_size(); - void report_error(save_error type, rewind_operation operation, int index = REWIND_INDEX_FIRST); + + bool check_size(); + bool current_index_is_last() { return m_current_index == m_state_list.size() - 1; } + void report_error(save_error type, rewind_operation operation); public: rewinder(save_manager &save); bool enabled() { return m_enabled; } - int invalidate(); - void capture(); - void step(); + void clamp_capacity(); + void invalidate(); + bool capture(); + bool step(); }; diff --git a/src/osd/modules/debugger/win/debugwininfo.cpp b/src/osd/modules/debugger/win/debugwininfo.cpp index 2f2b43a429a..ac60b980439 100644 --- a/src/osd/modules/debugger/win/debugwininfo.cpp +++ b/src/osd/modules/debugger/win/debugwininfo.cpp @@ -201,8 +201,10 @@ bool debugwin_info::handle_key(WPARAM wparam, LPARAM lparam) return true; case VK_F11: - if (GetAsyncKeyState(VK_SHIFT) & 0x8000) + if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) && ((GetAsyncKeyState(VK_CONTROL) & 0x8000) == 0)) SendMessage(m_wnd, WM_COMMAND, ID_STEP_OUT, 0); + else if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + SendMessage(m_wnd, WM_COMMAND, ID_REWIND_STEP, 0); else SendMessage(m_wnd, WM_COMMAND, ID_STEP, 0); return true; @@ -325,6 +327,21 @@ bool debugwin_info::handle_command(WPARAM wparam, LPARAM lparam) machine().debugger().cpu().get_visible_cpu()->debug()->single_step_out(); return true; + case ID_REWIND_STEP: + machine().rewind_step(); + + // clear all PC & memory tracks + for (device_t &device : device_iterator(machine().root_device())) + { + device.debug()->track_pc_data_clear(); + device.debug()->track_mem_data_clear(); + } + + // update debugger and emulator window + machine().debug_view().update_all(); + machine().debugger().refresh_display(); + return true; + case ID_HARD_RESET: machine().schedule_hard_reset(); return true; @@ -533,6 +550,7 @@ HMENU debugwin_info::create_standard_menubar() AppendMenu(debugmenu, MF_ENABLED, ID_STEP, TEXT("Step Into\tF11")); AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OVER, TEXT("Step Over\tF10")); AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OUT, TEXT("Step Out\tShift+F11")); + AppendMenu(debugmenu, MF_ENABLED, ID_REWIND_STEP, TEXT("Rewind Step\tCtrl+F11")); AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(debugmenu, MF_ENABLED, ID_SOFT_RESET, TEXT("Soft Reset\tF3")); AppendMenu(debugmenu, MF_ENABLED, ID_HARD_RESET, TEXT("Hard Reset\tShift+F3")); diff --git a/src/osd/modules/debugger/win/debugwininfo.h b/src/osd/modules/debugger/win/debugwininfo.h index c967329447b..0d871f79b6f 100644 --- a/src/osd/modules/debugger/win/debugwininfo.h +++ b/src/osd/modules/debugger/win/debugwininfo.h @@ -70,6 +70,7 @@ protected: ID_STEP, ID_STEP_OVER, ID_STEP_OUT, + ID_REWIND_STEP, ID_HARD_RESET, ID_SOFT_RESET, ID_EXIT, |