/* SCCS Id: @(#)msdos.c 3.3 2000/07/30 */ /* Copyright (c) NetHack PC Development Team 1990, 1991, 1992, 1993, 1994 */ /* NetHack may be freely redistributed. See license for details. */ /* * MSDOS system functions. * Many thanks to Don Kneller who originated the DOS port and * contributed much to the cause. */ #define NEED_VARARGS #include "hack.h" #ifdef MSDOS #include "pcvideo.h" #include #include /* * MS-DOS functions */ #define DIRECT_INPUT 0x07 /* Unfiltered Character Input Without Echo */ #define FATINFO 0x1B /* Get Default Drive Data */ /* MS-DOS 2.0+: */ #define GETDTA 0x2F /* Get DTA Address */ #define FREESPACE 0x36 /* Get Drive Allocation Info */ #define GETSWITCHAR 0x3700 /* Get Switch Character */ #define FINDFIRST 0x4E /* Find First File */ #define FINDNEXT 0x4F /* Find Next File */ #define SETFILETIME 0x5701 /* Set File Date & Time */ /* * BIOS interrupts */ #ifdef PC9800 #define KEYBRD_BIOS 0x18 #else #define KEYBRD_BIOS 0x16 #endif /* * Keyboard BIOS functions */ #define READCHAR 0x00 /* Read Character from Keyboard */ #define GETKEYFLAGS 0x02 /* Get Keyboard Flags */ /*#define KEY_DEBUG /* print values of unexpected key codes - devel*/ void FDECL(get_cursor,(int *, int *)); #ifdef OVL0 /* direct bios calls are used only when iflags.BIOS is set */ static char NDECL(DOSgetch); static char NDECL(BIOSgetch); static char * NDECL(getdta); static unsigned int FDECL(dos_ioctl, (int,int,unsigned)); #ifdef USE_TILES extern boolean FDECL(pckeys,(unsigned char, unsigned char)); /* pckeys.c */ #endif int tgetch() { char ch; /* BIOSgetch can use the numeric key pad on IBM compatibles. */ # ifdef SIMULATE_CURSOR if (iflags.grmode && cursor_flag) DrawCursor(); # endif if (iflags.BIOS) ch = BIOSgetch(); else ch = DOSgetch(); # ifdef SIMULATE_CURSOR if (iflags.grmode && cursor_flag) HideCursor(); # endif return ((ch == '\r') ? '\n' : ch); } /* * Keyboard translation tables. */ #ifdef PC9800 #define KEYPADLO 0x38 #define KEYPADHI 0x50 #else #define KEYPADLO 0x47 #define KEYPADHI 0x53 #endif #define PADKEYS (KEYPADHI - KEYPADLO + 1) #define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) /* * Keypad keys are translated to the normal values below. * When iflags.BIOS is active, shifted keypad keys are translated to the * shift values below. */ static const struct pad { char normal, shift, cntrl; } keypad[PADKEYS] = { #ifdef PC9800 {'>', '>', '>'}, /* Ins */ {'<', '<', '<'}, /* Del */ {'k', 'K', C('k')}, /* Up */ {'h', 'H', C('h')}, /* Left */ {'l', 'L', C('l')}, /* Right */ {'j', 'J', C('j')}, /* Down */ { 0 , 0 , 0 }, /* HomeClr */ {'?', '?', '?' }, /* Help */ {'m', C('p'), C('p')}, /* - */ {'/', '/', '/'}, /* / */ {'y', 'Y', C('y')}, /* 7 */ {'k', 'K', C('k')}, /* 8 */ {'u', 'U', C('u')}, /* 9 */ {'*', '*', '*'}, /* * */ {'h', 'H', C('h')}, /* 4 */ {'g', 'g', 'g'}, /* 5 */ {'l', 'L', C('l')}, /* 6 */ {'p', 'P', C('p')}, /* + */ {'b', 'B', C('b')}, /* 1 */ {'j', 'J', C('j')}, /* 2 */ {'n', 'N', C('n')}, /* 3 */ {'=', '=', '='}, /* = */ {'i', 'I', C('i')}, /* 0 */ {',', ':', ':'}, /* , */ {'.', '.', '.'} /* . */ #else {'y', 'Y', C('y')}, /* 7 */ {'k', 'K', C('k')}, /* 8 */ {'u', 'U', C('u')}, /* 9 */ {'m', C('p'), C('p')}, /* - */ {'h', 'H', C('h')}, /* 4 */ {'g', 'g', 'g'}, /* 5 */ {'l', 'L', C('l')}, /* 6 */ {'p', 'P', C('p')}, /* + */ {'b', 'B', C('b')}, /* 1 */ {'j', 'J', C('j')}, /* 2 */ {'n', 'N', C('n')}, /* 3 */ {'i', 'I', C('i')}, /* Ins */ {'.', ':', ':'} /* Del */ #endif }, numpad[PADKEYS] = { #ifdef PC9800 {'>', '>', '>'}, /* Ins */ {'<', '<', '<'}, /* Del */ {'8', M('8'), '8'}, /* Up */ {'4', M('4'), '4'}, /* Left */ {'6', M('6'), '6'}, /* Right */ {'2', M('2'), '2'}, /* Down */ { 0 , 0 , 0 }, /* HomeClr */ {'?', '?', '?'}, /* Help */ {'m', C('p'), C('p')}, /* - */ {'/', '/', '/'}, /* / */ {'7', M('7'), '7'}, /* 7 */ {'8', M('8'), '8'}, /* 8 */ {'9', M('9'), '9'}, /* 9 */ {'*', '*', '*'}, /* * */ {'4', M('4'), '4'}, /* 4 */ {'g', 'G', 'g'}, /* 5 */ {'6', M('6'), '6'}, /* 6 */ {'p', 'P', C('p')}, /* + */ {'1', M('1'), '1'}, /* 1 */ {'2', M('2'), '2'}, /* 2 */ {'3', M('3'), '3'}, /* 3 */ {'=', '=', '='}, /* = */ {'i', 'I', C('i')}, /* 0 */ {',', ':', ':'}, /* , */ {'.', '.', '.'} /* . */ #else {'7', M('7'), '7'}, /* 7 */ {'8', M('8'), '8'}, /* 8 */ {'9', M('9'), '9'}, /* 9 */ {'m', C('p'), C('p')}, /* - */ {'4', M('4'), '4'}, /* 4 */ {'g', 'G', 'g'}, /* 5 */ {'6', M('6'), '6'}, /* 6 */ {'p', 'P', C('p')}, /* + */ {'1', M('1'), '1'}, /* 1 */ {'2', M('2'), '2'}, /* 2 */ {'3', M('3'), '3'}, /* 3 */ {'i', 'I', C('i')}, /* Ins */ {'.', ':', ':'} /* Del */ #endif }; /* * Unlike Ctrl-letter, the Alt-letter keystrokes have no specific ASCII * meaning unless assigned one by a keyboard conversion table, so the * keyboard BIOS normally does not return a character code when Alt-letter * is pressed. So, to interpret unassigned Alt-letters, we must use a * scan code table to translate the scan code into a letter, then set the * "meta" bit for it. -3. */ #ifdef PC9800 #define SCANLO 0x5 #else #define SCANLO 0x10 #endif /* PC9800 */ static const char scanmap[] = { /* ... */ #ifdef PC9800 0, 0, 0, 0, 0, 0, '-','^','\\','\b', '\t','q','w','e','r','t','y','u','i','o','p','@','[', '\n', 'a','s','d','f','g','h','j','k','l',';',':', ']', 'z','x','c','v','b','N','m',',','.','/' /* ... */ #else 'q','w','e','r','t','y','u','i','o','p','[',']', '\n', 0, 'a','s','d','f','g','h','j','k','l',';','\'', '`', 0, '\\', 'z','x','c','v','b','n','m',',','.','?' /* ... */ #endif /* PC9800 */ }; #define inmap(x) (SCANLO <= (x) && (x) < SCANLO + SIZE(scanmap)) #ifdef NEW_ALT #define NUMERIC_SCANLO 0x78 static const char numeric_scanmap[] = { /* ... */ '1','2','3','4','5','6','7','8','9','0','-','=' }; # define in_numericmap(x) (NUMERIC_SCANLO <= (x) && \ (x) < NUMERIC_SCANLO + SIZE(numeric_scanmap)) # endif /* * BIOSgetch gets keys directly with a BIOS call. */ #ifdef PC9800 #define SHIFT 0x1 #define KANA 0x4 #define GRPH 0x8 #define CTRL 0x10 #else #define SHIFT (0x1 | 0x2) #define CTRL 0x4 #define ALT 0x8 #endif /* PC9800 */ static char BIOSgetch() { unsigned char scan, shift, ch=0; const struct pad *kpad; union REGS regs; do { /* Get scan code. */ regs.h.ah = READCHAR; int86(KEYBRD_BIOS, ®s, ®s); ch = regs.h.al; scan = regs.h.ah; /* Get shift status. */ regs.h.ah = GETKEYFLAGS; int86(KEYBRD_BIOS, ®s, ®s); shift = regs.h.al; /* Translate keypad keys */ if (iskeypad(scan)) { kpad = iflags.num_pad ? numpad : keypad; if (shift & SHIFT) ch = kpad[scan - KEYPADLO].shift; else if (shift & CTRL) ch = kpad[scan - KEYPADLO].cntrl; else ch = kpad[scan - KEYPADLO].normal; } #ifdef USE_TILES /* Check for special interface manipulation keys */ if (pckeys(scan, shift)) { ch = 0xFF; continue; } #endif /* Translate unassigned Alt-letters */ #ifdef PC9800 if (shift & KANA) return 0; if ((shift & GRPH) && (ch >= 0x80)) { #else if ((shift & ALT) && !ch) { #endif #if 0 pline("Scan code: %d 0x%03X", scan, scan); #endif if (inmap(scan)) ch = scanmap[scan - SCANLO]; #ifdef NEW_ALT else if (in_numericmap(scan)) ch = numeric_scanmap[scan - NUMERIC_SCANLO]; #endif return (isprint(ch) ? M(ch) : ch); } } while (ch == 0xFF); return ch; } static char DOSgetch() { union REGS regs; char ch; struct pad (*kpad)[PADKEYS]; regs.h.ah = DIRECT_INPUT; intdos(®s, ®s); ch = regs.h.al; #ifdef PC9800 if (ch < 0) /* KANA letters and GRPH-shifted letters(?) */ ch = 0; /* munch it */ #else /* * The extended codes for Alt-shifted letters, and unshifted keypad * and function keys, correspond to the scan codes. So we can still * translate the unshifted cursor keys and Alt-letters. -3. */ if (ch == 0) { /* an extended key */ regs.h.ah = DIRECT_INPUT; intdos(®s, ®s); /* get the extended key code */ ch = regs.h.al; if (iskeypad(ch)) { /* unshifted keypad keys */ kpad = (void *)(iflags.num_pad ? numpad : keypad); ch = (*kpad)[ch - KEYPADLO].normal; } else if (inmap(ch)) { /* Alt-letters */ ch = scanmap[ch - SCANLO]; if (isprint(ch)) ch = M(ch); } else ch = 0; /* munch it */ } #endif return (ch); } char switchar() { union REGS regs; regs.x.ax = GETSWITCHAR; intdos(®s, ®s); return regs.h.dl; } long freediskspace(path) char *path; { union REGS regs; regs.h.ah = FREESPACE; if (path[0] && path[1] == ':') regs.h.dl = (toupper(path[0]) - 'A') + 1; else regs.h.dl = 0; intdos(®s, ®s); if (regs.x.ax == 0xFFFF) return -1L; /* bad drive number */ else return ((long) regs.x.bx * regs.x.cx * regs.x.ax); } #ifndef __GO32__ /* * Functions to get filenames using wildcards */ int findfirst_file(path) char *path; { union REGS regs; struct SREGS sregs; regs.h.ah = FINDFIRST; regs.x.cx = 0; /* attribute: normal files */ regs.x.dx = FP_OFF(path); sregs.ds = FP_SEG(path); intdosx(®s, ®s, &sregs); return !regs.x.cflag; } int findnext_file() { union REGS regs; regs.h.ah = FINDNEXT; intdos(®s, ®s); return !regs.x.cflag; } char * foundfile_buffer() { return (getdta() + 30); } /* Get disk transfer area */ static char * getdta() { union REGS regs; struct SREGS sregs; char *ret; regs.h.ah = GETDTA; intdosx(®s, ®s, &sregs); # ifdef MK_FP ret = (char *)MK_FP(sregs.es, regs.x.bx); # else FP_OFF(ret) = regs.x.bx; FP_SEG(ret) = sregs.es; # endif return ret; } long filesize_nh(file) char *file; { char *dta; if (findfirst_file(file)) { dta = getdta(); return (* (long *) (dta + 26)); } else return -1L; } #endif /* __GO32__ */ /* * Chdrive() changes the default drive. */ void chdrive(str) char *str; { # define SELECTDISK 0x0E char *ptr; union REGS inregs; char drive; if ((ptr = index(str, ':')) != (char *)0) { drive = toupper(*(ptr - 1)); inregs.h.ah = SELECTDISK; inregs.h.dl = drive - 'A'; intdos(&inregs, &inregs); } return; } /* Use the IOCTL DOS function call to change stdin and stdout to raw * mode. For stdin, this prevents MSDOS from trapping ^P, thus * freeing us of ^P toggling 'echo to printer'. * Thanks to Mark Zbikowski (markz@microsoft.UUCP). */ #define DEVICE 0x80 #define RAW 0x20 #define IOCTL 0x44 #define STDIN fileno(stdin) #define STDOUT fileno(stdout) #define GETBITS 0 #define SETBITS 1 static unsigned int old_stdin, old_stdout; void disable_ctrlP() { if (!iflags.rawio) return; old_stdin = dos_ioctl(STDIN, GETBITS, 0); old_stdout = dos_ioctl(STDOUT, GETBITS, 0); if (old_stdin & DEVICE) dos_ioctl(STDIN, SETBITS, old_stdin | RAW); if (old_stdout & DEVICE) dos_ioctl(STDOUT, SETBITS, old_stdout | RAW); return; } void enable_ctrlP() { if (!iflags.rawio) return; if (old_stdin) (void) dos_ioctl(STDIN, SETBITS, old_stdin); if (old_stdout) (void) dos_ioctl(STDOUT, SETBITS, old_stdout); return; } static unsigned int dos_ioctl(handle, mode, setvalue) int handle, mode; unsigned setvalue; { union REGS regs; regs.h.ah = IOCTL; regs.h.al = mode; regs.x.bx = handle; regs.h.dl = setvalue; regs.h.dh = 0; /* Zero out dh */ intdos(®s, ®s); return (regs.x.dx); } # endif /* OVLB */ #endif /* MSDOS */