/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Main system driver for the DOS library. * * By Shawn Hargreaves. * * See readme.txt for copyright information. */ #ifndef SCAN_DEPEND #include #include #include #include #include #include #include #endif #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintdos.h" #ifndef ALLEGRO_DOS #error something is wrong with the makefile #endif #ifndef SCAN_DEPEND #ifdef ALLEGRO_DJGPP /* these headers only apply to djgpp */ #include #include #include #endif #ifdef ALLEGRO_WATCOM /* horrible hackery here, so the rest of the file will work unchanged */ #include #define _write write #define _get_dos_version(n) ((_osmajor << 8) | _osminor) #define ScreenRows() -1 #define _set_screen_lines(n) #endif #endif /* some DOS-specific globals */ int i_love_bill = FALSE; static int a_rez = 3; static int a_lines = -1; static int console_virgin = TRUE; /* previous signal handlers */ static void *old_sig_abrt = NULL; static void *old_sig_fpe = NULL; static void *old_sig_ill = NULL; static void *old_sig_segv = NULL; static void *old_sig_term = NULL; static void *old_sig_int = NULL; #ifdef SIGKILL static void *old_sig_kill = NULL; #endif #ifdef SIGQUIT static void *old_sig_quit = NULL; #endif #ifdef SIGTRAP static void *old_sig_trap = NULL; #endif /* system driver functions */ static int sys_dos_init(void); static void sys_dos_exit(void); static void sys_dos_get_executable_name(char *output, int size); static void sys_dos_save_console_state(void); static void sys_dos_restore_console_state(void); static void sys_dos_read_palette(void); static void sys_dos_set_palette(AL_CONST struct RGB *p, int from, int to, int vsync); static void sys_dos_get_gfx_safe_mode(int *driver, struct GFX_MODE *mode); static void sys_dos_yield_timeslice(void); #ifdef ALLEGRO_DJGPP static void sys_dos_assert(AL_CONST char *msg); #else #define sys_dos_assert NULL #endif /* the main system driver for running under dos */ SYSTEM_DRIVER system_dos = { SYSTEM_DOS, /* id */ empty_string, /* name */ empty_string, /* desc */ "DOS", /* ascii_name */ sys_dos_init, sys_dos_exit, sys_dos_get_executable_name, NULL, /* find_resource */ NULL, /* set_window_title */ NULL, /* set_close_button_callback */ NULL, /* message */ sys_dos_assert, sys_dos_save_console_state, sys_dos_restore_console_state, NULL, /* create_bitmap */ NULL, /* created_bitmap */ NULL, /* create_sub_bitmap */ NULL, /* created_sub_bitmap */ NULL, /* destroy_bitmap */ sys_dos_read_palette, sys_dos_set_palette, NULL, /* get_vtable */ NULL, /* set_display_switch_mode */ NULL, /* display_switch_lock */ NULL, /* desktop_color_depth */ NULL, /* get_desktop_resolution */ sys_dos_get_gfx_safe_mode, sys_dos_yield_timeslice, NULL, /* create_mutex */ NULL, /* destroy_mutex */ NULL, /* lock_mutex */ NULL, /* unlock_mutex */ NULL, /* gfx_drivers */ NULL, /* digi_drivers */ NULL, /* midi_drivers */ NULL, /* keyboard_drivers */ NULL, /* mouse_drivers */ NULL, /* joystick_drivers */ NULL /* timer_drivers */ }; /* list of available system drivers */ _DRIVER_INFO _system_driver_list[] = { { SYSTEM_DOS, &system_dos, TRUE }, { SYSTEM_NONE, &system_none, FALSE }, { 0, NULL, 0 } }; /* signal_handler: * Used to trap various signals, to make sure things get shut down cleanly. */ static void signal_handler(int num) { static char msg[] = "Shutting down Allegro\r\n"; allegro_exit(); _write(STDERR_FILENO, msg, sizeof(msg)-1); raise(num); } /* detect_os: * Operating system autodetection routine. */ static void detect_os(void) { __dpmi_regs r; char buf[16]; char *p; int i; os_type = OSTYPE_UNKNOWN; /* check for Windows 3.x or 9x */ r.x.ax = 0x1600; __dpmi_int(0x2F, &r); if ((r.h.al != 0) && (r.h.al != 1) && (r.h.al != 0x80) && (r.h.al != 0xFF)) { os_version = r.h.al; os_revision = r.h.ah; os_multitasking = TRUE; if (os_version == 4) { if (os_revision == 90) os_type = OSTYPE_WINME; else if (os_revision == 10) os_type = OSTYPE_WIN98; else os_type = OSTYPE_WIN95; } else os_type = OSTYPE_WIN3; i_love_bill = TRUE; return; } /* check for Windows NT */ p = getenv("OS"); if (((p) && (stricmp(p, "Windows_NT") == 0)) || (_get_dos_version(1) == 0x0532)) { os_type = OSTYPE_WINNT; os_multitasking = TRUE; i_love_bill = TRUE; return; } /* check for OS/2 */ r.x.ax = 0x4010; __dpmi_int(0x2F, &r); if (r.x.ax != 0x4010) { if (r.x.ax == 0) os_type = OSTYPE_WARP; else os_type = OSTYPE_OS2; os_multitasking = TRUE; i_love_bill = TRUE; return; } /* check for Linux DOSEMU */ _farsetsel(_dos_ds); for (i=0; i<8; i++) buf[i] = _farnspeekb(0xFFFF5+i); buf[8] = 0; if (!strcmp(buf, "02/25/93")) { r.x.ax = 0; __dpmi_int(0xE6, &r); if (r.x.ax == 0xAA55) { os_type = OSTYPE_DOSEMU; os_multitasking = TRUE; i_love_bill = TRUE; /* (evil chortle) */ return; } } /* check if running under OpenDOS */ r.x.ax = 0x4452; __dpmi_int(0x21, &r); if ((r.x.ax >= 0x1072) && !(r.x.flags & 1)) { os_type = OSTYPE_OPENDOS; /* now check for OpenDOS EMM386.EXE */ r.x.ax = 0x12FF; r.x.bx = 0x0106; __dpmi_int(0x2F, &r); if ((r.x.ax == 0) && (r.x.bx == 0xEDC0)) { i_love_bill = TRUE; } return; } /* check for that stupid win95 "stealth mode" setting */ r.x.ax = 0x8102; r.x.bx = 0; r.x.dx = 0; __dpmi_int(0x4B, &r); if ((r.x.bx == 3) && !(r.x.flags & 1)) { os_type = OSTYPE_WIN95; os_multitasking = TRUE; i_love_bill = TRUE; return; } /* fetch DOS version if pure DOS is likely to be the running OS */ if (os_type == OSTYPE_UNKNOWN) { r.x.ax = 0x3000; __dpmi_int(0x21, &r); os_version = r.h.al; os_revision = r.h.ah; } } /* sys_dos_init: * Top level system driver wakeup call. */ static int sys_dos_init(void) { #ifdef ALLEGRO_DJGPP /* make sure djgpp won't move our memory around */ _crt0_startup_flags &= ~_CRT0_FLAG_UNIX_SBRK; _crt0_startup_flags |= _CRT0_FLAG_NONMOVE_SBRK; #endif /* initialise the irq wrapper functions */ _dos_irq_init(); /* check which OS we are running under */ detect_os(); /* detect CRTC register address */ _vga_regs_init(); /* install emergency-exit signal handlers */ old_sig_abrt = signal(SIGABRT, signal_handler); old_sig_fpe = signal(SIGFPE, signal_handler); old_sig_ill = signal(SIGILL, signal_handler); old_sig_segv = signal(SIGSEGV, signal_handler); old_sig_term = signal(SIGTERM, signal_handler); old_sig_int = signal(SIGINT, signal_handler); #ifdef SIGKILL old_sig_kill = signal(SIGKILL, signal_handler); #endif #ifdef SIGQUIT old_sig_quit = signal(SIGQUIT, signal_handler); #endif #ifdef SIGTRAP old_sig_trap = signal(SIGTRAP, signal_handler); #endif return 0; } /* sys_dos_exit: * The end of the world... */ static void sys_dos_exit(void) { _dos_irq_exit(); signal(SIGABRT, old_sig_abrt); signal(SIGFPE, old_sig_fpe); signal(SIGILL, old_sig_ill); signal(SIGSEGV, old_sig_segv); signal(SIGTERM, old_sig_term); signal(SIGINT, old_sig_int); #ifdef SIGKILL signal(SIGKILL, old_sig_kill); #endif #ifdef SIGQUIT signal(SIGQUIT, old_sig_quit); #endif #ifdef SIGTRAP signal(SIGTRAP, old_sig_trap); #endif } /* sys_dos_get_executable_name: * Return full path to the current executable. */ static void sys_dos_get_executable_name(char *output, int size) { #ifdef ALLEGRO_DJGPP /* djgpp stores the program name in the __crt0_argv[] global */ do_uconvert(__crt0_argv[0], U_ASCII, output, U_CURRENT, size); #elif defined ALLEGRO_WATCOM /* Watcom has a _cmdname() function to fetch the program name */ char buf[1024]; do_uconvert(_cmdname(buf), U_ASCII, output, U_CURRENT, size); #else #error unknown platform #endif } #ifdef ALLEGRO_DJGPP /* sys_dos_assert: * Handles an assert failure, generating a stack traceback. This routine * is heavily based on dpmiexcp.c from the djgpp libc sources, by Charles * Sandmann. It is reimplemented here because we don't want the register * dump, just the stack traceback, and so we can strip off the top couple * of stack entries (users don't need to see inside the Allegro assert * mechanism). And because it is cool. */ static void sys_dos_assert(AL_CONST char *msg) { extern unsigned int end __asm__ ("end"); extern unsigned int _stklen; unsigned int *vbp, *vbp_new, *tos, veip; int max, c; jmp_buf j; setjmp(j); allegro_exit(); fprintf(stderr, "%s\n\n", msg); fprintf(stderr, "Call frame traceback EIPs:\n"); tos = (unsigned int *)__djgpp_selector_limit; vbp = (unsigned int *)j->__ebp; if (isatty(fileno(stderr))) { max = _farpeekb(_dos_ds, 0x484) + 1; if ((max < 10) || (max > 75)) max = 19; else max -= 6; } else max = _stklen/8; c = 0; while (((unsigned int)vbp >= j->__esp) && (vbp >= &end) && (vbp < tos)) { vbp_new = (unsigned int *)vbp[0]; if (!vbp_new) break; veip = vbp[1]; if (c++) fprintf(stderr, " 0x%08x\n", veip); vbp = vbp_new; if (--max <= 0) break; } exit(1); } #endif /* sys_dos_save_console_state: * Called just before switching into a graphics mode, to remember what * the text console was like. */ static void sys_dos_save_console_state(void) { __dpmi_regs r; int c; if (!console_virgin) return; for (c=0; c<256; c++) { /* store current color palette */ outportb(0x3C7, c); _current_palette[c].r = inportb(0x3C9); _current_palette[c].g = inportb(0x3C9); _current_palette[c].b = inportb(0x3C9); } r.x.ax = 0x0F00; /* store current video mode */ __dpmi_int(0x10, &r); a_rez = r.x.ax & 0xFF; if (a_rez > 19) /* ignore non-standard modes */ a_rez = 3; if (a_rez == 3) /* store current screen height */ a_lines = ScreenRows(); else a_lines = -1; console_virgin = FALSE; } /* sys_dos_restore_console_state: * Puts us back into the original text mode. */ static void sys_dos_restore_console_state(void) { __dpmi_regs r; r.x.ax = 0x0F00; __dpmi_int(0x10, &r); if ((r.x.ax & 0xFF) != a_rez) { r.x.ax = a_rez; __dpmi_int(0x10, &r); } if (a_rez == 3) { if (ScreenRows() != a_lines) _set_screen_lines(a_lines); } } /* sys_dos_read_palette: * Reads the current palette from the video hardware. */ static void sys_dos_read_palette(void) { if (console_virgin) sys_dos_save_console_state(); } /* sys_dos_set_palette: * Writes a palette to the video hardware. */ static void sys_dos_set_palette(AL_CONST struct RGB *p, int from, int to, int vsync) { if (console_virgin) sys_dos_save_console_state(); _vga_set_palette_range(p, from, to, vsync); } /* sys_dos_get_gfx_safe_mode: * Defines the safe graphics mode for this system. */ static void sys_dos_get_gfx_safe_mode(int *driver, struct GFX_MODE *mode) { *driver = GFX_VGA; mode->width = 320; mode->height = 200; mode->bpp = 8; } /* _set_vga_mode: * Helper for the VGA and mode-X drivers to set a video mode. */ uintptr_t _set_vga_mode(int modenum) { __dpmi_regs r; if (modenum >= 0x100) { /* set VESA mode */ r.x.ax = 0x4F02; r.x.bx = modenum; __dpmi_int(0x10, &r); if (r.h.ah) return 0; } else { /* set VGA mode */ r.x.ax = modenum; __dpmi_int(0x10, &r); } return 0xA0000; } /* _unset_vga_mode: * Helper for the VGA and mode-X drivers to unset a video mode. */ void _unset_vga_mode(void) { /* nothing to be done in DOS */ } /* _save_vga_mode: * Helper for VGA and mode-X drivers to save state. */ void _save_vga_mode(void) { /* nothing to be done in DOS */ } /* _restore_vga_mode: * Helper to restore previously saved mode. */ void _restore_vga_mode(void) { /* nothing to be done in DOS */ } /* sys_dos_yield_timeslice: * Yields the remaining timeslice portion to the system */ static void sys_dos_yield_timeslice(void) { #ifdef ALLEGRO_DJGPP __dpmi_yield(); #endif }