ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Windows/main_windows.cpp
Revision: 1.17
Committed: 2009-08-18T18:26:11Z (15 years, 3 months ago) by asvitkine
Branch: MAIN
Changes since 1.16: +10 -6 lines
Log Message:
[Michael Schmitt]
Attached is a patch to SheepShaver to fix memory allocation problems when OS X 10.5 is the host. It also relaxes the 512 MB RAM limit on OS X hosts.


Problem
-------
Some users have been unable to run SheepShaver on OS X 10.5 (Leopard) hosts. The symptom is error "ERROR: Cannot map RAM: File already exists".

SheepShaver allocates RAM at fixed addresses. If it is running in "Real" addressing mode, and can't allocate at address 0, then it was hard-coded to allocate the RAM area at 0x20000000. The ROM area as allocated at 0x40800000.

The normal configuration is for SheepShaver to run under SDL, which is a Cocoa wrapper. By the time SheepShaver does its memory allocations, the Cocoa application has already started. The result is the SheepShaver memory address space already contains libraries, fonts, Input Managers, and IOKit areas.

On Leopard hosts these areas can land on the same addresses SheepShaver needs, so SheepShaver's memory allocation fails.


Solution
--------
The approach is to change SheepShaver (on Unix & OS X hosts) to allocate the RAM area anywhere it can find the space, rather than at a fixed address.

This could result in the RAM allocated higher than the ROM area, which causes a crash. To prevent this from occurring, the RAM and ROM areas are allocated contiguously.

Previously the ROM starting address was a constant ROM_BASE, which was used throughout the source files. The ROM start address is now a variable ROMBase. ROMBase is allocated and set by main_*.cpp just like RAMBase.

A side-effect of this change is that it lifts the 512 MB RAM limit for OS X hosts. The limit was because the fixed RAM and ROM addresses were such that the RAM could only be 512 MB before it overlapped the ROM area.


Impact
------
The change to make ROMBase a variable is throughout all hosts & addressing modes.

The RAM and ROM areas will only shift when run on Unix & OS X hosts, otherwise the same fixed allocation address is used as before.

This change is limited to "Real" addressing mode. Unlike Basilisk II, SheepShaver *pre-calculates* the offset for "Direct" addressing mode; the offset is compiled into the program. If the RAM address were allowed to shift, it could result in the RAM area wrapping around address 0.


Changes to main_unix.cpp
------------------------
1. Real addressing mode no longer defines a RAM_BASE constant.

2. The base address of the Mac ROM (ROMBase) is defined and exported by this program.

3. Memory management helper vm_mac_acquire is renamed to vm_mac_acquire_fixed. Added a new memory management helper vm_mac_acquire, which allocates memory at any address.

4. Changed and rearranged the allocation of RAM and ROM areas.

Before it worked like this:

  - Allocate ROM area
  - If can, attempt to allocate RAM at address zero
  - If RAM not allocated at 0, allocate at fixed address

We still want to try allocating the RAM at zero, and if using DIRECT addressing we're still going to use the fixed addresses. So we don't know where the ROM should be until after we do the RAM. The new logic is:

  - If can, attempt to allocate RAM at address zero
  - If RAM not allocated at 0
      if REAL addressing
         allocate RAM and ROM together. The ROM address is aligned to a 1 MB boundary
      else (direct addressing)
         allocate RAM at fixed address
  - If ROM hasn't been allocated yet, allocate at fixed address

5. Calculate ROMBase and ROMBaseHost based on where the ROM was loaded.

6. There is a crash if the RAM is allocated too high. To try and catch this, check if it was allocated higher than the kernel data address.

7. Change subsequent code from using constant ROM_BASE to variable ROMBase.


Changes to Other Programs
-------------------------
emul_op.cpp, main.cpp, name_registery.cpp, rom_patches.cpp, rsrc_patches.cpp, emul_ppc.cpp, sheepshaver_glue.cpp, ppc-translate-cpp:
Change from constant ROM_BASE to variable ROMBase.

ppc_asm.S: It was setting register to a hard-coded literal address: 0x40b0d000. Changed to set it to ROMBase + 0x30d000.

ppc_asm.tmpl: It defined a macro ASM_LO16 but it assumed that the macro would always be used with operands that included a register specification. This is not true. Moved the register specification from the macro to the macro invocations.

