ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/main_windows.cpp
Revision: 1.5
Committed: 2005-01-30T21:42:15Z (19 years, 10 months ago) by gbeauche
Branch: MAIN
Changes since 1.4: +1 -1 lines
Log Message:
Happy New Year!

File Contents

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