ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/main_windows.cpp
Revision: 1.9
Committed: 2006-05-01T06:12:50Z (18 years, 7 months ago) by gbeauche
Branch: MAIN
Changes since 1.8: +5 -1 lines
Log Message:
Implement better Windows suspend/resume routines so that we don't oversleep.
i.e. really wake up the thread on next TriggerInterrupt().

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