Don't try to enter loadstate with loadstate already in progress
[lsnes.git] / src / core / mainloop.cpp
blob5cc04f30e36090a6bb2a71cb5f63c0bb003d6bf4
1 #include "lsnes.hpp"
3 #include "cmdhelp/loadsave.hpp"
4 #include "cmdhelp/mhold.hpp"
5 #include "core/advdumper.hpp"
6 #include "core/command.hpp"
7 #include "core/controller.hpp"
8 #include "core/debug.hpp"
9 #include "core/dispatch.hpp"
10 #include "core/emustatus.hpp"
11 #include "core/framebuffer.hpp"
12 #include "core/framerate.hpp"
13 #include "core/instance.hpp"
14 #include "core/inthread.hpp"
15 #include "core/jukebox.hpp"
16 #include "core/keymapper.hpp"
17 #include "core/mainloop.hpp"
18 #include "core/memorymanip.hpp"
19 #include "core/memorywatch.hpp"
20 #include "core/messages.hpp"
21 #include "core/moviedata.hpp"
22 #include "core/moviefile.hpp"
23 #include "core/multitrack.hpp"
24 #include "core/project.hpp"
25 #include "core/queue.hpp"
26 #include "core/random.hpp"
27 #include "core/rom.hpp"
28 #include "core/runmode.hpp"
29 #include "core/settings.hpp"
30 #include "core/window.hpp"
31 #include "interface/callbacks.hpp"
32 #include "interface/c-interface.hpp"
33 #include "interface/romtype.hpp"
34 #include "library/framebuffer.hpp"
35 #include "library/settingvar.hpp"
36 #include "library/string.hpp"
37 #include "library/zip.hpp"
38 #include "lua/lua.hpp"
40 #include <iomanip>
41 #include <cassert>
42 #include <sstream>
43 #include <iostream>
44 #include <limits>
45 #include <set>
46 #include <sys/time.h>
49 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary(lsnes_setgrp,
50 "movie-default-binary", "Movie‣Saving‣Movies binary", false);
51 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> save_dflt_binary(lsnes_setgrp,
52 "savestate-default-binary", "Movie‣Saving‣Savestates binary", false);
54 namespace
56 settingvar::supervariable<settingvar::model_int<0,999999>> SET_advance_timeout_first(lsnes_setgrp,
57 "advance-timeout", "Delays‣First frame advance", 500);
58 settingvar::supervariable<settingvar::model_int<0,999999>> SET_advance_timeout_subframe(lsnes_setgrp,
59 "advance-subframe-timeout", "Delays‣Subframe advance", 100);
60 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> SET_pause_on_end(lsnes_setgrp,
61 "pause-on-end", "Movie‣Pause on end", false);
63 //Mode and filename of pending load, one of LOAD_* constants.
64 int loadmode;
65 std::string pending_load;
66 std::string pending_new_project;
67 //Queued saves (all savestates).
68 std::set<std::pair<std::string, int>> queued_saves;
69 //Unsafe rewind.
70 bool do_unsafe_rewind = false;
71 void* unsafe_rewind_obj = NULL;
72 //Stop at frame.
73 bool stop_at_frame_active = false;
74 uint64_t stop_at_frame = 0;
75 //Macro hold.
76 bool macro_hold_1;
77 bool macro_hold_2;
80 void mainloop_signal_need_rewind(void* ptr)
82 auto& core = CORE();
83 if(ptr)
84 core.runmode->start_load();
85 do_unsafe_rewind = true;
86 unsafe_rewind_obj = ptr;
89 bool movie_logic::notify_user_poll() throw(std::bad_alloc, std::runtime_error)
91 return CORE().runmode->is_skiplag();
94 portctrl::frame movie_logic::update_controls(bool subframe, bool forced) throw(std::bad_alloc, std::runtime_error)
96 auto& core = CORE();
97 if(core.lua2->requests_subframe_paint)
98 core.fbuf->redraw_framebuffer();
100 if(subframe) {
101 if(core.runmode->is_advance_subframe()) {
102 //Note that platform::wait() may change value of cancel flag.
103 if(!core.runmode->test_cancel()) {
104 if(core.runmode->set_and_test_advanced())
105 platform::wait(SET_advance_timeout_subframe(*core.settings) * 1000);
106 else
107 platform::wait(SET_advance_timeout_first(*core.settings) * 1000);
108 core.runmode->set_and_test_advanced();
110 if(core.runmode->clear_and_test_cancel()) {
111 stop_at_frame_active = false;
112 core.runmode->set_pause();
114 platform::set_paused(core.runmode->is_paused());
115 } else if(core.runmode->is_advance_frame()) {
117 } else {
118 if(core.runmode->is_skiplag() && forced) {
119 stop_at_frame_active = false;
120 core.runmode->set_pause();
122 core.runmode->clear_and_test_cancel();
124 platform::set_paused(core.runmode->is_paused());
125 core.runmode->set_point(emulator_runmode::P_NONE);
126 core.supdater->update();
127 } else {
128 core.runmode->decay_skiplag();
129 if(core.runmode->is_advance()) {
130 //Note that platform::wait() may change value of cancel flag.
131 if(!core.runmode->test_cancel()) {
132 uint64_t wait = 0;
133 if(!core.runmode->test_advanced())
134 wait = SET_advance_timeout_first(*core.settings) * 1000;
135 else if(core.runmode->is_advance_subframe())
136 wait = SET_advance_timeout_subframe(*core.settings) * 1000;
137 else
138 wait = core.framerate->to_wait_frame(framerate_regulator::get_utime());
139 platform::wait(wait);
140 core.runmode->set_and_test_advanced();
142 if(core.runmode->clear_and_test_cancel()) {
143 stop_at_frame_active = false;
144 core.runmode->set_pause();
146 platform::set_paused(core.runmode->is_paused());
147 } else if(core.runmode->is_freerunning() && core.mlogic->get_movie().readonly_mode() &&
148 SET_pause_on_end(*core.settings) && !stop_at_frame_active) {
149 if(core.mlogic->get_movie().get_current_frame() ==
150 core.mlogic->get_movie().get_frame_count()) {
151 stop_at_frame_active = false;
152 core.runmode->set_pause();
153 platform::set_paused(true);
155 } else if(core.runmode->is_freerunning() && stop_at_frame_active) {
156 if(core.mlogic->get_movie().get_current_frame() >= stop_at_frame) {
157 stop_at_frame_active = false;
158 core.runmode->set_pause();
159 platform::set_paused(true);
161 } else {
162 platform::set_paused(core.runmode->is_paused());
164 core.runmode->set_point(emulator_runmode::P_START);
165 core.supdater->update();
167 platform::flush_command_queue();
168 portctrl::frame tmp = core.controls->get(core.mlogic->get_movie().get_current_frame());
169 core.rom->pre_emulate_frame(tmp); //Preset controls, the lua will override if needed.
170 core.lua2->callback_do_input(tmp, subframe);
171 core.mteditor->process_frame(tmp);
172 core.controls->commit(tmp);
173 return tmp;
176 namespace
179 //Do pending load (automatically unpauses).
180 void mark_pending_load(std::string filename, int lmode)
182 auto& core = CORE();
183 //Ignore requests to loadstate when loadstate is already in progress. Should fix that "gets stuck
184 //on high speed" bug.
185 if(core.runmode->get() == emulator_runmode::LOAD) return;
186 //Convert break pause to ordinary pause.
187 loadmode = lmode;
188 pending_load = filename;
189 core.runmode->decay_break();
190 core.runmode->start_load();
191 platform::cancel_wait();
192 platform::set_paused(false);
195 void mark_pending_save(std::string filename, int smode, int binary)
197 auto& core = CORE();
198 int tmp = -1;
199 if(smode == SAVE_MOVIE) {
200 //Just do this immediately.
201 do_save_movie(filename, binary);
202 core.slotcache->flush(translate_name_mprefix(filename, tmp, -1));
203 return;
205 if(core.runmode->get_point() == emulator_runmode::P_SAVE) {
206 //We can save immediately here.
207 do_save_state(filename, binary);
208 core.slotcache->flush(translate_name_mprefix(filename, tmp, -1));
209 return;
211 queued_saves.insert(std::make_pair(filename, binary));
212 messages << "Pending save on '" << filename << "'" << std::endl;
217 struct lsnes_callbacks : public emucore_callbacks
219 public:
220 ~lsnes_callbacks() throw()
224 int16_t get_input(unsigned port, unsigned index, unsigned control)
226 auto& core = CORE();
227 int16_t x;
228 x = core.mlogic->input_poll(port, index, control);
229 core.lua2->callback_snoop_input(port, index, control, x);
230 return x;
233 int16_t set_input(unsigned port, unsigned index, unsigned control, int16_t value)
235 auto& core = CORE();
236 if(!core.mlogic->get_movie().readonly_mode()) {
237 portctrl::frame f = core.mlogic->get_movie().get_controls();
238 f.axis3(port, index, control, value);
239 core.mlogic->get_movie().set_controls(f);
241 return core.mlogic->get_movie().next_input(port, index, control);
244 void notify_latch(std::list<std::string>& args)
246 CORE().lua2->callback_do_latch(args);
249 void timer_tick(uint32_t increment, uint32_t per_second)
251 auto& core = CORE();
252 if(!*core.mlogic)
253 return;
254 auto& m = core.mlogic->get_mfile();
255 m.dyn.rtc_subsecond += increment;
256 while(m.dyn.rtc_subsecond >= per_second) {
257 m.dyn.rtc_second++;
258 m.dyn.rtc_subsecond -= per_second;
262 std::string get_firmware_path()
264 return SET_firmwarepath(*CORE().settings);
267 std::string get_base_path()
269 return CORE().rom->get_msu1_base();
272 time_t get_time()
274 auto& core = CORE();
275 return *core.mlogic ? core.mlogic->get_mfile().dyn.rtc_second : 0;
278 time_t get_randomseed()
280 return CORE().random_seed_value;
283 void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d)
285 auto& core = CORE();
286 core.lua2->callback_do_frame_emulated();
287 core.runmode->set_point(emulator_runmode::P_VIDEO);
288 core.fbuf->redraw_framebuffer(screen, false, true);
289 auto rate = core.rom->get_audio_rate();
290 uint32_t gv = gcd(fps_n, fps_d);
291 uint32_t ga = gcd(rate.first, rate.second);
292 core.mdumper->on_rate_change(rate.first / ga, rate.second / ga);
293 core.mdumper->on_frame(screen, fps_n / gv, fps_d / gv);
296 void action_state_updated()
298 CORE().dispatch->action_update();
301 void memory_read(uint64_t addr, uint64_t value)
303 CORE().dbg->do_callback_read(addr, value);
306 void memory_write(uint64_t addr, uint64_t value)
308 CORE().dbg->do_callback_write(addr, value);
311 void memory_execute(uint64_t addr, uint64_t proc)
313 CORE().dbg->do_callback_exec(addr, proc);
316 void memory_trace(uint64_t proc, const char* str, bool insn)
318 CORE().dbg->do_callback_trace(proc, str, insn);
322 namespace
324 lsnes_callbacks lsnes_callbacks_obj;
325 command::fnptr<> CMD_segfault(lsnes_cmds, "segfault", "Trigger SIGSEGV",
326 "segfault\nTrigger segmentation fault",
327 []() throw(std::bad_alloc, std::runtime_error) {
328 char* ptr = (char*)0x1234;
329 *ptr = 0;
332 command::fnptr<> CMD_div0(lsnes_cmds, "divide-by-0", "Do div0", "divide-by-0\nDo divide by 0",
333 []() throw(std::bad_alloc, std::runtime_error) {
334 static int ptr = 1;
335 static int ptr2 = 0;
336 ptr = ptr / ptr2;
339 command::fnptr<const std::string&> CMD_test4(lsnes_cmds, "test4", "test", "test",
340 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
341 auto& core = CORE();
342 std::list<std::string> _args;
343 std::string args2 = args;
344 for(auto& sym : token_iterator<char>::foreach(args, {" ", "\t"}))
345 _args.push_back(sym);
346 core.lua2->callback_do_latch(_args);
348 command::fnptr<> CMD_count_rerecords(lsnes_cmds, "count-rerecords", "Count rerecords",
349 "Syntax: count-rerecords\nCounts rerecords.\n",
350 []() throw(std::bad_alloc, std::runtime_error) {
351 std::vector<char> tmp;
352 uint64_t x = CORE().mlogic->get_rrdata().write(tmp);
353 messages << x << " rerecord(s)" << std::endl;
356 command::fnptr<const std::string&> CMD_quit_emulator(lsnes_cmds, "quit-emulator", "Quit the emulator",
357 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
358 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
359 CORE().runmode->set_quit();
360 platform::set_paused(false);
361 platform::cancel_wait();
364 command::fnptr<> CMD_unpause_emulator(lsnes_cmds, "unpause-emulator", "Unpause the emulator",
365 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
366 []() throw(std::bad_alloc, std::runtime_error) {
367 auto& core = CORE();
368 if(core.runmode->is_special())
369 return;
370 core.runmode->set_freerunning();
371 platform::set_paused(false);
372 platform::cancel_wait();
375 command::fnptr<> CMD_pause_emulator(lsnes_cmds, "pause-emulator", "(Un)pause the emulator",
376 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
377 []() throw(std::bad_alloc, std::runtime_error) {
378 auto& core = CORE();
379 if(core.runmode->is_special())
381 else if(core.runmode->is_freerunning()) {
382 platform::cancel_wait();
383 stop_at_frame_active = false;
384 core.runmode->set_pause();
385 } else {
386 core.runmode->set_freerunning();
387 platform::set_paused(false);
388 platform::cancel_wait();
392 command::fnptr<> CMD_load_jukebox(lsnes_cmds, CLOADSAVE::ldj,
393 []() throw(std::bad_alloc, std::runtime_error) {
394 auto& core = CORE();
395 mark_pending_load(core.jukebox->get_slot_name(), LOAD_STATE_CURRENT);
398 command::fnptr<> CMD_load_jukebox_readwrite(lsnes_cmds, CLOADSAVE::ldjrw,
399 []() throw(std::bad_alloc, std::runtime_error) {
400 auto& core = CORE();
401 mark_pending_load(core.jukebox->get_slot_name(), LOAD_STATE_RW);
404 command::fnptr<> CMD_load_jukebox_readonly(lsnes_cmds, CLOADSAVE::ldjro,
405 []() throw(std::bad_alloc, std::runtime_error) {
406 auto& core = CORE();
407 mark_pending_load(core.jukebox->get_slot_name(), LOAD_STATE_RO);
410 command::fnptr<> CMD_load_jukebox_preserve(lsnes_cmds, CLOADSAVE::ldjp,
411 []() throw(std::bad_alloc, std::runtime_error) {
412 auto& core = CORE();
413 mark_pending_load(core.jukebox->get_slot_name(), LOAD_STATE_PRESERVE);
416 command::fnptr<> CMD_load_jukebox_movie(lsnes_cmds, CLOADSAVE::ldjm,
417 []() throw(std::bad_alloc, std::runtime_error) {
418 auto& core = CORE();
419 mark_pending_load(core.jukebox->get_slot_name(), LOAD_STATE_MOVIE);
422 command::fnptr<> CMD_save_jukebox_c(lsnes_cmds, CLOADSAVE::saj,
423 []() throw(std::bad_alloc, std::runtime_error) {
424 auto& core = CORE();
425 mark_pending_save(core.jukebox->get_slot_name(), SAVE_STATE, -1);
428 command::fnptr<> CMD_padvance_frame(lsnes_cmds, "+advance-frame", "Advance one frame",
429 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
430 []() throw(std::bad_alloc, std::runtime_error) {
431 auto& core = CORE();
432 if(core.runmode->is_special())
433 return;
434 core.runmode->set_frameadvance();
435 platform::cancel_wait();
436 platform::set_paused(false);
439 command::fnptr<> CMD_nadvance_frame(lsnes_cmds, "-advance-frame", "Advance one frame",
440 "No help available\n",
441 []() throw(std::bad_alloc, std::runtime_error) {
442 CORE().runmode->set_cancel();
443 platform::cancel_wait();
444 platform::set_paused(false);
447 command::fnptr<> CMD_padvance_poll(lsnes_cmds, "+advance-poll", "Advance one subframe",
448 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
449 []() throw(std::bad_alloc, std::runtime_error) {
450 auto& core = CORE();
451 if(core.runmode->is_special())
452 return;
453 core.runmode->set_subframeadvance();
454 platform::cancel_wait();
455 platform::set_paused(false);
458 command::fnptr<> CMD_nadvance_poll(lsnes_cmds, "-advance-poll", "Advance one subframe",
459 "No help available\n",
460 []() throw(std::bad_alloc, std::runtime_error) {
461 auto& core = CORE();
462 core.runmode->decay_break();
463 core.runmode->set_cancel();
464 platform::cancel_wait();
465 platform::set_paused(false);
468 command::fnptr<> CMD_advance_skiplag(lsnes_cmds, "advance-skiplag", "Skip to next poll",
469 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
470 []() throw(std::bad_alloc, std::runtime_error) {
471 CORE().runmode->set_skiplag_pending();
472 platform::cancel_wait();
473 platform::set_paused(false);
476 command::fnptr<> CMD_reset_c(lsnes_cmds, "reset", "Reset the system",
477 "Syntax: reset\nReset\nResets the system in beginning of the next frame.\n",
478 []() throw(std::bad_alloc, std::runtime_error) {
479 auto& core = CORE();
480 int sreset_action = core.rom->reset_action(false);
481 if(sreset_action < 0) {
482 platform::error_message("Core does not support resets");
483 messages << "Emulator core does not support resets" << std::endl;
484 return;
486 core.rom->execute_action(sreset_action, std::vector<interface_action_paramval>());
489 command::fnptr<> CMD_hreset_c(lsnes_cmds, "reset-hard", "Reset the system",
490 "Syntax: reset-hard\nReset-hard\nHard resets the system in beginning of the next frame.\n",
491 []() throw(std::bad_alloc, std::runtime_error) {
492 auto& core = CORE();
493 int hreset_action = core.rom->reset_action(true);
494 if(hreset_action < 0) {
495 platform::error_message("Core does not support hard resets");
496 messages << "Emulator core does not support hard resets" << std::endl;
497 return;
499 core.rom->execute_action(hreset_action, std::vector<interface_action_paramval>());
502 command::fnptr<command::arg_filename> CMD_load_c(lsnes_cmds, CLOADSAVE::ld,
503 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
504 mark_pending_load(args, LOAD_STATE_CURRENT);
507 command::fnptr<command::arg_filename> CMD_load_smart_c(lsnes_cmds, CLOADSAVE::ldsm,
508 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
509 mark_pending_load(args, LOAD_STATE_DEFAULT);
512 command::fnptr<command::arg_filename> CMD_load_state_c(lsnes_cmds, CLOADSAVE::ldrw,
513 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
514 mark_pending_load(args, LOAD_STATE_RW);
517 command::fnptr<command::arg_filename> CMD_load_readonly(lsnes_cmds, CLOADSAVE::ldro,
518 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
519 mark_pending_load(args, LOAD_STATE_RO);
522 command::fnptr<command::arg_filename> CMD_load_preserve(lsnes_cmds, CLOADSAVE::ldp,
523 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
524 mark_pending_load(args, LOAD_STATE_PRESERVE);
527 command::fnptr<command::arg_filename> CMD_load_movie_c(lsnes_cmds, CLOADSAVE::ldm,
528 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
529 mark_pending_load(args, LOAD_STATE_MOVIE);
532 command::fnptr<command::arg_filename> CMD_load_allbr_c(lsnes_cmds, CLOADSAVE::ldab,
533 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
534 mark_pending_load(args, LOAD_STATE_ALLBRANCH);
537 command::fnptr<command::arg_filename> CMD_save_state(lsnes_cmds, CLOADSAVE::sa,
538 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
539 mark_pending_save(args, SAVE_STATE, -1);
542 command::fnptr<command::arg_filename> CMD_save_state2(lsnes_cmds, CLOADSAVE::sasb,
543 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
544 mark_pending_save(args, SAVE_STATE, 1);
547 command::fnptr<command::arg_filename> CMD_save_state3(lsnes_cmds, CLOADSAVE::sasz,
548 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
549 mark_pending_save(args, SAVE_STATE, 0);
552 command::fnptr<command::arg_filename> CMD_save_movie(lsnes_cmds, CLOADSAVE::sam,
553 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
554 mark_pending_save(args, SAVE_MOVIE, -1);
557 command::fnptr<command::arg_filename> CMD_save_movie2(lsnes_cmds, CLOADSAVE::samb,
558 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
559 mark_pending_save(args, SAVE_MOVIE, 1);
562 command::fnptr<command::arg_filename> CMD_save_movie3(lsnes_cmds, CLOADSAVE::samz,
563 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
564 mark_pending_save(args, SAVE_MOVIE, 0);
567 command::fnptr<command::arg_filename> CMD_load_rom(lsnes_cmds, CLOADSAVE::lrom,
568 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
569 romload_request req;
570 req.packfile = args;
571 load_new_rom(req);
574 command::fnptr<> CMD_reload_rom(lsnes_cmds, CLOADSAVE::rlrom,
575 []() throw(std::bad_alloc, std::runtime_error) {
576 reload_current_rom();
579 command::fnptr<> CMD_close_rom(lsnes_cmds, CLOADSAVE::clrom,
580 []() throw(std::bad_alloc, std::runtime_error) {
581 close_rom();
584 command::fnptr<> CMD_set_rwmode(lsnes_cmds, "set-rwmode", "Switch to recording mode",
585 "Syntax: set-rwmode\nSwitches to recording mode\n",
586 []() throw(std::bad_alloc, std::runtime_error) {
587 auto& core = CORE();
588 core.lua2->callback_movie_lost("readwrite");
589 core.mlogic->get_movie().readonly_mode(false);
590 core.dispatch->mode_change(false);
591 core.lua2->callback_do_readwrite();
592 core.supdater->update();
595 command::fnptr<> CMD_set_romode(lsnes_cmds, "set-romode", "Switch to playback mode",
596 "Syntax: set-romode\nSwitches to playback mode\n",
597 []() throw(std::bad_alloc, std::runtime_error) {
598 auto& core = CORE();
599 core.mlogic->get_movie().readonly_mode(true);
600 core.dispatch->mode_change(true);
601 core.supdater->update();
604 command::fnptr<> CMD_toggle_rwmode(lsnes_cmds, "toggle-rwmode", "Toggle recording mode",
605 "Syntax: toggle-rwmode\nToggles recording mode\n",
606 []() throw(std::bad_alloc, std::runtime_error) {
607 auto& core = CORE();
608 bool c = core.mlogic->get_movie().readonly_mode();
609 if(c)
610 core.lua2->callback_movie_lost("readwrite");
611 core.mlogic->get_movie().readonly_mode(!c);
612 core.dispatch->mode_change(!c);
613 if(c)
614 core.lua2->callback_do_readwrite();
615 core.supdater->update();
618 command::fnptr<> CMD_repaint(lsnes_cmds, "repaint", "Redraw the screen",
619 "Syntax: repaint\nRedraws the screen\n",
620 []() throw(std::bad_alloc, std::runtime_error) {
621 CORE().fbuf->redraw_framebuffer();
624 command::fnptr<> CMD_tpon(lsnes_cmds, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
625 []() throw(std::bad_alloc, std::runtime_error) {
626 auto& core = CORE();
627 bool tmp = SET_pause_on_end(*core.settings);
628 SET_pause_on_end(*core.settings, !tmp);
629 messages << "Pause-on-end is now " << (tmp ? "OFF" : "ON") << std::endl;
632 command::fnptr<> CMD_spon(lsnes_cmds, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
633 []() throw(std::bad_alloc, std::runtime_error) {
634 SET_pause_on_end(*CORE().settings, true);
635 messages << "Pause-on-end is now ON" << std::endl;
638 command::fnptr<> CMD_cpon(lsnes_cmds, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
639 []() throw(std::bad_alloc, std::runtime_error) {
640 SET_pause_on_end(*CORE().settings, false);
641 messages << "Pause-on-end is now OFF" << std::endl;
644 command::fnptr<> CMD_rewind_movie(lsnes_cmds, CLOADSAVE::rewind,
645 []() throw(std::bad_alloc, std::runtime_error) {
646 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
649 command::fnptr<> CMD_cancel_save(lsnes_cmds, CLOADSAVE::cancel,
650 []() throw(std::bad_alloc, std::runtime_error) {
651 queued_saves.clear();
652 messages << "Pending saves canceled." << std::endl;
655 command::fnptr<> CMD_mhold1(lsnes_cmds, CMHOLD::p, []() throw(std::bad_alloc, std::runtime_error) {
656 macro_hold_1 = true;
659 command::fnptr<> CMD_mhold2(lsnes_cmds, CMHOLD::r, []() throw(std::bad_alloc, std::runtime_error) {
660 macro_hold_1 = false;
663 command::fnptr<> CMD_mhold3(lsnes_cmds, CMHOLD::t, []() throw(std::bad_alloc, std::runtime_error) {
664 macro_hold_2 = !macro_hold_2;
665 if(macro_hold_2)
666 messages << "Macros are held for next frame." << std::endl;
667 else
668 messages << "Macros are not held for next frame." << std::endl;
671 keyboard::invbind_info IBIND_ipause_emulator(lsnes_invbinds, "pause-emulator", "Speed‣(Un)pause");
672 keyboard::invbind_info IBIND_iadvframe(lsnes_invbinds, "+advance-frame", "Speed‣Advance frame");
673 keyboard::invbind_info IBIND_iadvsubframe(lsnes_invbinds, "+advance-poll", "Speed‣Advance subframe");
674 keyboard::invbind_info IBIND_iskiplag(lsnes_invbinds, "advance-skiplag", "Speed‣Advance poll");
675 keyboard::invbind_info IBIND_ireset(lsnes_invbinds, "reset", "System‣Reset");
676 keyboard::invbind_info IBIND_iset_rwmode(lsnes_invbinds, "set-rwmode", "Movie‣Switch to recording");
677 keyboard::invbind_info IBIND_itoggle_romode(lsnes_invbinds, "set-romode", "Movie‣Switch to playback");
678 keyboard::invbind_info IBIND_itoggle_rwmode(lsnes_invbinds, "toggle-rwmode", "Movie‣Toggle playback");
679 keyboard::invbind_info IBIND_irepaint(lsnes_invbinds, "repaint", "System‣Repaint screen");
680 keyboard::invbind_info IBIND_itogglepause(lsnes_invbinds, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
682 class mywindowcallbacks : public master_dumper::notifier
684 public:
685 mywindowcallbacks(emulator_dispatch& dispatch, emulator_runmode& runmode, status_updater& _supdater)
686 : supdater(_supdater)
688 closenotify.set(dispatch.close, [this, &runmode]() {
689 try {
690 runmode.set_quit();
691 platform::set_paused(false);
692 platform::cancel_wait();
693 } catch(...) {
697 ~mywindowcallbacks() throw() {}
698 void dump_status_change() throw()
700 supdater.update();
702 private:
703 struct dispatch::target<> closenotify;
704 status_updater& supdater;
707 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
708 //failing.
709 int handle_load()
711 auto& core = CORE();
712 std::string old_project = *core.mlogic ? core.mlogic->get_mfile().projectid : "";
713 jumpback:
714 if(do_unsafe_rewind && unsafe_rewind_obj) {
715 if(!*core.mlogic)
716 return 0;
717 uint64_t t = framerate_regulator::get_utime();
718 core.lua2->callback_do_unsafe_rewind(core.mlogic->get_movie(), unsafe_rewind_obj);
719 core.dispatch->mode_change(false);
720 do_unsafe_rewind = false;
721 core.runmode->set_point(emulator_runmode::P_SAVE);
722 core.supdater->update();
723 core.runmode->end_load(); //Restore previous mode.
724 messages << "Rewind done in " << (framerate_regulator::get_utime() - t) << " usec."
725 << std::endl;
726 return 1;
728 if(pending_new_project != "") {
729 std::string id = pending_new_project;
730 pending_new_project = "";
731 project_info* old = core.project->get();
732 if(old && old->id == id)
733 goto nothing_to_do;
734 try {
735 auto& p = core.project->load(id);
736 core.project->set(&p);
737 if(core.project->get() != old)
738 delete old;
739 core.slotcache->flush(); //Wrong movie may be stale.
740 core.runmode->end_load(); //Restore previous mode.
741 if(core.mlogic->get_mfile().dyn.save_frame)
742 core.runmode->set_point(emulator_runmode::P_SAVE);
743 core.supdater->update();
744 return 1;
745 } catch(std::exception& e) {
746 platform::error_message(std::string("Can't switch projects: ") + e.what());
747 messages << "Can't switch projects: " << e.what() << std::endl;
748 goto nothing_to_do;
750 nothing_to_do:
751 core.runmode->end_load();
752 platform::set_paused(core.runmode->is_paused());
753 platform::flush_command_queue();
754 if(core.runmode->is_load())
755 goto jumpback;
756 return 0;
758 if(pending_load != "") {
759 try {
760 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
761 !do_load_state(pending_load, loadmode)) {
762 core.runmode->end_load();
763 pending_load = "";
764 return -1;
766 if(loadmode == LOAD_STATE_BEGINNING)
767 do_load_rewind();
768 if(loadmode == LOAD_STATE_ROMRELOAD)
769 do_load_rom();
770 core.runmode->clear_corrupt();
771 } catch(std::exception& e) {
772 core.runmode->set_corrupt();
773 platform::error_message(std::string("Load failed: ") + e.what());
774 messages << "Load failed: " << e.what() << std::endl;
776 pending_load = "";
777 if(!core.runmode->is_corrupt()) {
778 core.runmode->end_load();
779 core.runmode->set_point(emulator_runmode::P_SAVE);
780 core.supdater->update();
781 platform::flush_command_queue();
782 if(core.runmode->is_quit())
783 return -1;
784 if(core.runmode->is_load())
785 goto jumpback;
787 if(old_project != (*core.mlogic ? core.mlogic->get_mfile().projectid : ""))
788 core.slotcache->flush(); //Wrong movie may be stale.
789 return 1;
791 return 0;
794 //If there are pending saves, perform them.
795 void handle_saves()
797 auto& core = CORE();
798 if(!*core.mlogic)
799 return;
800 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
801 core.rom->runtosave();
802 for(auto i : queued_saves) {
803 do_save_state(i.first, i.second);
804 int tmp = -1;
805 core.slotcache->flush(translate_name_mprefix(i.first, tmp, -1));
807 if(do_unsafe_rewind && !unsafe_rewind_obj) {
808 uint64_t t = framerate_regulator::get_utime();
809 core.mlogic->get_mfile().dyn.savestate = core.rom->save_core_state(true);
810 core.lua2->callback_do_unsafe_rewind(core.mlogic->get_movie(), NULL);
811 do_unsafe_rewind = false;
812 messages << "Rewind point set in " << (framerate_regulator::get_utime() - t)
813 << " usec." << std::endl;
816 queued_saves.clear();
819 bool handle_corrupt()
821 auto& core = CORE();
822 if(!core.runmode->is_corrupt())
823 return false;
824 while(core.runmode->is_corrupt()) {
825 platform::set_paused(true);
826 platform::flush_command_queue();
827 handle_load();
828 if(core.runmode->is_quit())
829 return true;
831 return true;
835 void init_main_callbacks()
837 ecore_callbacks = &lsnes_callbacks_obj;
840 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
841 std::runtime_error)
843 lsnes_instance.emu_thread = threads::id();
844 auto& core = CORE();
845 mywindowcallbacks mywcb(*core.dispatch, *core.runmode, *core.supdater);
846 core.iqueue->system_thread_available = true;
847 //Basic initialization.
848 core.commentary->init();
849 core.fbuf->init_special_screens();
850 core.jukebox->set_update([&core]() { core.supdater->update(); });
851 *core.rom = rom;
852 init_main_callbacks();
853 initialize_all_builtin_c_cores();
854 core_core::install_all_handlers();
856 //Load our given movie.
857 bool first_round = false;
858 bool just_did_loadstate = false;
859 bool used = false;
860 try {
861 do_load_state(initial, LOAD_STATE_INITIAL, used);
862 core.runmode->set_point(emulator_runmode::P_SAVE);
863 core.supdater->update();
864 first_round = core.mlogic->get_mfile().dyn.save_frame;
865 just_did_loadstate = first_round;
866 } catch(std::bad_alloc& e) {
867 OOM_panic();
868 } catch(std::exception& e) {
869 if(!used)
870 delete &initial;
871 platform::error_message(std::string("Can't load initial state: ") + e.what());
872 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
873 if(load_has_to_succeed) {
874 messages << "FATAL: Can't load movie" << std::endl;
875 throw;
877 core.runmode->set_corrupt();
878 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt);
881 core.runmode->set_pause_cond(initial.start_paused);
882 platform::set_paused(core.runmode->is_paused());
883 stop_at_frame_active = false;
885 core.lua2->run_startup_scripts();
887 while(!core.runmode->is_quit() || !queued_saves.empty()) {
888 if(handle_corrupt()) {
889 first_round = *core.mlogic && core.mlogic->get_mfile().dyn.save_frame;
890 just_did_loadstate = first_round;
891 continue;
893 core.framerate->ack_frame_tick(framerate_regulator::get_utime());
894 core.runmode->decay_skiplag();
896 if(!first_round) {
897 core.controls->reset_framehold();
898 if(!macro_hold_1 && !macro_hold_2) {
899 core.controls->advance_macros();
901 macro_hold_2 = false;
902 core.mlogic->get_movie().get_pollcounters().set_framepflag(false);
903 core.mlogic->new_frame_starting(core.runmode->is_skiplag());
904 core.mlogic->get_movie().get_pollcounters().set_framepflag(true);
905 if(core.runmode->is_quit() && queued_saves.empty())
906 break;
907 handle_saves();
908 int r = 0;
909 if(queued_saves.empty())
910 r = handle_load();
911 if(r > 0 || core.runmode->is_corrupt()) {
912 core.mlogic->get_movie().get_pollcounters().set_framepflag(
913 core.mlogic->get_mfile().dyn.save_frame != 0);
914 first_round = core.mlogic->get_mfile().dyn.save_frame;
915 stop_at_frame_active = false;
916 just_did_loadstate = first_round;
917 core.controls->reset_framehold();
918 core.dbg->do_callback_frame(core.mlogic->get_movie().get_current_frame(), true);
919 continue;
920 } else if(r < 0) {
921 //Not exactly desriable, but this at least won't desync.
922 stop_at_frame_active = false;
923 if(core.runmode->is_quit())
924 goto out;
925 core.runmode->set_pause();
928 if(just_did_loadstate) {
929 //If we just loadstated, we are up to date.
930 if(core.runmode->is_quit())
931 break;
932 platform::set_paused(core.runmode->is_paused());
933 platform::flush_command_queue();
934 //We already have done the reset this frame if we are going to do one at all.
935 core.mlogic->get_movie().set_controls(core.mlogic->update_controls(true));
936 core.mlogic->get_movie().set_all_DRDY();
937 just_did_loadstate = false;
939 core.dbg->do_callback_frame(core.mlogic->get_movie().get_current_frame(), false);
940 core.rom->emulate();
941 random_mix_timing_entropy();
942 if(core.runmode->is_freerunning())
943 platform::wait(core.framerate->to_wait_frame(framerate_regulator::get_utime()));
944 first_round = false;
945 core.lua2->callback_do_frame();
947 out:
948 core.jukebox->unset_update();
949 core.mdumper->end_dumps();
950 core.commentary->kill();
951 core.iqueue->system_thread_available = false;
952 //Kill some things to avoid crashes.
953 core.dbg->core_change();
954 core.project->set(NULL, true);
955 core.mwatch->clear_multi(core.mwatch->enumerate());
956 //Close the ROM.
957 load_null_rom();
958 do_load_rom();
959 //Uninstall the handlers. Don't do this with ROM loaded.
960 core_core::uninstall_all_handlers();
963 void set_stop_at_frame(uint64_t frame)
965 auto& core = CORE();
966 stop_at_frame = frame;
967 stop_at_frame_active = (frame != 0);
968 if(!core.runmode->is_special())
969 core.runmode->set_freerunning();
970 platform::set_paused(false);
973 void do_flush_slotinfo()
975 CORE().slotcache->flush();
978 void switch_projects(const std::string& newproj)
980 pending_new_project = newproj;
981 CORE().runmode->start_load();
982 platform::cancel_wait();
983 platform::set_paused(false);
986 void load_new_rom(const romload_request& req)
988 if(_load_new_rom(req)) {
989 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
993 void reload_current_rom()
995 if(reload_active_rom()) {
996 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1000 void close_rom()
1002 if(load_null_rom()) {
1003 CORE().runmode->set_pause();
1004 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1008 void do_break_pause()
1010 auto& core = CORE();
1011 core.runmode->set_break();
1012 core.supdater->update();
1013 core.fbuf->redraw_framebuffer();
1014 while(core.runmode->is_paused_break()) {
1015 platform::set_paused(true);
1016 platform::flush_command_queue();
1020 void convert_break_to_pause()
1022 auto& core = CORE();
1023 if(core.runmode->is_paused_break()) {
1024 core.runmode->set_pause();
1025 core.supdater->update();