ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/main_windows.cpp
Revision: 1.7
Committed: 2005-11-22T06:26:35Z (19 years ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-17
Changes since 1.6: +4 -0 lines
Log Message:
Windows HACK: default to the DIB driver as it looks smoother to me and
doesn't require locking the surfaces, which probably the cause of apparent
slowness when using the DirectX backend. This needs to be investigated more

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