1 #include "mainloop.hpp"
4 #include "controller.hpp"
5 #include "framebuffer.hpp"
6 #include "moviedata.hpp"
8 #include "framerate.hpp"
9 #include "memorywatch.hpp"
14 #include "moviefile.hpp"
17 #include "settings.hpp"
22 #include "memorymanip.hpp"
27 #include <snes/snes.hpp>
28 #include <ui-libsnes/libsnes.hpp>
29 #include "framerate.hpp"
31 #define SPECIAL_FRAME_START 0
32 #define SPECIAL_FRAME_VIDEO 1
33 #define SPECIAL_SAVEPOINT 2
34 #define SPECIAL_NONE 3
36 #define RTC_SUBSECOND_INCREMENT_PAL 69242724928ULL
37 #define RTC_SUBSECOND_INCREMENT_NTSC 57615439935ULL
38 #define RTC_SUBSECOND_INCREMENT_NTSC_I 57615762380ULL
39 #define RTC_SUBSECONDS_PER_SECOND 3462619485020ULL
42 void update_movie_state();
48 ADVANCE_QUIT
, //Quit the emulator.
49 ADVANCE_AUTO
, //Normal (possibly slowed down play).
50 ADVANCE_FRAME
, //Frame advance.
51 ADVANCE_SUBFRAME
, //Subframe advance.
52 ADVANCE_SKIPLAG
, //Skip lag (oneshot, reverts to normal).
53 ADVANCE_SKIPLAG_PENDING
, //Activate skip lag mode at next frame.
54 ADVANCE_PAUSE
, //Unconditional pause.
58 std::map
<std::string
, std::string
> memory_watches
;
59 //Previous mouse mask.
60 int prev_mouse_mask
= 0;
61 //Flags related to repeating advance.
64 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
65 enum advance_mode amode
;
66 //Mode and filename of pending load, one of LOAD_* constants.
68 std::string pending_load
;
69 //Queued saves (all savestates).
70 std::set
<std::string
> queued_saves
;
71 bool stepping_into_save
;
73 std::vector
<std::string
> save_jukebox
;
74 size_t save_jukebox_pointer
;
75 //Emulator status area.
76 std::map
<std::string
, std::string
>* status
;
77 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
78 long pending_reset_cycles
= -1;
79 //Set by every video refresh.
80 bool video_refresh_done
;
81 //Special subframe location. One of SPECIAL_* constants.
84 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
89 class firmware_path_setting
: public setting
92 firmware_path_setting() : setting("firmwarepath") { _firmwarepath
= "."; default_firmware
= true; }
93 void blank() throw(std::bad_alloc
, std::runtime_error
)
96 default_firmware
= true;
101 return !default_firmware
;
104 void set(const std::string
& value
) throw(std::bad_alloc
, std::runtime_error
)
106 _firmwarepath
= value
;
107 default_firmware
= false;
110 std::string
get() throw(std::bad_alloc
)
112 return _firmwarepath
;
115 operator std::string() throw(std::bad_alloc
)
117 return _firmwarepath
;
120 std::string _firmwarepath
;
121 bool default_firmware
;
122 } firmwarepath_setting
;
124 controls_t
movie_logic::update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
126 if(lua_requests_subframe_paint
)
127 redraw_framebuffer();
130 if(amode
== ADVANCE_SUBFRAME
) {
131 if(!cancel_advance
&& !advanced_once
) {
132 window::wait_usec(advance_timeout_first
* 1000);
133 advanced_once
= true;
136 amode
= ADVANCE_PAUSE
;
137 cancel_advance
= false;
139 window::paused(amode
== ADVANCE_PAUSE
);
140 } else if(amode
== ADVANCE_FRAME
) {
143 window::paused(amode
== ADVANCE_SKIPLAG
|| amode
== ADVANCE_PAUSE
);
144 cancel_advance
= false;
146 if(amode
== ADVANCE_SKIPLAG
)
147 amode
= ADVANCE_AUTO
;
148 location_special
= SPECIAL_NONE
;
149 update_movie_state();
151 if(amode
== ADVANCE_SKIPLAG_PENDING
)
152 amode
= ADVANCE_SKIPLAG
;
153 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
154 if(!cancel_advance
) {
155 window::wait_usec(advanced_once
? to_wait_frame(get_utime()) :
156 (advance_timeout_first
* 1000));
157 advanced_once
= true;
160 amode
= ADVANCE_PAUSE
;
161 cancel_advance
= false;
163 window::paused(amode
== ADVANCE_PAUSE
);
165 window::paused((amode
== ADVANCE_PAUSE
));
166 cancel_advance
= false;
168 location_special
= SPECIAL_FRAME_START
;
169 update_movie_state();
172 window::notify_screen_update();
173 window::poll_inputs();
174 if(!subframe
&& pending_reset_cycles
>= 0)
175 set_curcontrols_reset(pending_reset_cycles
);
177 set_curcontrols_reset(-1);
178 controls_t tmp
= get_current_controls(movb
.get_movie().get_current_frame());
179 lua_callback_do_input(tmp
, subframe
);
187 //Do pending load (automatically unpauses).
188 void mark_pending_load(const std::string
& filename
, int lmode
)
191 pending_load
= filename
;
192 amode
= ADVANCE_AUTO
;
193 window::cancel_wait();
194 window::paused(false);
197 void mark_pending_save(const std::string
& filename
, int smode
)
199 if(smode
== SAVE_MOVIE
) {
200 //Just do this immediately.
201 do_save_movie(filename
);
204 queued_saves
.insert(filename
);
205 window::message("Pending save on '" + filename
+ "'");
208 class dump_watch
: public av_snooper::dump_notification
210 void dump_starting() throw()
212 update_movie_state();
214 void dump_ending() throw()
216 update_movie_state();
220 uint16_t lpalette
[0x80000];
223 static bool palette_init
= false;
227 for(unsigned i
= 0; i
< 0x80000; i
++) {
228 unsigned l
= (i
>> 15) & 0xF;
229 unsigned b
= (i
>> 10) & 0x1F;
230 unsigned g
= (i
>> 5) & 0x1F;
231 unsigned r
= (i
>> 0) & 0x1F;
232 double _l
= static_cast<double>(l
) / 15;
236 lpalette
[i
] = (r
<< 10) | (g
<< 5) | b
;
241 void update_movie_state()
243 auto& _status
= window::get_emustatus();
244 if(!system_corrupt
) {
245 std::ostringstream x
;
246 x
<< movb
.get_movie().get_current_frame() << "(";
247 if(location_special
== SPECIAL_FRAME_START
)
249 else if(location_special
== SPECIAL_SAVEPOINT
)
251 else if(location_special
== SPECIAL_FRAME_VIDEO
)
254 x
<< movb
.get_movie().next_poll_number();
255 x
<< ";" << movb
.get_movie().get_lag_frames() << ")/" << movb
.get_movie().get_frame_count();
256 _status
["Frame"] = x
.str();
258 _status
["Frame"] = "N/A";
259 #ifndef NO_TIME_INTERCEPT
260 if(!system_corrupt
) {
261 time_t timevalue
= static_cast<time_t>(our_movie
.rtc_second
);
262 struct tm
* time_decompose
= gmtime(&timevalue
);
263 char datebuffer
[512];
264 char timebuffer
[512];
265 strftime(datebuffer
, 511, "%a %d %b %Y", time_decompose
);
266 strftime(timebuffer
, 511, "%H:%M:%S", time_decompose
);
267 _status
["RTCdate"] = datebuffer
;
268 _status
["RTCtime"] = timebuffer
;
270 _status
["RTCdate"] = "N/A";
271 _status
["RTCtime"] = "N/A";
275 std::ostringstream x
;
278 else if(movb
.get_movie().readonly_mode())
282 if(av_snooper::dump_in_progress())
284 _status
["Flags"] = x
.str();
286 if(save_jukebox
.size() > 0)
287 _status
["Saveslot"] = save_jukebox
[save_jukebox_pointer
];
289 _status
.erase("Saveslot");
290 for(auto i
= memory_watches
.begin(); i
!= memory_watches
.end(); i
++) {
292 _status
["M[" + i
->first
+ "]"] = evaluate_watch(i
->second
);
298 if(movb
.get_movie().readonly_mode())
299 c
= movb
.get_movie().get_controls();
301 c
= get_current_controls(movb
.get_movie().get_current_frame());
302 for(unsigned i
= 0; i
< 8; i
++) {
303 unsigned pindex
= controller_index_by_logical(i
);
304 unsigned port
= pindex
>> 2;
305 unsigned dev
= pindex
& 3;
306 auto ctype
= controller_type_by_logical(i
);
307 std::ostringstream x
;
310 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_LEFT
) ? "l" : " ");
311 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_RIGHT
) ? "r" : " ");
312 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_UP
) ? "u" : " ");
313 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_DOWN
) ? "d" : " ");
314 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_A
) ? "A" : " ");
315 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_B
) ? "B" : " ");
316 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_X
) ? "X" : " ");
317 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_Y
) ? "Y" : " ");
318 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_L
) ? "L" : " ");
319 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_R
) ? "R" : " ");
320 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_START
) ? "S" : " ");
321 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_SELECT
) ? "s" : " ");
324 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_X
) << " ";
325 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_Y
) << " ";
326 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_LEFT
) ? "L" : " ");
327 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_RIGHT
) ? "R" : " ");
330 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_X
) << " ";
331 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_Y
) << " ";
332 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
) ? "T" : " ");
333 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
) ? "C" : " ");
334 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TURBO
) ? "t" : " ");
335 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
) ? "P" : " ");
338 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_X
) << " ";
339 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_Y
) << " ";
340 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_START
) ? "T" : " ");
341 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_TRIGGER
) ? "S" : " ");
346 char y
[3] = {'P', 0, 0};
348 _status
[std::string(y
)] = x
.str();
353 class my_interface
: public SNES::Interface
355 string
path(SNES::Cartridge::Slot slot
, const string
&hint
)
357 const char* _hint
= hint
;
358 std::string _hint2
= _hint
;
359 std::string fwp
= firmwarepath_setting
;
360 std::string finalpath
= fwp
+ "/" + _hint2
;
361 return finalpath
.c_str();
364 void videoRefresh(const uint32_t* data
, bool hires
, bool interlace
, bool overscan
)
367 static uint16_t _data
[512 * 512];
368 if(stepping_into_save
)
369 window::message("Got video refresh in runtosave, expect desyncs!");
370 video_refresh_done
= true;
371 bool region
= (SNES::system
.region() == SNES::System::Region::PAL
);
372 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
373 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
374 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
375 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
376 for(size_t i
= 0; i
< 512 * 512; i
++)
377 _data
[i
] = lpalette
[data
[i
] & 0x7FFFF];
378 lcscreen
ls(_data
, hires
, interlace
, overscan
, region
);
380 location_special
= SPECIAL_FRAME_VIDEO
;
381 update_movie_state();
382 redraw_framebuffer();
383 uint32_t fps_n
, fps_d
;
387 our_movie
.rtc_subsecond
+= RTC_SUBSECOND_INCREMENT_PAL
;
388 } else if(!interlace
) {
391 our_movie
.rtc_subsecond
+= RTC_SUBSECOND_INCREMENT_NTSC
;
393 //Yes, interlace makes difference with NTSC but not on PAL.
396 our_movie
.rtc_subsecond
+= RTC_SUBSECOND_INCREMENT_NTSC_I
;
398 if(our_movie
.rtc_subsecond
>= RTC_SUBSECONDS_PER_SECOND
) {
399 our_movie
.rtc_second
++;
400 our_movie
.rtc_subsecond
-= RTC_SUBSECONDS_PER_SECOND
;
402 av_snooper::frame(ls
, fps_n
, fps_d
, true);
405 void audioSample(int16_t l_sample
, int16_t r_sample
)
407 uint16_t _l
= l_sample
;
408 uint16_t _r
= r_sample
;
409 window::play_audio_sample(_l
+ 32768, _r
+ 32768);
410 av_snooper::sample(_l
, _r
, true);
413 int16_t inputPoll(bool port
, SNES::Input::Device device
, unsigned index
, unsigned id
)
416 x
= movb
.input_poll(port
, index
, id
);
417 //if(id == SNES_DEVICE_ID_JOYPAD_START)
418 // std::cerr << "bsnes polling for start on (" << port << "," << index << ")=" << x << std::endl;
419 lua_callback_snoop_input(port
? 1 : 0, index
, id
, x
);
426 function_ptr_command
<> count_rerecords("count-rerecords", "Count rerecords",
427 "Syntax: count-rerecords\nCounts rerecords.\n",
428 []() throw(std::bad_alloc
, std::runtime_error
) {
429 std::vector
<char> tmp
;
430 uint64_t x
= rrdata::write(tmp
);
431 messages
<< x
<< " rerecord(s)" << std::endl
;
434 function_ptr_command
<const std::string
&> quit_emulator("quit-emulator", "Quit the emulator",
435 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
436 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
437 if(args
== "/y" || window::modal_message("Really quit?", true)) {
438 amode
= ADVANCE_QUIT
;
439 window::paused(false);
440 window::cancel_wait();
444 function_ptr_command
<> pause_emulator("pause-emulator", "(Un)pause the emulator",
445 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
446 []() throw(std::bad_alloc
, std::runtime_error
) {
447 if(amode
!= ADVANCE_AUTO
) {
448 amode
= ADVANCE_AUTO
;
449 window::paused(false);
450 window::cancel_wait();
451 window::message("Unpaused");
453 window::cancel_wait();
454 cancel_advance
= false;
455 amode
= ADVANCE_PAUSE
;
456 window::message("Paused");
460 function_ptr_command
<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
461 "Syntax: cycle-jukebox-backwards\nCycle save jukebox backwards\n",
462 []() throw(std::bad_alloc
, std::runtime_error
) {
463 if(save_jukebox_pointer
== 0)
464 save_jukebox_pointer
= save_jukebox
.size() - 1;
466 save_jukebox_pointer
--;
467 if(save_jukebox_pointer
>= save_jukebox
.size())
468 save_jukebox_pointer
= 0;
469 update_movie_state();
470 window::notify_screen_update();
473 function_ptr_command
<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
474 "Syntax: cycle-jukebox-forwards\nCycle save jukebox forwards\n",
475 []() throw(std::bad_alloc
, std::runtime_error
) {
476 if(save_jukebox_pointer
== save_jukebox
.size() - 1)
477 save_jukebox_pointer
= 0;
479 save_jukebox_pointer
++;
480 if(save_jukebox_pointer
>= save_jukebox
.size())
481 save_jukebox_pointer
= 0;
482 update_movie_state();
483 window::notify_screen_update();
486 function_ptr_command
<arg_filename
> add_jukebox("add-jukebox-save", "Add save to jukebox",
487 "Syntax: add-jukebox-save\nAdd save to jukebox\n",
488 [](arg_filename filename
) throw(std::bad_alloc
, std::runtime_error
) {
489 save_jukebox
.push_back(filename
);
490 update_movie_state();
491 window::notify_screen_update();
494 function_ptr_command
<> load_jukebox("load-jukebox", "Load save from jukebox",
495 "Syntax: load-jukebox\nLoad save from jukebox\n",
496 []() throw(std::bad_alloc
, std::runtime_error
) {
497 if(!save_jukebox
.size())
498 throw std::runtime_error("No saves in jukebox");
499 mark_pending_load(save_jukebox
[save_jukebox_pointer
], LOAD_STATE_CURRENT
);
502 function_ptr_command
<> save_jukebox_c("save-jukebox", "Save save to jukebox",
503 "Syntax: save-jukebox\nSave save to jukebox\n",
504 []() throw(std::bad_alloc
, std::runtime_error
) {
505 if(!save_jukebox
.size())
506 throw std::runtime_error("No saves in jukebox");
507 mark_pending_save(save_jukebox
[save_jukebox_pointer
], SAVE_STATE
);
510 function_ptr_command
<> padvance_frame("+advance-frame", "Advance one frame",
511 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
512 []() throw(std::bad_alloc
, std::runtime_error
) {
513 amode
= ADVANCE_FRAME
;
514 cancel_advance
= false;
515 advanced_once
= false;
516 window::cancel_wait();
517 window::paused(false);
520 function_ptr_command
<> nadvance_frame("-advance-frame", "Advance one frame",
521 "No help available\n",
522 []() throw(std::bad_alloc
, std::runtime_error
) {
523 cancel_advance
= true;
524 window::cancel_wait();
525 window::paused(false);
528 function_ptr_command
<> padvance_poll("+advance-poll", "Advance one subframe",
529 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
530 []() throw(std::bad_alloc
, std::runtime_error
) {
531 amode
= ADVANCE_SUBFRAME
;
532 cancel_advance
= false;
533 advanced_once
= false;
534 window::cancel_wait();
535 window::paused(false);
538 function_ptr_command
<> nadvance_poll("-advance-poll", "Advance one subframe",
539 "No help available\n",
540 []() throw(std::bad_alloc
, std::runtime_error
) {
541 cancel_advance
= true;
542 window::cancel_wait();
543 window::paused(false);
546 function_ptr_command
<> advance_skiplag("advance-skiplag", "Skip to next poll",
547 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
548 []() throw(std::bad_alloc
, std::runtime_error
) {
549 amode
= ADVANCE_SKIPLAG
;
550 window::cancel_wait();
551 window::paused(false);
554 function_ptr_command
<> reset_c("reset", "Reset the SNES",
555 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
556 []() throw(std::bad_alloc
, std::runtime_error
) {
557 pending_reset_cycles
= 0;
560 function_ptr_command
<arg_filename
> load_c("load", "Load savestate (current mode)",
561 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
562 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
563 mark_pending_load(args
, LOAD_STATE_CURRENT
);
566 function_ptr_command
<arg_filename
> load_state_c("load-state", "Load savestate (R/W)",
567 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
568 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
569 mark_pending_load(args
, LOAD_STATE_RW
);
572 function_ptr_command
<arg_filename
> load_readonly("load-readonly", "Load savestate (RO)",
573 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
574 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
575 mark_pending_load(args
, LOAD_STATE_RO
);
578 function_ptr_command
<arg_filename
> load_preserve("load-preserve", "Load savestate (preserve input)",
579 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
580 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
581 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
584 function_ptr_command
<arg_filename
> load_movie_c("load-movie", "Load movie",
585 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
586 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
587 mark_pending_load(args
, LOAD_STATE_MOVIE
);
591 function_ptr_command
<arg_filename
> save_state("save-state", "Save state",
592 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
593 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
594 mark_pending_save(args
, SAVE_STATE
);
597 function_ptr_command
<arg_filename
> save_movie("save-movie", "Save movie",
598 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
599 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
600 mark_pending_save(args
, SAVE_MOVIE
);
603 function_ptr_command
<> set_rwmode("set-rwmode", "Switch to read/write mode",
604 "Syntax: set-rwmode\nSwitches to read/write mode\n",
605 []() throw(std::bad_alloc
, std::runtime_error
) {
606 movb
.get_movie().readonly_mode(false);
607 lua_callback_do_readwrite();
608 update_movie_state();
609 window::notify_screen_update();
612 function_ptr_command
<> set_romode("set-romode", "Switch to read-only mode",
613 "Syntax: set-romode\nSwitches to read-only mode\n",
614 []() throw(std::bad_alloc
, std::runtime_error
) {
615 movb
.get_movie().readonly_mode(true);
616 update_movie_state();
617 window::notify_screen_update();
620 function_ptr_command
<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
621 "Syntax: toggle-rwmode\nToggles read/write mode\n",
622 []() throw(std::bad_alloc
, std::runtime_error
) {
623 bool c
= movb
.get_movie().readonly_mode();
624 movb
.get_movie().readonly_mode(!c
);
626 lua_callback_do_readwrite();
627 update_movie_state();
628 window::notify_screen_update();
631 function_ptr_command
<> repaint("repaint", "Redraw the screen",
632 "Syntax: repaint\nRedraws the screen\n",
633 []() throw(std::bad_alloc
, std::runtime_error
) {
634 redraw_framebuffer();
637 function_ptr_command
<tokensplitter
&> add_watch("add-watch", "Add a memory watch",
638 "Syntax: add-watch <name> <expression>\nAdds a new memory watch\n",
639 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
640 std::string name
= t
;
641 if(name
== "" || t
.tail() == "")
642 throw std::runtime_error("syntax: add-watch <name> <expr>");
643 std::cerr
<< "Add watch: '" << name
<< "'" << std::endl
;
644 memory_watches
[name
] = t
.tail();
645 update_movie_state();
648 function_ptr_command
<tokensplitter
&> remove_watch("remove-watch", "Remove a memory watch",
649 "Syntax: remove-watch <name>\nRemoves a memory watch\n",
650 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
651 std::string name
= t
;
652 if(name
== "" || t
.tail() != "") {
653 messages
<< "syntax: remove-watch <name>" << std::endl
;
656 std::cerr
<< "Erase watch: '" << name
<< "'" << std::endl
;
657 memory_watches
.erase(name
);
658 auto& _status
= window::get_emustatus();
659 _status
.erase("M[" + name
+ "]");
660 update_movie_state();
664 function_ptr_command
<> test1("test-1", "no description available", "No help available\n",
665 []() throw(std::bad_alloc
, std::runtime_error
) {
666 framebuffer
= screen_nosignal
;
667 redraw_framebuffer();
670 function_ptr_command
<> test2("test-2", "no description available", "No help available\n",
671 []() throw(std::bad_alloc
, std::runtime_error
) {
672 framebuffer
= screen_corrupt
;
673 redraw_framebuffer();
676 function_ptr_command
<> test3("test-3", "no description available", "No help available\n",
677 []() throw(std::bad_alloc
, std::runtime_error
) {
682 bool on_quit_prompt
= false;
683 class mywindowcallbacks
: public window_callback
686 void on_close() throw()
689 amode
= ADVANCE_QUIT
;
690 window::paused(false);
691 window::cancel_wait();
694 on_quit_prompt
= true;
696 if(window::modal_message("Really quit?", true)) {
697 amode
= ADVANCE_QUIT
;
698 window::paused(false);
699 window::cancel_wait();
703 on_quit_prompt
= false;
706 void on_click(int32_t x
, int32_t y
, uint32_t buttonmask
) throw()
708 if(buttonmask
& ~prev_mouse_mask
& 1)
709 send_analog_input(x
, y
, 0);
710 if(buttonmask
& ~prev_mouse_mask
& 2)
711 send_analog_input(x
, y
, 1);
712 if(buttonmask
& ~prev_mouse_mask
& 4)
713 send_analog_input(x
, y
, 2);
714 prev_mouse_mask
= buttonmask
;
718 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
722 if(pending_load
!= "") {
723 system_corrupt
= false;
724 if(!do_load_state(pending_load
, loadmode
)) {
728 redraw_framebuffer();
730 pending_reset_cycles
= -1;
731 amode
= ADVANCE_AUTO
;
732 window::cancel_wait();
733 window::paused(false);
734 if(!system_corrupt
) {
735 location_special
= SPECIAL_SAVEPOINT
;
736 update_movie_state();
737 window::notify_screen_update();
738 window::poll_inputs();
745 //If there are pending saves, perform them.
748 if(!queued_saves
.empty()) {
749 stepping_into_save
= true;
750 SNES::system
.runtosave();
751 stepping_into_save
= false;
752 for(auto i
= queued_saves
.begin(); i
!= queued_saves
.end(); i
++)
755 queued_saves
.clear();
758 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
759 bool handle_reset(long cycles
)
763 video_refresh_done
= false;
765 window::message("SNES reset");
766 else if(cycles
> 0) {
767 window::message("SNES delayed reset not implemented (doing immediate reset)");
768 /* ... This code is just too buggy.
769 long cycles_executed = 0;
770 messages << "Executing delayed reset... This can take some time!" << std::endl;
771 while(cycles_executed < cycles && !video_refresh_done) {
772 //Poll inputs once in a while to prevent activating watchdog.
773 if(cycles_executed % 100 == 0)
774 window::poll_inputs();
778 if(!video_refresh_done)
779 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
781 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
784 SNES::system
.reset();
785 framebuffer
= screen_nosignal
;
786 lua_callback_do_reset();
787 redraw_framebuffer();
788 if(video_refresh_done
) {
789 to_wait_frame(get_utime());
795 bool handle_corrupt()
799 while(system_corrupt
) {
800 redraw_framebuffer();
801 window::cancel_wait();
802 window::paused(true);
803 window::poll_inputs();
805 if(amode
== ADVANCE_QUIT
)
811 void print_controller_mappings()
813 for(unsigned i
= 0; i
< 8; i
++) {
814 std::string type
= "unknown";
815 if(controller_type_by_logical(i
) == DT_NONE
)
816 type
= "disconnected";
817 if(controller_type_by_logical(i
) == DT_GAMEPAD
)
819 if(controller_type_by_logical(i
) == DT_MOUSE
)
821 if(controller_type_by_logical(i
) == DT_SUPERSCOPE
)
823 if(controller_type_by_logical(i
) == DT_JUSTIFIER
)
825 messages
<< "Physical controller mapping: Logical " << (i
+ 1) << " is physical " <<
826 controller_index_by_logical(i
) << " (" << type
<< ")" << std::endl
;
831 void main_loop(struct loaded_rom
& rom
, struct moviefile
& initial
, bool load_has_to_succeed
) throw(std::bad_alloc
,
834 //Basic initialization.
835 init_special_screens();
838 auto old_inteface
= SNES::interface
;
839 SNES::interface
= &intrf
;
840 intrf
.initialize(&intrf
);
841 status
= &window::get_emustatus();
842 window_callback::set_callback_handler(mywcb
);
844 //Load our given movie.
845 bool first_round
= false;
846 bool just_did_loadstate
= false;
848 do_load_state(initial
, LOAD_STATE_DEFAULT
);
849 location_special
= SPECIAL_SAVEPOINT
;
850 update_movie_state();
851 first_round
= our_movie
.is_savestate
;
852 just_did_loadstate
= first_round
;
853 } catch(std::bad_alloc
& e
) {
855 } catch(std::exception
& e
) {
856 messages
<< "ERROR: Can't load initial state: " << e
.what() << std::endl
;
857 if(load_has_to_succeed
) {
858 messages
<< "FATAL: Can't load movie" << std::endl
;
859 window::fatal_error();
861 system_corrupt
= true;
862 update_movie_state();
863 framebuffer
= screen_corrupt
;
864 redraw_framebuffer();
867 lua_callback_startup();
869 //print_controller_mappings();
870 av_snooper::add_dump_notifier(dumpwatch
);
871 window::set_main_surface(main_screen
);
872 redraw_framebuffer();
873 window::paused(false);
874 amode
= ADVANCE_PAUSE
;
875 while(amode
!= ADVANCE_QUIT
) {
876 if(handle_corrupt()) {
877 first_round
= our_movie
.is_savestate
;
878 just_did_loadstate
= first_round
;
881 long resetcycles
= -1;
882 ack_frame_tick(get_utime());
883 if(amode
== ADVANCE_SKIPLAG_PENDING
)
884 amode
= ADVANCE_SKIPLAG
;
887 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
888 if(amode
== ADVANCE_QUIT
)
890 bool delayed_reset
= (resetcycles
> 0);
891 pending_reset_cycles
= -1;
892 if(!handle_reset(resetcycles
)) {
898 int r
= handle_load();
899 if(r
> 0 || system_corrupt
) {
900 first_round
= our_movie
.is_savestate
;
901 amode
= ADVANCE_PAUSE
;
902 just_did_loadstate
= first_round
;
905 //Not exactly desriable, but this at least won't desync.
906 amode
= ADVANCE_PAUSE
;
909 if(just_did_loadstate
) {
910 if(amode
== ADVANCE_QUIT
)
912 amode
= ADVANCE_PAUSE
;
913 redraw_framebuffer();
914 window::cancel_wait();
915 window::paused(true);
916 window::poll_inputs();
917 //We already have done the reset this frame if we are going to do one at all.
918 movb
.get_movie().set_controls(get_current_controls(movb
.get_movie().get_current_frame()));
919 just_did_loadstate
= false;
922 if(amode
== ADVANCE_AUTO
)
923 window::wait_usec(to_wait_frame(get_utime()));
926 av_snooper::end(true);
927 SNES::interface
= old_inteface
;