1 /*
2  * main.c - VICE main startup entry.
3  *
4  * Written by
5  *  Ettore Perazzoli <ettore@comm2000.it>
6  *  Teemu Rantanen <tvr@cs.hut.fi>
7  *  Vesa-Matti Puro <vmp@lut.fi>
8  *  Jarkko Sonninen <sonninen@lut.fi>
9  *  Jouko Valta <jopi@stekt.oulu.fi>
10  *  Andre Fachat <a.fachat@physik.tu-chemnitz.de>
11  *  Andreas Boose <viceteam@t-online.de>
12  *
13  * This file is part of VICE, the Versatile Commodore Emulator.
14  * See README for copyright notice.
15  *
16  *  This program is free software; you can redistribute it and/or modify
17  *  it under the terms of the GNU General Public License as published by
18  *  the Free Software Foundation; either version 2 of the License, or
19  *  (at your option) any later version.
20  *
21  *  This program is distributed in the hope that it will be useful,
22  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *  GNU General Public License for more details.
25  *
26  *  You should have received a copy of the GNU General Public License
27  *  along with this program; if not, write to the Free Software
28  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29  *  02111-1307  USA.
30  *
31  */
32 
33 /* #define DEBUG_MAIN */
34 
35 #include "vice.h"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #ifdef USE_VICE_THREAD
41 #include <pthread.h>
42 #endif
43 
44 #include "archdep.h"
45 #include "cmdline.h"
46 #include "console.h"
47 #include "debug.h"
48 #include "drive.h"
49 #include "fullscreen.h"
50 #include "gfxoutput.h"
51 #include "info.h"
52 #include "init.h"
53 #include "initcmdline.h"
54 #include "lib.h"
55 #include "log.h"
56 #include "machine.h"
57 #include "maincpu.h"
58 #include "main.h"
59 #include "mainlock.h"
60 #include "resources.h"
61 #include "sysfile.h"
62 #include "tick.h"
63 #include "types.h"
64 #include "uiapi.h"
65 #include "version.h"
66 #include "video.h"
67 #include "vsyncapi.h"
68 
69 #ifdef USE_SVN_REVISION
70 #include "svnversion.h"
71 #endif
72 
73 #ifdef DEBUG_MAIN
74 #define DBG(x)  printf x
75 #else
76 #define DBG(x)
77 #endif
78 
79 int console_mode = 0;
80 int video_disabled_mode = 0;
81 
82 void main_loop_forever(void);
83 
84 #ifdef USE_VICE_THREAD
85 void *vice_thread_main(void *);
86 static pthread_t vice_thread;
87 #endif
88 
89 
90 /** \brief  Size of buffer used to write core team members' names to log/stdout
91  *
92  * 79 characters + 1 byte for '\0'. Assuming a terminal width of 80 characters,
93  * we can only use 79 when calling log_message() since that function adds a
94  * newline to its ouput.
95  */
96 #define TERM_TMP_SIZE  80
97 
98 /* ------------------------------------------------------------------------- */
99 
100 /* This is the main program entry point.  Call this from `main()'.  */
main_program(int argc,char ** argv)101 int main_program(int argc, char **argv)
102 {
103     int i, n;
104     const char *program_name;
105     int ishelp = 0;
106     int loadconfig = 1;
107     char term_tmp[TERM_TMP_SIZE];
108     size_t name_len;
109 
110     lib_init();
111 
112     /* Check for some options at the beginning of the commandline before
113        initializing the user interface or loading the config file.
114        -default => use default config, do not load any config
115        -config  => use specified configuration file
116        -console => no user interface
117     */
118     DBG(("main:early cmdline(argc:%d)\n", argc));
119     for (i = 1; i < argc; i++) {
120         if ((!strcmp(argv[i], "-console")) || (!strcmp(argv[i], "--console"))) {
121             console_mode = 1;
122             video_disabled_mode = 1;
123         } else
124         if ((!strcmp(argv[i], "-config")) || (!strcmp(argv[i], "--config"))) {
125             if ((i + 1) < argc) {
126                 vice_config_file = lib_strdup(argv[++i]);
127                 loadconfig = 1;
128             }
129         } else if (!strcmp(argv[i], "-default")) {
130             loadconfig = 0;
131         } else {
132             break;
133         }
134     }
135     /* remove the already handled items from the commandline, else they will
136        get parsed again later, which causes surprising effects. */
137     for (n = 1; i < argc; n++, i++) {
138         argv[n] = argv[i];
139     }
140     argv[n] = NULL;
141     argc = n;
142 
143     /* help is also special, but we want it NOT to be ignored by the main
144        commandline handler */
145     for (i = 1; i < argc; i++) {
146         if ((!strcmp(argv[i], "-help")) ||
147                    (!strcmp(argv[i], "--help")) ||
148                    (!strcmp(argv[i], "-h")) ||
149                    (!strcmp(argv[i], "-?"))) {
150             ishelp = 1;
151         }
152     }
153 
154     DBG(("main:archdep_init(argc:%d)\n", argc));
155     if (archdep_init(&argc, argv) != 0) {
156         archdep_startup_log_error("archdep_init failed.\n");
157         return -1;
158     }
159 
160     tick_init();
161     maincpu_early_init();
162     machine_setup_context();
163     drive_setup_context();
164     machine_early_init();
165 
166     /* Initialize system file locator.  */
167     sysfile_init(machine_name);
168 
169     gfxoutput_early_init(ishelp);
170     if ((init_resources() < 0) || (init_cmdline_options() < 0)) {
171         return -1;
172     }
173 
174     /* Set factory defaults.  */
175     if (resources_set_defaults() < 0) {
176         archdep_startup_log_error("Cannot set defaults.\n");
177         return -1;
178     }
179 
180     /* Initialize the user interface.  `ui_init()' might need to handle the
181        command line somehow, so we call it before parsing the options.
182        (e.g. under X11, the `-display' option is handled independently).  */
183     DBG(("main:ui_init(argc:%d)\n", argc));
184     if (!console_mode && ui_init(&argc, argv) < 0) {
185         archdep_startup_log_error("Cannot initialize the UI.\n");
186         return -1;
187     }
188 
189     if ((!ishelp) && (loadconfig)) {
190         /* Load the user's default configuration file.  */
191         if (resources_reset_and_load(NULL) < 0) {
192             /* The resource file might contain errors, and thus certain
193             resources might have been initialized anyway.  */
194             if (resources_set_defaults() < 0) {
195                 archdep_startup_log_error("Cannot set defaults.\n");
196                 return -1;
197             }
198         }
199     }
200 
201     if (log_init() < 0) {
202         const char *logfile = NULL;
203 
204         /* assuming LogFileName exists */
205         resources_get_string("LogFileName", &logfile);
206 
207         if (logfile != NULL && *logfile != '\0') {
208             archdep_startup_log_error(
209                     "Cannot start logging system, failed to open '%s' for writing",
210                     logfile);
211         } else {
212             archdep_startup_log_error("Cannot startup logging system.\n");
213         }
214     }
215 
216     DBG(("main:initcmdline_check_args(argc:%d)\n", argc));
217     if (initcmdline_check_args(argc, argv) < 0) {
218         return -1;
219     }
220 
221     program_name = archdep_program_name();
222 
223     /* VICE boot sequence.  */
224     log_message(LOG_DEFAULT, " ");
225 #ifdef USE_SVN_REVISION
226     log_message(LOG_DEFAULT, "*** VICE Version %s, rev %s ***", VERSION, VICE_SVN_REV_STRING);
227 #else
228     log_message(LOG_DEFAULT, "*** VICE Version %s ***", VERSION);
229 #endif
230     log_message(LOG_DEFAULT, " ");
231     if (machine_class == VICE_MACHINE_VSID) {
232         log_message(LOG_DEFAULT, "Welcome to %s, the free portable SID Player.",
233                     program_name);
234     } else {
235         log_message(LOG_DEFAULT, "Welcome to %s, the free portable %s Emulator.",
236                     program_name, machine_name);
237     }
238     log_message(LOG_DEFAULT, " ");
239 
240     log_message(LOG_DEFAULT, "Current VICE team members:");
241     n = 0; *term_tmp = 0;
242     for (i = 0; core_team[i].name != NULL; i++) {
243         name_len = strlen(core_team[i].name);
244         /* XXX: reject names that will never fit, for now */
245         if ((int)name_len + 3 > TERM_TMP_SIZE) {
246             log_warning(LOG_DEFAULT, "%s:%d: name '%s' too large for buffer",
247                     __FILE__, __LINE__, core_team[i].name);
248             break;  /* this will still write out whatever was in the buffer */
249         }
250 
251         if (n + (int)name_len + 3 > TERM_TMP_SIZE) {    /* +3 for ", \0" */
252             log_message(LOG_DEFAULT, "%s", term_tmp);
253             strcpy(term_tmp, core_team[i].name);
254             n = (int)name_len;
255         } else {
256             strcat(term_tmp, core_team[i].name);
257             n += (int)name_len;
258         }
259         if (core_team[i + 1].name == NULL) {
260             strcat(term_tmp, ".");
261         } else {
262             strcat(term_tmp, ", ");
263             n += 2;
264         }
265     }
266     log_message(LOG_DEFAULT, "%s", term_tmp);
267 
268     log_message(LOG_DEFAULT, " ");
269     log_message(LOG_DEFAULT, "This is free software with ABSOLUTELY NO WARRANTY.");
270     log_message(LOG_DEFAULT, "See the \"About VICE\" command for more info.");
271     log_message(LOG_DEFAULT, " ");
272 
273     /* lib_free(program_name); */
274 
275     /* Complete the GUI initialization (after loading the resources and
276        parsing the command-line) if necessary.  */
277     if (!console_mode && ui_init_finish() < 0) {
278         return -1;
279     }
280 
281     if (!console_mode && video_init() < 0) {
282         return -1;
283     }
284 
285     if (initcmdline_check_psid() < 0) {
286         return -1;
287     }
288 
289     if (init_main() < 0) {
290         return -1;
291     }
292 
293     initcmdline_check_attach();
294 
295 #ifdef USE_VICE_THREAD
296 
297     if (pthread_create(&vice_thread, NULL, vice_thread_main, NULL)) {
298         log_error(LOG_DEFAULT, "Fatal: failed to launch main thread");
299         return 1;
300     }
301 
302 #else /* #ifdef USE_VICE_THREAD */
303 
304     main_loop_forever();
305 
306 #endif /* #ifdef USE_VICE_THREAD */
307 
308     return 0;
309 }
310 
main_loop_forever(void)311 void main_loop_forever(void)
312 {
313     log_message(LOG_DEFAULT, "Main CPU: starting at ($FFFC).");
314 
315     /* This doesn't return. The thread will directly exit when requested. */
316     maincpu_mainloop();
317 
318     log_error(LOG_DEFAULT, "perkele! (THREAD)");
319 }
320 
321 #ifdef USE_VICE_THREAD
322 
vice_thread_shutdown(void)323 void vice_thread_shutdown(void)
324 {
325     if (!vice_thread) {
326         /* We're exiting early in program life, such as when invoked with -help */
327         return;
328     }
329 
330     if (pthread_equal(pthread_self(), vice_thread)) {
331         printf("FIXME! VICE thread is trying to shut itself down directly, this needs to be called from the ui thread for a correct shutdown!\n");
332         mainlock_initiate_shutdown();
333         return;
334     }
335 
336     mainlock_obtain();
337     mainlock_initiate_shutdown();
338     mainlock_release();
339 
340     pthread_join(vice_thread, NULL);
341 
342     log_message(LOG_DEFAULT, "VICE thread has been joined.");
343 }
344 
vice_thread_main(void * unused)345 void *vice_thread_main(void *unused)
346 {
347     archdep_thread_init();
348 
349     mainlock_init();
350 
351     main_loop_forever();
352 
353     /*
354      * main_loop_forever() does not return, so we call archdep_thread_shutdown()
355      * in the mainlock system which manages a direct pthread based thread exit.
356      */
357 
358     return NULL;
359 }
360 
361 #endif /* #ifdef USE_VICE_THREAD */
362