main_beos.cpp, main_windows.cpp: Since the subprograms are all expecting a variable ROMBase, all the main_*.cpp pgrams have to define and export it. The ROM_BASE constant is moved here for consistency. The mains for beos and windows just allocate the ROM at the same fixed address as before, set ROMBaseHost and ROMBase to that address, and then use ROMBase for the subsequent code.

cpu_emulation.h: removed ROM_BASE constant. This value is moved to the main_*.cpp modules, to be consistent with RAM_BASE.

user_strings_unix.cpp, user_strings_unix.h: Added new error messages related to errors that occur when the RAM and ROM are allocated anywhere.

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * main_windows.cpp - Emulation core, Windows implementation
3     *
4 gbeauche 1.15 * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
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 <errno.h>
22     #include <stdio.h>
23     #include <stdlib.h>
24     #include <string.h>
25    
26     #include <SDL.h>
27    
28     #include "sysdeps.h"
29     #include "main.h"
30     #include "version.h"
31     #include "prefs.h"
32     #include "prefs_editor.h"
33     #include "cpu_emulation.h"
34     #include "emul_op.h"
35     #include "xlowmem.h"
36     #include "xpram.h"
37     #include "timer.h"
38     #include "adb.h"
39     #include "video.h"
40     #include "sys.h"
41     #include "macos_util.h"
42     #include "rom_patches.h"
43     #include "user_strings.h"
44     #include "vm_alloc.h"
45     #include "sigsegv.h"
46 gbeauche 1.3 #include "util_windows.h"
47 gbeauche 1.11 #include "kernel_windows.h"
48 gbeauche 1.1
49     #define DEBUG 0
50     #include "debug.h"
51    
52     #ifdef ENABLE_MON
53     #include "mon.h"
54     #endif
55    
56    
57     // Constants
58     const char ROM_FILE_NAME[] = "ROM";
59     const char ROM_FILE_NAME2[] = "Mac OS ROM";
60    
61 asvitkine 1.17 const uintptr ROM_BASE = 0x40800000; // Base address of ROM
62    
63 gbeauche 1.1 const uint32 SIG_STACK_SIZE = 0x10000; // Size of signal stack
64    
65    
66     // Global variables (exported)
67     uint32 RAMBase; // Base address of Mac RAM
68     uint32 RAMSize; // Size of Mac RAM
69 asvitkine 1.17 uint32 ROMBase; // Base address of Mac ROM
70 gbeauche 1.1 uint32 KernelDataAddr; // Address of Kernel Data
71     uint32 BootGlobsAddr; // Address of BootGlobs structure at top of Mac RAM
72     uint32 DRCacheAddr; // Address of DR Cache
73     uint32 PVR; // Theoretical PVR
74     int64 CPUClockSpeed; // Processor clock speed (Hz)
75     int64 BusClockSpeed; // Bus clock speed (Hz)
76     int64 TimebaseSpeed; // Timebase clock speed (Hz)
77     uint8 *RAMBaseHost; // Base address of Mac RAM (host address space)
78     uint8 *ROMBaseHost; // Base address of Mac ROM (host address space)
79 gbeauche 1.9 DWORD win_os; // Windows OS id
80     DWORD win_os_major; // Windows OS version major
81 gbeauche 1.1
82    
83     // Global variables
84     static int kernel_area = -1; // SHM ID of Kernel Data area
85     static bool rom_area_mapped = false; // Flag: Mac ROM mmap()ped
86     static bool ram_area_mapped = false; // Flag: Mac RAM mmap()ped
87     static bool dr_cache_area_mapped = false; // Flag: Mac DR Cache mmap()ped
88     static bool dr_emulator_area_mapped = false;// Flag: Mac DR Emulator mmap()ped
89     static KernelData *kernel_data; // Pointer to Kernel Data
90     static EmulatorData *emulator_data;
91    
92     static uint8 last_xpram[XPRAM_SIZE]; // Buffer for monitoring XPRAM changes
93     static bool nvram_thread_active = false; // Flag: NVRAM watchdog installed
94     static volatile bool nvram_thread_cancel; // Flag: Cancel NVRAM thread
95 gbeauche 1.3 static HANDLE nvram_thread = NULL; // NVRAM watchdog
96 gbeauche 1.1 static bool tick_thread_active = false; // Flag: MacOS thread installed
97     static volatile bool tick_thread_cancel; // Flag: Cancel 60Hz thread
98 gbeauche 1.3 static HANDLE tick_thread = NULL; // 60Hz thread
99     static HANDLE emul_thread = NULL; // MacOS thread
100 gbeauche 1.1 static uintptr sig_stack = 0; // Stack for PowerPC interrupt routine
101    
102     uint32 SheepMem::page_size; // Size of a native page
103     uintptr SheepMem::zero_page = 0; // Address of ro page filled in with zeros
104     uintptr SheepMem::base = 0x60000000; // Address of SheepShaver data
105     uintptr SheepMem::proc; // Bottom address of SheepShave procedures
106     uintptr SheepMem::data; // Top of SheepShaver data (stack like storage)
107    
108    
109     // Prototypes
110     static bool kernel_data_init(void);
111     static void kernel_data_exit(void);
112     static void Quit(void);
113 gbeauche 1.3 static DWORD WINAPI nvram_func(void *arg);
114     static DWORD WINAPI tick_func(void *arg);
115 gbeauche 1.1
116     static void jump_to_rom(uint32 entry);
117     extern void emul_ppc(uint32 start);
118     extern void init_emul_ppc(void);
119     extern void exit_emul_ppc(void);
120 gbeauche 1.14 sigsegv_return_t sigsegv_handler(sigsegv_info_t *sip);
121 gbeauche 1.1
122    
123     /*
124     * Return signal stack base
125     */
126    
127     uintptr SignalStackBase(void)
128     {
129     return sig_stack + SIG_STACK_SIZE;
130     }
131    
132    
133     /*
134     * Memory management helpers
135     */
136    
137     static inline int vm_mac_acquire(uint32 addr, uint32 size)
138     {
139     return vm_acquire_fixed(Mac2HostAddr(addr), size);
140     }
141    
142     static inline int vm_mac_release(uint32 addr, uint32 size)
143     {
144     return vm_release(Mac2HostAddr(addr), size);
145     }
146    
147    
148     /*
149     * Main program
150     */
151    
152     static void usage(const char *prg_name)
153     {
154     printf("Usage: %s [OPTION...]\n", prg_name);
155     printf("\nUnix options:\n");
156     printf(" --display STRING\n X display to use\n");
157     PrefsPrintUsage();
158     exit(0);
159     }
160    
161     int main(int argc, char **argv)
162     {
163     char str[256];
164     int16 i16;
165     HANDLE rom_fh;
166     const char *rom_path;
167     uint32 rom_size;
168     DWORD actual;
169     uint8 *rom_tmp;
170    
171     // Initialize variables
172     RAMBase = 0;
173    
174     // Print some info
175     printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
176     printf(" %s\n", GetString(STR_ABOUT_TEXT2));
177    
178     // Read preferences
179 asvitkine 1.16 PrefsInit(NULL, argc, argv);
180 gbeauche 1.1
181     // Parse command line arguments
182     for (int i=1; i<argc; i++) {
183     if (strcmp(argv[i], "--help") == 0) {
184     usage(argv[0]);
185     } else if (argv[i][0] == '-') {
186     fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
187     usage(argv[0]);
188     }
189     }
190    
191 gbeauche 1.9 // Check we are using a Windows NT kernel >= 4.0
192     OSVERSIONINFO osvi;
193     ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
194     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
195     if (!GetVersionEx(&osvi)) {
196     ErrorAlert("Could not determine OS type");
197     QuitEmulator();
198     }
199     win_os = osvi.dwPlatformId;
200     win_os_major = osvi.dwMajorVersion;
201     if (win_os != VER_PLATFORM_WIN32_NT || win_os_major < 4) {
202     ErrorAlert(GetString(STR_NO_WIN32_NT_4));
203     QuitEmulator();
204     }
205    
206     // Check that drivers are installed
207     if (!check_drivers())
208     QuitEmulator();
209    
210 gbeauche 1.11 // Load win32 libraries
211     KernelInit();
212    
213 gbeauche 1.10 // FIXME: default to DIB driver
214     if (getenv("SDL_VIDEODRIVER") == NULL)
215     putenv("SDL_VIDEODRIVER=windib");
216    
217 gbeauche 1.1 // Initialize SDL system
218     int sdl_flags = 0;
219     #ifdef USE_SDL_VIDEO
220     sdl_flags |= SDL_INIT_VIDEO;
221     #endif
222     #ifdef USE_SDL_AUDIO
223     sdl_flags |= SDL_INIT_AUDIO;
224     #endif
225     assert(sdl_flags != 0);
226     if (SDL_Init(sdl_flags) == -1) {
227     char str[256];
228     sprintf(str, "Could not initialize SDL: %s.\n", SDL_GetError());
229     ErrorAlert(str);
230     goto quit;
231     }
232     atexit(SDL_Quit);
233    
234     #ifdef ENABLE_MON
235     // Initialize mon
236     mon_init();
237     #endif
238    
239     // Install SIGSEGV handler for CPU emulator
240     if (!sigsegv_install_handler(sigsegv_handler)) {
241     sprintf(str, GetString(STR_SIGSEGV_INSTALL_ERR), strerror(errno));
242     ErrorAlert(str);
243     goto quit;
244     }
245    
246     // Initialize VM system
247     vm_init();
248    
249     // Get system info
250     PVR = 0x00040000; // Default: 604
251     CPUClockSpeed = 100000000; // Default: 100MHz
252     BusClockSpeed = 100000000; // Default: 100MHz
253     TimebaseSpeed = 25000000; // Default: 25MHz
254     PVR = 0x000c0000; // Default: 7400 (with AltiVec)
255     D(bug("PVR: %08x (assumed)\n", PVR));
256    
257     // Init system routines
258     SysInit();
259    
260     // Show preferences editor
261     if (!PrefsFindBool("nogui"))
262     if (!PrefsEditor())
263     goto quit;
264    
265     // Create areas for Kernel Data
266     if (!kernel_data_init())
267     goto quit;
268     kernel_data = (KernelData *)Mac2HostAddr(KERNEL_DATA_BASE);
269     emulator_data = &kernel_data->ed;
270     KernelDataAddr = KERNEL_DATA_BASE;
271     D(bug("Kernel Data at %p (%08x)\n", kernel_data, KERNEL_DATA_BASE));
272     D(bug("Emulator Data at %p (%08x)\n", emulator_data, KERNEL_DATA_BASE + offsetof(KernelData, ed)));
273    
274     // Create area for DR Cache
275     if (vm_mac_acquire(DR_EMULATOR_BASE, DR_EMULATOR_SIZE) < 0) {
276     sprintf(str, GetString(STR_DR_EMULATOR_MMAP_ERR), strerror(errno));
277     ErrorAlert(str);
278     goto quit;
279     }
280     dr_emulator_area_mapped = true;
281     if (vm_mac_acquire(DR_CACHE_BASE, DR_CACHE_SIZE) < 0) {
282     sprintf(str, GetString(STR_DR_CACHE_MMAP_ERR), strerror(errno));
283     ErrorAlert(str);
284     goto quit;
285     }
286     dr_cache_area_mapped = true;
287     DRCacheAddr = (uint32)Mac2HostAddr(DR_CACHE_BASE);
288     D(bug("DR Cache at %p (%08x)\n", DRCacheAddr, DR_CACHE_BASE));
289    
290     // Create area for SheepShaver data
291     if (!SheepMem::Init()) {
292     sprintf(str, GetString(STR_SHEEP_MEM_MMAP_ERR), strerror(errno));
293     ErrorAlert(str);
294     goto quit;
295     }
296    
297     // Create area for Mac ROM
298     if (vm_mac_acquire(ROM_BASE, ROM_AREA_SIZE) < 0) {
299     sprintf(str, GetString(STR_ROM_MMAP_ERR), strerror(errno));
300     ErrorAlert(str);
301     goto quit;
302     }
303 asvitkine 1.17 ROMBase = ROM_BASE;
304     ROMBaseHost = Mac2HostAddr(ROMBase);
305 gbeauche 1.1 rom_area_mapped = true;
306 asvitkine 1.17 D(bug("ROM area at %p (%08x)\n", ROMBaseHost, ROMBase));
307 gbeauche 1.1
308     // Create area for Mac RAM
309     RAMSize = PrefsFindInt32("ramsize");
310     if (RAMSize < 8*1024*1024) {
311     WarningAlert(GetString(STR_SMALL_RAM_WARN));
312     RAMSize = 8*1024*1024;
313     }
314 gbeauche 1.13 RAMBase = 0;
315     if (vm_mac_acquire(RAMBase, RAMSize) < 0) {
316 gbeauche 1.1 sprintf(str, GetString(STR_RAM_MMAP_ERR), strerror(errno));
317     ErrorAlert(str);
318     goto quit;
319     }
320 gbeauche 1.13 RAMBaseHost = Mac2HostAddr(RAMBase);
321 gbeauche 1.1 ram_area_mapped = true;
322     D(bug("RAM area at %p (%08x)\n", RAMBaseHost, RAMBase));
323    
324 asvitkine 1.17 if (RAMBase > ROMBase) {
325 gbeauche 1.1 ErrorAlert(GetString(STR_RAM_HIGHER_THAN_ROM_ERR));
326     goto quit;
327     }
328    
329     // Load Mac ROM
330     rom_path = PrefsFindString("rom");
331     rom_fh = CreateFile(rom_path ? rom_path : ROM_FILE_NAME,
332     GENERIC_READ, 0, NULL, OPEN_EXISTING,
333     FILE_ATTRIBUTE_NORMAL, NULL);
334    
335     if (rom_fh == INVALID_HANDLE_VALUE) {
336     rom_fh = CreateFile(rom_path ? rom_path : ROM_FILE_NAME2,
337     GENERIC_READ, 0, NULL, OPEN_EXISTING,
338     FILE_ATTRIBUTE_NORMAL, NULL);
339    
340     if (rom_fh == INVALID_HANDLE_VALUE) {
341     ErrorAlert(GetString(STR_NO_ROM_FILE_ERR));
342     goto quit;
343     }
344     }
345     printf(GetString(STR_READING_ROM_FILE));
346     rom_size = GetFileSize(rom_fh, NULL);
347     rom_tmp = new uint8[ROM_SIZE];
348     ReadFile(rom_fh, (void *)rom_tmp, ROM_SIZE, &actual, NULL);
349     CloseHandle(rom_fh);
350    
351     // Decode Mac ROM
352     if (!DecodeROM(rom_tmp, actual)) {
353     if (rom_size != 4*1024*1024) {
354     ErrorAlert(GetString(STR_ROM_SIZE_ERR));
355     goto quit;
356     } else {
357     ErrorAlert(GetString(STR_ROM_FILE_READ_ERR));
358     goto quit;
359     }
360     }
361     delete[] rom_tmp;
362 gbeauche 1.8
363     // Initialize native timers
364     timer_init();
365 gbeauche 1.1
366 gbeauche 1.6 // Initialize everything
367     if (!InitAll())
368 gbeauche 1.1 goto quit;
369 gbeauche 1.6 D(bug("Initialization complete\n"));
370 gbeauche 1.1
371     // Write protect ROM
372 gbeauche 1.6 vm_protect(ROMBaseHost, ROM_AREA_SIZE, VM_PAGE_READ);
373 gbeauche 1.1
374     // Start 60Hz thread
375     tick_thread_cancel = false;
376 gbeauche 1.3 tick_thread_active = ((tick_thread = create_thread(tick_func)) != NULL);
377     SetThreadPriority(tick_thread, THREAD_PRIORITY_ABOVE_NORMAL);
378 gbeauche 1.1 D(bug("Tick thread installed (%ld)\n", tick_thread));
379    
380     // Start NVRAM watchdog thread
381     memcpy(last_xpram, XPRAM, XPRAM_SIZE);
382     nvram_thread_cancel = false;
383 gbeauche 1.3 nvram_thread_active = ((nvram_thread = create_thread(nvram_func, NULL)) != NULL);
384     SetThreadPriority(nvram_thread, THREAD_PRIORITY_BELOW_NORMAL);
385 gbeauche 1.1 D(bug("NVRAM thread installed (%ld)\n", nvram_thread));
386    
387 gbeauche 1.3 // Get my thread ID and jump to ROM boot routine
388     emul_thread = GetCurrentThread();
389 gbeauche 1.1 D(bug("Jumping to ROM\n"));
390 asvitkine 1.17 jump_to_rom(ROMBase + 0x310000);
391 gbeauche 1.1 D(bug("Returned from ROM\n"));
392    
393     quit:
394     Quit();
395     return 0;
396     }
397    
398    
399     /*
400     * Cleanup and quit
401     */
402    
403     static void Quit(void)
404     {
405     // Exit PowerPC emulation
406     exit_emul_ppc();
407    
408     // Stop 60Hz thread
409     if (tick_thread_active) {
410     tick_thread_cancel = true;
411 gbeauche 1.3 wait_thread(tick_thread);
412 gbeauche 1.1 }
413    
414     // Stop NVRAM watchdog thread
415     if (nvram_thread_active) {
416     nvram_thread_cancel = true;
417 gbeauche 1.3 wait_thread(nvram_thread);
418 gbeauche 1.1 }
419    
420 gbeauche 1.6 // Deinitialize everything
421     ExitAll();
422 gbeauche 1.1
423     // Delete SheepShaver globals
424     SheepMem::Exit();
425    
426     // Delete RAM area
427     if (ram_area_mapped)
428 gbeauche 1.13 vm_mac_release(RAMBase, RAMSize);
429 gbeauche 1.1
430     // Delete ROM area
431     if (rom_area_mapped)
432 asvitkine 1.17 vm_mac_release(ROMBase, ROM_AREA_SIZE);
433 gbeauche 1.1
434     // Delete DR cache areas
435     if (dr_emulator_area_mapped)
436     vm_mac_release(DR_EMULATOR_BASE, DR_EMULATOR_SIZE);
437     if (dr_cache_area_mapped)
438     vm_mac_release(DR_CACHE_BASE, DR_CACHE_SIZE);
439    
440     // Delete Kernel Data area
441     kernel_data_exit();
442    
443     // Exit system routines
444     SysExit();
445    
446     // Exit preferences
447     PrefsExit();
448    
449 gbeauche 1.11 // Release win32 libraries
450     KernelExit();
451    
452 gbeauche 1.1 #ifdef ENABLE_MON
453     // Exit mon
454     mon_exit();
455     #endif
456    
457     exit(0);
458     }
459    
460    
461     /*
462     * Initialize Kernel Data segments
463     */
464    
465     static HANDLE kernel_handle; // Shared memory handle for Kernel Data
466     static DWORD allocation_granule; // Minimum size of allocateable are (64K)
467     static DWORD kernel_area_size; // Size of Kernel Data area
468    
469     static bool kernel_data_init(void)
470     {
471     char str[256];
472     SYSTEM_INFO si;
473     GetSystemInfo(&si);
474     allocation_granule = si.dwAllocationGranularity;
475     kernel_area_size = (KERNEL_AREA_SIZE + allocation_granule - 1) & -allocation_granule;
476    
477     char rcs[10];
478     LPVOID kernel_addr;
479     kernel_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, kernel_area_size, NULL);
480     if (kernel_handle == NULL) {
481     sprintf(rcs, "%d", GetLastError());
482     sprintf(str, GetString(STR_KD_SHMGET_ERR), rcs);
483     ErrorAlert(str);
484     return false;
485     }
486     kernel_addr = (LPVOID)Mac2HostAddr(KERNEL_DATA_BASE & -allocation_granule);
487     if (MapViewOfFileEx(kernel_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kernel_area_size, kernel_addr) != kernel_addr) {
488     sprintf(rcs, "%d", GetLastError());
489     sprintf(str, GetString(STR_KD_SHMAT_ERR), rcs);
490     ErrorAlert(str);
491     return false;
492     }
493     kernel_addr = (LPVOID)Mac2HostAddr(KERNEL_DATA2_BASE & -allocation_granule);
494     if (MapViewOfFileEx(kernel_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kernel_area_size, kernel_addr) != kernel_addr) {
495     sprintf(rcs, "%d", GetLastError());
496     sprintf(str, GetString(STR_KD2_SHMAT_ERR), rcs);
497     ErrorAlert(str);
498     return false;
499     }
500     return true;
501     }
502    
503    
504     /*
505     * Deallocate Kernel Data segments
506     */
507    
508     static void kernel_data_exit(void)
509     {
510     if (kernel_handle) {
511     UnmapViewOfFile(Mac2HostAddr(KERNEL_DATA_BASE & -allocation_granule));
512     UnmapViewOfFile(Mac2HostAddr(KERNEL_DATA2_BASE & -allocation_granule));
513     CloseHandle(kernel_handle);
514     }
515     }
516    
517    
518     /*
519     * Jump into Mac ROM, start 680x0 emulator
520     */
521    
522     void jump_to_rom(uint32 entry)
523     {
524     init_emul_ppc();
525     emul_ppc(entry);
526     }
527    
528    
529     /*
530     * Quit emulator (cause return from jump_to_rom)
531     */
532    
533     void QuitEmulator(void)
534     {
535     Quit();
536     }
537    
538    
539     /*
540     * Pause/resume emulator
541     */
542    
543     void PauseEmulator(void)
544     {
545 gbeauche 1.3 SuspendThread(emul_thread);
546 gbeauche 1.1 }
547    
548     void ResumeEmulator(void)
549     {
550 gbeauche 1.3 ResumeThread(emul_thread);
551 gbeauche 1.1 }
552    
553    
554     /*
555     * Dump 68k registers
556     */
557    
558     void Dump68kRegs(M68kRegisters *r)
559     {
560     // Display 68k registers
561     for (int i=0; i<8; i++) {
562     printf("d%d: %08x", i, r->d[i]);
563     if (i == 3 || i == 7)
564     printf("\n");
565     else
566     printf(", ");
567     }
568     for (int i=0; i<8; i++) {
569     printf("a%d: %08x", i, r->a[i]);
570     if (i == 3 || i == 7)
571     printf("\n");
572     else
573     printf(", ");
574     }
575     }
576    
577    
578     /*
579     * Make code executable
580     */
581    
582     void MakeExecutable(int dummy, uint32 start, uint32 length)
583     {
584 asvitkine 1.17 if ((start >= ROMBase) && (start < (ROMBase + ROM_SIZE)))
585 gbeauche 1.1 return;
586     FlushCodeCache(start, start + length);
587     }
588    
589    
590     /*
591     * NVRAM watchdog thread (saves NVRAM every minute)
592     */
593    
594     static void nvram_watchdog(void)
595     {
596     if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
597     memcpy(last_xpram, XPRAM, XPRAM_SIZE);
598     SaveXPRAM();
599     }
600     }
601    
602 gbeauche 1.3 static DWORD nvram_func(void *arg)
603 gbeauche 1.1 {
604     while (!nvram_thread_cancel) {
605     for (int i=0; i<60 && !nvram_thread_cancel; i++)
606     Delay_usec(999999); // Only wait 1 second so we quit promptly when nvram_thread_cancel becomes true
607     nvram_watchdog();
608     }
609     return 0;
610     }
611    
612    
613     /*
614     * 60Hz thread (really 60.15Hz)
615     */
616    
617 gbeauche 1.3 static DWORD tick_func(void *arg)
618 gbeauche 1.1 {
619     int tick_counter = 0;
620     uint64 start = GetTicks_usec();
621     int64 ticks = 0;
622     uint64 next = GetTicks_usec();
623    
624     while (!tick_thread_cancel) {
625    
626     // Wait
627     next += 16625;
628     int64 delay = next - GetTicks_usec();
629     if (delay > 0)
630     Delay_usec(delay);
631     else if (delay < -16625)
632     next = GetTicks_usec();
633     ticks++;
634    
635     // Pseudo Mac 1Hz interrupt, update local time
636     if (++tick_counter > 60) {
637     tick_counter = 0;
638     WriteMacInt32(0x20c, TimerDateTime());
639     }
640    
641     // Trigger 60Hz interrupt
642     if (ReadMacInt32(XLM_IRQ_NEST) == 0) {
643     SetInterruptFlag(INTFLAG_VIA);
644     TriggerInterrupt();
645     }
646     }
647    
648     uint64 end = GetTicks_usec();
649 gbeauche 1.13 D(bug("%lu ticks in %lu usec = %f ticks/sec\n", (unsigned long)ticks, (unsigned long)(end - start), ticks * 1000000.0 / (end - start)));
650 gbeauche 1.1 return 0;
651     }
652    
653    
654     /*
655     * Mutexes
656     */
657    
658     struct B2_mutex {
659 gbeauche 1.4 mutex_t m;
660 gbeauche 1.1 };
661    
662     B2_mutex *B2_create_mutex(void)
663     {
664     return new B2_mutex;
665     }
666    
667     void B2_lock_mutex(B2_mutex *mutex)
668     {
669 gbeauche 1.4 mutex->m.lock();
670 gbeauche 1.1 }
671    
672     void B2_unlock_mutex(B2_mutex *mutex)
673     {
674 gbeauche 1.4 mutex->m.unlock();
675 gbeauche 1.1 }
676    
677     void B2_delete_mutex(B2_mutex *mutex)
678     {
679     delete mutex;
680     }
681    
682    
683     /*
684     * Interrupt flags (must be handled atomically!)
685     */
686    
687     volatile uint32 InterruptFlags = 0;
688 gbeauche 1.4 static mutex_t intflags_mutex;
689 gbeauche 1.1
690     void SetInterruptFlag(uint32 flag)
691     {
692 gbeauche 1.4 intflags_mutex.lock();
693     InterruptFlags |= flag;
694     intflags_mutex.unlock();
695 gbeauche 1.1 }
696    
697     void ClearInterruptFlag(uint32 flag)
698     {
699 gbeauche 1.4 intflags_mutex.lock();
700     InterruptFlags &= ~flag;
701     intflags_mutex.unlock();
702 gbeauche 1.1 }
703    
704    
705     /*
706     * Disable interrupts
707     */
708    
709     void DisableInterrupt(void)
710     {
711     WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) + 1);
712     }
713    
714    
715     /*
716     * Enable interrupts
717     */
718    
719     void EnableInterrupt(void)
720     {
721     WriteMacInt32(XLM_IRQ_NEST, int32(ReadMacInt32(XLM_IRQ_NEST)) - 1);
722     }
723    
724    
725     /*
726     * Helpers to share 32-bit addressable data with MacOS
727     */
728    
729     bool SheepMem::Init(void)
730     {
731     // Size of a native page
732 gbeauche 1.5 page_size = vm_get_page_size();
733 gbeauche 1.1
734     // Allocate SheepShaver globals
735     proc = base;
736     if (vm_mac_acquire(base, size) < 0)
737     return false;
738    
739     // Allocate page with all bits set to 0, right in the middle
740     // This is also used to catch undesired overlaps between proc and data areas
741     zero_page = proc + (size / 2);
742     Mac_memset(zero_page, 0, page_size);
743     if (vm_protect(Mac2HostAddr(zero_page), page_size, VM_PAGE_READ) < 0)
744     return false;
745    
746     // Allocate alternate stack for PowerPC interrupt routine
747     sig_stack = base + size;
748     if (vm_mac_acquire(sig_stack, SIG_STACK_SIZE) < 0)
749     return false;
750    
751     data = base + size;
752     return true;
753     }
754    
755     void SheepMem::Exit(void)
756     {
757     if (data) {
758     // Delete SheepShaver globals
759     vm_mac_release(base, size);
760    
761     // Delete alternate stack for PowerPC interrupt routine
762     vm_mac_release(sig_stack, SIG_STACK_SIZE);
763     }
764     }
765    
766    
767     /*
768     * Get the main window handle
769     */
770    
771     #ifdef USE_SDL_VIDEO
772     #include <SDL_syswm.h>
773 gbeauche 1.12 HWND GetMainWindowHandle(void)
774 gbeauche 1.1 {
775     SDL_SysWMinfo wmInfo;
776 gbeauche 1.12 SDL_VERSION(&wmInfo.version);
777 gbeauche 1.1 return SDL_GetWMInfo(&wmInfo) ? wmInfo.window : NULL;
778     }
779     #endif
780    
781    
782     /*
783     * Display alert
784     */
785    
786     static void display_alert(int title_id, const char *text, int flags)
787     {
788     HWND hMainWnd = GetMainWindowHandle();
789     MessageBox(hMainWnd, text, GetString(title_id), MB_OK | flags);
790     }
791    
792    
793     /*
794     * Display error alert
795     */
796    
797     void ErrorAlert(const char *text)
798     {
799     if (PrefsFindBool("nogui"))
800     return;
801    
802     VideoQuitFullScreen();
803     display_alert(STR_ERROR_ALERT_TITLE, text, MB_ICONSTOP);
804     }
805    
806    
807     /*
808     * Display warning alert
809     */
810    
811     void WarningAlert(const char *text)
812     {
813     if (PrefsFindBool("nogui"))
814     return;
815    
816     display_alert(STR_WARNING_ALERT_TITLE, text, MB_ICONINFORMATION);
817     }
818    
819    
820     /*
821     * Display choice alert
822     */
823    
824     bool ChoiceAlert(const char *text, const char *pos, const char *neg)
825     {
826     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
827     return false; //!!
828     }