--- /dev/null
+/* 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 <dos.h>
+#include <ctype.h>
+
+/*
+ * 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);
+#ifndef __GO32__
+static char * NDECL(getdta);
+#endif
+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 */