Fix the speed throttle
[lsnes.git] / src / core / mainloop.cpp
blobda4d23fa1cec790b4301021ca2a27f7b9e8662fd
1 #include "lsnes.hpp"
2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
5 #include "core/command.hpp"
6 #include "core/controller.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/framebuffer.hpp"
9 #include "core/framerate.hpp"
10 #include "core/lua.hpp"
11 #include "core/mainloop.hpp"
12 #include "core/movie.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/moviefile.hpp"
15 #include "core/memorymanip.hpp"
16 #include "core/memorywatch.hpp"
17 #include "core/render.hpp"
18 #include "core/rom.hpp"
19 #include "core/rrdata.hpp"
20 #include "core/settings.hpp"
21 #include "core/window.hpp"
23 #include <iomanip>
24 #include <cassert>
25 #include <sstream>
26 #include <iostream>
27 #include <set>
28 #include <sys/time.h>
30 #define SPECIAL_FRAME_START 0
31 #define SPECIAL_FRAME_VIDEO 1
32 #define SPECIAL_SAVEPOINT 2
33 #define SPECIAL_NONE 3
35 void update_movie_state();
36 time_t random_seed_value = 0;
38 namespace
40 enum advance_mode
42 ADVANCE_QUIT, //Quit the emulator.
43 ADVANCE_AUTO, //Normal (possibly slowed down play).
44 ADVANCE_LOAD, //Loading a state.
45 ADVANCE_FRAME, //Frame advance.
46 ADVANCE_SUBFRAME, //Subframe advance.
47 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
48 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
49 ADVANCE_PAUSE, //Unconditional pause.
52 //Previous mouse mask.
53 int prev_mouse_mask = 0;
54 //Flags related to repeating advance.
55 bool advanced_once;
56 bool cancel_advance;
57 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
58 enum advance_mode amode;
59 //Mode and filename of pending load, one of LOAD_* constants.
60 int loadmode;
61 std::string pending_load;
62 //Queued saves (all savestates).
63 std::set<std::string> queued_saves;
64 bool stepping_into_save;
65 //Save jukebox.
66 std::vector<std::string> save_jukebox;
67 size_t save_jukebox_pointer;
68 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
69 long pending_reset_cycles = -1;
70 //Set by every video refresh.
71 bool video_refresh_done;
72 //Special subframe location. One of SPECIAL_* constants.
73 int location_special;
74 //Few settings.
75 numeric_setting advance_timeout_first("advance-timeout", 0, 999999999, 500);
76 boolean_setting pause_on_end("pause-on-end", false);
77 //Last frame params.
78 bool last_hires = false;
79 bool last_interlace = false;
82 class firmware_path_setting : public setting
84 public:
85 firmware_path_setting() : setting("firmwarepath") { _firmwarepath = "."; default_firmware = true; }
86 void blank() throw(std::bad_alloc, std::runtime_error)
88 _firmwarepath = ".";
89 default_firmware = true;
92 bool is_set() throw()
94 return !default_firmware;
97 void set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
99 _firmwarepath = value;
100 default_firmware = false;
103 std::string get() throw(std::bad_alloc)
105 return _firmwarepath;
108 operator std::string() throw(std::bad_alloc)
110 return _firmwarepath;
112 private:
113 std::string _firmwarepath;
114 bool default_firmware;
115 } firmwarepath_setting;
117 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
119 if(lua_requests_subframe_paint)
120 redraw_framebuffer();
122 if(subframe) {
123 if(amode == ADVANCE_SUBFRAME) {
124 if(!cancel_advance && !advanced_once) {
125 platform::wait(advance_timeout_first * 1000);
126 advanced_once = true;
128 if(cancel_advance) {
129 amode = ADVANCE_PAUSE;
130 cancel_advance = false;
132 platform::set_paused(amode == ADVANCE_PAUSE);
133 } else if(amode == ADVANCE_FRAME) {
135 } else {
136 if(amode == ADVANCE_SKIPLAG)
137 amode = ADVANCE_PAUSE;
138 platform::set_paused(amode == ADVANCE_PAUSE);
139 cancel_advance = false;
141 if(amode == ADVANCE_SKIPLAG)
142 amode = ADVANCE_AUTO;
143 location_special = SPECIAL_NONE;
144 update_movie_state();
145 } else {
146 if(amode == ADVANCE_SKIPLAG_PENDING)
147 amode = ADVANCE_SKIPLAG;
148 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
149 if(!cancel_advance) {
150 platform::wait(advanced_once ? to_wait_frame(get_utime()) :
151 (advance_timeout_first * 1000));
152 advanced_once = true;
154 if(cancel_advance) {
155 amode = ADVANCE_PAUSE;
156 cancel_advance = false;
158 platform::set_paused(amode == ADVANCE_PAUSE);
159 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end) {
160 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count() + 1) {
161 amode = ADVANCE_PAUSE;
162 platform::set_paused(true);
164 } else {
165 platform::set_paused((amode == ADVANCE_PAUSE));
166 cancel_advance = false;
168 location_special = SPECIAL_FRAME_START;
169 update_movie_state();
172 information_dispatch::do_status_update();
173 platform::flush_command_queue();
174 if(!subframe && pending_reset_cycles >= 0)
175 controls.reset(pending_reset_cycles);
176 else if(!subframe)
177 controls.reset(-1);
178 controller_frame tmp = controls.commit(movb.get_movie().get_current_frame());
179 lua_callback_do_input(tmp, subframe);
180 return tmp;
183 namespace
185 enum advance_mode old_mode;
187 //Do pending load (automatically unpauses).
188 void mark_pending_load(const std::string& filename, int lmode)
190 loadmode = lmode;
191 pending_load = filename;
192 old_mode = amode;
193 amode = ADVANCE_LOAD;
194 platform::cancel_wait();
195 platform::set_paused(false);
198 void mark_pending_save(const std::string& filename, int smode)
200 if(smode == SAVE_MOVIE) {
201 //Just do this immediately.
202 do_save_movie(filename);
203 return;
205 queued_saves.insert(filename);
206 messages << "Pending save on '" << filename << "'" << std::endl;
209 uint32_t lpalette[0x80000];
210 void init_palette()
212 static bool palette_init = false;
213 if(palette_init)
214 return;
215 palette_init = true;
216 for(unsigned i = 0; i < 0x80000; i++) {
217 unsigned l = (i >> 15) & 0xF;
218 unsigned r = (i >> 0) & 0x1F;
219 unsigned g = (i >> 5) & 0x1F;
220 unsigned b = (i >> 10) & 0x1F;
221 double _l = static_cast<double>(l);
222 double m = 17.0 / 31.0;
223 r = floor(m * r * _l + 0.5);
224 g = floor(m * g * _l + 0.5);
225 b = floor(m * b * _l + 0.5);
226 lpalette[i] = r * 65536 + g * 256 + b;
231 void update_movie_state()
233 auto& _status = platform::get_emustatus();
234 if(!system_corrupt) {
235 std::ostringstream x;
236 x << movb.get_movie().get_current_frame() << "(";
237 if(location_special == SPECIAL_FRAME_START)
238 x << "0";
239 else if(location_special == SPECIAL_SAVEPOINT)
240 x << "S";
241 else if(location_special == SPECIAL_FRAME_VIDEO)
242 x << "V";
243 else
244 x << movb.get_movie().next_poll_number();
245 x << ";" << movb.get_movie().get_lag_frames() << ")/" << movb.get_movie().get_frame_count();
246 _status.set("Frame", x.str());
247 } else
248 _status.set("Frame", "N/A");
249 if(!system_corrupt) {
250 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
251 struct tm* time_decompose = gmtime(&timevalue);
252 char datebuffer[512];
253 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
254 _status.set("RTC", datebuffer);
255 } else {
256 _status.set("RTC", "N/A");
259 std::ostringstream x;
260 auto& mo = movb.get_movie();
261 x << (information_dispatch::get_dumper_count() ? "D" : "-");
262 x << (last_hires ? "H" : "-");
263 x << (last_interlace ? "I" : "-");
264 if(system_corrupt)
265 x << "C";
266 else if(!mo.readonly_mode())
267 x << "R";
268 else if(mo.get_frame_count() >= mo.get_current_frame())
269 x << "P";
270 else
271 x << "F";
272 _status.set("Flags", x.str());
274 if(save_jukebox.size() > 0)
275 _status.set("Saveslot", translate_name_mprefix(save_jukebox[save_jukebox_pointer]));
276 else
277 _status.erase("Saveslot");
279 std::ostringstream x;
280 x << get_framerate();
281 _status.set("SPD%", x.str());
283 do_watch_memory();
285 controller_frame c;
286 if(movb.get_movie().readonly_mode())
287 c = movb.get_movie().get_controls();
288 else
289 c = controls.get_committed();
290 for(unsigned i = 0; i < 8; i++) {
291 unsigned pindex = controls.lcid_to_pcid(i);
292 devicetype_t dtype = controls.pcid_to_type(pindex);
293 if(dtype == DT_NONE)
294 continue;
295 char buffer[MAX_DISPLAY_LENGTH];
296 c.display(pindex, buffer);
297 char y[3] = {'P', 0, 0};
298 y[1] = 49 + i;
299 _status.set(y, buffer);
303 uint64_t audio_irq_time;
304 uint64_t controller_irq_time;
305 uint64_t frame_irq_time;
308 class my_interface : public SNES::Interface
310 string path(SNES::Cartridge::Slot slot, const string &hint)
312 const char* _hint = hint;
313 std::string _hint2 = _hint;
314 std::string fwp = firmwarepath_setting;
315 std::string finalpath = fwp + "/" + _hint2;
316 return finalpath.c_str();
319 time_t currentTime()
321 return our_movie.rtc_second;
324 time_t randomSeed()
326 return random_seed_value;
329 void videoRefresh(const uint32_t* data, bool hires, bool interlace, bool overscan)
331 // uint64_t time_x = get_utime();
332 last_hires = hires;
333 last_interlace = interlace;
334 init_palette();
335 if(stepping_into_save)
336 messages << "Got video refresh in runtosave, expect desyncs!" << std::endl;
337 video_refresh_done = true;
338 bool region = (SNES::system.region() == SNES::System::Region::PAL);
339 information_dispatch::do_raw_frame(data, hires, interlace, overscan, region ? VIDEO_REGION_PAL :
340 VIDEO_REGION_NTSC);
341 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
342 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
343 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
344 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
345 lcscreen ls(data, hires, interlace, overscan, region);
346 location_special = SPECIAL_FRAME_VIDEO;
347 update_movie_state();
348 redraw_framebuffer(ls);
349 uint32_t fps_n, fps_d;
350 uint32_t fclocks;
351 if(region)
352 fclocks = interlace ? DURATION_PAL_FIELD : DURATION_PAL_FRAME;
353 else
354 fclocks = interlace ? DURATION_NTSC_FIELD : DURATION_NTSC_FRAME;
355 fps_n = SNES::system.cpu_frequency();
356 fps_d = fclocks;
357 uint32_t g = gcd(fps_n, fps_d);
358 fps_n /= g;
359 fps_d /= g;
360 information_dispatch::do_frame(ls, fps_n, fps_d);
361 // time_x = get_utime() - time_x;
362 // std::cerr << "IRQ TIMINGS (microseconds): "
363 // << "V: " << time_x << " "
364 // << "A: " << audio_irq_time << " "
365 // << "C: " << controller_irq_time << " "
366 // << "F: " << frame_irq_time << " "
367 // << "Total: " << (time_x + audio_irq_time + controller_irq_time + frame_irq_time) << std::endl;
368 audio_irq_time = controller_irq_time = 0;
371 void audioSample(int16_t l_sample, int16_t r_sample)
373 // uint64_t time_x = get_utime();
374 uint16_t _l = l_sample;
375 uint16_t _r = r_sample;
376 platform::audio_sample(_l + 32768, _r + 32768);
377 information_dispatch::do_sample(l_sample, r_sample);
378 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
379 our_movie.rtc_subsecond += 768;
380 while(our_movie.rtc_subsecond >= SNES::system.apu_frequency()) {
381 our_movie.rtc_second++;
382 our_movie.rtc_subsecond -= SNES::system.apu_frequency();
384 // audio_irq_time += get_utime() - time_x;
387 int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
389 // uint64_t time_x = get_utime();
390 int16_t x;
391 x = movb.input_poll(port, index, id);
392 lua_callback_snoop_input(port ? 1 : 0, index, id, x);
393 // controller_irq_time += get_utime() - time_x;
394 return x;
398 namespace
400 function_ptr_command<> count_rerecords("count-rerecords", "Count rerecords",
401 "Syntax: count-rerecords\nCounts rerecords.\n",
402 []() throw(std::bad_alloc, std::runtime_error) {
403 std::vector<char> tmp;
404 uint64_t x = rrdata::write(tmp);
405 messages << x << " rerecord(s)" << std::endl;
408 function_ptr_command<const std::string&> quit_emulator("quit-emulator", "Quit the emulator",
409 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
410 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
411 amode = ADVANCE_QUIT;
412 platform::set_paused(false);
413 platform::cancel_wait();
416 function_ptr_command<> pause_emulator("pause-emulator", "(Un)pause the emulator",
417 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
418 []() throw(std::bad_alloc, std::runtime_error) {
419 if(amode != ADVANCE_AUTO) {
420 amode = ADVANCE_AUTO;
421 platform::set_paused(false);
422 platform::cancel_wait();
423 messages << "Unpaused" << std::endl;
424 } else {
425 platform::cancel_wait();
426 cancel_advance = false;
427 amode = ADVANCE_PAUSE;
428 messages << "Paused" << std::endl;
432 function_ptr_command<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
433 "Syntax: cycle-jukebox-backwards\nCycle save jukebox backwards\n",
434 []() throw(std::bad_alloc, std::runtime_error) {
435 if(save_jukebox_pointer == 0)
436 save_jukebox_pointer = save_jukebox.size() - 1;
437 else
438 save_jukebox_pointer--;
439 if(save_jukebox_pointer >= save_jukebox.size())
440 save_jukebox_pointer = 0;
441 update_movie_state();
442 information_dispatch::do_status_update();
445 function_ptr_command<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
446 "Syntax: cycle-jukebox-forwards\nCycle save jukebox forwards\n",
447 []() throw(std::bad_alloc, std::runtime_error) {
448 if(save_jukebox_pointer == save_jukebox.size() - 1)
449 save_jukebox_pointer = 0;
450 else
451 save_jukebox_pointer++;
452 if(save_jukebox_pointer >= save_jukebox.size())
453 save_jukebox_pointer = 0;
454 update_movie_state();
455 information_dispatch::do_status_update();
458 function_ptr_command<arg_filename> add_jukebox("add-jukebox-save", "Add save to jukebox",
459 "Syntax: add-jukebox-save\nAdd save to jukebox\n",
460 [](arg_filename filename) throw(std::bad_alloc, std::runtime_error) {
461 save_jukebox.push_back(filename);
462 update_movie_state();
463 information_dispatch::do_status_update();
466 function_ptr_command<> load_jukebox("load-jukebox", "Load save from jukebox",
467 "Syntax: load-jukebox\nLoad save from jukebox\n",
468 []() throw(std::bad_alloc, std::runtime_error) {
469 if(!save_jukebox.size())
470 throw std::runtime_error("No saves in jukebox");
471 mark_pending_load(save_jukebox[save_jukebox_pointer], LOAD_STATE_CURRENT);
474 function_ptr_command<> save_jukebox_c("save-jukebox", "Save save to jukebox",
475 "Syntax: save-jukebox\nSave save to jukebox\n",
476 []() throw(std::bad_alloc, std::runtime_error) {
477 if(!save_jukebox.size())
478 throw std::runtime_error("No saves in jukebox");
479 mark_pending_save(save_jukebox[save_jukebox_pointer], SAVE_STATE);
482 function_ptr_command<> padvance_frame("+advance-frame", "Advance one frame",
483 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
484 []() throw(std::bad_alloc, std::runtime_error) {
485 amode = ADVANCE_FRAME;
486 cancel_advance = false;
487 advanced_once = false;
488 platform::cancel_wait();
489 platform::set_paused(false);
492 function_ptr_command<> nadvance_frame("-advance-frame", "Advance one frame",
493 "No help available\n",
494 []() throw(std::bad_alloc, std::runtime_error) {
495 cancel_advance = true;
496 platform::cancel_wait();
497 platform::set_paused(false);
500 function_ptr_command<> padvance_poll("+advance-poll", "Advance one subframe",
501 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
502 []() throw(std::bad_alloc, std::runtime_error) {
503 amode = ADVANCE_SUBFRAME;
504 cancel_advance = false;
505 advanced_once = false;
506 platform::cancel_wait();
507 platform::set_paused(false);
510 function_ptr_command<> nadvance_poll("-advance-poll", "Advance one subframe",
511 "No help available\n",
512 []() throw(std::bad_alloc, std::runtime_error) {
513 cancel_advance = true;
514 platform::cancel_wait();
515 platform::set_paused(false);
518 function_ptr_command<> advance_skiplag("advance-skiplag", "Skip to next poll",
519 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
520 []() throw(std::bad_alloc, std::runtime_error) {
521 amode = ADVANCE_SKIPLAG;
522 platform::cancel_wait();
523 platform::set_paused(false);
526 function_ptr_command<> reset_c("reset", "Reset the SNES",
527 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
528 []() throw(std::bad_alloc, std::runtime_error) {
529 pending_reset_cycles = 0;
532 function_ptr_command<arg_filename> load_c("load", "Load savestate (current mode)",
533 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
534 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
535 mark_pending_load(args, LOAD_STATE_CURRENT);
538 function_ptr_command<arg_filename> load_state_c("load-state", "Load savestate (R/W)",
539 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
540 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
541 mark_pending_load(args, LOAD_STATE_RW);
544 function_ptr_command<arg_filename> load_readonly("load-readonly", "Load savestate (RO)",
545 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
546 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
547 mark_pending_load(args, LOAD_STATE_RO);
550 function_ptr_command<arg_filename> load_preserve("load-preserve", "Load savestate (preserve input)",
551 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
552 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
553 mark_pending_load(args, LOAD_STATE_PRESERVE);
556 function_ptr_command<arg_filename> load_movie_c("load-movie", "Load movie",
557 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
558 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
559 mark_pending_load(args, LOAD_STATE_MOVIE);
563 function_ptr_command<arg_filename> save_state("save-state", "Save state",
564 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
565 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
566 mark_pending_save(args, SAVE_STATE);
569 function_ptr_command<arg_filename> save_movie("save-movie", "Save movie",
570 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
571 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
572 mark_pending_save(args, SAVE_MOVIE);
575 function_ptr_command<> set_rwmode("set-rwmode", "Switch to read/write mode",
576 "Syntax: set-rwmode\nSwitches to read/write mode\n",
577 []() throw(std::bad_alloc, std::runtime_error) {
578 movb.get_movie().readonly_mode(false);
579 information_dispatch::do_mode_change(false);
580 lua_callback_do_readwrite();
581 update_movie_state();
582 information_dispatch::do_status_update();
585 function_ptr_command<> set_romode("set-romode", "Switch to read-only mode",
586 "Syntax: set-romode\nSwitches to read-only mode\n",
587 []() throw(std::bad_alloc, std::runtime_error) {
588 movb.get_movie().readonly_mode(true);
589 information_dispatch::do_mode_change(true);
590 update_movie_state();
591 information_dispatch::do_status_update();
594 function_ptr_command<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
595 "Syntax: toggle-rwmode\nToggles read/write mode\n",
596 []() throw(std::bad_alloc, std::runtime_error) {
597 bool c = movb.get_movie().readonly_mode();
598 movb.get_movie().readonly_mode(!c);
599 information_dispatch::do_mode_change(!c);
600 if(c)
601 lua_callback_do_readwrite();
602 update_movie_state();
603 information_dispatch::do_status_update();
606 function_ptr_command<> repaint("repaint", "Redraw the screen",
607 "Syntax: repaint\nRedraws the screen\n",
608 []() throw(std::bad_alloc, std::runtime_error) {
609 redraw_framebuffer();
612 function_ptr_command<> tpon("toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
613 []() throw(std::bad_alloc, std::runtime_error) {
614 bool newstate = !static_cast<bool>(pause_on_end);
615 pause_on_end.set(newstate ? "1" : "0");
616 messages << "Pause-on-end is now " << (newstate ? "ON" : "OFF") << std::endl;
619 function_ptr_command<> rewind_movie("rewind-movie", "Rewind movie to the beginning",
620 "Syntax: rewind-movie\nRewind movie to the beginning\n",
621 []() throw(std::bad_alloc, std::runtime_error) {
622 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
625 function_ptr_command<> cancel_save("cancel-saves", "Cancel all pending saves", "Syntax: cancel-save\n"
626 "Cancel pending saves\n",
627 []() throw(std::bad_alloc, std::runtime_error) {
628 queued_saves.clear();
629 messages << "Pending saves canceled." << std::endl;
632 function_ptr_command<> test1("test-1", "no description available", "No help available\n",
633 []() throw(std::bad_alloc, std::runtime_error) {
634 redraw_framebuffer(screen_nosignal);
637 function_ptr_command<> test2("test-2", "no description available", "No help available\n",
638 []() throw(std::bad_alloc, std::runtime_error) {
639 redraw_framebuffer(screen_corrupt);
642 function_ptr_command<> test3("test-3", "no description available", "No help available\n",
643 []() throw(std::bad_alloc, std::runtime_error) {
644 while(1);
648 bool on_quit_prompt = false;
649 class mywindowcallbacks : public information_dispatch
651 public:
652 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
653 void on_new_dumper(const std::string& n)
655 update_movie_state();
657 void on_destroy_dumper(const std::string& n)
659 update_movie_state();
661 void on_close() throw()
663 if(on_quit_prompt) {
664 amode = ADVANCE_QUIT;
665 platform::set_paused(false);
666 platform::cancel_wait();
667 return;
669 on_quit_prompt = true;
670 try {
671 amode = ADVANCE_QUIT;
672 platform::set_paused(false);
673 platform::cancel_wait();
674 } catch(...) {
676 on_quit_prompt = false;
678 } mywcb;
680 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
681 //failing.
682 int handle_load()
684 if(pending_load != "") {
685 system_corrupt = false;
686 if(loadmode != LOAD_STATE_BEGINNING && !do_load_state(pending_load, loadmode)) {
687 pending_load = "";
688 return -1;
690 if(loadmode == LOAD_STATE_BEGINNING)
691 do_load_beginning();
692 pending_load = "";
693 pending_reset_cycles = -1;
694 amode = ADVANCE_AUTO;
695 platform::cancel_wait();
696 platform::set_paused(false);
697 if(!system_corrupt) {
698 location_special = SPECIAL_SAVEPOINT;
699 update_movie_state();
700 information_dispatch::do_status_update();
701 platform::flush_command_queue();
703 return 1;
705 return 0;
708 //If there are pending saves, perform them.
709 void handle_saves()
711 if(!queued_saves.empty()) {
712 stepping_into_save = true;
713 SNES::system.runtosave();
714 stepping_into_save = false;
715 for(auto i : queued_saves)
716 do_save_state(i);
718 queued_saves.clear();
721 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
722 bool handle_reset(long cycles)
724 if(cycles < 0)
725 return true;
726 video_refresh_done = false;
727 if(cycles == 0)
728 messages << "SNES reset" << std::endl;
729 else if(cycles > 0) {
730 messages << "SNES delayed reset not implemented (doing immediate reset)" << std::endl;
731 /* ... This code is just too buggy.
732 long cycles_executed = 0;
733 messages << "Executing delayed reset... This can take some time!" << std::endl;
734 while(cycles_executed < cycles && !video_refresh_done) {
735 //Poll inputs once in a while to prevent activating watchdog.
736 if(cycles_executed % 100 == 0)
737 platform::flush_command_queue();
738 SNES::cpu.op_step();
739 cycles_executed++;
741 if(!video_refresh_done)
742 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
743 else
744 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
747 SNES::system.reset();
748 lua_callback_do_reset();
749 redraw_framebuffer(screen_nosignal);
750 if(video_refresh_done) {
751 to_wait_frame(get_utime());
752 return false;
754 return true;
757 bool handle_corrupt()
759 if(!system_corrupt)
760 return false;
761 while(system_corrupt) {
762 platform::cancel_wait();
763 platform::set_paused(true);
764 platform::flush_command_queue();
765 handle_load();
766 if(amode == ADVANCE_QUIT)
767 return true;
769 return true;
772 void print_controller_mappings()
774 for(unsigned i = 0; i < 8; i++) {
775 std::string type = controls.lcid_to_typestring(i);
776 messages << "Physical controller mapping: Logical " << (i + 1) << " is physical " <<
777 controls.lcid_to_pcid(i) << " (" << type << ")" << std::endl;
782 std::vector<std::string> get_jukebox_names()
784 return save_jukebox;
787 void set_jukebox_names(const std::vector<std::string>& newj)
789 save_jukebox = newj;
790 if(save_jukebox_pointer >= save_jukebox.size())
791 save_jukebox_pointer = 0;
792 update_movie_state();
795 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
796 std::runtime_error)
798 //Basic initialization.
799 init_special_screens();
800 our_rom = &rom;
801 my_interface intrf;
802 auto old_inteface = SNES::interface;
803 SNES::interface = &intrf;
804 SNES::system.init();
806 //Load our given movie.
807 bool first_round = false;
808 bool just_did_loadstate = false;
809 try {
810 do_load_state(initial, LOAD_STATE_DEFAULT);
811 location_special = SPECIAL_SAVEPOINT;
812 update_movie_state();
813 first_round = our_movie.is_savestate;
814 just_did_loadstate = first_round;
815 } catch(std::bad_alloc& e) {
816 OOM_panic();
817 } catch(std::exception& e) {
818 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
819 if(load_has_to_succeed) {
820 messages << "FATAL: Can't load movie" << std::endl;
821 platform::fatal_error();
823 system_corrupt = true;
824 update_movie_state();
825 redraw_framebuffer(screen_corrupt);
828 lua_callback_startup();
830 //print_controller_mappings();
831 platform::set_paused(false);
832 amode = ADVANCE_AUTO;
833 uint64_t time_x = get_utime();
834 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
835 if(handle_corrupt()) {
836 first_round = our_movie.is_savestate;
837 just_did_loadstate = first_round;
838 continue;
840 long resetcycles = -1;
841 ack_frame_tick(get_utime());
842 if(amode == ADVANCE_SKIPLAG_PENDING)
843 amode = ADVANCE_SKIPLAG;
845 if(!first_round) {
846 resetcycles = movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
847 if(amode == ADVANCE_QUIT && queued_saves.empty())
848 break;
849 bool delayed_reset = (resetcycles > 0);
850 pending_reset_cycles = -1;
851 if(!handle_reset(resetcycles)) {
852 continue;
854 if(!delayed_reset) {
855 handle_saves();
857 int r = 0;
858 if(queued_saves.empty())
859 r = handle_load();
860 if(r > 0 || system_corrupt) {
861 first_round = our_movie.is_savestate;
862 if(system_corrupt)
863 amode = ADVANCE_PAUSE;
864 else
865 amode = old_mode;
866 just_did_loadstate = first_round;
867 continue;
868 } else if(r < 0) {
869 //Not exactly desriable, but this at least won't desync.
870 amode = ADVANCE_PAUSE;
873 if(just_did_loadstate) {
874 //If we just loadstated, we are up to date.
875 if(amode == ADVANCE_QUIT)
876 break;
877 platform::cancel_wait();
878 platform::set_paused(amode == ADVANCE_PAUSE);
879 platform::flush_command_queue();
880 //We already have done the reset this frame if we are going to do one at all.
881 movb.get_movie().set_controls(controls.get(movb.get_movie().get_current_frame()));
882 just_did_loadstate = false;
884 frame_irq_time = get_utime() - time_x;
885 SNES::system.run();
886 time_x = get_utime();
887 if(amode == ADVANCE_AUTO)
888 platform::wait(to_wait_frame(get_utime()));
889 first_round = false;
890 lua_callback_do_frame();
892 information_dispatch::do_dump_end();
893 SNES::interface = old_inteface;