ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/main_windows.cpp
Revision: 1.11
Committed: 2007-01-13T18:21:30Z (17 years, 10 months ago) by gbeauche
Branch: MAIN
Changes since 1.10: +1 -11 lines
Log Message:
Remove the 33-bit addressing hack as it's overly complex for not much gain.
Rather, use an address override prefix (0x67) though Intel Core optimization
reference guide says to avoid LCP prefixes. In practise, impact on performance
is measurably marginal on e.g. Speedometer tests.

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