summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emu/debug/debugcmd.cpp25
-rw-r--r--src/emu/debug/debughlp.cpp2
-rw-r--r--src/emu/diexec.cpp3
-rw-r--r--src/emu/emuopts.cpp2
-rw-r--r--src/emu/inpttype.h2
-rw-r--r--src/emu/machine.cpp16
-rw-r--r--src/emu/machine.h4
-rw-r--r--src/emu/save.cpp410
-rw-r--r--src/emu/save.h41
-rw-r--r--src/osd/modules/debugger/win/debugwininfo.cpp20
-rw-r--r--src/osd/modules/debugger/win/debugwininfo.h1
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> &params)
{
- 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,