ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/main_windows.cpp
Revision: 1.16
Committed: 2010-01-15T01:53:31Z (14 years, 10 months ago) by asvitkine
Branch: MAIN
CVS Tags: HEAD
Changes since 1.15: +1 -1 lines
Log Message:
Fix PrefsInit() calls to take correct number of parameter

File Contents

# Content
1 /*
2 * main_windows.cpp - Startup code for Windows
3 *
4 * Basilisk II (C) 1997-2008 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <errno.h>
27
28 #include <SDL.h>
29 #include <SDL_mutex.h>
30 #include <SDL_thread.h>
31
32 #include <string>
33 using std::string;
34
35 #include "cpu_emulation.h"
36 #include "sys.h"
37 #include "rom_patches.h"
38 #include "xpram.h"
39 #include "timer.h"
40 #include "video.h"
41 #include "cdrom.h"
42 #include "emul_op.h"
43 #include "prefs.h"
44 #include "prefs_editor.h"
45 #include "macos_util.h"
46 #include "user_strings.h"
47 #include "version.h"
48 #include "main.h"
49 #include "vm_alloc.h"
50 #include "sigsegv.h"
51 #include "util_windows.h"
52 #include "kernel_windows.h"
53
54 #if USE_JIT
55 extern void flush_icache_range(uint8 *start, uint32 size); // from compemu_support.cpp
56 #endif
57
58 #ifdef ENABLE_MON
59 # include "mon.h"
60 #endif
61
62 #define DEBUG 0
63 #include "debug.h"
64
65
66 // Constants
67 const char ROM_FILE_NAME[] = "ROM";
68 const int SCRATCH_MEM_SIZE = 0x10000; // Size of scratch memory area
69
70
71 // CPU and FPU type, addressing mode
72 int CPUType;
73 bool CPUIs68060;
74 int FPUType;
75 bool TwentyFourBitAddressing;
76
77
78 // Global variables
79 HANDLE emul_thread = NULL; // Handle of MacOS emulation thread (main thread)
80
81 static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
82 static bool xpram_thread_active = false; // Flag: XPRAM watchdog installed
83 static volatile bool xpram_thread_cancel = false; // Flag: Cancel XPRAM thread
84 static SDL_Thread *xpram_thread = NULL; // XPRAM watchdog
85
86 static bool tick_thread_active = false; // Flag: 60Hz thread installed
87 static volatile bool tick_thread_cancel = false; // Flag: Cancel 60Hz thread
88 static SDL_Thread *tick_thread; // 60Hz thread
89
90 static SDL_mutex *intflag_lock = NULL; // Mutex to protect InterruptFlags
91 #define LOCK_INTFLAGS SDL_LockMutex(intflag_lock)
92 #define UNLOCK_INTFLAGS SDL_UnlockMutex(intflag_lock)
93
94 DWORD win_os; // Windows OS id
95 DWORD win_os_major; // Windows OS version major
96
97 #if USE_SCRATCHMEM_SUBTERFUGE
98 uint8 *ScratchMem = NULL; // Scratch memory for Mac ROM writes
99 #endif
100
101 #if REAL_ADDRESSING
102 static bool lm_area_mapped = false; // Flag: Low Memory area mmap()ped
103 #endif
104
105
106 // Prototypes
107 static int xpram_func(void *arg);
108 static int tick_func(void *arg);
109 static void one_tick(...);
110
111
112 /*
113 * Ersatz functions
114 */
115
116 extern "C" {
117
118 #ifndef HAVE_STRDUP
119 char *strdup(const char *s)
120 {
121 char *n = (char *)malloc(strlen(s) + 1);
122 strcpy(n, s);
123 return n;
124 }
125 #endif
126
127 }
128
129
130 /*
131 * Map memory that can be accessed from the Mac side
132 */
133
134 void *vm_acquire_mac(size_t size)
135 {
136 return vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_32BIT);
137 }
138
139
140 /*
141 * SIGSEGV handler
142 */
143
144 static sigsegv_return_t sigsegv_handler(sigsegv_info_t *sip)
145 {
146 const uintptr fault_address = (uintptr)sigsegv_get_fault_address(sip);
147 #if ENABLE_VOSF
148 // Handle screen fault
149 extern bool Screen_fault_handler(sigsegv_info_t *sip);
150 if (Screen_fault_handler(sip))
151 return SIGSEGV_RETURN_SUCCESS;
152 #endif
153
154 #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
155 // Ignore writes to ROM
156 if (((uintptr)fault_address - (uintptr)ROMBaseHost) < ROMSize)
157 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
158
159 // Ignore all other faults, if requested
160 if (PrefsFindBool("ignoresegv"))
161 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
162 #endif
163
164 return SIGSEGV_RETURN_FAILURE;
165 }
166
167 /*
168 * Dump state when everything went wrong after a SEGV
169 */
170
171 static void sigsegv_dump_state(sigsegv_info_t *sip)
172 {
173 const sigsegv_address_t fault_address = sigsegv_get_fault_address(sip);
174 const sigsegv_address_t fault_instruction = sigsegv_get_fault_instruction_address(sip);
175 fprintf(stderr, "Caught SIGSEGV at address %p", fault_address);
176 if (fault_instruction != SIGSEGV_INVALID_ADDRESS)
177 fprintf(stderr, " [IP=%p]", fault_instruction);
178 fprintf(stderr, "\n");
179 uaecptr nextpc;
180 extern void m68k_dumpstate(uaecptr *nextpc);
181 m68k_dumpstate(&nextpc);
182 #if USE_JIT && JIT_DEBUG
183 extern void compiler_dumpstate(void);
184 compiler_dumpstate();
185 #endif
186 VideoQuitFullScreen();
187 #ifdef ENABLE_MON
188 char *arg[4] = {"mon", "-m", "-r", NULL};
189 mon(3, arg);
190 QuitEmulator();
191 #endif
192 }
193
194
195 /*
196 * Main program
197 */
198
199 static void usage(const char *prg_name)
200 {
201 printf(
202 "Usage: %s [OPTION...]\n"
203 "\nUnix options:\n"
204 " --config FILE\n read/write configuration from/to FILE\n"
205 " --display STRING\n X display to use\n"
206 " --break ADDRESS\n set ROM breakpoint\n"
207 " --rominfo\n dump ROM information\n", prg_name
208 );
209 LoadPrefs(NULL); // read the prefs file so PrefsPrintUsage() will print the correct default values
210 PrefsPrintUsage();
211 exit(0);
212 }
213
214 int main(int argc, char **argv)
215 {
216 char str[256];
217 bool cd_boot = false;
218
219 // Initialize variables
220 RAMBaseHost = NULL;
221 ROMBaseHost = NULL;
222 srand(time(NULL));
223 tzset();
224
225 // Print some info
226 printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
227 printf(" %s\n", GetString(STR_ABOUT_TEXT2));
228
229 // Parse command line arguments
230 for (int i=1; i<argc; i++) {
231 if (strcmp(argv[i], "--help") == 0) {
232 usage(argv[0]);
233 } else if (strcmp(argv[i], "--break") == 0) {
234 argv[i++] = NULL;
235 if (i < argc) {
236 ROMBreakpoint = strtol(argv[i], NULL, 0);
237 argv[i] = NULL;
238 }
239 } else if (strcmp(argv[i], "--config") == 0) {
240 argv[i++] = NULL;
241 if (i < argc) {
242 extern string UserPrefsPath; // from prefs_unix.cpp
243 UserPrefsPath = argv[i];
244 argv[i] = NULL;
245 }
246 } else if (strcmp(argv[i], "--rominfo") == 0) {
247 argv[i] = NULL;
248 PrintROMInfo = true;
249 } else if (strcmp(argv[i], "--cdboot") == 0) {
250 argv[i] = NULL;
251 cd_boot = true;
252 }
253 }
254
255 // Remove processed arguments
256 for (int i=1; i<argc; i++) {
257 int k;
258 for (k=i; k<argc; k++)
259 if (argv[k] != NULL)
260 break;
261 if (k > i) {
262 k -= i;
263 for (int j=i+k; j<argc; j++)
264 argv[j-k] = argv[j];
265 argc -= k;
266 }
267 }
268
269 // Read preferences
270 PrefsInit(NULL, argc, argv);
271
272 // Boot MacOS from CD-ROM?
273 if (cd_boot)
274 PrefsReplaceInt32("bootdriver", CDROMRefNum);
275
276 // Any command line arguments left?
277 for (int i=1; i<argc; i++) {
278 if (argv[i][0] == '-') {
279 fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
280 usage(argv[0]);
281 }
282 }
283
284 // Check we are using a Windows NT kernel >= 4.0
285 OSVERSIONINFO osvi;
286 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
287 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
288 if (!GetVersionEx(&osvi)) {
289 ErrorAlert("Could not determine OS type");
290 QuitEmulator();
291 }
292 win_os = osvi.dwPlatformId;
293 win_os_major = osvi.dwMajorVersion;
294 if (win_os != VER_PLATFORM_WIN32_NT || win_os_major < 4) {
295 ErrorAlert(STR_NO_WIN32_NT_4);
296 QuitEmulator();
297 }
298
299 // Check that drivers are installed
300 if (!check_drivers())
301 QuitEmulator();
302
303 // Load win32 libraries
304 KernelInit();
305
306 // FIXME: default to DIB driver
307 if (getenv("SDL_VIDEODRIVER") == NULL)
308 putenv("SDL_VIDEODRIVER=windib");
309
310 // Initialize SDL system
311 int sdl_flags = 0;
312 #ifdef USE_SDL_VIDEO
313 sdl_flags |= SDL_INIT_VIDEO;
314 #endif
315 #ifdef USE_SDL_AUDIO
316 sdl_flags |= SDL_INIT_AUDIO;
317 #endif
318 assert(sdl_flags != 0);
319 if (SDL_Init(sdl_flags) == -1) {
320 char str[256];
321 sprintf(str, "Could not initialize SDL: %s.\n", SDL_GetError());
322 ErrorAlert(str);
323 QuitEmulator();
324 }
325 atexit(SDL_Quit);
326
327 // Init system routines
328 SysInit();
329
330 // Show preferences editor
331 if (!PrefsFindBool("nogui"))
332 if (!PrefsEditor())
333 QuitEmulator();
334
335 // Install the handler for SIGSEGV
336 if (!sigsegv_install_handler(sigsegv_handler)) {
337 sprintf(str, GetString(STR_SIG_INSTALL_ERR), "SIGSEGV", strerror(errno));
338 ErrorAlert(str);
339 QuitEmulator();
340 }
341
342 // Register dump state function when we got mad after a segfault
343 sigsegv_set_dump_state(sigsegv_dump_state);
344
345 // Read RAM size
346 RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary
347 if (RAMSize < 1024*1024) {
348 WarningAlert(GetString(STR_SMALL_RAM_WARN));
349 RAMSize = 1024*1024;
350 }
351
352 // Initialize VM system
353 vm_init();
354
355 // Create areas for Mac RAM and ROM
356 RAMBaseHost = (uint8 *)vm_acquire_mac(RAMSize);
357 ROMBaseHost = (uint8 *)vm_acquire_mac(0x100000);
358 if (RAMBaseHost == VM_MAP_FAILED || ROMBaseHost == VM_MAP_FAILED) {
359 ErrorAlert(STR_NO_MEM_ERR);
360 QuitEmulator();
361 }
362
363 #if USE_SCRATCHMEM_SUBTERFUGE
364 // Allocate scratch memory
365 ScratchMem = (uint8 *)vm_acquire(SCRATCH_MEM_SIZE);
366 if (ScratchMem == VM_MAP_FAILED) {
367 ErrorAlert(STR_NO_MEM_ERR);
368 QuitEmulator();
369 }
370 ScratchMem += SCRATCH_MEM_SIZE/2; // ScratchMem points to middle of block
371 #endif
372
373 #if DIRECT_ADDRESSING
374 // RAMBaseMac shall always be zero
375 MEMBaseDiff = (uintptr)RAMBaseHost;
376 RAMBaseMac = 0;
377 ROMBaseMac = Host2MacAddr(ROMBaseHost);
378 #endif
379 D(bug("Mac RAM starts at %p (%08x)\n", RAMBaseHost, RAMBaseMac));
380 D(bug("Mac ROM starts at %p (%08x)\n", ROMBaseHost, ROMBaseMac));
381
382 // Get rom file path from preferences
383 const char *rom_path = PrefsFindString("rom");
384
385 // Load Mac ROM
386 HANDLE rom_fh = CreateFile(rom_path ? rom_path : ROM_FILE_NAME,
387 GENERIC_READ,
388 0, NULL,
389 OPEN_EXISTING,
390 FILE_ATTRIBUTE_NORMAL,
391 NULL);
392 if (rom_fh == INVALID_HANDLE_VALUE) {
393 ErrorAlert(STR_NO_ROM_FILE_ERR);
394 QuitEmulator();
395 }
396 printf(GetString(STR_READING_ROM_FILE));
397 ROMSize = GetFileSize(rom_fh, NULL);
398 if (ROMSize != 64*1024 && ROMSize != 128*1024 && ROMSize != 256*1024 && ROMSize != 512*1024 && ROMSize != 1024*1024) {
399 ErrorAlert(STR_ROM_SIZE_ERR);
400 CloseHandle(rom_fh);
401 QuitEmulator();
402 }
403 DWORD bytes_read;
404 if (ReadFile(rom_fh, ROMBaseHost, ROMSize, &bytes_read, NULL) == 0 || bytes_read != ROMSize) {
405 ErrorAlert(STR_ROM_FILE_READ_ERR);
406 CloseHandle(rom_fh);
407 QuitEmulator();
408 }
409
410 // Initialize native timers
411 timer_init();
412
413 // Initialize everything
414 if (!InitAll(NULL))
415 QuitEmulator();
416 D(bug("Initialization complete\n"));
417
418 // Get handle of main thread
419 emul_thread = GetCurrentThread();
420
421 // SDL threads available, start 60Hz thread
422 tick_thread_active = ((tick_thread = SDL_CreateThread(tick_func, NULL)) != NULL);
423 if (!tick_thread_active) {
424 sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno));
425 ErrorAlert(str);
426 QuitEmulator();
427 }
428 D(bug("60Hz thread started\n"));
429
430 // Start XPRAM watchdog thread
431 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
432 xpram_thread_active = ((xpram_thread = SDL_CreateThread(xpram_func, NULL)) != NULL);
433 D(bug("XPRAM thread started\n"));
434
435 // Start 68k and jump to ROM boot routine
436 D(bug("Starting emulation...\n"));
437 Start680x0();
438
439 QuitEmulator();
440 return 0;
441 }
442
443
444 /*
445 * Quit emulator
446 */
447
448 void QuitEmulator(void)
449 {
450 D(bug("QuitEmulator\n"));
451
452 // Exit 680x0 emulation
453 Exit680x0();
454
455 // Stop 60Hz thread
456 if (tick_thread_active) {
457 tick_thread_cancel = true;
458 SDL_WaitThread(tick_thread, NULL);
459 }
460
461 // Stop XPRAM watchdog thread
462 if (xpram_thread_active) {
463 xpram_thread_cancel = true;
464 SDL_WaitThread(xpram_thread, NULL);
465 }
466
467 // Deinitialize everything
468 ExitAll();
469
470 // Free ROM/RAM areas
471 if (RAMBaseHost != VM_MAP_FAILED) {
472 vm_release(RAMBaseHost, RAMSize);
473 RAMBaseHost = NULL;
474 }
475 if (ROMBaseHost != VM_MAP_FAILED) {
476 vm_release(ROMBaseHost, 0x100000);
477 ROMBaseHost = NULL;
478 }
479
480 #if USE_SCRATCHMEM_SUBTERFUGE
481 // Delete scratch memory area
482 if (ScratchMem != (uint8 *)VM_MAP_FAILED) {
483 vm_release((void *)(ScratchMem - SCRATCH_MEM_SIZE/2), SCRATCH_MEM_SIZE);
484 ScratchMem = NULL;
485 }
486 #endif
487
488 // Exit VM wrappers
489 vm_exit();
490
491 // Exit system routines
492 SysExit();
493
494 // Exit preferences
495 PrefsExit();
496
497 // Release win32 libraries
498 KernelExit();
499
500 exit(0);
501 }
502
503
504 /*
505 * Code was patched, flush caches if neccessary (i.e. when using a real 680x0
506 * or a dynamically recompiling emulator)
507 */
508
509 void FlushCodeCache(void *start, uint32 size)
510 {
511 #if USE_JIT
512 if (UseJIT)
513 flush_icache_range((uint8 *)start, size);
514 #endif
515 }
516
517
518 /*
519 * Mutexes
520 */
521
522 struct B2_mutex {
523 B2_mutex() { m = SDL_CreateMutex(); }
524 ~B2_mutex() { if (m) SDL_DestroyMutex(m); }
525 SDL_mutex *m;
526 };
527
528 B2_mutex *B2_create_mutex(void)
529 {
530 return new B2_mutex;
531 }
532
533 void B2_lock_mutex(B2_mutex *mutex)
534 {
535 if (mutex)
536 SDL_LockMutex(mutex->m);
537 }
538
539 void B2_unlock_mutex(B2_mutex *mutex)
540 {
541 if (mutex)
542 SDL_UnlockMutex(mutex->m);
543 }
544
545 void B2_delete_mutex(B2_mutex *mutex)
546 {
547 delete mutex;
548 }
549
550
551 /*
552 * Interrupt flags (must be handled atomically!)
553 */
554
555 uint32 InterruptFlags = 0;
556
557 void SetInterruptFlag(uint32 flag)
558 {
559 LOCK_INTFLAGS;
560 InterruptFlags |= flag;
561 UNLOCK_INTFLAGS;
562 }
563
564 void ClearInterruptFlag(uint32 flag)
565 {
566 LOCK_INTFLAGS;
567 InterruptFlags &= ~flag;
568 UNLOCK_INTFLAGS;
569 }
570
571
572 /*
573 * XPRAM watchdog thread (saves XPRAM every minute)
574 */
575
576 static void xpram_watchdog(void)
577 {
578 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
579 memcpy(last_xpram, XPRAM, XPRAM_SIZE);
580 SaveXPRAM();
581 }
582 }
583
584 static int xpram_func(void *arg)
585 {
586 while (!xpram_thread_cancel) {
587 for (int i=0; i<60 && !xpram_thread_cancel; i++)
588 Delay_usec(999999); // Only wait 1 second so we quit promptly when xpram_thread_cancel becomes true
589 xpram_watchdog();
590 }
591 return 0;
592 }
593
594
595 /*
596 * 60Hz thread (really 60.15Hz)
597 */
598
599 static void one_second(void)
600 {
601 // Pseudo Mac 1Hz interrupt, update local time
602 WriteMacInt32(0x20c, TimerDateTime());
603
604 SetInterruptFlag(INTFLAG_1HZ);
605 TriggerInterrupt();
606 }
607
608 static void one_tick(...)
609 {
610 static int tick_counter = 0;
611 if (++tick_counter > 60) {
612 tick_counter = 0;
613 one_second();
614 }
615
616 // Trigger 60Hz interrupt
617 if (ROMVersion != ROM_VERSION_CLASSIC || HasMacStarted()) {
618 SetInterruptFlag(INTFLAG_60HZ);
619 TriggerInterrupt();
620 }
621 }
622
623 static int tick_func(void *arg)
624 {
625 uint64 start = GetTicks_usec();
626 int64 ticks = 0;
627 uint64 next = GetTicks_usec();
628 while (!tick_thread_cancel) {
629 one_tick();
630 next += 16625;
631 int64 delay = next - GetTicks_usec();
632 if (delay > 0)
633 Delay_usec(delay);
634 else if (delay < -16625)
635 next = GetTicks_usec();
636 ticks++;
637 }
638 uint64 end = GetTicks_usec();
639 D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
640 return 0;
641 }
642
643
644 /*
645 * Get the main window handle
646 */
647
648 #ifdef USE_SDL_VIDEO
649 #include <SDL_syswm.h>
650 HWND GetMainWindowHandle(void)
651 {
652 SDL_SysWMinfo wmInfo;
653 SDL_VERSION(&wmInfo.version);
654 return SDL_GetWMInfo(&wmInfo) ? wmInfo.window : NULL;
655 }
656 #endif
657
658
659 /*
660 * Display alert
661 */
662
663 static void display_alert(int title_id, const char *text, int flags)
664 {
665 HWND hMainWnd = GetMainWindowHandle();
666 MessageBox(hMainWnd, text, GetString(title_id), MB_OK | flags);
667 }
668
669
670 /*
671 * Display error alert
672 */
673
674 void ErrorAlert(const char *text)
675 {
676 if (PrefsFindBool("nogui"))
677 return;
678
679 VideoQuitFullScreen();
680 display_alert(STR_ERROR_ALERT_TITLE, text, MB_ICONSTOP);
681 }
682
683
684 /*
685 * Display warning alert
686 */
687
688 void WarningAlert(const char *text)
689 {
690 if (PrefsFindBool("nogui"))
691 return;
692
693 display_alert(STR_WARNING_ALERT_TITLE, text, MB_ICONINFORMATION);
694 }
695
696
697 /*
698 * Display choice alert
699 */
700
701 bool ChoiceAlert(const char *text, const char *pos, const char *neg)
702 {
703 printf(GetString(STR_SHELL_WARNING_PREFIX), text);
704 return false; //!!
705 }