Make class for emulator run mode

This commit is contained in:
Ilari Liusvaara 2014-06-09 01:10:29 +03:00
parent 4afda2550e
commit b8b4233255
7 changed files with 464 additions and 180 deletions

View file

@ -28,6 +28,7 @@ class lua_state;
class audioapi_instance;
class loaded_rom;
class save_jukebox;
class emulator_runmode;
namespace command { class group; }
namespace lua { class state; }
namespace settingvar { class group; }
@ -123,6 +124,7 @@ struct emulator_instance
audioapi_instance* audio;
loaded_rom* rom;
save_jukebox* jukebox;
emulator_runmode* runmode;
threads::id emu_thread;
time_t random_seed_value;
dtor_list D;

View file

@ -29,8 +29,6 @@
#define SAVE_STATE 0
#define SAVE_MOVIE 1
extern bool system_corrupt;
std::string resolve_relative_path(const std::string& path);
std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
std::runtime_error);

181
include/core/runmode.hpp Normal file
View file

@ -0,0 +1,181 @@
#ifndef _runmode__hpp__included__
#define _runmode__hpp__included__
#include <cstdint>
class emulator_runmode
{
public:
const static uint64_t QUIT;
const static uint64_t NORMAL;
const static uint64_t LOAD;
const static uint64_t ADVANCE_FRAME;
const static uint64_t ADVANCE_SUBFRAME;
const static uint64_t SKIPLAG;
const static uint64_t SKIPLAG_PENDING;
const static uint64_t PAUSE;
const static uint64_t PAUSE_BREAK;
const static uint64_t CORRUPT;
const static unsigned P_START;
const static unsigned P_VIDEO;
const static unsigned P_SAVE;
const static unsigned P_NONE;
/**
* Ctor.
*/
emulator_runmode();
/**
* Save current mode and set mode to LOAD.
*/
void start_load();
/**
* Restore saved mode.
*/
void end_load();
/**
* Decay SKIPLAG_PENDING to SKIPLAG.
*/
void decay_skiplag();
/**
* Decay PAUSE_BREAK into PAUSE.
*/
void decay_break();
/**
* Is paused?
*/
bool is_paused() { return is(PAUSE | PAUSE_BREAK); }
/**
* Is paused normally?
*/
bool is_paused_normal() { return is(PAUSE); }
/**
* Is paused debug break?
*/
bool is_paused_break() { return is(PAUSE_BREAK); }
/**
* Is advancing frames?
*/
bool is_advance_frame() { return is(ADVANCE_FRAME); }
/**
* Is advancing subframes?
*/
bool is_advance_subframe() { return is(ADVANCE_SUBFRAME); }
/**
* Is advancing (frames or subframes)?
*/
bool is_advance() { return is(ADVANCE_FRAME|ADVANCE_SUBFRAME); }
/**
* Is skipping lag?
*/
bool is_skiplag() { return is(SKIPLAG); }
/**
* Is running free?
*/
bool is_freerunning() { return is(NORMAL); }
/**
* Is special?
*/
bool is_special() { return is(QUIT|LOAD|CORRUPT); }
/**
* Is load?
*/
bool is_load() { return is(LOAD); }
/**
* Is quit?
*/
bool is_quit() { return is(QUIT); }
/**
* Set pause.
*/
void set_pause() { set(PAUSE); }
/**
* Set break.
*/
void set_break() { set(PAUSE_BREAK); }
/**
* Set quit.
*/
void set_quit() { set(QUIT); }
/**
* Set freerunning.
*/
void set_freerunning() { set(NORMAL); }
/**
* Set advance frame.
*
* The advanced and cancel flags are cleared.
*/
void set_frameadvance() { set(ADVANCE_FRAME); }
/**
* Set advance subframe.
*
* The advanced and cancel flags are cleared.
*/
void set_subframeadvance() { set(ADVANCE_SUBFRAME); }
/**
* Set pending skiplag.
*/
void set_skiplag_pending() { set(SKIPLAG_PENDING); }
/**
* Set pause or freerunning.
*/
void set_pause_cond(bool paused) { set(paused ? PAUSE : NORMAL); }
/**
* Set advanced flag and return previous value.
*/
bool set_and_test_advanced();
/**
* Set cancel flag.
*/
void set_cancel();
/**
* Test and clear cancel flag.
*/
bool clear_and_test_cancel();
/**
* Is cancel flag set?
*/
bool test_cancel();
/**
* Is advanced flag set?
*/
bool test_advanced();
/**
* Test corrupt flag.
*/
bool is_corrupt() { return is(CORRUPT); }
/**
* Set corrupt flag.
*/
void set_corrupt() { set(CORRUPT); }
/**
* Clear corrupt flag.
*/
void clear_corrupt() { set(LOAD); }
/**
* Set current point
*/
void set_point(unsigned _point);
/**
* Get current point
*/
unsigned get_point();
private:
void revalidate();
uint64_t get();
void set(uint64_t m);
bool is(uint64_t m);
uint64_t mode;
uint64_t saved_mode;
uint64_t magic; //If mode is QUIT, this has to be QUIT_MAGIC.
//Flags relating to repeating advance.
bool advanced; //This is second or subsequent advance.
bool cancel; //Cancel advance at next oppurtunity.
bool saved_advanced;
bool saved_cancel;
//Current point.
unsigned point;
};
#endif

