summaryrefslogtreecommitdiffstats
path: root/usr.bin/doscmd/dos.c
diff options
context:
space:
mode:
authordyson <dyson@FreeBSD.org>1997-08-09 01:43:15 +0000
committerdyson <dyson@FreeBSD.org>1997-08-09 01:43:15 +0000
commit305573cb2990c5d329d149cef5a3b5533b1e8fd9 (patch)
treedf06304b637358dbe8a006fdb7a6ea5955fee179 /usr.bin/doscmd/dos.c
parentdede28832bba6a9de7a428ff58df92439bddbc9c (diff)
downloadFreeBSD-src-305573cb2990c5d329d149cef5a3b5533b1e8fd9.zip
FreeBSD-src-305573cb2990c5d329d149cef5a3b5533b1e8fd9.tar.gz
Add our doscmd to the tree. This is a result of work from BSDI, and
a group of dos emulator developers. Submitted by: Jonathan Lemon <jlemon@americantv.com> Obtained from: BSDI
Diffstat (limited to 'usr.bin/doscmd/dos.c')
-rw-r--r--usr.bin/doscmd/dos.c2508
1 files changed, 2508 insertions, 0 deletions
diff --git a/usr.bin/doscmd/dos.c b/usr.bin/doscmd/dos.c
new file mode 100644
index 0000000..918afde
--- /dev/null
+++ b/usr.bin/doscmd/dos.c
@@ -0,0 +1,2508 @@
+/*
+ * Copyright (c) 1996
+ * Michael Smith. All rights reserved.
+ * Copyright (c) 1992, 1993, 1996
+ * Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Berkeley Software
+ * Design, Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * BSDI int21.c,v 2.2 1996/04/08 19:32:51 bostic Exp
+ *
+ * $Id: dos.c,v 1.8 1996/09/23 09:59:24 miff Exp $
+ */
+
+#include "doscmd.h"
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <time.h>
+#include <glob.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stddef.h>
+
+#include "dispatch.h"
+
+static u_long upcase_vector;
+
+/* Country Info */
+struct {
+ ushort ciDateFormat;
+ char ciCurrency[5];
+ char ciThousands[2];
+ char ciDecimal[2];
+ char ciDateSep[2];
+ char ciTimeSep[2];
+ char ciCurrencyFormat;
+ char ciCurrencyPlaces;
+ char ciTimeFormat;
+ ushort ciCaseMapOffset;
+ ushort ciCaseMapSegment;
+ char ciDataSep[2];
+#if 0
+ char ciReserved[10];
+#endif
+} countryinfo = {
+ 0, "$", ",", ".", "-", ":", 0, 2, 0, 0xffff, 0xffff, "?"
+};
+
+/* DOS File Control Block */
+struct fcb {
+ u_char fcbMagic;
+ u_char fcbResoived[5];
+ u_char fcbAttribute;
+ u_char fcbDriveID;
+ u_char fcbFileName[8];
+ u_char fcbExtent[3];
+ u_short fcbCurBlockNo;
+ u_short fcbRecSize;
+ u_long fcbFileSize;
+ u_short fcbFileDate;
+ u_short fcbFileTime;
+ int fcbReserved;
+ int fcb_fd; /* hide UNIX FD here */
+ u_char fcbCurRecNo;
+ u_long fcbRandomRecNo;
+}/* __attribute__((__packed__))*/;
+
+/* exports */
+void encode_dos_file_time (time_t, u_short *, u_short *);
+int diskdrive = 2; /* C: */
+char *InDOS;
+unsigned long disk_transfer_addr;
+
+/* locals */
+static int ctrl_c_flag = 0;
+static int return_status = 0;
+static int doserrno = 0;
+static int memory_strategy = 0; /* first fit (we ignore this) */
+
+
+static u_char upc_table[0x80] = {
+ 0x80, 0x9a, 'E', 'A', 0x8e, 'A', 0x8f, 0x80,
+ 'E', 'E', 'E', 'I', 'I', 'I', 0x8e, 0x8f,
+ 0x90, 0x92, 0x92, 'O', 0x99, 'O', 'U', 'U',
+ 'Y', 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 'A', 'I', 'O', 'U', 0xa5, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+};
+
+
+/******************************************************************************
+** utility functions
+*/
+
+static u_char
+upcase(u_char c)
+{
+
+ if (islower(c))
+ return (toupper(c));
+ else if (c >= 0x80)
+ return (upc_table[c - 0x80]);
+ else
+ return (c);
+}
+
+static void
+upcase_entry(regcontext_t *REGS)
+{
+ u_char c;
+
+ R_AL = upcase(R_AL);
+}
+
+
+/*
+** Handle the DOS drive info/free space/etc. calls.
+*/
+static int
+int21_free(regcontext_t *REGS)
+{
+ fsstat_t fs;
+ struct statfs fsstat;
+ char fspath[PATH_MAX];
+ int junk;
+ int spc,fclus,bps,nclus;
+ long nsec;
+ int error;
+ int dd_save;
+ int drive;
+
+ /* work out drive */
+ switch (R_AH) {
+ case 0x1c:
+ case 0x36:
+ drive = R_DL;
+ if (drive)
+ break;
+ /* FALLTHROUGH */
+ case 0x1b:
+ drive = diskdrive;
+ break;
+ default:
+ fatal("int21_free called on unknown function %x\n",R_AH);
+ }
+
+ error = get_space(drive, &fs);
+ if (error)
+ return(error);
+
+ R_AL = fs.sectors_cluster; /* sectors per cluster */
+ R_CX = fs.bytes_sector; /* bytes per sector */
+ R_DX = fs.total_clusters; /* total clusters */
+
+ switch (R_AH) {
+ case 0x1b:
+ case 0x1c:
+ BIOSDATA[0xb4] = 0xf0; /* "reserved" area, "other media" FAT ID */
+ R_DX = 0x40; /* BIOS data area */
+ R_BX = 0xb4;
+ break;
+
+ case 0x36:
+ R_BX = fs.avail_clusters; /* number of available clusters */
+ break;
+ }
+ return(0);
+}
+
+static void
+pack_name(u_char *p, u_char *q)
+{
+ int i;
+
+ for (i = 8; i > 0 && *p != ' '; i--)
+ *q++ = *p++;
+ p += i;
+ if (*p != ' ') {
+ *q++ = '.';
+ for (i = 3; i > 0 && *p != ' '; i--)
+ *q++ = *p++;
+ p += i;
+ }
+ *q = '\0';
+}
+
+static void
+dosdir_to_dta(dosdir_t *dosdir, find_block_t *dta)
+{
+ u_char *p, *q;
+
+ dta->attr = dosdir->attr;
+ dta->time = dosdir->time;
+ dta->date = dosdir->date;
+ dta->size = dosdir->size;
+ pack_name(dosdir->name, dta->name);
+}
+
+/* exported */
+void
+encode_dos_file_time(time_t t, u_short *dosdatep, u_short *dostimep)
+{
+ struct tm tm;
+
+ tm = *localtime(&t);
+ *dostimep = (tm.tm_hour << 11) |
+ (tm.tm_min << 5) |
+ ((tm.tm_sec / 2) << 0);
+ *dosdatep = ((tm.tm_year - 80) << 9) |
+ ((tm.tm_mon + 1) << 5) |
+ (tm.tm_mday << 0);
+}
+
+time_t
+decode_dos_file_time(u_short dosdate, u_short dostime)
+{
+ struct tm tm;
+ time_t then;
+
+ tm.tm_hour = (dostime >> 11) & 0x1f;
+ tm.tm_min = (dostime >> 5) & 0x3f;
+ tm.tm_sec = ((dostime >> 0) & 0x1f) * 2;
+ tm.tm_year = ((dosdate >> 9) & 0x7f) + 80;
+ tm.tm_mon = ((dosdate >> 5) & 0x0f) - 1;
+ tm.tm_mday = (dosdate >> 0) & 0x1f;
+ /* tm_wday and tm_yday are ignored. */
+ tm.tm_isdst = 0;
+ /* tm_gmtoff is ignored. */
+ then = mktime(&tm);
+ return (then);
+}
+
+int
+translate_filename(u_char *dname, u_char *uname, int *drivep)
+{
+ u_char newpath[1024];
+ int error;
+
+ if (!strcasecmp(dname, "con")) {
+ *drivep = -1;
+ strcpy(uname, "/dev/tty");
+ return (0);
+ }
+
+ error = dos_makepath(dname, newpath);
+ if (error)
+ return (error);
+
+ error = dos_to_real_path(newpath, uname, drivep);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static u_char magic[0x7e] = {
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+
+ 0x08, 0x0f, 0x06, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x04, 0x04, 0x0f, 0x0e, 0x06,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0f,
+
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x06, 0x06, 0x06, 0x0f, 0x0f,
+
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x04, 0x0f,
+};
+
+#define isvalid(x) ((magic[x] & 0x01) != 0)
+#define issep(x) ((magic[x] & 0x02) == 0)
+#define iswhite(x) ((magic[x] & 0x04) == 0)
+
+static char *
+skipwhite(char *p)
+{
+ while (iswhite(*p))
+ ++p;
+ return (p);
+}
+
+#define get_drive_letter(x) ((x) - 0x40)
+
+int
+parse_filename(int flag, char *str, char *fcb, int *nb)
+{
+ char *p;
+ int ret = 0;
+ int i;
+
+ p = str;
+
+ p = skipwhite(p);
+ if (flag & 1) {
+ if (issep(*p)) {
+ ++p;
+ p = skipwhite(p);
+ }
+ }
+
+ if (isvalid(*p) && p[1] == ':') {
+ *fcb++ = get_drive_letter(upcase(*p));
+ p += 2;
+ } else if (flag & 2) {
+ fcb++;
+ } else {
+ *fcb++ = 0; /* default drive */
+ }
+
+ i = 8;
+ if (isvalid(*p)) {
+ for (;;) {
+ if (!isvalid(*p)) {
+ for (; i > 0; i--)
+ *fcb++ = ' ';
+ break;
+ }
+ if (i > 0) {
+ switch (*p) {
+ case '*':
+ ret = 1;
+ for (; i > 0; i--)
+ *fcb++ = '?';
+ break;
+ case '?':
+ ret = 1;
+ default:
+ *fcb++ = upcase(*p);
+ i--;
+ break;
+ }
+ }
+ ++p;
+ }
+ } else if (flag & 4) {
+ fcb += i;
+ } else {
+ for (; i > 0; i--)
+ *fcb++ = ' ';
+ }
+
+ i = 3;
+ if (*p == '.') {
+ ++p;
+ for (;;) {
+ if (!isvalid(*p)) {
+ for (; i > 0; i--)
+ *fcb++ = ' ';
+ break;
+ }
+ if (i > 0) {
+ switch (*p) {
+ case '*':
+ ret = 1;
+ for (; i > 0; i--)
+ *fcb++ = '?';
+ break;
+ case '?':
+ ret = 1;
+ default:
+ *fcb++ = upcase(*p);
+ i--;
+ break;
+ }
+ }
+ ++p;
+ }
+ } else if (flag & 8) {
+ fcb += i;
+ } else {
+ for (; i > 0; i--)
+ *fcb++ = ' ';
+ }
+
+ for (i = 4; i > 0; i--)
+ *fcb++ = 0; /* filler */
+
+ *nb = p - str;
+ return (ret);
+}
+
+/******************************************************************************
+** int21 functions
+*/
+
+/*
+** 21:00
+**
+** terminate
+*/
+static int
+int21_00(regcontext_t *REGS)
+{
+ done(REGS,0);
+}
+
+/*
+** 21:01
+**
+** read character with echo
+*/
+static int
+int21_01(regcontext_t *REGS)
+{
+ int n;
+
+ if ((n = tty_read(&REGS->sc, TTYF_BLOCKALL)) >= 0)
+ R_AL = n;
+ return(0);
+}
+
+/*
+** 21:02
+**
+** write char to stdout
+*/
+static int
+int21_02(regcontext_t *REGS)
+{
+ tty_write(R_DL, TTYF_REDIRECT);
+ return(0);
+}
+
+/*
+** 21:06
+**
+** direct console I/O
+**
+** (dl) is output char unless 0xff, when we read instead
+*/
+static int
+int21_06(regcontext_t *REGS)
+{
+ int n;
+
+ /* XXX - should be able to read a file */
+ if (R_DL == 0xff) {
+ n = tty_read(&REGS->sc, TTYF_ECHO|TTYF_REDIRECT);
+ if (n < 0) {
+ R_FLAGS |= PSL_Z; /* nothing available */
+ R_AL = 0;
+ } else {
+ R_AL = n; /* got character */
+ R_FLAGS &= ~PSL_Z;
+ }
+ } else {
+ /* write and return char in %al */
+ tty_write(R_DL, TTYF_REDIRECT);
+ R_AL = R_DL;
+ }
+ return(0);
+}
+
+/*
+** 21:07
+**
+** direct console input with no echo
+*/
+static int
+int21_07(regcontext_t *REGS)
+{
+ R_AL = tty_read(&REGS->sc, TTYF_BLOCK|TTYF_REDIRECT) & 0xff;
+ return(0);
+}
+
+/*
+** 21:08
+**
+** character input with no echo
+*/
+static int
+int21_08(regcontext_t *REGS)
+{
+ int n;
+
+ if ((n = tty_read(&REGS->sc, TTYF_BLOCK|TTYF_CTRL|TTYF_REDIRECT)) >= 0)
+ R_AL = n;
+ return(0);
+}
+
+/*
+** 21:09
+**
+** write string to standard out.
+**
+** We're a little paranoid here; if the string is very long, truncate it.
+*/
+static int
+int21_09(regcontext_t *REGS)
+{
+ char *addr;
+ int len;
+
+ /* pointer to string */
+ addr = (char *)N_GETPTR(R_DS, R_DX);
+
+ /* walk string looking for terminator or overlength */
+ for (len = 0; len < 10000; len++, addr++) {
+ if (*addr == '$')
+ break;
+ tty_write(*addr, TTYF_REDIRECT);
+ }
+ R_AL = 0x24;
+ return(0);
+}
+
+/*
+** 21:0a
+**
+** buffered input
+*/
+static int
+int21_0a(regcontext_t *REGS)
+{
+ unsigned char *addr;
+ int nbytes,avail;
+ int n;
+
+ /* pointer to buffer */
+ addr = (unsigned char *)N_GETPTR(R_DS, R_DL);
+
+ /* capacity of buffer */
+ avail = addr[0];
+ if (avail == 0) /* no space */
+ return(0);
+ nbytes = 0; /* read nothing yet */
+
+ /* read loop */
+ while (1) {
+ n = tty_read(&REGS->sc, TTYF_BLOCK|TTYF_CTRL|TTYF_REDIRECT);
+ if (n < 0) /* end of input */
+ n = '\r'; /* make like CR */
+
+ switch (n) {
+ case '\r': /* done */
+ addr[1] = nbytes;
+ addr[nbytes+2] = '\r';
+ addr[nbytes+3] = '\0'; /* XXX is this necessary? */
+ return (0);
+ case '\n': /* ignore */
+ case '\0':
+ break;
+ case '\b': /* backspace */
+ if (nbytes > 0) {
+ --nbytes;
+ tty_write('\b', TTYF_REDIRECT);
+ if (addr[nbytes+2] < ' ')
+ tty_write('\b', TTYF_REDIRECT);
+ }
+ break;
+ default:
+ if (nbytes >= (avail-2)) { /* buffer full */
+ tty_write('\007', TTYF_REDIRECT);
+ } else { /* add to end */
+ addr[(nbytes++) +2] = n;
+ if (n != '\t' && n < ' ') {
+ tty_write('^', TTYF_REDIRECT);
+ tty_write(n + '@', TTYF_REDIRECT);
+ } else
+ tty_write(n, TTYF_REDIRECT);
+ }
+ break;
+ }
+ }
+}
+
+/*
+** 21:0b
+**
+** get stdin status
+**
+** This is a favorite for camping on, so we do some poll-counting
+** here as well.
+*/
+static int
+int21_0b(regcontext_t *REGS)
+{
+ int n;
+
+ /* XXX this is pretty bogus, actually */
+ if (!xmode) {
+ R_AL = 0xff; /* no X mode, always claim data available */
+ return(0);
+ }
+ /* XXX tty_peek is broken */
+ n = tty_peek(&REGS->sc, poll_cnt ? 0 : TTYF_POLL) ? 0xff : 0;
+ if (n < 0) /* control-break */
+ return (0);
+ R_AL = n; /* will be 0 or 0xff */
+ if (poll_cnt)
+ --poll_cnt;
+ return(0);
+}
+
+/*
+** 21:0c
+**
+** flush stdin and read using other function
+*/
+static int
+int21_0c(regcontext_t *REGS)
+{
+ if (xmode) /* XXX should always flush! */
+ tty_flush();
+
+ switch(R_AL) { /* which subfunction? */
+ case 0x01:
+ return(int21_01(REGS));
+ case 0x06:
+ return(int21_06(REGS));
+ case 0x07:
+ return(int21_07(REGS));
+ case 0x08:
+ return(int21_08(REGS));
+ case 0x0a:
+ return(int21_0a(REGS));
+ }
+ return(0);
+}
+
+/*
+** 21:0e
+**
+** select default drive
+*/
+static int
+int21_0e(regcontext_t *REGS)
+{
+ diskdrive = R_DL; /* XXX rangecheck? */
+ R_AL = ndisks + 2; /* report actual limit */
+ return(0);
+}
+
+/*
+** 21:19
+**
+** get default drive
+*/
+static int
+int21_19(regcontext_t *REGS)
+{
+ R_AL = diskdrive;
+ return(0);
+}
+
+/*
+** 21:1a
+**
+** set DTA
+*/
+static int
+int21_1a(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "set dta to %x:%x\n", R_DS, R_DX);
+ disk_transfer_addr = N_GETVEC(R_DS, R_DX);
+ return(0);
+}
+
+/*
+** 21:25
+**
+** set interrupt vector
+*/
+static int
+int21_25(regcontext_t *REGS)
+{
+ debug(D_MEMORY, "%02x -> %04x:%04x\n", R_AL, R_DS, R_DX);
+ ivec[R_AL] = N_GETVEC(R_DS, R_DX);
+ return(0);
+}
+
+/*
+** 21:26
+**
+** Create PSP
+*/
+static int
+int21_26(regcontext_t *REGS)
+{
+ unsigned char *addr;
+
+ /* address of new PSP */
+ addr = (unsigned char *)MAKEPTR(R_DX, 0);
+
+ /* copy this process' PSP - XXX needs some work 8( */
+ memcpy (addr, dosmem, 256);
+ return(0);
+}
+
+/*
+** 21:2a
+**
+** Get date
+*/
+static int
+int21_2a(regcontext_t *REGS)
+{
+ struct timeval tv;
+ struct timezone tz;
+ struct tm tm;
+ time_t now;
+
+ gettimeofday(&tv, &tz); /* get time and apply DOS offset */
+ now = tv.tv_sec + delta_clock;
+
+ tm = *localtime(&now); /* deconstruct and timezoneify */
+ R_CX = tm.tm_year + 1900;
+ R_DH = tm.tm_mon + 1;
+ R_DL = tm.tm_mday;
+ R_AL = tm.tm_wday;
+ return(0);
+}
+
+/*
+** 21:2b
+**
+** set date
+*/
+static int
+int21_2b(regcontext_t *REGS)
+{
+ struct timeval tv;
+ struct timezone tz;
+ struct tm tm;
+ time_t now;
+
+ gettimeofday(&tv, &tz); /* get time and apply DOS offset */
+ now = tv.tv_sec + delta_clock;
+ tm = *localtime(&now);
+
+ tm.tm_year = R_CX - 1900;
+ tm.tm_mon = R_DH - 1;
+ tm.tm_mday = R_DL;
+ tm.tm_wday = R_AL;
+
+ now = mktime(&tm);
+ if (now == -1)
+ return (DATA_INVALID);
+
+ delta_clock = now - tv.tv_sec; /* compute new offset? */
+ R_AL = 0;
+ return(0);
+}
+
+/*
+** 21:2c
+**
+** Get time
+*/
+static int
+int21_2c(regcontext_t *REGS)
+{
+ struct timeval tv;
+ struct timezone tz;
+ struct tm tm;
+ time_t now;
+
+ gettimeofday(&tv, &tz);
+ now = tv.tv_sec + delta_clock;
+ tm = *localtime(&now);
+
+ R_CH = tm.tm_hour;
+ R_CL = tm.tm_min;
+ R_DH = tm.tm_sec;
+ R_DL = tv.tv_usec / 10000;
+ return(0);
+}
+
+/*
+** 21:2d
+**
+** Set time
+*/
+static int
+int21_2d(regcontext_t *REGS)
+{
+ struct timeval tv;
+ struct timezone tz;
+ struct tm tm;
+ time_t now;
+
+ gettimeofday(&tv, &tz);
+ now = tv.tv_sec + delta_clock;
+ tm = *localtime(&now);
+
+ tm.tm_hour = R_CH;
+ tm.tm_min = R_CL;
+ tm.tm_sec = R_DH;
+ tv.tv_usec = R_DL * 10000;
+
+ now = mktime(&tm);
+ if (now == -1)
+ return (DATA_INVALID);
+
+ delta_clock = now - tv.tv_sec;
+ R_AL = 0;
+ return(0);
+}
+
+/*
+** 21:2f
+**
+** get DTA
+*/
+static int
+int21_2f(regcontext_t *REGS)
+{
+ N_PUTVEC(R_ES, R_BX, disk_transfer_addr);
+ debug(D_FILE_OPS, "get dta at %x:%x\n", R_ES, R_BX);
+ return(0);
+}
+
+/*
+** 21:30
+**
+** get DOS version number.
+**
+** XXX begging for a rewrite
+*/
+static int
+int21_30(regcontext_t *REGS)
+{
+ int v;
+
+ char *cmd = (char *)MAKEPTR(get_env(), 0);
+
+ /*
+ * retch. I think this skips the environment and looks for the name
+ * of the current command.
+ */
+ do {
+ while (*cmd)
+ ++cmd;
+ } while (*++cmd);
+ ++cmd;
+ cmd += *(short *)cmd + 1;
+ while (cmd[-1] && cmd[-1] != '\\' && cmd[-1] != ':')
+ --cmd;
+
+ /* get the version we're pretending to be for this sucker */
+ v = getver(cmd);
+ R_AL = (v / 100) & 0xff;
+ R_AH = v % 100;
+ return(0);
+}
+
+/*
+** 21:33:05
+**
+** Get boot drive
+*/
+static int
+int21_33_5(regcontext_t *REGS)
+{
+ R_DL = 3; /* always booted from C */
+ return(0);
+}
+
+/*
+** 21:33:06
+**
+** get true DOS version
+*/
+static int
+int21_33_6(regcontext_t *REGS)
+{
+ int v;
+
+ v = getver(NULL);
+ R_BL = (v / 100) & 0xff;
+ R_BH = v % 100;
+ R_DH = 0;
+ R_DL = 0;
+ return(0);
+}
+
+/*
+** 21:33
+**
+** extended break checking
+*/
+static int
+int21_33(regcontext_t *REGS)
+{
+ int ftemp;
+
+ switch (R_AL) {
+ case 0x00:
+ R_DL = ctrl_c_flag;
+ break;
+ case 0x01:
+ ctrl_c_flag = R_DL;
+ break;
+ case 0x02:
+ ftemp = ctrl_c_flag;
+ ctrl_c_flag = R_DL;
+ R_DL = ftemp;
+ break;
+
+ default:
+ unknown_int3(0x21, 0x33, R_AL, REGS);
+ return(FUNC_NUM_IVALID);
+ }
+ return(0);
+}
+
+/*
+** 21:34
+**
+** Get address of InDos flag
+**
+** XXX check interrupt list WRT location of critical error flag too.
+*/
+static int
+int21_34(regcontext_t *REGS)
+{
+ N_PUTVEC(R_ES, R_BX, (u_long)InDOS);
+ return(0);
+}
+
+/*
+** 21:35
+**
+** get interrupt vector
+*/
+static int
+int21_35(regcontext_t *REGS)
+{
+ N_PUTVEC(R_ES, R_BX, ivec[R_AL]);
+ debug(D_MEMORY, "%02x <- %04x:%04x\n", R_AL, R_ES, R_BX);
+ return(0);
+}
+
+/*
+** 21:37
+**
+** switch character manipulation
+**
+*/
+static int
+int21_37(regcontext_t *REGS)
+{
+ switch (R_AL) {
+ case 0: /* get switch character */
+ R_DL = '/';
+ break;
+
+ case 1: /* set switch character (normally /) */
+ /* ignored by most versions of DOS */
+ break;
+ default:
+ unknown_int3(0x21, 0x37, R_AL, REGS);
+ return (FUNC_NUM_IVALID);
+ }
+ return(0);
+
+}
+
+/*
+** 21:38
+**
+** country code information
+**
+** XXX internat guru?
+*/
+static int
+int21_38(regcontext_t *REGS)
+{
+ char *addr;
+
+ if (R_DX == 0xffff) {
+ debug(D_HALF, "warning: set country code ignored");
+ return(0);
+ }
+ addr = (char *)N_GETPTR(R_DS, R_DX);
+ N_PUTVEC(countryinfo.ciCaseMapSegment, countryinfo.ciCaseMapOffset,
+ upcase_vector);
+ memcpy(addr, &countryinfo, sizeof(countryinfo));
+ return(0);
+}
+
+/*
+** 21:39
+** 21:3a
+** 21:41
+** 21:56
+**
+** mkdir, rmdir, unlink, rename
+*/
+static int
+int21_dirfn(regcontext_t *REGS)
+{
+ int error;
+ char fname[PATH_MAX],tname[PATH_MAX];
+ int drive;
+
+ error = translate_filename((u_char *)N_GETPTR(R_DS, R_DX), fname, &drive);
+ if (error)
+ return (error);
+
+ if (dos_readonly(drive))
+ return (WRITE_PROT_DISK);
+
+ switch(R_AH) {
+ case 0x39:
+ debug(D_FILE_OPS, "mkdir(%s)\n", fname);
+ error = mkdir(fname, 0777);
+ break;
+ case 0x3a:
+ debug(D_FILE_OPS, "rmdir(%s)\n", fname);
+ error = rmdir(fname);
+ break;
+ case 0x41:
+ debug(D_FILE_OPS, "unlink(%s)\n", fname);
+ error = unlink(fname);
+ break;
+ case 0x56: /* rename - some extra work */
+ error = translate_filename((u_char *)GETPTR(R_ES, R_DI), tname, &drive);
+ if (error)
+ return (error);
+
+ debug(D_FILE_OPS, "rename(%s, %s)\n", fname, tname);
+ error = rename(fname, tname);
+ break;
+
+ default:
+ fatal("call to int21_dirfn for unknown function %x\n",R_AH);
+ }
+ if (error < 0) {
+ switch (errno) {
+ case ENOTDIR:
+ case ENOENT:
+ return (PATH_NOT_FOUND);
+ case EXDEV:
+ return (NOT_SAME_DEV);
+ default:
+ return (ACCESS_DENIED);
+ }
+ }
+ return(0);
+}
+
+/*
+** 21:3b
+**
+** chdir
+*/
+static int
+int21_3b(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "chdir(%s)\n",(u_char *)N_GETPTR(R_DS, R_DX));
+ return(dos_setcwd((u_char *)N_GETPTR(R_DS, R_DX)));
+}
+
+/*
+** 21:3c
+** 21:5b
+** 21:6c
+**
+** open, creat, creat new, multipurpose creat
+*/
+static int
+int21_open(regcontext_t *REGS)
+{
+ int error;
+ char fname[PATH_MAX];
+ struct stat sb;
+ int mode,action,status;
+ char *pname;
+ int drive;
+ int fd;
+
+ switch(R_AH) {
+ case 0x3c: /* creat */
+ pname = (char *)N_GETPTR(R_DS, R_DX);
+ action = 0x12; /* create/truncate regardless */
+ mode = O_RDWR;
+ debug(D_FILE_OPS, "creat");
+ break;
+
+ case 0x3d: /* open */
+ pname = (char *)N_GETPTR(R_DS, R_DX);
+ action = 0x01; /* fail if not exist, open if exists */
+ switch (R_AL & 3) {
+ case 0:
+ mode = O_RDONLY;
+ break;
+ case 1:
+ mode = O_WRONLY;
+ break;
+ case 2:
+ mode = O_RDWR;
+ break;
+ default:
+ return (FUNC_NUM_IVALID);
+ }
+ debug(D_FILE_OPS, "open");
+ break;
+
+ case 0x5b: /* creat new */
+ pname = (char *)N_GETPTR(R_DS, R_DL);
+ action = 0x10; /* create if not exist, fail if exists */
+ mode = O_RDWR;
+ debug(D_FILE_OPS, "creat_new");
+ break;
+
+ case 0x6c: /* multipurpose */
+ pname = (char *)N_GETPTR(R_DS, R_SI);
+ action = R_DX;
+ switch (R_BL & 3) {
+ case 0:
+ mode = O_RDONLY;
+ break;
+ case 1:
+ mode = O_WRONLY;
+ break;
+ case 2:
+ mode = O_RDWR;
+ break;
+ default:
+ return (FUNC_NUM_IVALID);
+ }
+ debug(D_FILE_OPS, "mopen");
+ break;
+
+ default:
+ fatal("called int21_creat for unknown function %x\n",R_AH);
+ }
+ if (action & 0x02) /* replace/open mode */
+ mode |= O_TRUNC;
+
+ /* consider proposed name */
+ error = translate_filename(pname, fname, &drive);
+ if (error)
+ return (error);
+
+ debug(D_FILE_OPS, "(%s)\n", fname);
+
+ if (dos_readonly(drive) && (mode != O_RDONLY))
+ return (WRITE_PROT_DISK);
+
+ if (ustat(fname, &sb) < 0) { /* file does not exist */
+ if (action & 0x10) { /* create? */
+ sb.st_ino = 0;
+ mode |= O_CREAT; /* have to create as we go */
+ status = 0x02; /* file created */
+ } else {
+ return(FILE_NOT_FOUND);
+ }
+ } else {
+ if (S_ISDIR(sb.st_mode))
+ return(ACCESS_DENIED);
+ if (action & 0x03) { /* exists, work with it */
+ if (action & 0x02) {
+ if (!S_ISREG(sb.st_mode)) { /* only allowed for files */
+ debug(D_FILE_OPS,"attempt to truncate non-regular file\n");
+ return(ACCESS_DENIED);
+ }
+ status = 0x03; /* we're going to truncate it */
+ } else {
+ status = 0x01; /* just open it */
+ }
+ } else {
+ return(FILE_ALREADY_EXISTS); /* exists, fail */
+ }
+ }
+
+ if ((fd = open(fname, mode, from_dos_attr(R_CX))) < 0) {
+ debug(D_FILE_OPS,"failed to open %s : %s\n",fname,strerror(errno));
+ return (ACCESS_DENIED);
+ }
+
+ if (R_AH == 0x6c) /* need to return status too */
+ R_CX = status;
+ R_AX = fd; /* return fd */
+ return(0);
+}
+
+/*
+** 21:3e
+**
+** close
+*/
+static int
+int21_3e(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "close(%d)\n", R_BX);
+
+ if (R_BX == fileno(debugf)) {
+ printf("attempt to close debugging fd\n");
+ return (HANDLE_INVALID);
+ }
+
+ if (close(R_BX) < 0)
+ return (HANDLE_INVALID);
+ return(0);
+}
+
+/*
+** 21:3f
+**
+** read
+*/
+static int
+int21_3f(regcontext_t *REGS)
+{
+ int fd;
+ char *addr;
+ int nbytes,n;
+ int avail;
+
+ addr = (char *)N_GETPTR(R_DS, R_DX);
+
+ debug(D_FILE_OPS, "read(%d, %d)\n", R_BX, R_CX);
+
+ if (R_BX == 0) {
+ if (redirect0) {
+ n = read (R_BX, addr, R_CX);
+ } else {
+ n = 0;
+ while (n < R_CX) {
+ avail = tty_read(REGS, TTYF_BLOCK|TTYF_CTRL|TTYF_ECHONL);
+ if (avail < 0)
+ return (0);
+ if ((addr[n++] = avail) == '\r')
+ break;
+ }
+ }
+ } else {
+ n = read (R_BX, addr, R_CX);
+ }
+ if (n < 0)
+ return (READ_FAULT);
+
+ R_AX = n;
+ return(0);
+}
+
+/*
+** 21:40
+**
+** write
+*/
+static int
+int21_40(regcontext_t *REGS)
+{
+ char *addr;
+ int nbytes,n;
+
+ addr = (char *)N_GETPTR(R_DS, R_DX);
+ nbytes = R_CX;
+
+ debug(D_FILE_OPS, "write(%d, %d)\n", R_BX, nbytes);
+
+ switch (R_BX) {
+ case 0:
+ if (redirect0) {
+ n = write (R_BX, addr, nbytes);
+ break;
+ }
+ n = nbytes;
+ while (nbytes-- > 0)
+ tty_write(*addr++, -1);
+ break;
+ case 1:
+ if (redirect1) {
+ n = write (R_BX, addr, nbytes);
+ break;
+ }
+ n = nbytes;
+ while (nbytes-- > 0)
+ tty_write(*addr++, -1);
+ break;
+ case 2:
+ if (redirect2) {
+ n = write (R_BX, addr, nbytes);
+ break;
+ }
+ n = nbytes;
+ while (nbytes-- > 0)
+ tty_write(*addr++, -1);
+ break;
+ default:
+ n = write (R_BX, addr, nbytes);
+ break;
+ }
+ if (n < 0)
+ return (WRITE_FAULT);
+
+ R_AX = n;
+ return(0);
+}
+
+/*
+** 21:42
+**
+** seek
+*/
+static int
+int21_42(regcontext_t *REGS)
+{
+ int whence;
+ off_t offset;
+
+ offset = ((off_t)R_CX << 16) + R_DX;
+ switch (R_AL) {
+ case 0:
+ whence = SEEK_SET;
+ break;
+ case 1:
+ whence = SEEK_CUR;
+ break;
+ case 2:
+ whence = SEEK_END;
+ break;
+ default:
+ return (FUNC_NUM_IVALID);
+ }
+
+ debug(D_FILE_OPS, "seek(%d, 0x%qx, %d)\n", R_BX, offset, whence);
+
+ if ((offset = lseek(R_BX, offset, whence)) < 0) {
+ if (errno == EBADF)
+ return (HANDLE_INVALID);
+ else
+ return (SEEK_ERROR);
+ }
+
+ R_DX = (offset >> 16) & 0xffff;
+ R_AX = offset & 0xffff;
+ return(0);
+}
+
+/*
+** 21:43
+**
+** get/set attributes
+*/
+static int
+int21_43(regcontext_t *REGS)
+{
+ int error;
+ char fname[PATH_MAX];
+ struct stat sb;
+ int mode;
+ int drive;
+
+ error = translate_filename((u_char *)N_GETPTR(R_DS, R_DX), fname, &drive);
+ if (error)
+ return (error);
+
+ debug(D_FILE_OPS, "get/set attributes: %s, cx=%x, al=%d\n",
+ fname, R_CX, R_AL);
+
+ if (stat(fname, &sb) < 0) {
+ debug(D_FILE_OPS, "stat failed for %s\n", fname);
+ return (FILE_NOT_FOUND);
+ }
+
+ switch (R_AL) {
+ case 0: /* get attributes */
+ mode = 0;
+ if (dos_readonly(drive) || access(fname, W_OK))
+ mode |= 0x01;
+ if (S_ISDIR(sb.st_mode))
+ mode |= 0x10;
+ R_CX = mode;
+ break;
+
+ case 1: /* set attributes - XXX ignored */
+ if (R_CX & 0x18)
+ return (ACCESS_DENIED);
+ break;
+
+ default:
+ return (FUNC_NUM_IVALID);
+ }
+ return(0);
+}
+
+/*
+** 21:44:0
+**
+** ioctl - get device info
+**
+** XXX it would be nice to detect EOF.
+*/
+static int
+int21_44_0(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "ioctl get %d\n", R_BX);
+
+ switch (R_BX) {
+ case 0:
+ R_DX = 0x80 | 0x01; /* is device, is standard output */
+ break;
+ case 1:
+ R_DX = 0x80 | 0x02; /* is device, is standard input */
+ break;
+ case 2:
+ R_DX = 0x80; /* is device */
+ break;
+ default:
+ if (isatty (R_BX))
+ R_DX = 0x80; /* is a device */
+ else
+ R_DX = 0; /* is a file */
+ break;
+ }
+ return(0);
+}
+
+/*
+** 21:44:01
+**
+** ioctl - set device info
+*/
+static int
+int21_44_1(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "ioctl set device info %d flags %x (ignored)\n",
+ R_BX, R_DX);
+ return(0);
+}
+
+/*
+** 21:44:8
+**
+** test for removable block device
+*/
+static int
+int21_44_8(regcontext_t *REGS)
+{
+ R_AX = 1; /* fixed */
+ return(0);
+}
+
+/*
+** 21:44:0
+**
+** test for remote device (disallow direct I/O)
+*/
+static int
+int21_44_9(regcontext_t *REGS)
+{
+ R_DX = 0x1200; /* disk is remote, direct I/O not allowed */
+}
+
+/*
+** 21:45
+**
+** dup
+*/
+static int
+int21_45(regcontext_t *REGS)
+{
+ int nfd;
+
+ debug(D_FILE_OPS, "dup(%d)\n", R_BX);
+
+ if ((nfd = dup(R_BX)) < 0) {
+ if (errno == EBADF)
+ return (HANDLE_INVALID);
+ else
+ return (TOO_MANY_OPEN_FILES);
+ }
+ R_AX = nfd;
+ return(0);
+}
+
+/*
+** 21:46
+**
+** dup2
+*/
+static int
+int21_46(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "dup2(%d, %d)\n", R_BX, R_CX);
+
+ if (dup2(R_BX, R_CX) < 0) {
+ if (errno == EMFILE)
+ return (TOO_MANY_OPEN_FILES);
+ else
+ return (HANDLE_INVALID);
+ }
+ return(0);
+}
+
+/*
+** 21:47
+**
+** getcwd
+*/
+static int
+int21_47(regcontext_t *REGS)
+{
+ int n,nbytes;
+ char *p,*addr;
+
+ n = R_DL;
+ if (!n--)
+ n = diskdrive;
+
+ p = (char *)dos_getcwd(n) + 1;
+ addr = (char *)N_GETPTR(R_DS, R_SI);
+
+ nbytes = strlen(p);
+ if (nbytes > 63)
+ nbytes = 63;
+
+ memcpy(addr, p, nbytes);
+ addr[nbytes] = 0;
+ return(0);
+}
+
+/*
+** 21:48
+**
+** allocate memory
+*/
+static int
+int21_48(regcontext_t *REGS)
+{
+ int memseg,avail;
+
+ memseg = mem_alloc(R_BX, pspseg, &avail);
+
+ if (memseg == 0L) {
+ R_BX = avail;
+ return (INSUF_MEM);
+ }
+
+ R_AX = memseg;
+ return(0);
+}
+
+/*
+** 21:49
+**
+** free memory
+*/
+static int
+int21_49(regcontext_t *REGS)
+{
+ if (mem_adjust(R_ES, 0, NULL) < 0)
+ return (MEM_BLK_ADDR_IVALID);
+ return(0);
+}
+
+/*
+** 21:4a
+**
+** resize memory block
+*/
+static int
+int21_4a(regcontext_t *REGS)
+{
+ int n,avail;
+
+ if ((n = mem_adjust(R_ES, R_BX, &avail)) < 0) {
+ R_BX = avail;
+ if (n == -1)
+ return (INSUF_MEM);
+ else
+ return (MEM_BLK_ADDR_IVALID);
+ }
+ return(0);
+}
+
+/*
+** 21:4b
+**
+** exec
+**
+** XXX verify!
+*/
+static int
+int21_4b(regcontext_t *REGS)
+{
+ int fd;
+ char *fname[PATH_MAX];
+ u_short *param;
+
+ debug(D_EXEC, "exec(%s)\n",(u_char *)N_GETPTR(R_DS, R_DX));
+
+ if ((fd = open_prog((u_char *)N_GETPTR(R_DS, R_DX))) < 0) {
+ debug(D_EXEC, "%s: command not found\n", fname);
+ return (FILE_NOT_FOUND);
+ }
+
+ /* child */
+ param = (u_short *)N_GETPTR(R_ES, R_BX);
+
+ switch (R_AL) {
+ case 0x00: /* load and execute */
+ exec_command(REGS, 1, fd, cmdname, param);
+ close(fd);
+ break;
+
+ case 0x01: /* just load */
+ exec_command(REGS, 0, fd, cmdname, param);
+ close(fd);
+ break;
+
+ case 0x03: /* load overlay */
+ load_overlay(fd, param[0], param[1]);
+ close(fd);
+ break;
+
+ default:
+ unknown_int3(0x21, 0x4b, R_AL, REGS);
+ return (FUNC_NUM_IVALID);
+ }
+ return(0);
+}
+
+/*
+** 21:4c
+**
+** return with code
+*/
+static int
+int21_4c(regcontext_t *REGS)
+{
+ return_status = R_AL;
+ done(REGS, R_AL);
+}
+
+/*
+** 21:4d
+**
+** get return code of child
+*/
+static int
+int21_4d(regcontext_t *REGS)
+{
+ R_AX = return_status;
+ return(0);
+}
+
+/*
+** 21:4e
+** 21:4f
+**
+** find first, find next
+*/
+static int
+int21_find(regcontext_t *REGS)
+{
+ find_block_t *dta;
+ dosdir_t dosdir;
+ int error;
+
+ dta = (find_block_t *)VECPTR(disk_transfer_addr);
+
+ switch (R_AH) {
+ case 0x4e: /* find first */
+ error = find_first((u_char *)N_GETPTR(R_DS, R_DX), R_CX, &dosdir, dta);
+ break;
+ case 0x4f:
+ error = find_next(&dosdir, dta);
+ break;
+ default:
+ fatal("called int21_find for unknown function %x\n",R_AH);
+ }
+ if (!error) {
+ dosdir_to_dta(&dosdir, dta);
+ R_AX = 0;
+ }
+ return(error);
+}
+
+/*
+** 21:50
+**
+** set PSP
+*/
+static int
+int21_50(regcontext_t *REGS)
+{
+ pspseg = R_BX;
+ return(0);
+}
+
+/*
+** 21:57:00
+**
+** get mtime for handle
+*/
+static int
+int21_57_0(regcontext_t *REGS)
+{
+ struct stat sb;
+ u_short date,time;
+
+ if (fstat(R_BX, &sb) < 0)
+ return (HANDLE_INVALID);
+ encode_dos_file_time(sb.st_mtime, &date, &time);
+ R_CX = time;
+ R_DX = date;
+ return(0);
+}
+
+/*
+** 21:57:01
+**
+** set mtime for handle
+*/
+static int
+int21_57_1(regcontext_t *REGS)
+{
+#ifdef __NetBSD__ /* XXX need futimes() */
+ struct stat sb;
+ struct timeval tv[2];
+ u_short date, time;
+
+ time = R_CX;
+ date = R_DX;
+ tv[0].tv_sec = tv[1].tv_sec = decode_dos_file_time(date, time);
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+ if (futimes(R_BX, tv) < 0)
+ return (HANDLE_INVALID);
+ break;
+#endif
+ return(0);
+}
+
+/*
+** 21:58
+**
+** get/set memory strategy
+** get/set UMB link state
+*/
+static int
+int21_58(regcontext_t *REGS)
+{
+ switch (R_AL) {
+ case 0x00: /* get memory strategy */
+ R_AX = memory_strategy;
+ break;
+ case 0x01: /* set memory strategy */
+ memory_strategy = R_BL;
+ if (memory_strategy > 2) /* higher make no sense without UMBs */
+ memory_strategy = 2;
+ break;
+ case 0x02: /* get UMB link state */
+ R_AL = 0; /* UMBs not in link chain */
+ break;
+ default: /* includes set, which is invalid */
+ unknown_int3(0x21, 0x58, R_AL, REGS);
+ return (FUNC_NUM_IVALID);
+ }
+ return(0);
+}
+
+/*
+** 21:59
+**
+** get extended error information
+*/
+static int
+int21_59(regcontext_t *REGS)
+{
+ R_AX = doserrno;
+ switch (doserrno) {
+ case 1:
+ case 6:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 15:
+ R_BH = 7; /* application error */
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ R_BH = 8; /* not found */
+ break;
+
+ case 7:
+ case 8:
+ R_BH = 1; /* out of resource */
+ break;
+
+ default:
+ R_BH = 12; /* already exists */
+ break;
+ }
+ R_BL = 6; /* always ignore! */
+ R_CH = 1; /* unknown/inappropriate */
+ return(0);
+}
+
+/*
+** 21:5a
+**
+** create temporary file
+*/
+static int
+int21_5a(regcontext_t *REGS)
+{
+ char fname[PATH_MAX];
+ char *pname;
+ int error;
+ int n;
+ int drive;
+ int fd;
+
+ /* get and check proposed path */
+ pname = (char *)N_GETPTR(R_DS, R_DX);
+ error = translate_filename(pname, fname, &drive);
+ if (error)
+ return (error);
+
+ debug(D_FILE_OPS, "tempname(%s)\n", fname);
+
+ if (dos_readonly(drive))
+ return (WRITE_PROT_DISK);
+
+ n = strlen(fname);
+ strcat(fname,"__dostmp.XXX");
+ fd = mkstemp(fname);
+ if (fd < 0)
+ return (ACCESS_DENIED);
+
+ strcat(pname, fname + n); /* give back the full name */
+ R_AX = fd;
+ return(0);
+}
+
+/*
+** 21:60
+**
+** canonicalise name
+*/
+static int
+int21_60(regcontext_t *REGS)
+{
+ return(dos_makepath((char *)GETPTR(R_DS, R_SI),
+ (char *)GETPTR(R_ES, R_DI)));
+}
+
+/*
+** 21:62
+**
+** get current PSP
+*/
+static int
+int21_62(regcontext_t *REGS)
+{
+ R_BX = pspseg;
+}
+
+/*
+** 21:65:23
+**
+** determine yes/no
+** (mostly for humour value 8)
+*/
+static int
+int21_65_23(regcontext_t *REGS)
+{
+ switch (R_DL) {
+ case 'n': /* no, nein, non, nyet */
+ case 'N':
+ R_AX = 0;
+ break;
+ case 'y': /* yes */
+ case 'Y':
+ case 'j': /* ja */
+ case 'J':
+ case 'o': /* oui */
+ case 'O':
+ case 'd': /* da */
+ case 'D':
+ R_AX = 1;
+ break;
+ default: /* maybe */
+ R_AX = 2;
+ break;
+ }
+ return(0);
+}
+
+/*
+** 21:68
+** 21:6a
+**
+** fflush/commit file
+*/
+static int
+int21_fflush(regcontext_t *REGS)
+{
+ debug(D_FILE_OPS, "fsync(%d)\n", R_BX);
+
+ if (fsync(R_BX) < 0)
+ return (HANDLE_INVALID);
+ return(0);
+}
+
+/******************************************************************************
+** 21:0f 21:10 21:11 21:12 21:16 21:27 21:28:21:29
+**
+** FCB functions
+*/
+static void
+openfcb(struct fcb *fcbp)
+{
+ struct stat statb;
+
+ fcbp->fcbDriveID = 3; /* drive C */
+ fcbp->fcbCurBlockNo = 0;
+ fcbp->fcbRecSize = 128;
+ if (fstat(fcbp->fcb_fd, &statb) < 0) {
+ debug(D_FILE_OPS, "open not complete with errno %d\n", errno);
+ return;
+ }
+ encode_dos_file_time(statb.st_mtime,
+ &fcbp->fcbFileDate, &fcbp->fcbFileTime);
+ fcbp->fcbFileSize = statb.st_size;
+}
+
+static int
+getfcb_rec(struct fcb *fcbp, int nrec)
+{
+ int n;
+
+ n = fcbp->fcbRandomRecNo;
+ if (fcbp->fcbRecSize >= 64)
+ n &= 0xffffff;
+ fcbp->fcbCurRecNo = n % 128;
+ fcbp->fcbCurBlockNo = n / 128;
+ if (lseek(fcbp->fcb_fd, n * fcbp->fcbRecSize, SEEK_SET) < 0)
+ return (-1);
+ return (nrec * fcbp->fcbRecSize);
+}
+
+
+static int
+setfcb_rec(struct fcb *fcbp, int n)
+{
+ int recs, total;
+
+ total = fcbp->fcbRandomRecNo;
+ if (fcbp->fcbRecSize >= 64)
+ total &= 0xffffff;
+ recs = (n+fcbp->fcbRecSize-1) / fcbp->fcbRecSize;
+ total += recs;
+
+ fcbp->fcbRandomRecNo = total;
+ fcbp->fcbCurRecNo = total % 128;
+ fcbp->fcbCurBlockNo = total / 128;
+}
+
+void
+fcb_to_string(fcbp, buf)
+ struct fcb *fcbp;
+ u_char *buf;
+{
+
+ if (fcbp->fcbDriveID != 0x00) {
+ *buf++ = fcbp->fcbDriveID - 1 + 'A';
+ *buf++ = ':';
+ }
+ pack_name(fcbp->fcbFileName, buf);
+}
+
+
+static int
+int21_fcb(regcontext_t *REGS)
+{
+ char buf[PATH_MAX];
+ char fname[PATH_MAX];
+ struct stat sb;
+ dosdir_t dosdir;
+ struct fcb *fcbp;
+ find_block_t *dta;
+ u_char *addr;
+ int error;
+ int drive;
+ int fd;
+ int nbytes,n;
+
+
+ fcbp = (struct fcb *)GETPTR(R_DS, R_DX);
+
+ switch (R_AH) {
+
+ case 0x0f: /* open file with FCB */
+ fcb_to_string(fcbp, buf);
+ error = translate_filename(buf, fname, &drive);
+ if (error)
+ return (error);
+
+ debug(D_FILE_OPS, "open FCB(%s)\n", fname);
+
+ if (ustat(fname, &sb) < 0)
+ sb.st_ino = 0;
+
+ if (dos_readonly(drive))
+ return (WRITE_PROT_DISK);
+
+ if (sb.st_ino == 0 || S_ISDIR(sb.st_mode))
+ return (FILE_NOT_FOUND);
+
+ if ((fd = open(fname, O_RDWR)) < 0) {
+ if (errno == ENOENT)
+ return (FILE_NOT_FOUND);
+ else
+ return (ACCESS_DENIED);
+ }
+
+ fcbp->fcb_fd = fd;
+ openfcb(fcbp);
+ R_AL = 0;
+ break;
+
+ case 0x10: /* close file with FCB */
+ debug(D_FILE_OPS, "close FCB(%d)\n", fcbp->fcb_fd);
+
+ if (close(fcbp->fcb_fd) < 0)
+ return (HANDLE_INVALID);
+
+ fcbp->fcb_fd = -1;
+ R_AL = 0;
+ break;
+
+ case 0x11: /* find_first with FCB */
+ dta = (find_block_t *)VECPTR(disk_transfer_addr);
+
+ fcb_to_string(fcbp, buf);
+ error = find_first(buf, fcbp->fcbAttribute, &dosdir, dta);
+ if (error)
+ return (error);
+
+ dosdir_to_dta(&dosdir, dta);
+ R_AL = 0;
+ break;
+
+ case 0x12: /* find_next with FCB */
+ dta = (find_block_t *)VECPTR(disk_transfer_addr);
+
+ error = find_next(&dosdir, dta);
+ if (error)
+ return (error);
+
+ dosdir_to_dta(&dosdir, dta);
+ R_AL = 0;
+ break;
+
+ case 0x16: /* create file with FCB */
+ fcb_to_string(fcbp, buf);
+ error = translate_filename(buf, fname, &drive);
+ if (error)
+ return (error);
+
+ debug(D_FILE_OPS, "creat FCB(%s)\n", fname);
+
+ if (ustat(fname, &sb) < 0)
+ sb.st_ino = 0;
+
+ if (dos_readonly(drive))
+ return (WRITE_PROT_DISK);
+
+ if (sb.st_ino && !S_ISREG(sb.st_mode))
+ return (ACCESS_DENIED);
+
+ if ((fd = open(fname, O_CREAT|O_TRUNC|O_RDWR, 0666)) < 0)
+ return (ACCESS_DENIED);
+
+ fcbp->fcb_fd = fd;
+ openfcb(fcbp);
+ R_AL = 0;
+ break;
+
+ case 0x27: /* random block read */
+ addr = (u_char *)VECPTR(disk_transfer_addr);
+ nbytes = getfcb_rec(fcbp, R_CX);
+
+ if (nbytes < 0)
+ return (READ_FAULT);
+ n = read(fcbp->fcb_fd, addr, nbytes);
+ if (n < 0)
+ return (READ_FAULT);
+ R_CX = setfcb_rec(fcbp, n);
+ if (n < nbytes) {
+ nbytes = n % fcbp->fcbRecSize;
+ if (0 == nbytes) {
+ R_AL = 0x01;
+ } else {
+ bzero(addr + n, fcbp->fcbRecSize - nbytes);
+ R_AL = 0x03;
+ }
+ } else {
+ R_AL = 0;
+ }
+ break;
+
+ case 0x28: /* random block write */
+ addr = (u_char *)VECPTR(disk_transfer_addr);
+ nbytes = getfcb_rec(fcbp, R_CX);
+
+ if (nbytes < 0)
+ return (WRITE_FAULT);
+ n = write(fcbp->fcb_fd, addr, nbytes);
+ if (n < 0)
+ return (WRITE_FAULT);
+ R_CX = setfcb_rec(fcbp, n);
+ if (n < nbytes) {
+ R_AL = 0x01;
+ } else {
+ R_AL = 0;
+ }
+ break;
+
+ case 0x29: /* parse filename */
+ debug(D_FILE_OPS,"parse filename: flag=%d, ", R_AL);
+
+ R_AX = parse_filename(R_AL,
+ (char *)N_GETPTR(R_DS, R_SI),
+ (char *)N_GETPTR(R_ES, R_DI),
+ &nbytes);
+ debug(D_FILE_OPS, "%d %s, FCB: %d, %.11s\n",
+ nbytes,
+ N_GETPTR(R_DS, R_SI),
+ *(int *)N_GETPTR(R_ES, R_DI),
+ N_GETPTR(R_ES, R_DI) + 1);
+
+ R_SI += nbytes;
+ break;
+
+ default:
+ fatal("called int21_fcb with unknown function %x\n",R_AH);
+ }
+ return(0);
+}
+
+/*
+** 21:5d
+** 21:5e
+** 21:5f
+**
+** network functions
+** XXX relevant?
+*/
+static int
+int21_net(regcontext_t *REGS)
+{
+ switch(R_AH) {
+ case 0x5d:
+ switch(R_AL) {
+ case 0x06:
+ debug(D_HALF, "Get Swapable Area\n");
+ return (ACCESS_DENIED);
+ case 0x08: /* Set redirected printer mode */
+ debug(D_HALF, "Redirection is %s\n",
+ R_DL ? "seperate jobs" : "combined");
+ break;
+ case 0x09: /* Flush redirected printer output */
+ break;
+ default:
+ unknown_int3(0x21, 0x5d, R_AL, REGS);
+ return (FUNC_NUM_IVALID);
+ }
+ break;
+
+ case 0x5e:
+ case 0x5f:
+ unknown_int2(0x21, R_AH, REGS);
+ return (FUNC_NUM_IVALID);
+ default:
+ fatal("called int21_net with unknown function %x\n",R_AH);
+ }
+ return(0);
+}
+
+/*
+** 21:??
+**
+** Unknown/unsupported
+*/
+static int
+int21_NOFUNC(regcontext_t *REGS)
+{
+ unknown_int2(0x21, R_AH, REGS);
+ return (FUNC_NUM_IVALID);
+}
+
+/*
+** 21:??
+**
+** Null function; no error, no action
+*/
+static int
+int21_NULLFUNC(regcontext_t *REGS)
+{
+ R_AL = 0;
+ return(0);
+}
+
+
+static struct intfunc_table int21_table [] = {
+ { 0x00, IFT_NOSUBFUNC, int21_00, "terminate"},
+ { 0x01, IFT_NOSUBFUNC, int21_01, "read character with echo"},
+ { 0x02, IFT_NOSUBFUNC, int21_02, "write char to stdout"},
+ { 0x03, IFT_NOSUBFUNC, int21_NOFUNC, "read char from stdaux"},
+ { 0x04, IFT_NOSUBFUNC, int21_NOFUNC, "write char to stdaux"},
+ { 0x05, IFT_NOSUBFUNC, int21_NOFUNC, "write char to printer"},
+ { 0x06, IFT_NOSUBFUNC, int21_06, "direct console I/O"},
+ { 0x07, IFT_NOSUBFUNC, int21_07, "direct console in without echo"},
+ { 0x08, IFT_NOSUBFUNC, int21_08, "read character, no echo"},
+ { 0x09, IFT_NOSUBFUNC, int21_09, "write string to standard out"},
+ { 0x0a, IFT_NOSUBFUNC, int21_0a, "buffered input"},
+ { 0x0b, IFT_NOSUBFUNC, int21_0b, "get stdin status"},
+ { 0x0c, IFT_NOSUBFUNC, int21_0c, "flush stdin and read"},
+ { 0x0d, IFT_NOSUBFUNC, int21_NULLFUNC, "disk reset"},
+ { 0x0e, IFT_NOSUBFUNC, int21_0e, "select default drive"},
+ { 0x19, IFT_NOSUBFUNC, int21_19, "get default drive"},
+ { 0x1a, IFT_NOSUBFUNC, int21_1a, "set DTA"},
+ { 0x1b, IFT_NOSUBFUNC, int21_free, "get allocation for default drive"},
+ { 0x1c, IFT_NOSUBFUNC, int21_free, "get allocation for specific drive"},
+ { 0x1f, IFT_NOSUBFUNC, int21_NOFUNC, "get DPB for current drive"},
+ { 0x25, IFT_NOSUBFUNC, int21_25, "set interrupt vector"},
+ { 0x26, IFT_NOSUBFUNC, int21_26, "create new PSP"},
+ { 0x2a, IFT_NOSUBFUNC, int21_2a, "get date"},
+ { 0x2b, IFT_NOSUBFUNC, int21_2b, "set date"},
+ { 0x2c, IFT_NOSUBFUNC, int21_2c, "get time"},
+ { 0x2d, IFT_NOSUBFUNC, int21_2d, "set time"},
+ { 0x2e, IFT_NOSUBFUNC, int21_NULLFUNC, "set verify flag"},
+ { 0x2f, IFT_NOSUBFUNC, int21_2f, "get DTA"},
+ { 0x30, IFT_NOSUBFUNC, int21_30, "get DOS version"},
+ { 0x31, IFT_NOSUBFUNC, int21_NOFUNC, "terminate and stay resident"},
+ { 0x32, IFT_NOSUBFUNC, int21_NOFUNC, "get DPB for specific drive"},
+ { 0x33, 0x05, int21_33_5, "get boot drive"},
+ { 0x33, 0x06, int21_33_6, "get true version number"},
+ { 0x33, IFT_NOSUBFUNC, int21_33, "extended break checking"},
+ { 0x34, IFT_NOSUBFUNC, int21_34, "get address of InDos flag"},
+ { 0x35, IFT_NOSUBFUNC, int21_35, "get interrupt vector"},
+ { 0x36, IFT_NOSUBFUNC, int21_free, "get disk free space"},
+ { 0x37, IFT_NOSUBFUNC, int21_37, "switch character"},
+ { 0x38, IFT_NOSUBFUNC, int21_38, "country code/information"},
+ { 0x39, IFT_NOSUBFUNC, int21_dirfn, "mkdir"},
+ { 0x3a, IFT_NOSUBFUNC, int21_dirfn, "rmdir"},
+ { 0x3b, IFT_NOSUBFUNC, int21_3b, "chdir"},
+ { 0x3c, IFT_NOSUBFUNC, int21_open, "creat"},
+ { 0x3d, IFT_NOSUBFUNC, int21_open, "open"},
+ { 0x3e, IFT_NOSUBFUNC, int21_3e, "close"},
+ { 0x3f, IFT_NOSUBFUNC, int21_3f, "read"},
+ { 0x40, IFT_NOSUBFUNC, int21_40, "write"},
+ { 0x41, IFT_NOSUBFUNC, int21_dirfn, "unlink"},
+ { 0x42, IFT_NOSUBFUNC, int21_42, "lseek"},
+ { 0x43, IFT_NOSUBFUNC, int21_43, "get/set file attributes"},
+ { 0x44, 0x00, int21_44_0, "ioctl(get)"},
+ { 0x44, 0x01, int21_44_1, "ioctl(set)"},
+ { 0x44, 0x08, int21_44_8, "ioctl(test removable)"},
+ { 0x44, 0x09, int21_44_9, "ioctl(test remote)"},
+ { 0x45, IFT_NOSUBFUNC, int21_45, "dup"},
+ { 0x46, IFT_NOSUBFUNC, int21_46, "dup2"},
+ { 0x47, IFT_NOSUBFUNC, int21_47, "getwd"},
+ { 0x48, IFT_NOSUBFUNC, int21_48, "allocate memory"},
+ { 0x49, IFT_NOSUBFUNC, int21_49, "free memory"},
+ { 0x4a, IFT_NOSUBFUNC, int21_4a, "resize memory block"},
+ { 0x4b, IFT_NOSUBFUNC, int21_4b, "exec"},
+ { 0x4c, IFT_NOSUBFUNC, int21_4c, "exit with return code"},
+ { 0x4d, IFT_NOSUBFUNC, int21_4d, "get return code from child"},
+ { 0x4e, IFT_NOSUBFUNC, int21_find, "findfirst"},
+ { 0x4f, IFT_NOSUBFUNC, int21_find, "findnext"},
+ { 0x50, IFT_NOSUBFUNC, int21_50, "set psp"},
+ { 0x50, IFT_NOSUBFUNC, int21_62, "get psp"},
+ { 0x52, IFT_NOSUBFUNC, int21_NOFUNC, "get LoL"},
+ { 0x53, IFT_NOSUBFUNC, int21_NOFUNC, "translate BPB to DPB"},
+ { 0x54, IFT_NOSUBFUNC, int21_NULLFUNC, "get verify flag"},
+ { 0x55, IFT_NOSUBFUNC, int21_NOFUNC, "create PSP"},
+ { 0x56, IFT_NOSUBFUNC, int21_dirfn, "rename"},
+ { 0x57, 0x00, int21_57_0, "get mtime"},
+ { 0x57, 0x01, int21_57_1, "set mtime"},
+ { 0x58, IFT_NOSUBFUNC, int21_58, "get/set memory strategy"},
+ { 0x59, IFT_NOSUBFUNC, int21_59, "get extended error information"},
+ { 0x5a, IFT_NOSUBFUNC, int21_5a, "create temporary file"},
+ { 0x5b, IFT_NOSUBFUNC, int21_open, "create new file"},
+ { 0x5c, IFT_NOSUBFUNC, int21_NOFUNC, "flock"},
+ { 0x5d, IFT_NOSUBFUNC, int21_net, "network functions"},
+ { 0x5e, IFT_NOSUBFUNC, int21_net, "network functions"},
+ { 0x5f, IFT_NOSUBFUNC, int21_net, "network functions"},
+ { 0x60, IFT_NOSUBFUNC, int21_60, "canonicalise name/path"},
+ { 0x61, IFT_NOSUBFUNC, int21_NULLFUNC, "network functions (reserved)"},
+ { 0x62, IFT_NOSUBFUNC, int21_62, "get current PSP"},
+ { 0x63, IFT_NOSUBFUNC, int21_NOFUNC, "get DBCS lead-byte table"},
+ { 0x64, IFT_NOSUBFUNC, int21_NOFUNC, "set device-driver lookahead"},
+ { 0x65, 0x23, int21_65_23, "determine yes/no"},
+ { 0x65, IFT_NOSUBFUNC, int21_NOFUNC, "get extended country information"},
+ { 0x66, IFT_NOSUBFUNC, int21_NOFUNC, "get/set codepage table"},
+ { 0x67, IFT_NOSUBFUNC, int21_NULLFUNC, "set handle count"},
+ { 0x68, IFT_NOSUBFUNC, int21_fflush, "fflush"},
+ { 0x69, IFT_NOSUBFUNC, int21_NOFUNC, "get/set disk serial number"},
+ { 0x6a, IFT_NOSUBFUNC, int21_fflush, "commit file"},
+ { 0x6b, IFT_NOSUBFUNC, int21_NULLFUNC, "IFS ioctl"},
+ { 0x6c, IFT_NOSUBFUNC, int21_open, "extended open/create"},
+
+/* FCB functions */
+ { 0x0f, IFT_NOSUBFUNC, int21_fcb, "open file"},
+ { 0x10, IFT_NOSUBFUNC, int21_fcb, "close file"},
+ { 0x11, IFT_NOSUBFUNC, int21_fcb, "find first"},
+ { 0x12, IFT_NOSUBFUNC, int21_fcb, "find next"},
+ { 0x13, IFT_NOSUBFUNC, int21_NOFUNC, "delete"},
+ { 0x14, IFT_NOSUBFUNC, int21_NOFUNC, "sequential read"},
+ { 0x15, IFT_NOSUBFUNC, int21_NOFUNC, "sequential write"},
+ { 0x16, IFT_NOSUBFUNC, int21_fcb, "create/truncate"},
+ { 0x17, IFT_NOSUBFUNC, int21_NOFUNC, "rename"},
+ { 0x21, IFT_NOSUBFUNC, int21_NOFUNC, "read random"},
+ { 0x22, IFT_NOSUBFUNC, int21_NOFUNC, "write random"},
+ { 0x23, IFT_NOSUBFUNC, int21_NOFUNC, "get file size"},
+ { 0x24, IFT_NOSUBFUNC, int21_NOFUNC, "set random record number"},
+ { 0x27, IFT_NOSUBFUNC, int21_fcb, "random block read"},
+ { 0x28, IFT_NOSUBFUNC, int21_fcb, "random block write"},
+ { 0x29, IFT_NOSUBFUNC, int21_fcb, "parse filename into FCB"},
+
+/* CPM compactability */
+ { 0x18, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
+ { 0x1d, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
+ { 0x1e, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
+ { 0x20, IFT_NOSUBFUNC, int21_NULLFUNC, "CPM"},
+
+ { -1, IFT_NOSUBFUNC, NULL, NULL} /* terminator */
+
+};
+
+static int int21_fastlookup[256];
+
+char *dos_return[] = {
+ "OK",
+ "FUNC_NUM_IVALID",
+ "FILE_NOT_FOUND",
+ "PATH_NOT_FOUND",
+ "TOO_MANY_OPEN_FILES",
+ "ACCESS_DENIED",
+ "HANDLE_INVALID",
+ "MEM_CB_DEST",
+ "INSUF_MEM",
+ "MEM_BLK_ADDR_IVALID",
+ "ENV_INVALID",
+ "FORMAT_INVALID",
+ "ACCESS_CODE_INVALID",
+ "DATA_INVALID",
+ "UNKNOWN_UNIT",
+ "DISK_DRIVE_INVALID",
+ "ATT_REM_CUR_DIR",
+ "NOT_SAME_DEV",
+ "NO_MORE_FILES",
+ "WRITE_PROT_DISK",
+ "UNKNOWN_UNIT_CERR",
+ "DRIVE_NOT_READY",
+ "UNKNOWN_COMMAND",
+ "DATA_ERROR_CRC",
+ "BAD_REQ_STRUCT_LEN",
+ "SEEK_ERROR",
+ "UNKNOWN_MEDIA_TYPE",
+ "SECTOR_NOT_FOUND",
+ "PRINTER_OUT_OF_PAPER",
+ "WRITE_FAULT",
+ "READ_FAULT",
+ "GENERAL_FAILURE"
+};
+
+const int dos_ret_size = (sizeof(dos_return) / sizeof(char *));
+
+/*
+** for want of anywhere better to go
+*/
+static void
+int20(regcontext_t *REGS)
+{
+ /* int 20 = exit(0) */
+ done(REGS, 0);
+}
+
+static void
+int29(regcontext_t *REGS)
+{
+ tty_write(R_AL, TTYF_REDIRECT);
+}
+
+/******************************************************************************
+** entrypoint for MS-DOS functions
+*/
+static void
+int21(regcontext_t *REGS)
+{
+ int error;
+ int index;
+
+ /* look for a handler */
+ index = intfunc_find(int21_table, int21_fastlookup, R_AH, R_AL);
+
+ if (index == -1) { /* no matching functions */
+ unknown_int2(0x21, R_AL, REGS);
+ return;
+ }
+
+ /* call the handler */
+ error = int21_table[index].handler(REGS);
+ debug(D_DOSCALL, "msdos call %02x (%s) returns %d (%s)\n",
+ int21_table[index].func, int21_table[index].desc, error,
+ ((error >= 0) && (error <= dos_ret_size)) ? dos_return[error] : "unknown");
+
+ if (error) {
+ doserrno = error;
+ R_FLAGS |= PSL_C;
+
+ /* XXX is this entirely legitimate? */
+ if (R_AH >= 0x2f)
+ R_AX = error;
+ else
+ R_AX = 0xff;
+ } else {
+ R_FLAGS &= ~PSL_C;
+ }
+ return;
+}
+
+static u_char upcase_trampoline[] = {
+ 0xf4, /* HLT */
+ 0xcb, /* RETF */
+};
+
+/*
+** initialise thyself
+*/
+void
+dos_init(void)
+{
+ u_long vec;
+ int hn;
+
+ /* hook vectors */
+ vec = insert_softint_trampoline();
+ ivec[0x20] = vec;
+ register_callback(vec, int20, "int 20");
+
+ vec = insert_softint_trampoline();
+ ivec[0x21] = vec;
+ register_callback(vec, int21, "int 21");
+
+ vec = insert_softint_trampoline();
+ ivec[0x29] = vec;
+ register_callback(vec, int29, "int 29");
+
+ vec = insert_null_trampoline();
+ ivec[0x28] = vec; /* dos idle */
+ ivec[0x2b] = vec; /* reserved */
+ ivec[0x2c] = vec; /* reserved */
+ ivec[0x2d] = vec; /* reserved */
+
+ upcase_vector = insert_generic_trampoline(
+ sizeof(upcase_trampoline), upcase_trampoline);
+ register_callback(upcase_vector, upcase_entry, "upcase");
+
+ /* build fastlookup index into the monster table of interrupts */
+ intfunc_init(int21_table, int21_fastlookup);
+}
OpenPOWER on IntegriCloud