1 /*
2  * machine.c - Interface to machine-specific implementations.
3  *
4  * Written by
5  *  Andreas Boose <viceteam@t-online.de>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #include "alarm.h"
34 #include "archdep.h"
35 #include "attach.h"
36 #include "autostart.h"
37 #include "clkguard.h"
38 #include "cmdline.h"
39 #include "console.h"
40 #include "diskimage.h"
41 #include "drive.h"
42 #include "vice-event.h"
43 #include "fliplist.h"
44 #include "fsdevice.h"
45 #include "gfxoutput.h"
46 #include "initcmdline.h"
47 #include "interrupt.h"
48 #include "joy.h"
49 #include "kbdbuf.h"
50 #include "keyboard.h"
51 #include "lib.h"
52 #include "log.h"
53 #include "machine-video.h"
54 #include "machine.h"
55 #include "maincpu.h"
56 #include "mem.h"
57 #include "monitor.h"
58 #include "monitor_network.h"
59 #include "monitor_binary.h"
60 #include "network.h"
61 #include "printer.h"
62 #include "resources.h"
63 #include "romset.h"
64 #include "screenshot.h"
65 #include "sound.h"
66 #include "sysfile.h"
67 #include "tape.h"
68 #include "traps.h"
69 #include "types.h"
70 #include "uiapi.h"
71 #include "util.h"
72 #include "video.h"
73 #include "vsync.h"
74 #include "zfile.h"
75 
76 /* #define DEBUGMACHINE */
77 
78 #ifndef EXIT_SUCCESS
79 #define EXIT_SUCCESS 0
80 #endif
81 
82 #ifdef DEBUGMACHINE
83 #define DBG(x) printf x
84 #else
85 #define DBG(x)
86 #endif
87 
88 static int machine_init_was_called = 0;
89 static int mem_initialized = 0;
90 static bool is_jammed = false;
91 static char *jam_reason = NULL;
92 static int jam_action = MACHINE_JAM_ACTION_DIALOG;
93 int machine_keymap_index;
94 static char *ExitScreenshotName = NULL;
95 static char *ExitScreenshotName1 = NULL;
96 
97 
98 
machine_jam(const char * format,...)99 unsigned int machine_jam(const char *format, ...)
100 {
101     va_list ap;
102     ui_jam_action_t ret = JAM_NONE;
103 
104     /* always ignore subsequent JAMs. reset would clear the flag again, not
105      * setting it when going to the monitor would just repeatedly pop up the
106      * jam dialog (until reset)
107      */
108     if (is_jammed) {
109         return JAM_NONE;
110     }
111 
112     is_jammed = true;
113 
114     va_start(ap, format);
115     if (jam_reason) {
116         lib_free(jam_reason);
117         jam_reason = NULL;
118     }
119     jam_reason = lib_mvsprintf(format, ap);
120     va_end(ap);
121 
122     log_message(LOG_DEFAULT, "*** %s", jam_reason);
123 
124     vsync_suspend_speed_eval();
125     sound_suspend();
126 
127     if (jam_action == MACHINE_JAM_ACTION_DIALOG) {
128         if (monitor_is_remote() || monitor_is_binary()) {
129             if (monitor_is_remote()) {
130                 ret = monitor_network_ui_jam_dialog(jam_reason);
131             }
132 
133             if (monitor_is_binary()) {
134                 ret = monitor_binary_ui_jam_dialog(jam_reason);
135             }
136         } else if (!console_mode) {
137             ret = ui_jam_dialog(jam_reason);
138         }
139     } else if (jam_action == MACHINE_JAM_ACTION_QUIT) {
140         archdep_vice_exit(EXIT_SUCCESS);
141     } else {
142         int actions[4] = {
143             -1, UI_JAM_MONITOR, UI_JAM_RESET, UI_JAM_HARD_RESET
144         };
145         ret = actions[jam_action - 1];
146     }
147 
148     switch (ret) {
149         case UI_JAM_RESET:
150             return JAM_RESET;
151         case UI_JAM_HARD_RESET:
152             return JAM_HARD_RESET;
153         case UI_JAM_MONITOR:
154             return JAM_MONITOR;
155         default:
156             break;
157     }
158     return JAM_NONE;
159 }
160 
machine_is_jammed(void)161 bool machine_is_jammed(void)
162 {
163     return is_jammed;
164 }
165 
machine_jam_reason(void)166 char *machine_jam_reason(void)
167 {
168     return jam_reason;
169 }
170 
machine_trigger_reset_internal(const unsigned int mode)171 static void machine_trigger_reset_internal(const unsigned int mode)
172 {
173     is_jammed = false;
174 
175     if (jam_reason) {
176         lib_free(jam_reason);
177         jam_reason = NULL;
178     }
179 
180     switch (mode) {
181         case MACHINE_RESET_MODE_HARD:
182             mem_initialized = 0; /* force memory initialization */
183             machine_specific_powerup();
184         /* Fall through.  */
185         case MACHINE_RESET_MODE_SOFT:
186             maincpu_trigger_reset();
187             break;
188     }
189 }
190 
machine_trigger_reset(const unsigned int mode)191 void machine_trigger_reset(const unsigned int mode)
192 {
193     if (event_playback_active()) {
194         return;
195     }
196 
197     if (network_connected()) {
198         network_event_record(EVENT_RESETCPU, (void *)&mode, sizeof(mode));
199     } else {
200         event_record(EVENT_RESETCPU, (void *)&mode, sizeof(mode));
201         machine_trigger_reset_internal(mode);
202     }
203 }
204 
machine_reset_event_playback(CLOCK offset,void * data)205 void machine_reset_event_playback(CLOCK offset, void *data)
206 {
207     machine_trigger_reset_internal(((unsigned int*)data)[0]);
208 }
209 
machine_reset(void)210 void machine_reset(void)
211 {
212     log_message(LOG_DEFAULT, "Main CPU: RESET.");
213 
214     is_jammed = false;
215 
216     if (jam_reason) {
217         lib_free(jam_reason);
218         jam_reason = NULL;
219     }
220 
221     /* Do machine-specific initialization.  */
222     if (!mem_initialized) {
223         mem_powerup();
224         mem_initialized = 1;
225     }
226 
227     machine_specific_reset();
228 
229     autostart_reset();
230 
231     mem_initialize_memory();
232 
233     event_reset_ack();
234 
235     /* Give the monitor a chance to break immediately */
236     monitor_reset_hook();
237 
238     vsync_reset_hook();
239 }
240 
machine_maincpu_clk_overflow_callback(CLOCK sub,void * data)241 static void machine_maincpu_clk_overflow_callback(CLOCK sub, void *data)
242 {
243     alarm_context_time_warp(maincpu_alarm_context, sub, -1);
244     interrupt_cpu_status_time_warp(maincpu_int_status, sub, -1);
245 }
246 
machine_maincpu_init(void)247 void machine_maincpu_init(void)
248 {
249     maincpu_init();
250     maincpu_monitor_interface = lib_calloc(1, sizeof(monitor_interface_t));
251 }
252 
machine_early_init(void)253 void machine_early_init(void)
254 {
255     maincpu_alarm_context = alarm_context_new("MainCPU");
256 
257     maincpu_clk_guard = clk_guard_new(&maincpu_clk, CLOCK_MAX
258                                       - CLKGUARD_SUB_MIN);
259 
260     clk_guard_add_callback(maincpu_clk_guard,
261                            machine_maincpu_clk_overflow_callback, NULL);
262 }
263 
machine_init(void)264 int machine_init(void)
265 {
266     machine_init_was_called = 1;
267 
268     machine_video_init();
269 
270     fsdevice_init();
271     file_system_init();
272     mem_initialize_memory();
273 
274     return machine_specific_init();
275 }
276 
machine_maincpu_shutdown(void)277 static void machine_maincpu_shutdown(void)
278 {
279     if (maincpu_alarm_context != NULL) {
280         alarm_context_destroy(maincpu_alarm_context);
281     }
282     if (maincpu_clk_guard != NULL) {
283         clk_guard_destroy(maincpu_clk_guard);
284     }
285 
286     lib_free(maincpu_monitor_interface);
287     maincpu_shutdown();
288 
289     if (jam_reason) {
290         lib_free(jam_reason);
291         jam_reason = NULL;
292     }
293 }
294 
screenshot_at_exit(void)295 static void screenshot_at_exit(void)
296 {
297     struct video_canvas_s *canvas;
298 
299     if ((ExitScreenshotName != NULL) && (ExitScreenshotName[0] != 0)) {
300         /* FIXME: this always uses the first canvas, for x128 this is the VDC */
301         canvas = machine_video_canvas_get(0);
302         /* FIXME: perhaps select driver based on the extension of the given name. for now PNG is good enough :) */
303         screenshot_save("PNG", ExitScreenshotName, canvas);
304     }
305     if (machine_class == VICE_MACHINE_C128) {
306         if ((ExitScreenshotName1 != NULL) && (ExitScreenshotName1[0] != 0)) {
307             /* FIXME: this always uses the second canvas, for x128 this is the VICII */
308             canvas = machine_video_canvas_get(1);
309             /* FIXME: perhaps select driver based on the extension of the given name. for now PNG is good enough :) */
310             screenshot_save("PNG", ExitScreenshotName1, canvas);
311         }
312     }
313 }
314 
machine_shutdown(void)315 void machine_shutdown(void)
316 {
317     int save_on_exit;
318 
319     if (!machine_init_was_called) {
320         /* happens at the -help command line command*/
321         return;
322     }
323 
324     /*
325      * Avoid SoundRecordDeviceName being written to vicerc when save-on-exit
326      * is enabled. If recording is/was active vicerc will contain some setting
327      * for this resource and display an error.
328      */
329     sound_stop_recording();
330 
331     resources_get_int("SaveResourcesOnExit", &save_on_exit);
332     if (save_on_exit) {
333         /*
334          * FIXME: I tried moving this to resources_shutdown, but if you try to save
335          * resources after machine_specific_shutdown() is called then it crashes.
336          * That's a bit of a code smell to me. --dqh 2020-08-01
337          */
338         resources_save(NULL);
339     }
340 
341     screenshot_at_exit();
342     screenshot_shutdown();
343 
344     file_system_detach_disk_shutdown();
345 
346     machine_specific_shutdown();
347 
348     autostart_shutdown();
349 
350     joystick_close();
351 
352     sound_close();
353 
354     printer_shutdown();
355     gfxoutput_shutdown();
356 
357     fliplist_shutdown();
358     file_system_shutdown();
359     fsdevice_shutdown();
360 
361     tape_shutdown();
362 
363     traps_shutdown();
364 
365     kbdbuf_shutdown();
366     keyboard_shutdown();
367 
368     monitor_shutdown();
369 
370     console_close_all();
371 
372     cmdline_shutdown();
373     initcmdline_shutdown();
374 
375     resources_shutdown();
376 
377     drive_shutdown();
378 
379     machine_maincpu_shutdown();
380 
381     video_shutdown();
382 
383     if (!console_mode) {
384         ui_shutdown();
385     }
386 
387     sysfile_shutdown();
388 
389     log_close_all();
390 
391     event_shutdown();
392 
393     network_shutdown();
394 
395     autostart_resources_shutdown();
396     sound_resources_shutdown();
397     video_resources_shutdown();
398     machine_resources_shutdown();
399     machine_common_resources_shutdown();
400 
401     sysfile_resources_shutdown();
402     zfile_shutdown();
403     ui_resources_shutdown();
404     log_resources_shutdown();
405     fliplist_resources_shutdown();
406     romset_resources_shutdown();
407 #ifdef HAVE_NETWORK
408     monitor_network_resources_shutdown();
409     monitor_binary_resources_shutdown();
410 #endif
411     monitor_resources_shutdown();
412 
413     archdep_shutdown();
414 }
415 
416 /* --------------------------------------------------------- */
417 /* Resources & cmdline */
418 
set_jam_action(int val,void * param)419 static int set_jam_action(int val, void *param)
420 {
421     switch (val) {
422         case MACHINE_JAM_ACTION_DIALOG:
423         case MACHINE_JAM_ACTION_CONTINUE:
424         case MACHINE_JAM_ACTION_MONITOR:
425         case MACHINE_JAM_ACTION_RESET:
426         case MACHINE_JAM_ACTION_HARD_RESET:
427         case MACHINE_JAM_ACTION_QUIT:
428             break;
429         default:
430             return -1;
431     }
432 
433     jam_action = val;
434 
435     return 0;
436 }
437 
set_exit_screenshot_name(const char * val,void * param)438 static int set_exit_screenshot_name(const char *val, void *param)
439 {
440     if (util_string_set(&ExitScreenshotName, val)) {
441         return 0;
442     }
443 
444     return 0;
445 }
446 
set_exit_screenshot_name1(const char * val,void * param)447 static int set_exit_screenshot_name1(const char *val, void *param)
448 {
449     if (util_string_set(&ExitScreenshotName1, val)) {
450         return 0;
451     }
452 
453     return 0;
454 }
455 
456 static resource_string_t resources_string[] = {
457     { "ExitScreenshotName", "", RES_EVENT_NO, NULL,
458       &ExitScreenshotName, set_exit_screenshot_name, NULL },
459     RESOURCE_STRING_LIST_END
460 };
461 
462 static resource_string_t resources_string_c128[] = {
463     { "ExitScreenshotName1", "", RES_EVENT_NO, NULL,
464       &ExitScreenshotName1, set_exit_screenshot_name1, NULL },
465     RESOURCE_STRING_LIST_END
466 };
467 
468 static const resource_int_t resources_int[] = {
469     { "JAMAction", MACHINE_JAM_ACTION_CONTINUE, RES_EVENT_SAME, NULL,
470       &jam_action, set_jam_action, NULL },
471     RESOURCE_INT_LIST_END
472 };
473 
machine_common_resources_init(void)474 int machine_common_resources_init(void)
475 {
476     if (machine_class != VICE_MACHINE_VSID) {
477         if (resources_register_string(resources_string) < 0) {
478            return -1;
479         }
480         if (machine_class == VICE_MACHINE_C128) {
481             if (resources_register_string(resources_string_c128) < 0) {
482             return -1;
483             }
484         }
485     }
486     return resources_register_int(resources_int);
487 }
488 
machine_common_resources_shutdown(void)489 void machine_common_resources_shutdown(void)
490 {
491     lib_free(ExitScreenshotName);
492     lib_free(ExitScreenshotName1);
493 }
494 
495 static const cmdline_option_t cmdline_options_c128[] =
496 {
497     { "-jamaction", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
498       NULL, NULL, "JAMAction", NULL,
499       "<Type>", "Set action on CPU JAM: (0: Ask, 1: continue, 2: Monitor, 3: Reset, 4: Hard Reset, 5: Quit Emulator)" },
500     { "-exitscreenshot", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
501       NULL, NULL, "ExitScreenshotName", NULL,
502       "<Name>", "Set name of screenshot to save when emulator exits." },
503     { "-exitscreenshotvicii", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
504       NULL, NULL, "ExitScreenshotName1", NULL,
505       "<Name>", "Set name of screenshot to save when emulator exits." },
506     CMDLINE_LIST_END
507 };
508 
509 static const cmdline_option_t cmdline_options[] =
510 {
511     { "-jamaction", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
512       NULL, NULL, "JAMAction", NULL,
513       "<Type>", "Set action on CPU JAM: (0: Ask, 1: continue, 2: Monitor, 3: Reset, 4: Hard Reset, 5: Quit Emulator)" },
514     { "-exitscreenshot", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
515       NULL, NULL, "ExitScreenshotName", NULL,
516       "<Name>", "Set name of screenshot to save when emulator exits." },
517     CMDLINE_LIST_END
518 };
519 
520 static const cmdline_option_t cmdline_options_vsid[] =
521 {
522     { "-jamaction", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
523       NULL, NULL, "JAMAction", NULL,
524       "<Type>", "Set action on CPU JAM: (0: Ask, 1: continue, 2: Monitor, 3: Reset, 4: Hard Reset, 5: Quit Emulator)" },
525     CMDLINE_LIST_END
526 };
527 
machine_common_cmdline_options_init(void)528 int machine_common_cmdline_options_init(void)
529 {
530     if (machine_class == VICE_MACHINE_C128) {
531         return cmdline_register_options(cmdline_options_c128);
532     } else if (machine_class == VICE_MACHINE_VSID) {
533         return cmdline_register_options(cmdline_options_vsid);
534     } else {
535         return cmdline_register_options(cmdline_options);
536     }
537 }
538