View file

@ -23,6 +23,7 @@
#include "core/queue.hpp"
#include "core/random.hpp"
#include "core/rom.hpp"
#include "core/runmode.hpp"
#include "core/settings.hpp"
#include "library/command.hpp"
#include "library/keyboard.hpp"
@ -106,6 +107,7 @@ emulator_instance::emulator_instance()
D.init(dbg, *dispatch, *rom);
D.init(framerate);
D.init(mdumper, *lua2);
D.init(runmode);
status_A->valid = false;
status_B->valid = false;

View file

@ -23,6 +23,7 @@
#include "core/queue.hpp"
#include "core/random.hpp"
#include "core/rom.hpp"
#include "core/runmode.hpp"
#include "core/settings.hpp"
#include "core/window.hpp"
#include "interface/callbacks.hpp"
@ -42,12 +43,6 @@
#include <set>
#include <sys/time.h>
#define QUIT_MAGIC 0x5a8c4bef
#define SPECIAL_FRAME_START 0
#define SPECIAL_FRAME_VIDEO 1
#define SPECIAL_SAVEPOINT 2
#define SPECIAL_NONE 3
void update_movie_state();
@ -65,35 +60,12 @@ namespace
settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> SET_pause_on_end(lsnes_setgrp,
"pause-on-end", "Movie‣Pause on end", false);
enum advance_mode
{
ADVANCE_INVALID, //In case someone trashes this.
ADVANCE_QUIT, //Quit the emulator.
ADVANCE_AUTO, //Normal (possibly slowed down play).
ADVANCE_LOAD, //Loading a state.
ADVANCE_FRAME, //Frame advance.
ADVANCE_SUBFRAME, //Subframe advance.
ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
ADVANCE_PAUSE, //Unconditional pause.
ADVANCE_BREAK_PAUSE, //Break pause.
};
//Flags related to repeating advance.
bool advanced_once;
bool cancel_advance;
//Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
enum advance_mode amode;
enum advance_mode old_mode;
//Mode and filename of pending load, one of LOAD_* constants.
bool load_paused;
int loadmode;
std::string pending_load;
std::string pending_new_project;
//Queued saves (all savestates).
std::set<std::pair<std::string, int>> queued_saves;
//Special subframe location. One of SPECIAL_* constants.
int location_special;
//Unsafe rewind.
bool do_unsafe_rewind = false;
void* unsafe_rewind_obj = NULL;
@ -103,32 +75,13 @@ namespace
//Macro hold.
bool macro_hold_1;
bool macro_hold_2;
//Quit magic.
unsigned quit_magic;
bool is_quitting()
{
if(amode == ADVANCE_QUIT && quit_magic == QUIT_MAGIC)
return true;
if(amode == ADVANCE_INVALID || (amode == ADVANCE_QUIT && quit_magic != QUIT_MAGIC) ||
amode > ADVANCE_BREAK_PAUSE) {
//Ouch.
if(lsnes_instance.mlogic)
emerg_save_movie(lsnes_instance.mlogic->get_mfile(),
lsnes_instance.mlogic->get_rrdata());
messages << "WARNING: Emulator runmode undefined, invoked movie dump." << std::endl;
amode = ADVANCE_PAUSE;
}
return false;
}
}
void mainloop_signal_need_rewind(void* ptr)
{
if(ptr) {
old_mode = amode;
amode = ADVANCE_LOAD;
}
auto& core = CORE();
if(ptr)
core.runmode->start_load();
do_unsafe_rewind = true;
unsafe_rewind_obj = ptr;
}
@ -140,72 +93,70 @@ controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_allo
core.fbuf->redraw_framebuffer();
if(subframe) {
if(amode == ADVANCE_SUBFRAME) {
if(!cancel_advance) {
if(!advanced_once)
platform::wait(SET_advance_timeout_first(*core.settings) * 1000);
else
if(core.runmode->is_advance_subframe()) {
//Note that platform::wait() may change value of cancel flag.
if(!core.runmode->test_cancel()) {
if(core.runmode->set_and_test_advanced())
platform::wait(SET_advance_timeout_subframe(*core.settings) * 1000);
advanced_once = true;
else
platform::wait(SET_advance_timeout_first(*core.settings) * 1000);
core.runmode->set_and_test_advanced();
}
if(cancel_advance) {
if(core.runmode->clear_and_test_cancel()) {
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
cancel_advance = false;
core.runmode->set_pause();
}
platform::set_paused(amode == ADVANCE_PAUSE);
} else if(amode == ADVANCE_FRAME) {
platform::set_paused(core.runmode->is_paused());
} else if(core.runmode->is_advance_frame()) {
;
} else {
if(amode == ADVANCE_SKIPLAG) {
if(core.runmode->is_skiplag()) {
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
core.runmode->set_pause();
}
platform::set_paused(amode == ADVANCE_PAUSE);
cancel_advance = false;
core.runmode->clear_and_test_cancel();
}
location_special = SPECIAL_NONE;
platform::set_paused(core.runmode->is_paused());
core.runmode->set_point(emulator_runmode::P_NONE);
update_movie_state();
} else {
if(amode == ADVANCE_SKIPLAG_PENDING)
amode = ADVANCE_SKIPLAG;
if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
if(!cancel_advance) {
core.runmode->decay_skiplag();
if(core.runmode->is_advance()) {
//Note that platform::wait() may change value of cancel flag.
if(!core.runmode->test_cancel()) {
uint64_t wait = 0;
if(!advanced_once)
if(!core.runmode->test_advanced())
wait = SET_advance_timeout_first(*core.settings) * 1000;
else if(amode == ADVANCE_SUBFRAME)
else if(core.runmode->is_advance_subframe())
wait = SET_advance_timeout_subframe(*core.settings) * 1000;
else
wait = core.framerate->to_wait_frame(framerate_regulator::get_utime());
platform::wait(wait);
advanced_once = true;
core.runmode->set_and_test_advanced();
}
if(cancel_advance) {
if(core.runmode->clear_and_test_cancel()) {
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
cancel_advance = false;
core.runmode->set_pause();
}
platform::set_paused(amode == ADVANCE_PAUSE);
} else if(amode == ADVANCE_AUTO && core.mlogic->get_movie().readonly_mode() &&
platform::set_paused(core.runmode->is_paused());
} else if(core.runmode->is_freerunning() && core.mlogic->get_movie().readonly_mode() &&
SET_pause_on_end(*core.settings) && !stop_at_frame_active) {
if(core.mlogic->get_movie().get_current_frame() ==
core.mlogic->get_movie().get_frame_count()) {
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
core.runmode->set_pause();
platform::set_paused(true);
}
} else if(amode == ADVANCE_AUTO && stop_at_frame_active) {
} else if(core.runmode->is_freerunning() && stop_at_frame_active) {
if(core.mlogic->get_movie().get_current_frame() >= stop_at_frame) {
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
core.runmode->set_pause();
platform::set_paused(true);
}
} else {
platform::set_paused((amode == ADVANCE_PAUSE));
cancel_advance = false;
platform::set_paused(core.runmode->is_paused());
}
location_special = SPECIAL_FRAME_START;
core.runmode->set_point(emulator_runmode::P_START);
update_movie_state();
}
platform::flush_command_queue();
@ -224,12 +175,11 @@ namespace
void mark_pending_load(std::string filename, int lmode)
{
//Convert break pause to ordinary pause.
if(amode == ADVANCE_BREAK_PAUSE)
amode = ADVANCE_PAUSE;
auto& core = CORE();
loadmode = lmode;
pending_load = filename;
old_mode = amode;
amode = ADVANCE_LOAD;
core.runmode->decay_break();
core.runmode->start_load();
platform::cancel_wait();
platform::set_paused(false);
}
@ -244,7 +194,7 @@ namespace
core.slotcache->flush(translate_name_mprefix(filename, tmp, -1));
return;
}
if(location_special == SPECIAL_SAVEPOINT) {
if(core.runmode->get_point() == emulator_runmode::P_SAVE) {
//We can save immediately here.
do_save_state(filename, binary);
core.slotcache->flush(translate_name_mprefix(filename, tmp, -1));
@ -271,16 +221,16 @@ void update_movie_state()
}
auto& _status = core.status->get_write();
try {
if(*core.mlogic && !system_corrupt) {
if(*core.mlogic && !core.runmode->is_corrupt()) {
_status.movie_valid = true;
_status.curframe = core.mlogic->get_movie().get_current_frame();
_status.length = core.mlogic->get_movie().get_frame_count();
_status.lag = core.mlogic->get_movie().get_lag_frames();
if(location_special == SPECIAL_FRAME_START)
if(core.runmode->get_point() == emulator_runmode::P_START)
_status.subframe = 0;
else if(location_special == SPECIAL_SAVEPOINT)
else if(core.runmode->get_point() == emulator_runmode::P_SAVE)
_status.subframe = _lsnes_status::subframe_savepoint;
else if(location_special == SPECIAL_FRAME_VIDEO)
else if(core.runmode->get_point() == emulator_runmode::P_VIDEO)
_status.subframe = _lsnes_status::subframe_video;
else
_status.subframe = core.mlogic->get_movie().next_poll_number();
@ -292,16 +242,16 @@ void update_movie_state()
_status.subframe = 0;
}
_status.dumping = (core.mdumper->get_dumper_count() > 0);
if(amode == ADVANCE_BREAK_PAUSE)
if(core.runmode->is_paused_break())
_status.pause = _lsnes_status::pause_break;
else if(amode == ADVANCE_PAUSE)
else if(core.runmode->is_paused_normal())
_status.pause = _lsnes_status::pause_normal;
else
_status.pause = _lsnes_status::pause_none;
if(*core.mlogic) {
auto& mo = core.mlogic->get_movie();
readonly = mo.readonly_mode();
if(system_corrupt)
if(core.runmode->is_corrupt())
_status.mode = 'C';
else if(!readonly)
_status.mode = 'R';
@ -329,7 +279,7 @@ void update_movie_state()
_status.speed = (unsigned)(100 * core.framerate->get_realized_multiplier() + 0.5);
if(*core.mlogic && !system_corrupt) {
if(*core.mlogic && !core.runmode->is_corrupt()) {
time_t timevalue = static_cast<time_t>(core.mlogic->get_mfile().rtc_second);
struct tm* time_decompose = gmtime(&timevalue);
char datebuffer[512];
@ -460,7 +410,7 @@ public:
{
auto& core = CORE();
core.lua2->callback_do_frame_emulated();
location_special = SPECIAL_FRAME_VIDEO;
core.runmode->set_point(emulator_runmode::P_VIDEO);
core.fbuf->redraw_framebuffer(screen, false, true);
auto rate = core.rom->get_audio_rate();
uint32_t gv = gcd(fps_n, fps_d);
@ -532,8 +482,7 @@ namespace
command::fnptr<const std::string&> CMD_quit_emulator(lsnes_cmds, "quit-emulator", "Quit the emulator",
"Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
amode = ADVANCE_QUIT;
quit_magic = QUIT_MAGIC;
CORE().runmode->set_quit();
platform::set_paused(false);
platform::cancel_wait();
});
@ -541,7 +490,10 @@ namespace
command::fnptr<> CMD_unpause_emulator(lsnes_cmds, "unpause-emulator", "Unpause the emulator",
"Syntax: unpause-emulator\nUnpauses the emulator.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
amode = ADVANCE_AUTO;
auto& core = CORE();
if(core.runmode->is_special())
return;
core.runmode->set_freerunning();
platform::set_paused(false);
platform::cancel_wait();
});
@ -549,15 +501,17 @@ namespace
command::fnptr<> CMD_pause_emulator(lsnes_cmds, "pause-emulator", "(Un)pause the emulator",
"Syntax: pause-emulator\n(Un)pauses the emulator.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(amode != ADVANCE_AUTO) {
amode = ADVANCE_AUTO;
auto& core = CORE();
if(core.runmode->is_special())
;
else if(core.runmode->is_freerunning()) {
platform::cancel_wait();
stop_at_frame_active = false;
core.runmode->set_pause();
} else {
core.runmode->set_freerunning();
platform::set_paused(false);
platform::cancel_wait();
} else {
platform::cancel_wait();
cancel_advance = false;
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
}
});
@ -630,9 +584,10 @@ namespace
command::fnptr<> CMD_padvance_frame(lsnes_cmds, "+advance-frame", "Advance one frame",
"Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
amode = ADVANCE_FRAME;
cancel_advance = false;
advanced_once = false;
auto& core = CORE();
if(core.runmode->is_special())
return;
core.runmode->set_frameadvance();
platform::cancel_wait();
platform::set_paused(false);
});
@ -640,7 +595,7 @@ namespace
command::fnptr<> CMD_nadvance_frame(lsnes_cmds, "-advance-frame", "Advance one frame",
"No help available\n",
[]() throw(std::bad_alloc, std::runtime_error) {
cancel_advance = true;
CORE().runmode->set_cancel();
platform::cancel_wait();
platform::set_paused(false);
});
@ -648,9 +603,10 @@ namespace
command::fnptr<> CMD_padvance_poll(lsnes_cmds, "+advance-poll", "Advance one subframe",
"Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
amode = ADVANCE_SUBFRAME;
cancel_advance = false;
advanced_once = false;
auto& core = CORE();
if(core.runmode->is_special())
return;
core.runmode->set_subframeadvance();
platform::cancel_wait();
platform::set_paused(false);
});
@ -658,9 +614,9 @@ namespace
command::fnptr<> CMD_nadvance_poll(lsnes_cmds, "-advance-poll", "Advance one subframe",
"No help available\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(amode == ADVANCE_BREAK_PAUSE)
amode = ADVANCE_PAUSE;
cancel_advance = true;
auto& core = CORE();
core.runmode->decay_break();
core.runmode->set_cancel();
platform::cancel_wait();
platform::set_paused(false);
});
@ -668,7 +624,7 @@ namespace
command::fnptr<> CMD_advance_skiplag(lsnes_cmds, "advance-skiplag", "Skip to next poll",
"Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
amode = ADVANCE_SKIPLAG_PENDING;
CORE().runmode->set_skiplag_pending();
platform::cancel_wait();
platform::set_paused(false);
});
@ -1003,12 +959,11 @@ namespace
class mywindowcallbacks : public master_dumper::notifier
{
public:
mywindowcallbacks(emulator_dispatch& dispatch)
mywindowcallbacks(emulator_dispatch& dispatch, emulator_runmode& runmode)
{
closenotify.set(dispatch.close, [this]() {
closenotify.set(dispatch.close, [this, &runmode]() {
try {
amode = ADVANCE_QUIT;
quit_magic = QUIT_MAGIC;
runmode.set_quit();
platform::set_paused(false);
platform::cancel_wait();
} catch(...) {
@ -1040,7 +995,7 @@ jumpback:
core.dispatch->mode_change(false);
do_unsafe_rewind = false;
core.mlogic->get_mfile().is_savestate = true;
location_special = SPECIAL_SAVEPOINT;
core.runmode->set_point(emulator_runmode::P_SAVE);
update_movie_state();
messages << "Rewind done in " << (framerate_regulator::get_utime() - t) << " usec."
<< std::endl;
@ -1065,21 +1020,18 @@ jumpback:
goto nothing_to_do;
}
nothing_to_do:
amode = old_mode;
platform::set_paused(amode == ADVANCE_PAUSE);
core.runmode->end_load();
platform::set_paused(core.runmode->is_paused());
platform::flush_command_queue();
if(amode == ADVANCE_LOAD)
if(core.runmode->is_load())
goto jumpback;
return 0;
}
if(pending_load != "") {
bool system_was_corrupt = system_corrupt;
system_corrupt = false;
try {
if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
!do_load_state(pending_load, loadmode)) {
if(system_was_corrupt)
system_corrupt = system_was_corrupt;
core.runmode->end_load();
pending_load = "";
return -1;
}
@ -1087,23 +1039,21 @@ nothing_to_do:
do_load_rewind();
if(loadmode == LOAD_STATE_ROMRELOAD)
do_load_rom();
core.runmode->clear_corrupt();
} catch(std::exception& e) {
if(!system_corrupt && system_was_corrupt)
system_corrupt = true;
core.runmode->set_corrupt();
platform::error_message(std::string("Load failed: ") + e.what());
messages << "Load failed: " << e.what() << std::endl;
}
pending_load = "";
amode = load_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
platform::set_paused(load_paused);
load_paused = false;
if(!system_corrupt) {
location_special = SPECIAL_SAVEPOINT;
if(!core.runmode->is_corrupt()) {
core.runmode->end_load();
core.runmode->set_point(emulator_runmode::P_SAVE);
update_movie_state();
platform::flush_command_queue();
if(is_quitting())
if(core.runmode->is_quit())
return -1;
if(amode == ADVANCE_LOAD)
if(core.runmode->is_load())
goto jumpback;
}
if(old_project != (*core.mlogic ? core.mlogic->get_mfile().projectid : ""))
@ -1143,13 +1093,14 @@ nothing_to_do:
bool handle_corrupt()
{
if(!system_corrupt)
auto& core = CORE();
if(!core.runmode->is_corrupt())
return false;
while(system_corrupt) {
while(core.runmode->is_corrupt()) {
platform::set_paused(true);
platform::flush_command_queue();
handle_load();
if(is_quitting())
if(core.runmode->is_quit())
return true;
}
return true;
@ -1166,7 +1117,7 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
{
lsnes_instance.emu_thread = threads::id();
auto& core = CORE();
mywindowcallbacks mywcb(*core.dispatch);
mywindowcallbacks mywcb(*core.dispatch, *core.runmode);
core.iqueue->system_thread_available = true;
//Basic initialization.
core.commentary->init();
@ -1182,7 +1133,7 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
bool used = false;
try {
do_load_state(initial, LOAD_STATE_INITIAL, used);
location_special = SPECIAL_SAVEPOINT;
core.runmode->set_point(emulator_runmode::P_SAVE);
update_movie_state();
first_round = core.mlogic->get_mfile().is_savestate;
just_did_loadstate = first_round;
@ -1197,25 +1148,24 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
messages << "FATAL: Can't load movie" << std::endl;
throw;
}
system_corrupt = true;
core.runmode->set_corrupt();
core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt);
}
platform::set_paused(initial.start_paused);
amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
core.runmode->set_pause_cond(initial.start_paused);
platform::set_paused(core.runmode->is_paused());
stop_at_frame_active = false;
core.lua2->run_startup_scripts();
while(!is_quitting() || !queued_saves.empty()) {
while(!core.runmode->is_quit() || !queued_saves.empty()) {
if(handle_corrupt()) {
first_round = *core.mlogic && core.mlogic->get_mfile().is_savestate;
just_did_loadstate = first_round;
continue;
}
core.framerate->ack_frame_tick(framerate_regulator::get_utime());
if(amode == ADVANCE_SKIPLAG_PENDING)
amode = ADVANCE_SKIPLAG;
core.runmode->decay_skiplag();
if(!first_round) {
core.controls->reset_framehold();
@ -1224,22 +1174,18 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
}
macro_hold_2 = false;
core.mlogic->get_movie().get_pollcounters().set_framepflag(false);
core.mlogic->new_frame_starting(amode == ADVANCE_SKIPLAG);
core.mlogic->new_frame_starting(core.runmode->is_skiplag());
core.mlogic->get_movie().get_pollcounters().set_framepflag(true);
if(is_quitting() && queued_saves.empty())
if(core.runmode->is_quit() && queued_saves.empty())
break;
handle_saves();
int r = 0;
if(queued_saves.empty())
r = handle_load();
if(r > 0 || system_corrupt) {
if(r > 0 || core.runmode->is_corrupt()) {
core.mlogic->get_movie().get_pollcounters().set_framepflag(
core.mlogic->get_mfile().is_savestate);
first_round = core.mlogic->get_mfile().is_savestate;
if(system_corrupt)
amode = ADVANCE_PAUSE;
else
amode = old_mode;
stop_at_frame_active = false;
just_did_loadstate = first_round;
core.controls->reset_framehold();
@ -1248,16 +1194,16 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
} else if(r < 0) {
//Not exactly desriable, but this at least won't desync.
stop_at_frame_active = false;
if(is_quitting())
if(core.runmode->is_quit())
goto out;
amode = ADVANCE_PAUSE;
core.runmode->set_pause();
}
}
if(just_did_loadstate) {
//If we just loadstated, we are up to date.
if(is_quitting())
if(core.runmode->is_quit())
break;
platform::set_paused(amode == ADVANCE_PAUSE);
platform::set_paused(core.runmode->is_paused());
platform::flush_command_queue();
//We already have done the reset this frame if we are going to do one at all.
core.mlogic->get_movie().set_controls(core.mlogic->update_controls(true));
@ -1267,7 +1213,7 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
core.dbg->do_callback_frame(core.mlogic->get_movie().get_current_frame(), false);
core.rom->emulate();
random_mix_timing_entropy();
if(amode == ADVANCE_AUTO)
if(core.runmode->is_freerunning())
platform::wait(core.framerate->to_wait_frame(framerate_regulator::get_utime()));
first_round = false;
core.lua2->callback_do_frame();
@ -1285,9 +1231,11 @@ out:
void set_stop_at_frame(uint64_t frame)
{
auto& core = CORE();
stop_at_frame = frame;
stop_at_frame_active = (frame != 0);
amode = ADVANCE_AUTO;
if(!core.runmode->is_special())
core.runmode->set_freerunning();
platform::set_paused(false);
}
@ -1299,8 +1247,7 @@ void do_flush_slotinfo()
void switch_projects(const std::string& newproj)
{
pending_new_project = newproj;
amode = ADVANCE_LOAD;
old_mode = ADVANCE_PAUSE;
CORE().runmode->start_load();
platform::cancel_wait();
platform::set_paused(false);
}
@ -1322,16 +1269,17 @@ void reload_current_rom()
void close_rom()
{
if(load_null_rom()) {
load_paused = true;
CORE().runmode->set_pause();
mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
}
}
void do_break_pause()
{
amode = ADVANCE_BREAK_PAUSE;
auto& core = CORE();
core.runmode->set_break();
update_movie_state();
while(amode == ADVANCE_BREAK_PAUSE) {
while(core.runmode->is_paused_break()) {
platform::set_paused(true);
platform::flush_command_queue();
}
@ -1339,8 +1287,9 @@ void do_break_pause()
void convert_break_to_pause()
{
if(amode == ADVANCE_BREAK_PAUSE) {
amode = ADVANCE_PAUSE;
auto& core = CORE();
if(core.runmode->is_paused_break()) {
core.runmode->set_pause();
update_movie_state();
}
}

View file

@ -13,6 +13,7 @@
#include "core/project.hpp"
#include "core/random.hpp"
#include "core/rom.hpp"
#include "core/runmode.hpp"
#include "core/settings.hpp"
#include "interface/romtype.hpp"
#include "library/directory.hpp"
@ -24,7 +25,6 @@
#include <iomanip>
#include <fstream>
bool system_corrupt;
std::string last_save;
void update_movie_state();
@ -476,7 +476,7 @@ void do_load_rom() throw(std::bad_alloc, std::runtime_error)
} catch(std::bad_alloc& e) {
OOM_panic();
} catch(std::exception& e) {
system_corrupt = true;
core.runmode->set_corrupt();
core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
throw;
}
@ -528,7 +528,7 @@ void do_load_rom() throw(std::bad_alloc, std::runtime_error)
} catch(std::bad_alloc& e) {
OOM_panic();
} catch(std::exception& e) {
system_corrupt = true;
core.runmode->set_corrupt();
core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
throw;
}
@ -572,7 +572,7 @@ void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
} catch(std::bad_alloc& e) {
OOM_panic();
} catch(std::exception& e) {
system_corrupt = true;
core.runmode->set_corrupt();
core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
throw;
}
@ -613,7 +613,7 @@ void do_load_state_preserve(struct moviefile& _movie)
} catch(std::bad_alloc& e) {
OOM_panic();
} catch(std::exception& e) {
system_corrupt = true;
core.runmode->set_corrupt();
core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
throw;
}
@ -741,7 +741,7 @@ void do_load_state(struct moviefile& _movie, int lmode, bool& used)
} catch(std::bad_alloc& e) {
OOM_panic();
} catch(std::exception& e) {
system_corrupt = true;
core.runmode->set_corrupt();
core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
throw;
}

152
src/core/runmode.cpp Normal file
View file

@ -0,0 +1,152 @@
#include "core/instance.hpp"
#include "core/messages.hpp"
#include "core/movie.hpp"
#include "core/runmode.hpp"
#include <stdexcept>
namespace
{
const uint64_t QUIT_MAGIC = 0x87dd4df349e5eff7ULL;
}
const uint64_t emulator_runmode::QUIT = 1;
const uint64_t emulator_runmode::NORMAL = 2;
const uint64_t emulator_runmode::LOAD = 4;
const uint64_t emulator_runmode::ADVANCE_FRAME = 8;
const uint64_t emulator_runmode::ADVANCE_SUBFRAME = 16;
const uint64_t emulator_runmode::SKIPLAG = 32;
const uint64_t emulator_runmode::SKIPLAG_PENDING = 64;
const uint64_t emulator_runmode::PAUSE = 128;
const uint64_t emulator_runmode::PAUSE_BREAK = 256;
const uint64_t emulator_runmode::CORRUPT = 512;
const unsigned emulator_runmode::P_START = 0;
const unsigned emulator_runmode::P_VIDEO = 1;
const unsigned emulator_runmode::P_SAVE = 2;
const unsigned emulator_runmode::P_NONE = 3;
emulator_runmode::emulator_runmode()
{
mode = PAUSE;
saved_mode = PAUSE;
magic = 0;
advanced = false;
cancel = false;
}
uint64_t emulator_runmode::get()
{
revalidate();
return mode;
}
void emulator_runmode::set(uint64_t m)
{
if(!m || m & (m - 1) || m > CORRUPT)
throw std::logic_error("Trying to set invalid runmode");
if(m == QUIT) {
magic = QUIT_MAGIC;
mode = QUIT;
} else
mode = m;
//If setting state, clear the variables to be at initial state.
advanced = false;
cancel = false;
}
bool emulator_runmode::is(uint64_t m)
{
revalidate();
return ((mode & m) != 0);
}
void emulator_runmode::start_load()
{
if(mode != CORRUPT) {
saved_mode = mode;
mode = LOAD;
saved_advanced = advanced;
saved_cancel = cancel;
advanced = false;
cancel = false;
}
}
void emulator_runmode::end_load()
{
if(mode != CORRUPT) {
mode = saved_mode;
advanced = saved_advanced;
cancel = saved_cancel;
saved_mode = 128;
}
}
void emulator_runmode::decay_skiplag()
{
revalidate();
if(mode == SKIPLAG_PENDING) {
mode = SKIPLAG;
}
}
void emulator_runmode::decay_break()
{
revalidate();
if(mode == PAUSE_BREAK) {
mode = PAUSE;
}
}
void emulator_runmode::revalidate()
{
if(!mode || mode & (mode - 1) || (mode == QUIT && magic != QUIT_MAGIC) || mode > CORRUPT) {
//Uh, oh.
auto& core = CORE();
if(core.mlogic)
emerg_save_movie(core.mlogic->get_mfile(), core.mlogic->get_rrdata());
messages << "WARNING: Emulator runmode undefined, invoked movie dump." << std::endl;
mode = PAUSE;
}
}
bool emulator_runmode::set_and_test_advanced()
{
bool x = advanced;
advanced = true;
return x;
}
void emulator_runmode::set_cancel()
{
cancel = true;
if(mode == LOAD)
saved_cancel = true;
}
bool emulator_runmode::clear_and_test_cancel()
{
bool x = cancel;
cancel = false;
return x;
}
bool emulator_runmode::test_cancel()
{
return cancel;
}
bool emulator_runmode::test_advanced()
{
return advanced;
}
void emulator_runmode::set_point(unsigned _point)
{
point = _point;
}
unsigned emulator_runmode::get_point()
{
return point;
}