1 /*	SCCS Id: @(#)msdos.c	 3.4	 2000/07/30		          */
2 /* Copyright (c) NetHack PC Development Team 1990, 1991, 1992, 1993, 1994 */
3 /* NetHack may be freely redistributed.  See license for details.         */
4 
5 /*
6  *  MSDOS system functions.
7  *  Many thanks to Don Kneller who originated the DOS port and
8  *  contributed much to the cause.
9  */
10 
11 #define NEED_VARARGS
12 #include "hack.h"
13 
14 #ifdef MSDOS
15 #include "pcvideo.h"
16 
17 #include <dos.h>
18 #include <ctype.h>
19 
20 /*
21  * MS-DOS functions
22  */
23 #define DIRECT_INPUT    0x07    /* Unfiltered Character Input Without Echo */
24 #define FATINFO     0x1B    /* Get Default Drive Data */
25 /* MS-DOS 2.0+: */
26 #define GETDTA      0x2F    /* Get DTA Address */
27 #define FREESPACE   0x36    /* Get Drive Allocation Info */
28 #define GETSWITCHAR 0x3700  /* Get Switch Character */
29 #define FINDFIRST   0x4E    /* Find First File */
30 #define FINDNEXT    0x4F    /* Find Next File */
31 #define SETFILETIME 0x5701  /* Set File Date & Time */
32 /*
33  * BIOS interrupts
34  */
35 #ifdef PC9800
36 #define KEYBRD_BIOS 0x18
37 #else
38 #define KEYBRD_BIOS 0x16
39 #endif
40 
41 /*
42  * Keyboard BIOS functions
43  */
44 #define READCHAR    0x00    /* Read Character from Keyboard */
45 #define GETKEYFLAGS 0x02    /* Get Keyboard Flags */
46 /*#define KEY_DEBUG	 */   /* print values of unexpected key codes - devel*/
47 
48 void FDECL(get_cursor,(int *, int *));
49 
50 #ifdef OVL0
51 
52 /* direct bios calls are used only when iflags.BIOS is set */
53 
54 static char NDECL(DOSgetch);
55 static char NDECL(BIOSgetch);
56 #ifndef __GO32__
57 static char * NDECL(getdta);
58 #endif
59 static unsigned int FDECL(dos_ioctl, (int,int,unsigned));
60 #ifdef USE_TILES
61 extern boolean FDECL(pckeys,(unsigned char, unsigned char));	/* pckeys.c */
62 #endif
63 
64 int
tgetch()65 tgetch()
66 {
67 	char ch;
68 
69 	/* BIOSgetch can use the numeric key pad on IBM compatibles. */
70 # ifdef SIMULATE_CURSOR
71 	if (iflags.grmode && cursor_flag) DrawCursor();
72 # endif
73 	if (iflags.BIOS)
74 		ch = BIOSgetch();
75 	else
76 		ch = DOSgetch();
77 # ifdef SIMULATE_CURSOR
78 	if (iflags.grmode && cursor_flag) HideCursor();
79 # endif
80 	return ((ch == '\r') ? '\n' : ch);
81 }
82 
83 
84 
85 /*
86  *  Keyboard translation tables.
87  */
88 #ifdef PC9800
89 #define KEYPADLO	0x38
90 #define KEYPADHI	0x50
91 #else
92 #define KEYPADLO	0x47
93 #define KEYPADHI	0x53
94 #endif
95 
96 #define PADKEYS 	(KEYPADHI - KEYPADLO + 1)
97 #define iskeypad(x)	(KEYPADLO <= (x) && (x) <= KEYPADHI)
98 
99 /*
100  * Keypad keys are translated to the normal values below.
101  * When iflags.BIOS is active, shifted keypad keys are translated to the
102  *    shift values below.
103  */
104 static const struct pad {
105 	char normal, shift, cntrl;
106 } keypad[PADKEYS] = {
107 #ifdef PC9800
108 			{'>', '>', '>'},		/* Ins */
109 			{'<', '<', '<'},		/* Del */
110 			{'k', 'K', C('k')},		/* Up */
111 			{'h', 'H', C('h')},		/* Left */
112 			{'l', 'L', C('l')},		/* Right */
113 			{'j', 'J', C('j')},		/* Down */
114 			{ 0 ,  0 ,  0 },		/* HomeClr */
115 			{'?', '?', '?' },		/* Help */
116 			{'m', C('p'), C('p')},		/* - */
117 			{'/', '/', '/'},		/* / */
118 			{'y', 'Y', C('y')},		/* 7 */
119 			{'k', 'K', C('k')},		/* 8 */
120 			{'u', 'U', C('u')},		/* 9 */
121 			{'*', '*', '*'},		/* * */
122 			{'h', 'H', C('h')},		/* 4 */
123 			{'g', 'g', 'g'},		/* 5 */
124 			{'l', 'L', C('l')},		/* 6 */
125 			{'p', 'P', C('p')},		/* + */
126 			{'b', 'B', C('b')},		/* 1 */
127 			{'j', 'J', C('j')},		/* 2 */
128 			{'n', 'N', C('n')},		/* 3 */
129 			{'=', '=', '='},		/* = */
130 			{'i', 'I', C('i')},		/* 0 */
131 			{',', ':', ':'}, 		/* , */
132 			{'.', '.', '.'} 		/* . */
133 #else
134 			{'y', 'Y', C('y')},		/* 7 */
135 			{'k', 'K', C('k')},		/* 8 */
136 			{'u', 'U', C('u')},		/* 9 */
137 			{'m', C('p'), C('p')},		/* - */
138 			{'h', 'H', C('h')},		/* 4 */
139 			{'g', 'g', 'g'},		/* 5 */
140 			{'l', 'L', C('l')},		/* 6 */
141 			{'p', 'P', C('p')},		/* + */
142 			{'b', 'B', C('b')},		/* 1 */
143 			{'j', 'J', C('j')},		/* 2 */
144 			{'n', 'N', C('n')},		/* 3 */
145 			{'i', 'I', C('i')},		/* Ins */
146 			{'.', ':', ':'}			/* Del */
147 #endif
148 }, numpad[PADKEYS] = {
149 #ifdef PC9800
150 			{'>', '>', '>'},		/* Ins */
151 			{'<', '<', '<'},		/* Del */
152 			{'8', M('8'), '8'},		/* Up */
153 			{'4', M('4'), '4'},		/* Left */
154 			{'6', M('6'), '6'},		/* Right */
155 			{'2', M('2'), '2'},		/* Down */
156 			{ 0 ,  0 ,  0 },		/* HomeClr */
157 			{'?', '?', '?'},		/* Help */
158 			{'m', C('p'), C('p')},		/* - */
159 			{'/', '/', '/'},		/* / */
160 			{'7', M('7'), '7'},		/* 7 */
161 			{'8', M('8'), '8'},		/* 8 */
162 			{'9', M('9'), '9'},		/* 9 */
163 			{'*', '*', '*'},		/* * */
164 			{'4', M('4'), '4'},		/* 4 */
165 			{'g', 'G', 'g'},		/* 5 */
166 			{'6', M('6'), '6'},		/* 6 */
167 			{'p', 'P', C('p')},		/* + */
168 			{'1', M('1'), '1'},		/* 1 */
169 			{'2', M('2'), '2'},		/* 2 */
170 			{'3', M('3'), '3'},		/* 3 */
171 			{'=', '=', '='},		/* = */
172 			{'i', 'I', C('i')},		/* 0 */
173 			{',', ':', ':'},		/* , */
174 			{'.', '.', '.'} 		/* . */
175 #else
176 			{'7', M('7'), '7'},		/* 7 */
177 			{'8', M('8'), '8'},		/* 8 */
178 			{'9', M('9'), '9'},		/* 9 */
179 			{'m', C('p'), C('p')},		/* - */
180 			{'4', M('4'), '4'},		/* 4 */
181 			{'5', M('5'), '5'},		/* 5 */
182 			{'6', M('6'), '6'},		/* 6 */
183 			{'p', 'P', C('p')},		/* + */
184 			{'1', M('1'), '1'},		/* 1 */
185 			{'2', M('2'), '2'},		/* 2 */
186 			{'3', M('3'), '3'},		/* 3 */
187 			{'0', M('0'), '0'},		/* Ins */
188 			{'.', ':', ':'}			/* Del */
189 #endif
190 };
191 
192 /*
193  * Unlike Ctrl-letter, the Alt-letter keystrokes have no specific ASCII
194  * meaning unless assigned one by a keyboard conversion table, so the
195  * keyboard BIOS normally does not return a character code when Alt-letter
196  * is pressed.	So, to interpret unassigned Alt-letters, we must use a
197  * scan code table to translate the scan code into a letter, then set the
198  * "meta" bit for it.  -3.
199  */
200 #ifdef PC9800
201 #define SCANLO		0x5
202 #else
203 #define SCANLO		0x10
204 #endif /* PC9800 */
205 
206 static const char scanmap[] = { 	/* ... */
207 #ifdef PC9800
208 			 0,  0,  0,  0,  0,  0, '-','^','\\','\b',
209 	'\t','q','w','e','r','t','y','u','i','o','p','@','[', '\n',
210 	'a','s','d','f','g','h','j','k','l',';',':', ']',
211 	'z','x','c','v','b','N','m',',','.','/'	/* ... */
212 #else
213 	'q','w','e','r','t','y','u','i','o','p','[',']', '\n',
214 	0, 'a','s','d','f','g','h','j','k','l',';','\'', '`',
215 	0, '\\', 'z','x','c','v','b','n','m',',','.','?'	/* ... */
216 #endif /* PC9800 */
217 };
218 
219 #define inmap(x)	(SCANLO <= (x) && (x) < SCANLO + SIZE(scanmap))
220 
221 #ifdef NEW_ALT
222 #define NUMERIC_SCANLO		0x78
223 static const char numeric_scanmap[] = { 	/* ... */
224 	'1','2','3','4','5','6','7','8','9','0','-','='
225 };
226 # define in_numericmap(x)	(NUMERIC_SCANLO <= (x) && \
227 					(x) < NUMERIC_SCANLO + SIZE(numeric_scanmap))
228 # endif
229 
230 /*
231  * BIOSgetch gets keys directly with a BIOS call.
232  */
233 #ifdef PC9800
234 #define SHIFT		0x1
235 #define KANA		0x4
236 #define GRPH		0x8
237 #define CTRL		0x10
238 #else
239 #define SHIFT		(0x1 | 0x2)
240 #define CTRL		0x4
241 #define ALT		0x8
242 #endif /* PC9800 */
243 
244 static char
BIOSgetch()245 BIOSgetch()
246 {
247       unsigned char scan, shift, ch=0;
248       const struct pad *kpad;
249       union REGS regs;
250 
251       do {
252 	/* Get scan code.
253 	 */
254 	regs.h.ah = READCHAR;
255 	int86(KEYBRD_BIOS, &regs, &regs);
256 	ch = regs.h.al;
257 	scan = regs.h.ah;
258 	/* Get shift status.
259 	 */
260 	regs.h.ah = GETKEYFLAGS;
261 	int86(KEYBRD_BIOS, &regs, &regs);
262 	shift = regs.h.al;
263 
264 	/* Translate keypad keys */
265 	if (iskeypad(scan)) {
266 		kpad = iflags.num_pad ? numpad : keypad;
267 		if (shift & SHIFT)
268 			ch = kpad[scan - KEYPADLO].shift;
269 		else if (shift & CTRL)
270 			ch = kpad[scan - KEYPADLO].cntrl;
271 		else
272 			ch = kpad[scan - KEYPADLO].normal;
273 	}
274 #ifdef USE_TILES
275 	/* Check for special interface manipulation keys */
276 	if (pckeys(scan, shift)) {
277 		ch = 0xFF;
278 		continue;
279 	}
280 #endif
281 	/* Translate unassigned Alt-letters */
282 #ifdef PC9800
283 	if (shift & KANA)
284 		return 0;
285 	if ((shift & GRPH) && (ch >= 0x80)) {
286 #else
287 	if ((shift & ALT) && !ch) {
288 #endif
289 #if 0
290 		pline("Scan code: %d 0x%03X", scan, scan);
291 #endif
292 		if (inmap(scan))
293 			ch = scanmap[scan - SCANLO];
294 #ifdef NEW_ALT
295 		else if (in_numericmap(scan))
296 			ch = numeric_scanmap[scan - NUMERIC_SCANLO];
297 #endif
298 		return (isprint(ch) ? M(ch) : ch);
299 	}
300       } while (ch == 0xFF);
301       return ch;
302 }
303 
304 static char
305 DOSgetch()
306 {
307 	union REGS regs;
308 	char ch;
309 	struct pad (*kpad)[PADKEYS];
310 
311 	regs.h.ah = DIRECT_INPUT;
312 	intdos(&regs, &regs);
313 	ch = regs.h.al;
314 
315 #ifdef PC9800
316 	if (ch < 0)	/* KANA letters and GRPH-shifted letters(?) */
317 		ch = 0; /* munch it */
318 #else
319 	/*
320 	 * The extended codes for Alt-shifted letters, and unshifted keypad
321 	 * and function keys, correspond to the scan codes.  So we can still
322 	 * translate the unshifted cursor keys and Alt-letters.  -3.
323 	 */
324 	if (ch == 0) {		/* an extended key */
325 		regs.h.ah = DIRECT_INPUT;
326 		intdos(&regs, &regs);	/* get the extended key code */
327 		ch = regs.h.al;
328 
329 		if (iskeypad(ch)) {	/* unshifted keypad keys */
330 			kpad = (void *)(iflags.num_pad ? numpad : keypad);
331 			ch = (*kpad)[ch - KEYPADLO].normal;
332 		} else if (inmap(ch)) { /* Alt-letters */
333 			ch = scanmap[ch - SCANLO];
334 			if (isprint(ch)) ch = M(ch);
335 		} else ch = 0;		/* munch it */
336 	}
337 #endif
338 	return (ch);
339 }
340 
341 char
342 switchar()
343 {
344 	union REGS regs;
345 
346 	regs.x.ax = GETSWITCHAR;
347 	intdos(&regs, &regs);
348 	return regs.h.dl;
349 }
350 
351 long
352 freediskspace(path)
353 char *path;
354 {
355 	union REGS regs;
356 
357 	regs.h.ah = FREESPACE;
358 	if (path[0] && path[1] == ':')
359 		regs.h.dl = (toupper(path[0]) - 'A') + 1;
360 	else
361 		regs.h.dl = 0;
362 	intdos(&regs, &regs);
363 	if (regs.x.ax == 0xFFFF)
364 		return -1L;		/* bad drive number */
365 	else
366 		return ((long) regs.x.bx * regs.x.cx * regs.x.ax);
367 }
368 
369 #ifndef __GO32__
370 /*
371  * Functions to get filenames using wildcards
372  */
373 int
374 findfirst_file(path)
375 char *path;
376 {
377 	union REGS regs;
378 	struct SREGS sregs;
379 
380 	regs.h.ah = FINDFIRST;
381 	regs.x.cx = 0;		/* attribute: normal files */
382 	regs.x.dx = FP_OFF(path);
383 	sregs.ds = FP_SEG(path);
384 	intdosx(&regs, &regs, &sregs);
385 	return !regs.x.cflag;
386 }
387 
388 int
389 findnext_file() {
390 	union REGS regs;
391 
392 	regs.h.ah = FINDNEXT;
393 	intdos(&regs, &regs);
394 	return !regs.x.cflag;
395 }
396 
397 char *
398 foundfile_buffer()
399 {
400 	return (getdta() + 30);
401 }
402 
403 
404 /* Get disk transfer area */
405 static char *
406 getdta()
407 {
408 	union REGS regs;
409 	struct SREGS sregs;
410 	char *ret;
411 
412 	regs.h.ah = GETDTA;
413 	intdosx(&regs, &regs, &sregs);
414 #   ifdef MK_FP
415 	ret = (char *)MK_FP(sregs.es, regs.x.bx);
416 #   else
417 	FP_OFF(ret) = regs.x.bx;
418 	FP_SEG(ret) = sregs.es;
419 #   endif
420 	return ret;
421 }
422 
423 long
424 filesize_nh(file)
425 char *file;
426 {
427 	char *dta;
428 
429 	if (findfirst_file(file)) {
430 		dta = getdta();
431 		return  (* (long *) (dta + 26));
432 	} else
433 		return -1L;
434 }
435 
436 #endif /* __GO32__ */
437 
438 /*
439  * Chdrive() changes the default drive.
440  */
441 void
442 chdrive(str)
443 char *str;
444 {
445 #  define SELECTDISK	0x0E
446 	char *ptr;
447 	union REGS inregs;
448 	char drive;
449 
450 	if ((ptr = index(str, ':')) != (char *)0) {
451 		drive = toupper(*(ptr - 1));
452 		inregs.h.ah = SELECTDISK;
453 		inregs.h.dl = drive - 'A';
454 		intdos(&inregs, &inregs);
455 	}
456 	return;
457 }
458 
459 
460 /* Use the IOCTL DOS function call to change stdin and stdout to raw
461  * mode.  For stdin, this prevents MSDOS from trapping ^P, thus
462  * freeing us of ^P toggling 'echo to printer'.
463  * Thanks to Mark Zbikowski (markz@microsoft.UUCP).
464  */
465 
466 #define DEVICE		0x80
467 #define RAW		0x20
468 #define IOCTL		0x44
469 #define STDIN		fileno(stdin)
470 #define STDOUT		fileno(stdout)
471 #define GETBITS		0
472 #define SETBITS		1
473 
474 static unsigned	int old_stdin, old_stdout;
475 
476 void
477 disable_ctrlP()
478 {
479 
480 	if (!iflags.rawio) return;
481 
482     old_stdin = dos_ioctl(STDIN, GETBITS, 0);
483     old_stdout = dos_ioctl(STDOUT, GETBITS, 0);
484 	if (old_stdin & DEVICE)
485         dos_ioctl(STDIN, SETBITS, old_stdin | RAW);
486 	if (old_stdout & DEVICE)
487         dos_ioctl(STDOUT, SETBITS, old_stdout | RAW);
488 	return;
489 }
490 
491 void
492 enable_ctrlP()
493 {
494 	if (!iflags.rawio) return;
495 	if (old_stdin)
496         (void) dos_ioctl(STDIN, SETBITS, old_stdin);
497 	if (old_stdout)
498         (void) dos_ioctl(STDOUT, SETBITS, old_stdout);
499 	return;
500 }
501 
502 static unsigned int
503 dos_ioctl(handle, mode, setvalue)
504 int handle, mode;
505 unsigned setvalue;
506 {
507 	union REGS regs;
508 
509 	regs.h.ah = IOCTL;
510 	regs.h.al = mode;
511 	regs.x.bx = handle;
512 	regs.h.dl = setvalue;
513 	regs.h.dh = 0;			/* Zero out dh */
514 	intdos(&regs, &regs);
515 	return (regs.x.dx);
516 }
517 
518 # endif /* OVLB */
519 
520 #endif /* MSDOS */
521