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, ®s, ®s);
256 ch = regs.h.al;
257 scan = regs.h.ah;
258 /* Get shift status.
259 */
260 regs.h.ah = GETKEYFLAGS;
261 int86(KEYBRD_BIOS, ®s, ®s);
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(®s, ®s);
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(®s, ®s); /* 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(®s, ®s);
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(®s, ®s);
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(®s, ®s, &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(®s, ®s);
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(®s, ®s, &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(®s, ®s);
515 return (regs.x.dx);
516 }
517
518 # endif /* OVLB */
519
520 #endif /* MSDOS */
521