kern_recvmsg() may not return a valid(non-NULL) pointer to `sa'
[dragonfly.git] / usr.bin / doscmd / dos.c
blob345c5af0262faa64a3b9c38893f50e08949299a9
1 /*
2 * Copyright (c) 1996
3 * Michael Smith. All rights reserved.
4 * Copyright (c) 1992, 1993, 1996
5 * Berkeley Software Design, Inc. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Berkeley Software
18 * Design, Inc.
20 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
32 * BSDI int21.c,v 2.2 1996/04/08 19:32:51 bostic Exp
34 * $FreeBSD: src/usr.bin/doscmd/dos.c,v 1.7.2.5 2002/04/25 11:04:50 tg Exp $
35 * $DragonFly: src/usr.bin/doscmd/dos.c,v 1.2 2003/06/17 04:29:25 dillon Exp $
38 #include <sys/ioctl.h>
39 #include <sys/param.h>
40 #include <sys/mount.h>
41 #include <ctype.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <glob.h>
45 #include <paths.h>
46 #include <stddef.h>
47 #include <time.h>
48 #include <unistd.h>
50 #include "doscmd.h"
51 #include "cwd.h"
52 #include "dispatch.h"
53 #include "tty.h"
55 /* Country Info */
56 struct {
57 ushort ciDateFormat;
58 char ciCurrency[5];
59 char ciThousands[2];
60 char ciDecimal[2];
61 char ciDateSep[2];
62 char ciTimeSep[2];
63 char ciCurrencyFormat;
64 char ciCurrencyPlaces;
65 char ciTimeFormat;
66 ushort ciCaseMapOffset;
67 ushort ciCaseMapSegment;
68 char ciDataSep[2];
69 #if 0
70 char ciReserved[10];
71 #endif
72 } countryinfo = {
73 0, "$", ",", ".", "-", ":", 0, 2, 0, 0xffff, 0xffff, "?"
76 /* DOS File Control Block */
77 struct fcb {
78 u_char fcbMagic;
79 u_char fcbResoived[5];
80 u_char fcbAttribute;
81 u_char fcbDriveID;
82 u_char fcbFileName[8];
83 u_char fcbExtent[3];
84 u_short fcbCurBlockNo;
85 u_short fcbRecSize;
86 u_long fcbFileSize;
87 u_short fcbFileDate;
88 u_short fcbFileTime;
89 int fcbReserved;
90 int fcb_fd; /* hide UNIX FD here */
91 u_char fcbCurRecNo;
92 u_long fcbRandomRecNo;
93 }/* __attribute__((__packed__))*/;
95 /* exports */
96 int diskdrive = 2; /* C: */
97 char *InDOS;
99 /* locals */
100 static void fcb_to_string(struct fcb *, u_char *);
102 static int ctrl_c_flag = 0;
103 static int return_status = 0;
104 static int doserrno = 0;
105 static int memory_strategy = 0; /* first fit (we ignore this) */
106 static u_long upcase_vector;
108 static u_char upc_table[0x80] = {
109 0x80, 0x9a, 'E', 'A', 0x8e, 'A', 0x8f, 0x80,
110 'E', 'E', 'E', 'I', 'I', 'I', 0x8e, 0x8f,
111 0x90, 0x92, 0x92, 'O', 0x99, 'O', 'U', 'U',
112 'Y', 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
113 'A', 'I', 'O', 'U', 0xa5, 0xa5, 0xa6, 0xa7,
114 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
115 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
116 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
117 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
118 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
119 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
120 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
121 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
122 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
123 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
124 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
128 /******************************************************************************
129 ** utility functions
132 static u_char
133 upcase(u_char c)
136 if (islower(c))
137 return (toupper(c));
138 else if (c >= 0x80)
139 return (upc_table[c - 0x80]);
140 else
141 return (c);
144 static void
145 upcase_entry(regcontext_t *REGS)
147 R_AL = upcase(R_AL);
152 ** Handle the DOS drive info/free space/etc. calls.
154 static int
155 int21_free(regcontext_t *REGS)
157 fsstat_t fs;
158 int error;
159 int drive;
161 /* work out drive */
162 switch (R_AH) {
163 case 0x1c:
164 case 0x36:
165 drive = R_DL;
166 if (drive)
167 break;
168 /* FALLTHROUGH */
169 case 0x1b:
170 drive = diskdrive;
171 break;
172 default:
173 fatal("int21_free called on unknown function %x\n",R_AH);
176 error = get_space(drive, &fs);
177 if (error)
178 return(error);
180 R_AL = fs.sectors_cluster; /* sectors per cluster */
181 R_CX = fs.bytes_sector; /* bytes per sector */
182 R_DX = fs.total_clusters; /* total clusters */
184 switch (R_AH) {
185 case 0x1b:
186 case 0x1c:
187 BIOSDATA[0xb4] = 0xf0; /* "reserved" area, "other media" FAT ID */
188 R_DX = 0x40; /* BIOS data area */
189 R_BX = 0xb4;
190 break;
192 case 0x36:
193 R_BX = fs.avail_clusters; /* number of available clusters */
194 break;
196 return(0);
199 static void
200 pack_name(u_char *p, u_char *q)
202 int i;
204 for (i = 8; i > 0 && *p != ' '; i--)
205 *q++ = *p++;
206 p += i;
207 if (*p != ' ') {
208 *q++ = '.';
209 for (i = 3; i > 0 && *p != ' '; i--)
210 *q++ = *p++;
211 p += i;
213 *q = '\0';
216 static void
217 dosdir_to_dta(dosdir_t *dosdir, find_block_t *dta)
219 dta->attr = dosdir->attr;
220 dta->time = dosdir->time;
221 dta->date = dosdir->date;
222 dta->size = dosdir->size;
223 pack_name(dosdir->name, dta->name);
226 /* exported */
227 void
228 encode_dos_file_time(time_t t, u_short *dosdatep, u_short *dostimep)
230 struct tm tm;
232 tm = *localtime(&t);
233 *dostimep = (tm.tm_hour << 11) |
234 (tm.tm_min << 5) |
235 ((tm.tm_sec / 2) << 0);
236 *dosdatep = ((tm.tm_year - 80) << 9) |
237 ((tm.tm_mon + 1) << 5) |
238 (tm.tm_mday << 0);
241 time_t
242 decode_dos_file_time(u_short dosdate, u_short dostime)
244 struct tm tm;
245 time_t then;
247 tm.tm_hour = (dostime >> 11) & 0x1f;
248 tm.tm_min = (dostime >> 5) & 0x3f;
249 tm.tm_sec = ((dostime >> 0) & 0x1f) * 2;
250 tm.tm_year = ((dosdate >> 9) & 0x7f) + 80;
251 tm.tm_mon = ((dosdate >> 5) & 0x0f) - 1;
252 tm.tm_mday = (dosdate >> 0) & 0x1f;
253 /* tm_wday and tm_yday are ignored. */
254 tm.tm_isdst = 0;
255 /* tm_gmtoff is ignored. */
256 then = mktime(&tm);
257 return (then);
261 translate_filename(u_char *dname, u_char *uname, int *drivep)
263 u_char newpath[1024];
264 int error;
266 if (!strcasecmp(dname, "con")) {
267 *drivep = -1;
268 strcpy(uname, _PATH_TTY);
269 return (0);
272 /* XXX KLUDGE for EMS support w/o booting DOS */
273 /* Really need a better way to handle devices */
274 if (!strcasecmp(dname, "emmxxxx0")) {
275 *drivep = -1;
276 strcpy(uname, _PATH_DEVNULL);
277 return (0);
280 error = dos_makepath(dname, newpath);
281 if (error)
282 return (error);
284 error = dos_to_real_path(newpath, uname, drivep);
285 if (error)
286 return (error);
288 return (0);
291 static u_char magic[0x7e] = {
292 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
293 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
294 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
295 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
297 0x08, 0x0f, 0x06, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
298 0x0f, 0x0f, 0x0f, 0x04, 0x04, 0x0f, 0x0e, 0x06,
299 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
300 0x0f, 0x0f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0f,
302 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
303 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
304 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
305 0x0f, 0x0f, 0x0f, 0x06, 0x06, 0x06, 0x0f, 0x0f,
307 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
308 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
309 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
310 0x0f, 0x0f, 0x0f, 0x0f, 0x04, 0x0f,
313 #define isvalid(x) ((magic[(int)(x)] & 0x01) != 0)
314 #define issep(x) ((magic[(int)(x)] & 0x02) == 0)
315 #define iswhite(x) ((magic[(int)(x)] & 0x04) == 0)
317 static char *
318 skipwhite(char *p)
320 while (iswhite(*p))
321 ++p;
322 return (p);
325 #define get_drive_letter(x) ((x) - 0x40)
328 parse_filename(int flag, char *str, char *fcb, int *nb)
330 char *p;
331 int ret = 0;
332 int i;
334 p = str;
336 p = skipwhite(p);
337 if (flag & 1) {
338 if (issep(*p)) {
339 ++p;
340 p = skipwhite(p);
344 if (isvalid(*p) && p[1] == ':') {
345 *fcb++ = get_drive_letter(upcase(*p));
346 p += 2;
347 } else if (flag & 2) {
348 fcb++;
349 } else {
350 *fcb++ = 0; /* default drive */
353 i = 8;
354 if (isvalid(*p)) {
355 for (;;) {
356 if (!isvalid(*p)) {
357 for (; i > 0; i--)
358 *fcb++ = ' ';
359 break;
361 if (i > 0) {
362 switch (*p) {
363 case '*':
364 ret = 1;
365 for (; i > 0; i--)
366 *fcb++ = '?';
367 break;
368 case '?':
369 ret = 1;
370 default:
371 *fcb++ = upcase(*p);
372 i--;
373 break;
376 ++p;
378 } else if (flag & 4) {
379 fcb += i;
380 } else {
381 for (; i > 0; i--)
382 *fcb++ = ' ';
385 i = 3;
386 if (*p == '.') {
387 ++p;
388 for (;;) {
389 if (!isvalid(*p)) {
390 for (; i > 0; i--)
391 *fcb++ = ' ';
392 break;
394 if (i > 0) {
395 switch (*p) {
396 case '*':
397 ret = 1;
398 for (; i > 0; i--)
399 *fcb++ = '?';
400 break;
401 case '?':
402 ret = 1;
403 default:
404 *fcb++ = upcase(*p);
405 i--;
406 break;
409 ++p;
411 } else if (flag & 8) {
412 fcb += i;
413 } else {
414 for (; i > 0; i--)
415 *fcb++ = ' ';
418 for (i = 4; i > 0; i--)
419 *fcb++ = 0; /* filler */
421 *nb = p - str;
422 return (ret);
425 /******************************************************************************
426 ** int21 functions
430 ** 21:00
432 ** terminate
434 static int
435 int21_00(regcontext_t *REGS)
437 done(REGS,0);
438 /* keep `gcc -Wall' happy */
439 return(0);
443 ** 21:01
445 ** read character with echo
447 static int
448 int21_01(regcontext_t *REGS)
450 int n;
452 if ((n = tty_read((regcontext_t *)&REGS->sc, TTYF_BLOCKALL)) >= 0)
453 R_AL = n;
454 return(0);
458 ** 21:02
460 ** write char to stdout
462 static int
463 int21_02(regcontext_t *REGS)
465 tty_write(R_DL, TTYF_REDIRECT);
466 return(0);
470 ** 21:06
472 ** direct console I/O
474 ** (dl) is output char unless 0xff, when we read instead
476 static int
477 int21_06(regcontext_t *REGS)
479 int n;
481 /* XXX - should be able to read a file */
482 if (R_DL == 0xff) {
483 n = tty_read((regcontext_t *)&REGS->sc, TTYF_ECHO|TTYF_REDIRECT);
484 if (n < 0) {
485 R_FLAGS |= PSL_Z; /* nothing available */
486 R_AL = 0;
487 } else {
488 R_AL = n; /* got character */
489 R_FLAGS &= ~PSL_Z;
491 } else {
492 /* write and return char in %al */
493 tty_write(R_DL, TTYF_REDIRECT);
494 R_AL = R_DL;
496 return(0);
500 ** 21:07
502 ** direct console input with no echo
504 static int
505 int21_07(regcontext_t *REGS)
507 R_AL = tty_read((regcontext_t *)&REGS->sc,
508 TTYF_BLOCK|TTYF_REDIRECT) & 0xff;
509 return(0);
513 ** 21:08
515 ** character input with no echo
517 static int
518 int21_08(regcontext_t *REGS)
520 int n;
522 if ((n = tty_read((regcontext_t *)&REGS->sc,
523 TTYF_BLOCK|TTYF_CTRL|TTYF_REDIRECT)) >= 0)
524 R_AL = n;
525 return(0);
529 ** 21:09
531 ** write string to standard out.
533 ** We're a little paranoid here; if the string is very long, truncate it.
535 static int
536 int21_09(regcontext_t *REGS)
538 char *addr;
539 int len;
541 /* pointer to string */
542 addr = (char *)MAKEPTR(R_DS, R_DX);
544 /* walk string looking for terminator or overlength */
545 for (len = 0; len < 10000; len++, addr++) {
546 if (*addr == '$')
547 break;
548 tty_write(*addr, TTYF_REDIRECT);
550 R_AL = 0x24;
551 return(0);
555 ** 21:0a
557 ** buffered input
559 static int
560 int21_0a(regcontext_t *REGS)
562 unsigned char *addr;
563 int nbytes,avail;
564 int n;
566 /* pointer to buffer */
567 addr = (unsigned char *)MAKEPTR(R_DS, R_DX);
569 /* capacity of buffer */
570 avail = addr[0];
571 if (avail == 0) /* no space */
572 return(0);
573 nbytes = 0; /* read nothing yet */
575 /* read loop */
576 while (1) {
577 n = tty_read((regcontext_t *)&REGS->sc,
578 TTYF_BLOCK|TTYF_CTRL|TTYF_REDIRECT);
579 if (n < 0) /* end of input */
580 n = '\r'; /* make like CR */
582 switch (n) {
583 case '\r': /* done */
584 addr[1] = nbytes;
585 addr[nbytes+2] = '\r';
586 addr[nbytes+3] = '\0'; /* XXX is this necessary? */
587 return (0);
588 case '\n': /* ignore */
589 case '\0':
590 break;
591 case '\b': /* backspace */
592 if (nbytes > 0) {
593 --nbytes;
594 tty_write('\b', TTYF_REDIRECT);
595 if (addr[nbytes+2] < ' ')
596 tty_write('\b', TTYF_REDIRECT);
598 break;
599 default:
600 if (nbytes >= (avail-2)) { /* buffer full */
601 tty_write('\007', TTYF_REDIRECT);
602 } else { /* add to end */
603 addr[(nbytes++) +2] = n;
604 if (n != '\t' && n < ' ') {
605 tty_write('^', TTYF_REDIRECT);
606 tty_write(n + '@', TTYF_REDIRECT);
607 } else
608 tty_write(n, TTYF_REDIRECT);
610 break;
616 ** 21:0b
618 ** get stdin status
620 ** This is a favorite for camping on, so we do some poll-counting
621 ** here as well.
623 static int
624 int21_0b(regcontext_t *REGS)
626 int n;
628 /* XXX this is pretty bogus, actually */
629 if (!xmode) {
630 R_AL = 0xff; /* no X mode, always claim data available */
631 return(0);
633 /* XXX tty_peek is broken */
634 n = tty_peek(REGS, poll_cnt ? 0 : TTYF_POLL) ? 0xff : 0;
635 if (n < 0) /* control-break */
636 return (0);
637 R_AL = n; /* will be 0 or 0xff */
638 if (poll_cnt)
639 --poll_cnt;
640 return(0);
644 ** 21:0c
646 ** flush stdin and read using other function
648 static int
649 int21_0c(regcontext_t *REGS)
651 if (xmode) /* XXX should always flush! */
652 tty_flush();
654 switch(R_AL) { /* which subfunction? */
655 case 0x01:
656 return(int21_01(REGS));
657 case 0x06:
658 return(int21_06(REGS));
659 case 0x07:
660 return(int21_07(REGS));
661 case 0x08:
662 return(int21_08(REGS));
663 case 0x0a:
664 return(int21_0a(REGS));
666 return(0);
670 ** 21:0e
672 ** select default drive
674 static int
675 int21_0e(regcontext_t *REGS)
677 diskdrive = R_DL; /* XXX rangecheck? */
678 R_AL = ndisks + 2; /* report actual limit */
679 return(0);
683 ** 21:19
685 ** get default drive
687 static int
688 int21_19(regcontext_t *REGS)
690 R_AL = diskdrive;
691 return(0);
695 ** 21:1a
697 ** set DTA
699 static int
700 int21_1a(regcontext_t *REGS)
702 debug(D_FILE_OPS, "set dta to %x:%x\n", R_DS, R_DX);
703 disk_transfer_addr = MAKEVEC(R_DS, R_DX);
704 return(0);
708 ** 21:23
710 ** Get file size for fcb
711 ** DS:DX -> unopened FCB, no wildcards.
712 ** pcb random record field filled in with number of records, rounded up.
714 static int
715 int21_23(regcontext_t *REGS)
717 debug(D_HALF, "Returning failure from get file size for fcb 21:23\n");
718 R_AL = 0xff;
719 return(0);
723 ** 21:25
725 ** set interrupt vector
727 static int
728 int21_25(regcontext_t *REGS)
730 debug(D_MEMORY, "%02x -> %04x:%04x\n", R_AL, R_DS, R_DX);
731 ivec[R_AL] = MAKEVEC(R_DS, R_DX);
732 return(0);
736 ** 21:26
738 ** Create PSP
740 static int
741 int21_26(regcontext_t *REGS)
743 unsigned char *addr;
745 /* address of new PSP */
746 addr = (unsigned char *)MAKEPTR(R_DX, 0);
748 /* copy this process' PSP - XXX needs some work 8( */
749 memcpy (addr, dosmem, 256);
750 return(0);
754 ** 21:2a
756 ** Get date
758 static int
759 int21_2a(regcontext_t *REGS)
761 struct timeval tv;
762 struct timezone tz;
763 struct tm tm;
764 time_t now;
766 gettimeofday(&tv, &tz); /* get time and apply DOS offset */
767 now = tv.tv_sec + delta_clock;
769 tm = *localtime(&now); /* deconstruct and timezoneify */
770 R_CX = tm.tm_year + 1900;
771 R_DH = tm.tm_mon + 1;
772 R_DL = tm.tm_mday;
773 R_AL = tm.tm_wday;
774 return(0);
778 ** 21:2b
780 ** set date
782 static int
783 int21_2b(regcontext_t *REGS)
785 struct timeval tv;
786 struct timezone tz;
787 struct tm tm;
788 time_t now;
790 gettimeofday(&tv, &tz); /* get time and apply DOS offset */
791 now = tv.tv_sec + delta_clock;
792 tm = *localtime(&now);
794 tm.tm_year = R_CX - 1900;
795 tm.tm_mon = R_DH - 1;
796 tm.tm_mday = R_DL;
797 tm.tm_wday = R_AL;
799 now = mktime(&tm);
800 if (now == -1)
801 return (DATA_INVALID);
803 delta_clock = now - tv.tv_sec; /* compute new offset? */
804 R_AL = 0;
805 return(0);
809 ** 21:2c
811 ** Get time
813 static int
814 int21_2c(regcontext_t *REGS)
816 struct timeval tv;
817 struct timezone tz;
818 struct tm tm;
819 time_t now;
821 gettimeofday(&tv, &tz);
822 now = tv.tv_sec + delta_clock;
823 tm = *localtime(&now);
825 R_CH = tm.tm_hour;
826 R_CL = tm.tm_min;
827 R_DH = tm.tm_sec;
828 R_DL = tv.tv_usec / 10000;
829 return(0);
833 ** 21:2d
835 ** Set time
837 static int
838 int21_2d(regcontext_t *REGS)
840 struct timeval tv;
841 struct timezone tz;
842 struct tm tm;
843 time_t now;
845 gettimeofday(&tv, &tz);
846 now = tv.tv_sec + delta_clock;
847 tm = *localtime(&now);
849 tm.tm_hour = R_CH;
850 tm.tm_min = R_CL;
851 tm.tm_sec = R_DH;
852 tv.tv_usec = R_DL * 10000;
854 now = mktime(&tm);
855 if (now == -1)
856 return (DATA_INVALID);
858 delta_clock = now - tv.tv_sec;
859 R_AL = 0;
860 return(0);
864 ** 21:2f
866 ** get DTA
868 static int
869 int21_2f(regcontext_t *REGS)
871 PUTVEC(R_ES, R_BX, disk_transfer_addr);
872 debug(D_FILE_OPS, "get dta at %x:%x\n", R_ES, R_BX);
873 return(0);
877 ** 21:30
879 ** get DOS version number.
881 ** XXX begging for a rewrite
883 static int
884 int21_30(regcontext_t *REGS)
886 int v;
888 char *cmd = (char *)MAKEPTR(get_env(), 0);
891 * retch. I think this skips the environment and looks for the name
892 * of the current command.
894 do {
895 while (*cmd)
896 ++cmd;
897 } while (*++cmd);
898 ++cmd;
899 cmd += *(short *)cmd + 1;
900 while (cmd[-1] && cmd[-1] != '\\' && cmd[-1] != ':')
901 --cmd;
903 /* get the version we're pretending to be for this sucker */
904 v = getver(cmd);
905 R_AL = (v / 100) & 0xff;
906 R_AH = v % 100;
907 return(0);
911 ** 21:33:05
913 ** Get boot drive
915 static int
916 int21_33_5(regcontext_t *REGS)
918 R_DL = 3; /* always booted from C */
919 return(0);
923 ** 21:33:06
925 ** get true DOS version
927 static int
928 int21_33_6(regcontext_t *REGS)
930 int v;
932 v = getver(NULL);
933 R_BL = (v / 100) & 0xff;
934 R_BH = v % 100;
935 R_DH = 0;
936 R_DL = 0;
937 return(0);
941 ** 21:33
943 ** extended break checking
945 static int
946 int21_33(regcontext_t *REGS)
948 int ftemp;
950 switch (R_AL) {
951 case 0x00:
952 R_DL = ctrl_c_flag;
953 break;
954 case 0x01:
955 ctrl_c_flag = R_DL;
956 break;
957 case 0x02:
958 ftemp = ctrl_c_flag;
959 ctrl_c_flag = R_DL;
960 R_DL = ftemp;
961 break;
963 default:
964 unknown_int3(0x21, 0x33, R_AL, REGS);
965 return(FUNC_NUM_IVALID);
967 return(0);
971 ** 21:34
973 ** Get address of InDos flag
975 ** XXX check interrupt list WRT location of critical error flag too.
977 static int
978 int21_34(regcontext_t *REGS)
980 PUTVEC(R_ES, R_BX, (u_long)InDOS);
981 return(0);
985 ** 21:35
987 ** get interrupt vector
989 static int
990 int21_35(regcontext_t *REGS)
992 PUTVEC(R_ES, R_BX, ivec[R_AL]);
993 debug(D_MEMORY, "%02x <- %04x:%04x\n", R_AL, R_ES, R_BX);
994 return(0);
998 ** 21:37
1000 ** switch character manipulation
1003 static int
1004 int21_37(regcontext_t *REGS)
1006 switch (R_AL) {
1007 case 0: /* get switch character */
1008 R_DL = '/';
1009 break;
1011 case 1: /* set switch character (normally /) */
1012 /* ignored by most versions of DOS */
1013 break;
1014 default:
1015 unknown_int3(0x21, 0x37, R_AL, REGS);
1016 return (FUNC_NUM_IVALID);
1018 return(0);
1023 ** 21:38
1025 ** country code information
1027 ** XXX internat guru?
1029 static int
1030 int21_38(regcontext_t *REGS)
1032 char *addr;
1034 if (R_DX == 0xffff) {
1035 debug(D_HALF, "warning: set country code ignored");
1036 return(0);
1038 addr = (char *)MAKEPTR(R_DS, R_DX);
1039 PUTVEC(countryinfo.ciCaseMapSegment, countryinfo.ciCaseMapOffset,
1040 upcase_vector);
1041 memcpy(addr, &countryinfo, sizeof(countryinfo));
1042 return(0);
1046 ** 21:39
1047 ** 21:3a
1048 ** 21:41
1049 ** 21:56
1051 ** mkdir, rmdir, unlink, rename
1053 static int
1054 int21_dirfn(regcontext_t *REGS)
1056 int error;
1057 char fname[PATH_MAX],tname[PATH_MAX];
1058 int drive;
1060 error = translate_filename((u_char *)MAKEPTR(R_DS, R_DX), fname, &drive);
1061 if (error)
1062 return (error);
1064 if (dos_readonly(drive))
1065 return (WRITE_PROT_DISK);
1067 switch(R_AH) {
1068 case 0x39:
1069 debug(D_FILE_OPS, "mkdir(%s)\n", fname);
1070 error = mkdir(fname, 0777);
1071 break;
1072 case 0x3a:
1073 debug(D_FILE_OPS, "rmdir(%s)\n", fname);
1074 error = rmdir(fname);
1075 break;
1076 case 0x41:
1077 debug(D_FILE_OPS, "unlink(%s)\n", fname);
1078 error = unlink(fname);
1079 break;
1080 case 0x56: /* rename - some extra work */
1081 error = translate_filename((u_char *)MAKEPTR(R_ES, R_DI), tname, &drive);
1082 if (error)
1083 return (error);
1085 debug(D_FILE_OPS, "rename(%s, %s)\n", fname, tname);
1086 error = rename(fname, tname);
1087 break;
1089 default:
1090 fatal("call to int21_dirfn for unknown function %x\n",R_AH);
1092 if (error < 0) {
1093 switch (errno) {
1094 case ENOTDIR:
1095 case ENOENT:
1096 return (PATH_NOT_FOUND);
1097 case EXDEV:
1098 return (NOT_SAME_DEV);
1099 default:
1100 return (ACCESS_DENIED);
1103 return(0);
1107 ** 21:3b
1109 ** chdir
1111 static int
1112 int21_3b(regcontext_t *REGS)
1114 debug(D_FILE_OPS, "chdir(%s)\n",(u_char *)MAKEPTR(R_DS, R_DX));
1115 return(dos_setcwd((u_char *)MAKEPTR(R_DS, R_DX)));
1119 ** 21:3c
1120 ** 21:5b
1121 ** 21:6c
1123 ** open, creat, creat new, multipurpose creat
1125 static int
1126 int21_open(regcontext_t *REGS)
1128 int error;
1129 char fname[PATH_MAX];
1130 struct stat sb;
1131 int mode,action,status;
1132 char *pname;
1133 int drive;
1134 int fd;
1136 switch(R_AH) {
1137 case 0x3c: /* creat */
1138 pname = (char *)MAKEPTR(R_DS, R_DX);
1139 action = 0x12; /* create/truncate regardless */
1140 mode = O_RDWR;
1141 debug(D_FILE_OPS, "creat");
1142 break;
1144 case 0x3d: /* open */
1145 pname = (char *)MAKEPTR(R_DS, R_DX);
1146 action = 0x01; /* fail if not exist, open if exists */
1147 switch (R_AL & 3) {
1148 case 0:
1149 mode = O_RDONLY;
1150 break;
1151 case 1:
1152 mode = O_WRONLY;
1153 break;
1154 case 2:
1155 mode = O_RDWR;
1156 break;
1157 default:
1158 return (FUNC_NUM_IVALID);
1160 debug(D_FILE_OPS, "open");
1161 break;
1163 case 0x5b: /* creat new */
1164 pname = (char *)MAKEPTR(R_DS, R_DL);
1165 action = 0x10; /* create if not exist, fail if exists */
1166 mode = O_RDWR;
1167 debug(D_FILE_OPS, "creat_new");
1168 break;
1170 case 0x6c: /* multipurpose */
1171 pname = (char *)MAKEPTR(R_DS, R_SI);
1172 action = R_DX;
1173 switch (R_BL & 3) {
1174 case 0:
1175 mode = O_RDONLY;
1176 break;
1177 case 1:
1178 mode = O_WRONLY;
1179 break;
1180 case 2:
1181 mode = O_RDWR;
1182 break;
1183 default:
1184 return (FUNC_NUM_IVALID);
1186 debug(D_FILE_OPS, "mopen");
1187 break;
1189 default:
1190 fatal("called int21_creat for unknown function %x\n",R_AH);
1192 if (action & 0x02) /* replace/open mode */
1193 mode |= O_TRUNC;
1195 /* consider proposed name */
1196 error = translate_filename(pname, fname, &drive);
1197 if (error)
1198 return (error);
1200 debug(D_FILE_OPS, "(%s)\n", fname);
1202 if (dos_readonly(drive) && (mode != O_RDONLY))
1203 return (WRITE_PROT_DISK);
1205 if (ustat(fname, &sb) < 0) { /* file does not exist */
1206 if (action & 0x10) { /* create? */
1207 sb.st_ino = 0;
1208 mode |= O_CREAT; /* have to create as we go */
1209 status = 0x02; /* file created */
1210 } else {
1211 return(FILE_NOT_FOUND);
1213 } else {
1214 if (S_ISDIR(sb.st_mode))
1215 return(ACCESS_DENIED);
1216 if (action & 0x03) { /* exists, work with it */
1217 if (action & 0x02) {
1218 if (!S_ISREG(sb.st_mode)) { /* only allowed for files */
1219 debug(D_FILE_OPS,"attempt to truncate non-regular file\n");
1220 return(ACCESS_DENIED);
1222 status = 0x03; /* we're going to truncate it */
1223 } else {
1224 status = 0x01; /* just open it */
1226 } else {
1227 return(FILE_ALREADY_EXISTS); /* exists, fail */
1231 if ((fd = open(fname, mode, from_dos_attr(R_CX))) < 0) {
1232 debug(D_FILE_OPS,"failed to open %s : %s\n",fname,strerror(errno));
1233 return (ACCESS_DENIED);
1236 if (R_AH == 0x6c) /* need to return status too */
1237 R_CX = status;
1238 R_AX = fd; /* return fd */
1239 return(0);
1243 ** 21:3e
1245 ** close
1247 static int
1248 int21_3e(regcontext_t *REGS)
1250 debug(D_FILE_OPS, "close(%d)\n", R_BX);
1252 if (R_BX == fileno(debugf)) {
1253 printf("attempt to close debugging fd\n");
1254 return (HANDLE_INVALID);
1257 if (close(R_BX) < 0)
1258 return (HANDLE_INVALID);
1259 return(0);
1263 ** 21:3f
1265 ** read
1267 static int
1268 int21_3f(regcontext_t *REGS)
1270 char *addr;
1271 int n;
1272 int avail;
1274 addr = (char *)MAKEPTR(R_DS, R_DX);
1276 debug(D_FILE_OPS, "read(%d, %d)\n", R_BX, R_CX);
1278 if (R_BX == 0) {
1279 if (redirect0) {
1280 n = read (R_BX, addr, R_CX);
1281 } else {
1282 n = 0;
1283 while (n < R_CX) {
1284 avail = tty_read(REGS, TTYF_BLOCK|TTYF_CTRL|TTYF_ECHONL);
1285 if (avail < 0)
1286 return (0);
1287 if ((addr[n++] = avail) == '\r')
1288 break;
1291 } else {
1292 n = read (R_BX, addr, R_CX);
1294 if (n < 0)
1295 return (READ_FAULT);
1297 R_AX = n;
1298 return(0);
1302 ** 21:40
1304 ** write
1306 static int
1307 write_or_truncate(int fd, char *addr, int len)
1309 off_t offset;
1311 if (len == 0) {
1312 offset = lseek(fd, 0, SEEK_CUR);
1313 if (offset < 0)
1314 return -1;
1315 else
1316 return ftruncate(fd, offset);
1317 } else {
1318 return write(fd, addr, len);
1322 static int
1323 int21_40(regcontext_t *REGS)
1325 char *addr;
1326 int nbytes,n;
1328 addr = (char *)MAKEPTR(R_DS, R_DX);
1329 nbytes = R_CX;
1331 debug(D_FILE_OPS, "write(%d, %d)\n", R_BX, nbytes);
1333 switch (R_BX) {
1334 case 0:
1335 if (redirect0) {
1336 n = write_or_truncate(R_BX, addr, nbytes);
1337 break;
1339 n = nbytes;
1340 while (nbytes-- > 0)
1341 tty_write(*addr++, -1);
1342 break;
1343 case 1:
1344 if (redirect1) {
1345 n = write_or_truncate(R_BX, addr, nbytes);
1346 break;
1348 n = nbytes;
1349 while (nbytes-- > 0)
1350 tty_write(*addr++, -1);
1351 break;
1352 case 2:
1353 if (redirect2) {
1354 n = write_or_truncate(R_BX, addr, nbytes);
1355 break;
1357 n = nbytes;
1358 while (nbytes-- > 0)
1359 tty_write(*addr++, -1);
1360 break;
1361 default:
1362 n = write_or_truncate(R_BX, addr, nbytes);
1363 break;
1365 if (n < 0)
1366 return (WRITE_FAULT);
1368 R_AX = n;
1369 return(0);
1373 ** 21:42
1375 ** seek
1377 static int
1378 int21_42(regcontext_t *REGS)
1380 int whence;
1381 off_t offset;
1383 offset = (off_t) ((int) (R_CX << 16) + R_DX);
1384 switch (R_AL) {
1385 case 0:
1386 whence = SEEK_SET;
1387 break;
1388 case 1:
1389 whence = SEEK_CUR;
1390 break;
1391 case 2:
1392 whence = SEEK_END;
1393 break;
1394 default:
1395 return (FUNC_NUM_IVALID);
1398 debug(D_FILE_OPS, "seek(%d, 0x%qx, %d)\n", R_BX, offset, whence);
1400 if ((offset = lseek(R_BX, offset, whence)) < 0) {
1401 if (errno == EBADF)
1402 return (HANDLE_INVALID);
1403 else
1404 return (SEEK_ERROR);
1407 R_DX = (offset >> 16) & 0xffff;
1408 R_AX = offset & 0xffff;
1409 return(0);
1413 ** 21:43
1415 ** get/set attributes
1417 static int
1418 int21_43(regcontext_t *REGS)
1420 int error;
1421 char fname[PATH_MAX];
1422 struct stat sb;
1423 int mode;
1424 int drive;
1426 error = translate_filename((u_char *)MAKEPTR(R_DS, R_DX), fname, &drive);
1427 if (error)
1428 return (error);
1430 debug(D_FILE_OPS, "get/set attributes: %s, cx=%x, al=%d\n",
1431 fname, R_CX, R_AL);
1433 if (stat(fname, &sb) < 0) {
1434 debug(D_FILE_OPS, "stat failed for %s\n", fname);
1435 return (FILE_NOT_FOUND);
1438 switch (R_AL) {
1439 case 0: /* get attributes */
1440 mode = 0;
1441 if (dos_readonly(drive) || access(fname, W_OK))
1442 mode |= 0x01;
1443 if (S_ISDIR(sb.st_mode))
1444 mode |= 0x10;
1445 R_CX = mode;
1446 break;
1448 case 1: /* set attributes - XXX ignored */
1449 if (R_CX & 0x18)
1450 return (ACCESS_DENIED);
1451 break;
1453 default:
1454 return (FUNC_NUM_IVALID);
1456 return(0);
1460 ** 21:44:0
1462 ** ioctl - get device info
1464 ** XXX it would be nice to detect EOF.
1466 static int
1467 int21_44_0(regcontext_t *REGS)
1469 debug(D_FILE_OPS, "ioctl get %d\n", R_BX);
1471 switch (R_BX) {
1472 case 0:
1473 R_DX = 0x80 | 0x01; /* is device, is standard output */
1474 break;
1475 case 1:
1476 R_DX = 0x80 | 0x02; /* is device, is standard input */
1477 break;
1478 case 2:
1479 R_DX = 0x80; /* is device */
1480 break;
1481 default:
1482 if (isatty (R_BX))
1483 R_DX = 0x80; /* is a device */
1484 else
1485 R_DX = 0; /* is a file */
1486 break;
1488 return(0);
1492 ** 21:44:01
1494 ** ioctl - set device info
1496 static int
1497 int21_44_1(regcontext_t *REGS)
1499 debug(D_FILE_OPS, "ioctl set device info %d flags %x (ignored)\n",
1500 R_BX, R_DX);
1501 return(0);
1505 ** 21:44:7
1507 ** Get output status
1509 static int
1510 int21_44_7(regcontext_t *REGS)
1512 /* XXX Should really check to see if BX is open or not */
1513 R_AX = 0xFF;
1514 return(0);
1518 ** 21:44:8
1520 ** test for removable block device
1522 static int
1523 int21_44_8(regcontext_t *REGS)
1525 R_AX = 1; /* fixed */
1526 return(0);
1530 ** 21:44:0
1532 ** test for remote device (disallow direct I/O)
1534 static int
1535 int21_44_9(regcontext_t *REGS)
1537 R_DX = 0x1200; /* disk is remote, direct I/O not allowed */
1538 return (0);
1542 ** 21:45
1544 ** dup
1546 static int
1547 int21_45(regcontext_t *REGS)
1549 int nfd;
1551 debug(D_FILE_OPS, "dup(%d)\n", R_BX);
1553 if ((nfd = dup(R_BX)) < 0) {
1554 if (errno == EBADF)
1555 return (HANDLE_INVALID);
1556 else
1557 return (TOO_MANY_OPEN_FILES);
1559 R_AX = nfd;
1560 return(0);
1564 ** 21:46
1566 ** dup2
1568 static int
1569 int21_46(regcontext_t *REGS)
1571 debug(D_FILE_OPS, "dup2(%d, %d)\n", R_BX, R_CX);
1573 if (dup2(R_BX, R_CX) < 0) {
1574 if (errno == EMFILE)
1575 return (TOO_MANY_OPEN_FILES);
1576 else
1577 return (HANDLE_INVALID);
1579 return(0);
1583 ** 21:47
1585 ** getcwd
1587 static int
1588 int21_47(regcontext_t *REGS)
1590 int n,nbytes;
1591 char *p,*addr;
1593 n = R_DL;
1594 if (!n--)
1595 n = diskdrive;
1597 p = (char *)dos_getcwd(n) + 1;
1598 addr = (char *)MAKEPTR(R_DS, R_SI);
1600 nbytes = strlen(p);
1601 if (nbytes > 63)
1602 nbytes = 63;
1604 memcpy(addr, p, nbytes);
1605 addr[nbytes] = 0;
1606 return(0);
1610 ** 21:48
1612 ** allocate memory
1614 static int
1615 int21_48(regcontext_t *REGS)
1617 int memseg,avail;
1619 memseg = mem_alloc(R_BX, pspseg, &avail);
1621 if (memseg == 0L) {
1622 R_BX = avail;
1623 return (INSUF_MEM);
1626 R_AX = memseg;
1627 return(0);
1631 ** 21:49
1633 ** free memory
1635 static int
1636 int21_49(regcontext_t *REGS)
1638 if (mem_adjust(R_ES, 0, NULL) < 0)
1639 return (MEM_BLK_ADDR_IVALID);
1640 return(0);
1644 ** 21:4a
1646 ** resize memory block
1648 static int
1649 int21_4a(regcontext_t *REGS)
1651 int n,avail;
1653 if ((n = mem_adjust(R_ES, R_BX, &avail)) < 0) {
1654 R_BX = avail;
1655 if (n == -1)
1656 return (INSUF_MEM);
1657 else
1658 return (MEM_BLK_ADDR_IVALID);
1660 return(0);
1664 ** 21:4b
1666 ** exec
1668 ** XXX verify!
1670 static int
1671 int21_4b(regcontext_t *REGS)
1673 int fd;
1674 u_short *param;
1676 debug(D_EXEC, "exec(%s)\n",(u_char *)MAKEPTR(R_DS, R_DX));
1678 if ((fd = open_prog((u_char *)MAKEPTR(R_DS, R_DX))) < 0) {
1679 debug(D_EXEC, "%s: command not found\n",
1680 (u_char *)MAKEPTR(R_DS, R_DX));
1681 return (FILE_NOT_FOUND);
1684 /* child */
1685 param = (u_short *)MAKEPTR(R_ES, R_BX);
1687 switch (R_AL) {
1688 case 0x00: /* load and execute */
1689 exec_command(REGS, 1, fd, cmdname, param);
1690 close(fd);
1691 break;
1693 case 0x01: /* just load */
1694 exec_command(REGS, 0, fd, cmdname, param);
1695 close(fd);
1696 break;
1698 case 0x03: /* load overlay */
1699 load_overlay(fd, param[0], param[1]);
1700 close(fd);
1701 break;
1703 default:
1704 unknown_int3(0x21, 0x4b, R_AL, REGS);
1705 return (FUNC_NUM_IVALID);
1707 return(0);
1711 ** 21:4c
1713 ** return with code
1715 static int
1716 int21_4c(regcontext_t *REGS)
1718 return_status = R_AL;
1719 done(REGS, R_AL);
1720 return 0;
1724 ** 21:4d
1726 ** get return code of child
1728 static int
1729 int21_4d(regcontext_t *REGS)
1731 R_AX = return_status;
1732 return(0);
1736 ** 21:4e
1737 ** 21:4f
1739 ** find first, find next
1741 static int
1742 int21_find(regcontext_t *REGS)
1744 find_block_t *dta;
1745 dosdir_t dosdir;
1746 int error;
1748 dta = (find_block_t *)VECPTR(disk_transfer_addr);
1750 switch (R_AH) {
1751 case 0x4e: /* find first */
1752 error = find_first((u_char *)MAKEPTR(R_DS, R_DX), R_CX, &dosdir, dta);
1753 break;
1754 case 0x4f:
1755 error = find_next(&dosdir, dta);
1756 break;
1757 default:
1758 fatal("called int21_find for unknown function %x\n",R_AH);
1760 if (!error) {
1761 dosdir_to_dta(&dosdir, dta);
1762 R_AX = 0;
1764 return(error);
1768 ** 21:50
1770 ** set PSP
1772 static int
1773 int21_50(regcontext_t *REGS)
1775 pspseg = R_BX;
1776 return(0);
1780 ** 21:57:00
1782 ** get mtime for handle
1784 static int
1785 int21_57_0(regcontext_t *REGS)
1787 struct stat sb;
1788 u_short date, mtime;
1790 if (fstat(R_BX, &sb) < 0)
1791 return (HANDLE_INVALID);
1792 encode_dos_file_time(sb.st_mtime, &date, &mtime);
1793 R_CX = mtime;
1794 R_DX = date;
1795 return(0);
1799 ** 21:57:01
1801 ** set mtime for handle
1803 static int
1804 int21_57_1(regcontext_t *REGS __unused)
1806 #ifdef __NetBSD__ /* XXX need futimes() */
1807 struct stat sb;
1808 struct timeval tv[2];
1809 u_short date, time;
1811 time = R_CX;
1812 date = R_DX;
1813 tv[0].tv_sec = tv[1].tv_sec = decode_dos_file_time(date, time);
1814 tv[0].tv_usec = tv[1].tv_usec = 0;
1815 if (futimes(R_BX, tv) < 0)
1816 return (HANDLE_INVALID);
1817 break;
1818 #endif
1819 return(0);
1823 ** 21:58
1825 ** get/set memory strategy
1826 ** get/set UMB link state
1828 static int
1829 int21_58(regcontext_t *REGS)
1831 switch (R_AL) {
1832 case 0x00: /* get memory strategy */
1833 R_AX = memory_strategy;
1834 break;
1835 case 0x01: /* set memory strategy */
1836 memory_strategy = R_BL;
1837 if (memory_strategy > 2) /* higher make no sense without UMBs */
1838 memory_strategy = 2;
1839 break;
1840 case 0x02: /* get UMB link state */
1841 R_AL = 0; /* UMBs not in link chain */
1842 break;
1843 default: /* includes set, which is invalid */
1844 unknown_int3(0x21, 0x58, R_AL, REGS);
1845 return (FUNC_NUM_IVALID);
1847 return(0);
1851 ** 21:59
1853 ** get extended error information
1855 static int
1856 int21_59(regcontext_t *REGS)
1858 R_AX = doserrno;
1859 switch (doserrno) {
1860 case 1:
1861 case 6:
1862 case 9:
1863 case 10:
1864 case 11:
1865 case 12:
1866 case 13:
1867 case 15:
1868 R_BH = 7; /* application error */
1869 break;
1871 case 2:
1872 case 3:
1873 case 4:
1874 case 5:
1875 R_BH = 8; /* not found */
1876 break;
1878 case 7:
1879 case 8:
1880 R_BH = 1; /* out of resource */
1881 break;
1883 default:
1884 R_BH = 12; /* already exists */
1885 break;
1887 R_BL = 6; /* always ignore! */
1888 R_CH = 1; /* unknown/inappropriate */
1889 return(0);
1893 ** 21:5a
1895 ** create temporary file
1897 static int
1898 int21_5a(regcontext_t *REGS)
1900 char fname[PATH_MAX];
1901 char *pname;
1902 int error;
1903 int n;
1904 int drive;
1905 int fd;
1907 /* get and check proposed path */
1908 pname = (char *)MAKEPTR(R_DS, R_DX);
1909 error = translate_filename(pname, fname, &drive);
1910 if (error)
1911 return (error);
1913 debug(D_FILE_OPS, "tempname(%s)\n", fname);
1915 if (dos_readonly(drive))
1916 return (WRITE_PROT_DISK);
1918 n = strlen(fname);
1919 strcat(fname,"__dostmp.XXX");
1920 fd = mkstemp(fname);
1921 if (fd < 0)
1922 return (ACCESS_DENIED);
1924 strcat(pname, fname + n); /* give back the full name */
1925 R_AX = fd;
1926 return(0);
1930 ** 21:60
1932 ** canonicalise name
1934 static int
1935 int21_60(regcontext_t *REGS)
1937 return(dos_makepath((char *)MAKEPTR(R_DS, R_SI),
1938 (char *)MAKEPTR(R_ES, R_DI)));
1942 ** 21:62
1944 ** get current PSP
1946 static int
1947 int21_62(regcontext_t *REGS)
1949 R_BX = pspseg;
1950 return(0);
1954 ** 21:65:23
1956 ** determine yes/no
1957 ** (mostly for humour value 8)
1959 static int
1960 int21_65_23(regcontext_t *REGS)
1962 switch (R_DL) {
1963 case 'n': /* no, nein, non, nyet */
1964 case 'N':
1965 R_AX = 0;
1966 break;
1967 case 'y': /* yes */
1968 case 'Y':
1969 case 'j': /* ja */
1970 case 'J':
1971 case 'o': /* oui */
1972 case 'O':
1973 case 'd': /* da */
1974 case 'D':
1975 R_AX = 1;
1976 break;
1977 default: /* maybe */
1978 R_AX = 2;
1979 break;
1981 return(0);
1985 ** 21:68
1986 ** 21:6a
1988 ** fflush/commit file
1990 static int
1991 int21_fflush(regcontext_t *REGS)
1993 debug(D_FILE_OPS, "fsync(%d)\n", R_BX);
1995 if (fsync(R_BX) < 0)
1996 return (HANDLE_INVALID);
1997 return(0);
2000 /******************************************************************************
2001 ** 21:0f 21:10 21:11 21:12 21:16 21:27 21:28:21:29
2003 ** FCB functions
2005 static void
2006 openfcb(struct fcb *fcbp)
2008 struct stat statb;
2010 fcbp->fcbDriveID = 3; /* drive C */
2011 fcbp->fcbCurBlockNo = 0;
2012 fcbp->fcbRecSize = 128;
2013 if (fstat(fcbp->fcb_fd, &statb) < 0) {
2014 debug(D_FILE_OPS, "open not complete with errno %d\n", errno);
2015 return;
2017 encode_dos_file_time(statb.st_mtime,
2018 &fcbp->fcbFileDate, &fcbp->fcbFileTime);
2019 fcbp->fcbFileSize = statb.st_size;
2022 static int
2023 getfcb_rec(struct fcb *fcbp, int nrec)
2025 int n;
2027 n = fcbp->fcbRandomRecNo;
2028 if (fcbp->fcbRecSize >= 64)
2029 n &= 0xffffff;
2030 fcbp->fcbCurRecNo = n % 128;
2031 fcbp->fcbCurBlockNo = n / 128;
2032 if (lseek(fcbp->fcb_fd, n * fcbp->fcbRecSize, SEEK_SET) < 0)
2033 return (-1);
2034 return (nrec * fcbp->fcbRecSize);
2038 static int
2039 setfcb_rec(struct fcb *fcbp, int n)
2041 int recs, total;
2043 total = fcbp->fcbRandomRecNo;
2044 if (fcbp->fcbRecSize >= 64)
2045 total &= 0xffffff;
2046 recs = (n+fcbp->fcbRecSize-1) / fcbp->fcbRecSize;
2047 total += recs;
2049 fcbp->fcbRandomRecNo = total;
2050 fcbp->fcbCurRecNo = total % 128;
2051 fcbp->fcbCurBlockNo = total / 128;
2053 return(0);
2056 static void
2057 fcb_to_string(fcbp, buf)
2058 struct fcb *fcbp;
2059 u_char *buf;
2062 if (fcbp->fcbDriveID != 0x00) {
2063 *buf++ = drntol(fcbp->fcbDriveID - 1);
2064 *buf++ = ':';
2066 pack_name(fcbp->fcbFileName, buf);
2070 static int
2071 int21_fcb(regcontext_t *REGS)
2073 char buf[PATH_MAX];
2074 char fname[PATH_MAX];
2075 struct stat sb;
2076 dosdir_t dosdir;
2077 struct fcb *fcbp;
2078 find_block_t *dta;
2079 u_char *addr;
2080 int error;
2081 int drive;
2082 int fd;
2083 int nbytes,n;
2086 fcbp = (struct fcb *)MAKEPTR(R_DS, R_DX);
2088 switch (R_AH) {
2090 case 0x0f: /* open file with FCB */
2091 fcb_to_string(fcbp, buf);
2092 error = translate_filename(buf, fname, &drive);
2093 if (error)
2094 return (error);
2096 debug(D_FILE_OPS, "open FCB(%s)\n", fname);
2098 if (ustat(fname, &sb) < 0)
2099 sb.st_ino = 0;
2101 if (dos_readonly(drive))
2102 return (WRITE_PROT_DISK);
2104 if (sb.st_ino == 0 || S_ISDIR(sb.st_mode))
2105 return (FILE_NOT_FOUND);
2107 if ((fd = open(fname, O_RDWR)) < 0) {
2108 if (errno == ENOENT)
2109 return (FILE_NOT_FOUND);
2110 else
2111 return (ACCESS_DENIED);
2114 fcbp->fcb_fd = fd;
2115 openfcb(fcbp);
2116 R_AL = 0;
2117 break;
2119 case 0x10: /* close file with FCB */
2120 debug(D_FILE_OPS, "close FCB(%d)\n", fcbp->fcb_fd);
2122 if (close(fcbp->fcb_fd) < 0)
2123 return (HANDLE_INVALID);
2125 fcbp->fcb_fd = -1;
2126 R_AL = 0;
2127 break;
2129 case 0x11: /* find_first with FCB */
2130 dta = (find_block_t *)VECPTR(disk_transfer_addr);
2132 fcb_to_string(fcbp, buf);
2133 error = find_first(buf, fcbp->fcbAttribute, &dosdir, dta);
2134 if (error)
2135 return (error);
2137 dosdir_to_dta(&dosdir, dta);
2138 R_AL = 0;
2139 break;
2141 case 0x12: /* find_next with FCB */
2142 dta = (find_block_t *)VECPTR(disk_transfer_addr);
2144 error = find_next(&dosdir, dta);
2145 if (error)
2146 return (error);
2148 dosdir_to_dta(&dosdir, dta);
2149 R_AL = 0;
2150 break;
2152 case 0x16: /* create file with FCB */
2153 fcb_to_string(fcbp, buf);
2154 error = translate_filename(buf, fname, &drive);
2155 if (error)
2156 return (error);
2158 debug(D_FILE_OPS, "creat FCB(%s)\n", fname);
2160 if (ustat(fname, &sb) < 0)
2161 sb.st_ino = 0;
2163 if (dos_readonly(drive))
2164 return (WRITE_PROT_DISK);
2166 if (sb.st_ino && !S_ISREG(sb.st_mode))
2167 return (ACCESS_DENIED);
2169 if ((fd = open(fname, O_CREAT|O_TRUNC|O_RDWR, 0666)) < 0)
2170 return (ACCESS_DENIED);
2172 fcbp->fcb_fd = fd;
2173 openfcb(fcbp);
2174 R_AL = 0;
2175 break;
2177 case 0x27: /* random block read */
2178 addr = (u_char *)VECPTR(disk_transfer_addr);
2179 nbytes = getfcb_rec(fcbp, R_CX);
2181 if (nbytes < 0)
2182 return (READ_FAULT);
2183 n = read(fcbp->fcb_fd, addr, nbytes);
2184 if (n < 0)
2185 return (READ_FAULT);
2186 R_CX = setfcb_rec(fcbp, n);
2187 if (n < nbytes) {
2188 nbytes = n % fcbp->fcbRecSize;
2189 if (0 == nbytes) {
2190 R_AL = 0x01;
2191 } else {
2192 bzero(addr + n, fcbp->fcbRecSize - nbytes);
2193 R_AL = 0x03;
2195 } else {
2196 R_AL = 0;
2198 break;
2200 case 0x28: /* random block write */
2201 addr = (u_char *)VECPTR(disk_transfer_addr);
2202 nbytes = getfcb_rec(fcbp, R_CX);
2204 if (nbytes < 0)
2205 return (WRITE_FAULT);
2206 n = write(fcbp->fcb_fd, addr, nbytes);
2207 if (n < 0)
2208 return (WRITE_FAULT);
2209 R_CX = setfcb_rec(fcbp, n);
2210 if (n < nbytes) {
2211 R_AL = 0x01;
2212 } else {
2213 R_AL = 0;
2215 break;
2217 case 0x29: /* parse filename */
2218 debug(D_FILE_OPS,"parse filename: flag=%d, ", R_AL);
2220 R_AX = parse_filename(R_AL,
2221 (char *)MAKEPTR(R_DS, R_SI),
2222 (char *)MAKEPTR(R_ES, R_DI),
2223 &nbytes);
2224 debug(D_FILE_OPS, "%d %s, FCB: %d, %.11s\n",
2225 nbytes,
2226 (char *)MAKEPTR(R_DS, R_SI),
2227 *(int *)MAKEPTR(R_ES, R_DI),
2228 (char *)MAKEPTR(R_ES, R_DI) + 1);
2230 R_SI += nbytes;
2231 break;
2233 default:
2234 fatal("called int21_fcb with unknown function %x\n",R_AH);
2236 return(0);
2240 ** 21:5d
2241 ** 21:5e
2242 ** 21:5f
2244 ** network functions
2245 ** XXX relevant?
2247 static int
2248 int21_net(regcontext_t *REGS)
2250 switch(R_AH) {
2251 case 0x5d:
2252 switch(R_AL) {
2253 case 0x06:
2254 debug(D_HALF, "Get Swapable Area\n");
2255 return (ACCESS_DENIED);
2256 case 0x08: /* Set redirected printer mode */
2257 debug(D_HALF, "Redirection is %s\n",
2258 R_DL ? "separate jobs" : "combined");
2259 break;
2260 case 0x09: /* Flush redirected printer output */
2261 break;
2262 default:
2263 unknown_int3(0x21, 0x5d, R_AL, REGS);
2264 return (FUNC_NUM_IVALID);
2266 break;
2268 case 0x5e:
2269 case 0x5f:
2270 unknown_int2(0x21, R_AH, REGS);
2271 return (FUNC_NUM_IVALID);
2272 default:
2273 fatal("called int21_net with unknown function %x\n",R_AH);
2275 return(0);
2279 ** 21:??
2281 ** Unknown/unsupported
2283 static int
2284 int21_NOFUNC(regcontext_t *REGS)
2286 unknown_int2(0x21, R_AH, REGS);
2287 return (FUNC_NUM_IVALID);
2291 ** 21:??
2293 ** Null function; no error, no action
2295 static int
2296 int21_NULLFUNC(regcontext_t *REGS)
2298 R_AL = 0;
2299 return(0);
2303 static struct intfunc_table int21_table [] = {
2304 { 0x00, IFT_NOSUBFUNC, int21_00, "terminate"},
2305 { 0x01, IFT_NOSUBFUNC, int21_01, "read character with echo"},
2306 { 0x02, IFT_NOSUBFUNC, int21_02, "write char to stdout"},
2307 { 0x03, IFT_NOSUBFUNC, int21_NOFUNC, "read char from stdaux"},
2308 { 0x04, IFT_NOSUBFUNC, int21_NOFUNC, "write char to stdaux"},
2309 { 0x05, IFT_NOSUBFUNC, int21_NOFUNC, "write char to printer"},
2310 { 0x06, IFT_NOSUBFUNC, int21_06, "direct console I/O"},
2311 { 0x07, IFT_NOSUBFUNC, int21_07, "direct console in without echo"},
2312 { 0x08, IFT_NOSUBFUNC, int21_08, "read character, no echo"},
2313 { 0x09, IFT_NOSUBFUNC, int21_09, "write string to standard out"},
2314 { 0x0a, IFT_NOSUBFUNC, int21_0a, "buffered input"},
2315 { 0x0b, IFT_NOSUBFUNC, int21_0b, "get stdin status"},
2316 { 0x0c, IFT_NOSUBFUNC, int21_0c, "flush stdin and read"},
2317 { 0x0d, IFT_NOSUBFUNC, int21_NULLFUNC, "disk reset"},
2318 { 0x0e, IFT_NOSUBFUNC, int21_0e, "select default drive"},
2319 { 0x19, IFT_NOSUBFUNC, int21_19, "get default drive"},
2320 { 0x1a, IFT_NOSUBFUNC, int21_1a, "set DTA"},
2321 { 0x1b, IFT_NOSUBFUNC, int21_free, "get allocation for default drive"},
2322 { 0x1c, IFT_NOSUBFUNC, int21_free, "get allocation for specific drive"},
2323 { 0x1f, IFT_NOSUBFUNC, int21_NOFUNC, "get DPB for current drive"},
2324 { 0x23, IFT_NOSUBFUNC, int21_23, "Get file size (old)"},
2325 { 0x25, IFT_NOSUBFUNC, int21_25, "set interrupt vector"},
2326 { 0x26, IFT_NOSUBFUNC, int21_26, "create new PSP"},
2327 { 0x2a, IFT_NOSUBFUNC, int21_2a, "get date"},
2328 { 0x2b, IFT_NOSUBFUNC, int21_2b, "set date"},
2329 { 0x2c, IFT_NOSUBFUNC, int21_2c, "get time"},
2330 { 0x2d, IFT_NOSUBFUNC, int21_2d, "set time"},
2331 { 0x2e, IFT_NOSUBFUNC, int21_NULLFUNC, "set verify flag"},
2332 { 0x2f, IFT_NOSUBFUNC, int21_2f, "get DTA"},
2333 { 0x30, IFT_NOSUBFUNC, int21_30, "get DOS version"},
2334 { 0x31, IFT_NOSUBFUNC, int21_NOFUNC, "terminate and stay resident"},
2335 { 0x32, IFT_NOSUBFUNC, int21_NOFUNC, "get DPB for specific drive"},
2336 { 0x33, 0x05, int21_33_5, "get boot drive"},
2337 { 0x33, 0x06, int21_33_6, "get true version number"},
2338 { 0x33, IFT_NOSUBFUNC, int21_33, "extended break checking"},
2339 { 0x34, IFT_NOSUBFUNC, int21_34, "get address of InDos flag"},
2340 { 0x35, IFT_NOSUBFUNC, int21_35, "get interrupt vector"},
2341 { 0x36, IFT_NOSUBFUNC, int21_free, "get disk free space"},
2342 { 0x37, IFT_NOSUBFUNC, int21_37, "switch character"},
2343 { 0x38, IFT_NOSUBFUNC, int21_38, "country code/information"},
2344 { 0x39, IFT_NOSUBFUNC, int21_dirfn, "mkdir"},
2345 { 0x3a, IFT_NOSUBFUNC, int21_dirfn, "rmdir"},
2346 { 0x3b, IFT_NOSUBFUNC, int21_3b, "chdir"},
2347 { 0x3c, IFT_NOSUBFUNC, int21_open, "creat"},
2348 { 0x3d, IFT_NOSUBFUNC, int21_open, "open"},
2349 { 0x3e, IFT_NOSUBFUNC, int21_3e, "close"},
2350 { 0x3f, IFT_NOSUBFUNC, int21_3f, "read"},
2351 { 0x40, IFT_NOSUBFUNC, int21_40, "write"},
2352 { 0x41, IFT_NOSUBFUNC, int21_dirfn, "unlink"},
2353 { 0x42, IFT_NOSUBFUNC, int21_42, "lseek"},
2354 { 0x43, IFT_NOSUBFUNC, int21_43, "get/set file attributes"},
2355 { 0x44, 0x00, int21_44_0, "ioctl(get)"},
2356 { 0x44, 0x01, int21_44_1, "ioctl(set)"},
2357 { 0x44, 0x07, int21_44_7, "ioctl(Check output status)"},
2358 { 0x44, 0x08, int21_44_8, "ioctl(test removable)"},
2359 { 0x44, 0x09, int21_44_9, "ioctl(test remote)"},
2360 { 0x45, IFT_NOSUBFUNC, int21_45, "dup"},
2361 { 0x46, IFT_NOSUBFUNC, int21_46, "dup2"},
2362 { 0x47, IFT_NOSUBFUNC, int21_47, "getwd"},
2363 { 0x48, IFT_NOSUBFUNC, int21_48, "allocate memory"},
2364 { 0x49, IFT_NOSUBFUNC, int21_49, "free memory"},
2365 { 0x4a, IFT_NOSUBFUNC, int21_4a, "resize memory block"},
2366 { 0x4b, IFT_NOSUBFUNC, int21_4b, "exec"},
2367 { 0x4c, IFT_NOSUBFUNC, int21_4c, "exit with return code"},
2368 { 0x4d, IFT_NOSUBFUNC, int21_4d, "get return code from child"},
2369 { 0x4e, IFT_NOSUBFUNC, int21_find, "findfirst"},
2370 { 0x4f, IFT_NOSUBFUNC, int21_find, "findnext"},
2371 { 0x50, IFT_NOSUBFUNC, int21_50, "set psp"},
2372 { 0x51, IFT_NOSUBFUNC, int21_62, "get psp"},
2373 { 0x52, IFT_NOSUBFUNC, int21_NOFUNC, "get LoL"},
2374 { 0x53, IFT_NOSUBFUNC, int21_NOFUNC, "translate BPB to DPB"},
2375 { 0x54, IFT_NOSUBFUNC, int21_NULLFUNC, "get verify flag"},
2376 { 0x55, IFT_NOSUBFUNC, int21_NOFUNC, "create PSP"},
2377 { 0x56, IFT_NOSUBFUNC, int21_dirfn, "rename"},
2378 { 0x57, 0x00, int21_57_0, "get mtime"},
2379 { 0x57, 0x01, int21_57_1, "set mtime"},
2380 { 0x58, IFT_NOSUBFUNC, int21_58, "get/set memory strategy"},
2381 { 0x59, IFT_NOSUBFUNC, int21_59, "get extended error information"},
2382 { 0x5a, IFT_NOSUBFUNC, int21_5a, "create temporary file"},
2383 { 0x5b, IFT_NOSUBFUNC, int21_open, "create new file"},
2384 { 0x5c, IFT_NOSUBFUNC, int21_NOFUNC, "flock"},
2385 { 0x5d, IFT_NOSUBFUNC, int21_net, "network functions"},
2386 { 0x5e, IFT_NOSUBFUNC, int21_net, "network functions"},
2387 { 0x5f, IFT_NOSUBFUNC, int21_net, "network functions"},
2388 { 0x60, IFT_NOSUBFUNC, int21_60, "canonicalise name/path"},
2389 { 0x61, IFT_NOSUBFUNC, int21_NULLFUNC, "network functions (reserved)"},
2390 { 0x62, IFT_NOSUBFUNC, int21_62, "get current PSP"},
2391 { 0x63, IFT_NOSUBFUNC, int21_NOFUNC, "get DBCS lead-byte table"},
2392 { 0x64, IFT_NOSUBFUNC, int21_NOFUNC, "set device-driver lookahead"},
2393 { 0x65, 0x23, int21_65_23, "determine yes/no"},
2394 { 0x65, IFT_NOSUBFUNC, int21_NOFUNC, "get extended country information"},
2395 { 0x66, IFT_NOSUBFUNC, int21_NOFUNC, "get/set codepage table"},
2396 { 0x67, IFT_NOSUBFUNC, int21_NULLFUNC, "set handle count"},
2397 { 0x68, IFT_NOSUBFUNC, int21_fflush, "fflush"},
2398 { 0x69, IFT_NOSUBFUNC, int21_NOFUNC, "get/set disk serial number"},
2399 { 0x6a, IFT_NOSUBFUNC, int21_fflush, "commit file"},
2400 { 0x6b, IFT_NOSUBFUNC, int21_NULLFUNC, "IFS ioctl"},
2401 { 0x6c, IFT_NOSUBFUNC, int21_open, "extended open/create"},
2403 /* FCB functions */
2404 { 0x0f, IFT_NOSUBFUNC, int21_fcb, "open file"},
2405 { 0x10, IFT_NOSUBFUNC, int21_fcb, "close file"},
2406 { 0x11, IFT_NOSUBFUNC, int21_fcb, "find first"},
2407 { 0x12, IFT_NOSUBFUNC, int21_fcb, "find next"},
2408 { 0x13, IFT_NOSUBFUNC, int21_NOFUNC, "delete"},
2409 { 0x14, IFT_NOSUBFUNC, int21_NOFUNC, "sequential read"},
2410 { 0x15, IFT_NOSUBFUNC, int21_NOFUNC, "sequential write"},
2411 { 0x16, IFT_NOSUBFUNC, int21_fcb, "create/truncate"},
2412 { 0x17, IFT_NOSUBFUNC, int21_NOFUNC, "rename"},
2413 { 0x21, IFT_NOSUBFUNC, int21_NOFUNC, "read random"},
2414 { 0x22, IFT_NOSUBFUNC, int21_NOFUNC, "write random"},
2415 { 0x23, IFT_NOSUBFUNC, int21_NOFUNC, "get file size"},
2416 { 0x24, IFT_NOSUBFUNC, int21_NOFUNC, "set random record number"},
2417 { 0x27, IFT_NOSUBFUNC, int21_fcb, "random block read"},
2418 { 0x28, IFT_NOSUBFUNC, int21_fcb, "random block write"},
2419 { 0x29, IFT_NOSUBFUNC, int21_fcb, "parse filename into FCB"},
2421 /* CPM compactability */
2422 { 0x18, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
2423 { 0x1d, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
2424 { 0x1e, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
2425 { 0x20, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
2427 { -1, IFT_NOSUBFUNC, NULL, NULL} /* terminator */
2431 static int int21_fastlookup[256];
2433 const char *dos_return[] = {
2434 "OK",
2435 "FUNC_NUM_IVALID",
2436 "FILE_NOT_FOUND",
2437 "PATH_NOT_FOUND",
2438 "TOO_MANY_OPEN_FILES",
2439 "ACCESS_DENIED",
2440 "HANDLE_INVALID",
2441 "MEM_CB_DEST",
2442 "INSUF_MEM",
2443 "MEM_BLK_ADDR_IVALID",
2444 "ENV_INVALID",
2445 "FORMAT_INVALID",
2446 "ACCESS_CODE_INVALID",
2447 "DATA_INVALID",
2448 "UNKNOWN_UNIT",
2449 "DISK_DRIVE_INVALID",
2450 "ATT_REM_CUR_DIR",
2451 "NOT_SAME_DEV",
2452 "NO_MORE_FILES",
2453 "WRITE_PROT_DISK",
2454 "UNKNOWN_UNIT_CERR",
2455 "DRIVE_NOT_READY",
2456 "UNKNOWN_COMMAND",
2457 "DATA_ERROR_CRC",
2458 "BAD_REQ_STRUCT_LEN",
2459 "SEEK_ERROR",
2460 "UNKNOWN_MEDIA_TYPE",
2461 "SECTOR_NOT_FOUND",
2462 "PRINTER_OUT_OF_PAPER",
2463 "WRITE_FAULT",
2464 "READ_FAULT",
2465 "GENERAL_FAILURE"
2468 const int dos_ret_size = (sizeof(dos_return) / sizeof(char *));
2471 ** for want of anywhere better to go
2473 static void
2474 int20(regcontext_t *REGS)
2476 /* int 20 = exit(0) */
2477 done(REGS, 0);
2480 static void
2481 int29(regcontext_t *REGS)
2483 tty_write(R_AL, TTYF_REDIRECT);
2486 /******************************************************************************
2487 ** entrypoint for MS-DOS functions
2489 static void
2490 int21(regcontext_t *REGS)
2492 int error;
2493 int idx;
2495 /* look for a handler */
2496 idx = intfunc_find(int21_table, int21_fastlookup, R_AH, R_AL);
2498 if (idx == -1) { /* no matching functions */
2499 unknown_int3(0x21, R_AH, R_AL, REGS);
2500 R_FLAGS |= PSL_C; /* Flag an error */
2501 R_AX = 0xff;
2502 return;
2505 /* call the handler */
2506 error = int21_table[idx].handler(REGS);
2507 debug(D_DOSCALL, "msdos call %02x (%s) returns %d (%s)\n",
2508 int21_table[idx].func, int21_table[idx].desc, error,
2509 ((error >= 0) && (error <= dos_ret_size)) ? dos_return[error] : "unknown");
2511 if (error) {
2512 doserrno = error;
2513 R_FLAGS |= PSL_C;
2515 /* XXX is this entirely legitimate? */
2516 if (R_AH >= 0x2f)
2517 R_AX = error;
2518 else
2519 R_AX = 0xff;
2520 } else {
2521 R_FLAGS &= ~PSL_C;
2523 return;
2526 static void
2527 int67(regcontext_t *REGS)
2529 ems_entry(REGS);
2532 static u_char upcase_trampoline[] = {
2533 0xf4, /* HLT */
2534 0xcb, /* RETF */
2538 ** initialise thyself
2540 void
2541 dos_init(void)
2543 u_long vec;
2545 /* hook vectors */
2546 vec = insert_softint_trampoline();
2547 ivec[0x20] = vec;
2548 register_callback(vec, int20, "int 20");
2550 vec = insert_softint_trampoline();
2551 ivec[0x21] = vec;
2552 register_callback(vec, int21, "int 21");
2554 vec = insert_softint_trampoline();
2555 ivec[0x29] = vec;
2556 register_callback(vec, int29, "int 29");
2558 vec = insert_softint_trampoline();
2559 ivec[0x67] = vec;
2560 register_callback(vec, int67, "int 67 (EMS)");
2562 vec = insert_null_trampoline();
2563 ivec[0x28] = vec; /* dos idle */
2564 ivec[0x2b] = vec; /* reserved */
2565 ivec[0x2c] = vec; /* reserved */
2566 ivec[0x2d] = vec; /* reserved */
2568 upcase_vector = insert_generic_trampoline(
2569 sizeof(upcase_trampoline), upcase_trampoline);
2570 register_callback(upcase_vector, upcase_entry, "upcase");
2572 /* build fastlookup index into the monster table of interrupts */
2573 intfunc_init(int21_table, int21_fastlookup);
2575 ems_init();