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