ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/main_windows.cpp
Revision: 1.3
Committed: 2004-12-05T15:28:39Z (19 years, 9 months ago) by gbeauche
Branch: MAIN
Changes since 1.2: +9 -0 lines
Log Message:
add --cdboot command line option akin to MoL's

File Contents

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