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
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
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
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>
63 char ciCurrencyFormat
;
64 char ciCurrencyPlaces
;
66 ushort ciCaseMapOffset
;
67 ushort ciCaseMapSegment
;
73 0, "$", ",", ".", "-", ":", 0, 2, 0, 0xffff, 0xffff, "?"
76 /* DOS File Control Block */
79 u_char fcbResoived
[5];
82 u_char fcbFileName
[8];
84 u_short fcbCurBlockNo
;
90 int fcb_fd
; /* hide UNIX FD here */
92 u_long fcbRandomRecNo
;
93 }/* __attribute__((__packed__))*/;
96 int diskdrive
= 2; /* C: */
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 /******************************************************************************
139 return (upc_table
[c
- 0x80]);
145 upcase_entry(regcontext_t
*REGS
)
152 ** Handle the DOS drive info/free space/etc. calls.
155 int21_free(regcontext_t
*REGS
)
173 fatal("int21_free called on unknown function %x\n",R_AH
);
176 error
= get_space(drive
, &fs
);
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 */
187 BIOSDATA
[0xb4] = 0xf0; /* "reserved" area, "other media" FAT ID */
188 R_DX
= 0x40; /* BIOS data area */
193 R_BX
= fs
.avail_clusters
; /* number of available clusters */
200 pack_name(u_char
*p
, u_char
*q
)
204 for (i
= 8; i
> 0 && *p
!= ' '; i
--)
209 for (i
= 3; i
> 0 && *p
!= ' '; i
--)
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
);
228 encode_dos_file_time(time_t t
, u_short
*dosdatep
, u_short
*dostimep
)
233 *dostimep
= (tm
.tm_hour
<< 11) |
235 ((tm
.tm_sec
/ 2) << 0);
236 *dosdatep
= ((tm
.tm_year
- 80) << 9) |
237 ((tm
.tm_mon
+ 1) << 5) |
242 decode_dos_file_time(u_short dosdate
, u_short dostime
)
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. */
255 /* tm_gmtoff is ignored. */
261 translate_filename(u_char
*dname
, u_char
*uname
, int *drivep
)
263 u_char newpath
[1024];
266 if (!strcasecmp(dname
, "con")) {
268 strcpy(uname
, _PATH_TTY
);
272 /* XXX KLUDGE for EMS support w/o booting DOS */
273 /* Really need a better way to handle devices */
274 if (!strcasecmp(dname
, "emmxxxx0")) {
276 strcpy(uname
, _PATH_DEVNULL
);
280 error
= dos_makepath(dname
, newpath
);
284 error
= dos_to_real_path(newpath
, uname
, drivep
);
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)
325 #define get_drive_letter(x) ((x) - 0x40)
328 parse_filename(int flag
, char *str
, char *fcb
, int *nb
)
344 if (isvalid(*p
) && p
[1] == ':') {
345 *fcb
++ = get_drive_letter(upcase(*p
));
347 } else if (flag
& 2) {
350 *fcb
++ = 0; /* default drive */
378 } else if (flag
& 4) {
411 } else if (flag
& 8) {
418 for (i
= 4; i
> 0; i
--)
419 *fcb
++ = 0; /* filler */
425 /******************************************************************************
435 int21_00(regcontext_t
*REGS
)
438 /* keep `gcc -Wall' happy */
445 ** read character with echo
448 int21_01(regcontext_t
*REGS
)
452 if ((n
= tty_read((regcontext_t
*)®S
->sc
, TTYF_BLOCKALL
)) >= 0)
460 ** write char to stdout
463 int21_02(regcontext_t
*REGS
)
465 tty_write(R_DL
, TTYF_REDIRECT
);
472 ** direct console I/O
474 ** (dl) is output char unless 0xff, when we read instead
477 int21_06(regcontext_t
*REGS
)
481 /* XXX - should be able to read a file */
483 n
= tty_read((regcontext_t
*)®S
->sc
, TTYF_ECHO
|TTYF_REDIRECT
);
485 R_FLAGS
|= PSL_Z
; /* nothing available */
488 R_AL
= n
; /* got character */
492 /* write and return char in %al */
493 tty_write(R_DL
, TTYF_REDIRECT
);
502 ** direct console input with no echo
505 int21_07(regcontext_t
*REGS
)
507 R_AL
= tty_read((regcontext_t
*)®S
->sc
,
508 TTYF_BLOCK
|TTYF_REDIRECT
) & 0xff;
515 ** character input with no echo
518 int21_08(regcontext_t
*REGS
)
522 if ((n
= tty_read((regcontext_t
*)®S
->sc
,
523 TTYF_BLOCK
|TTYF_CTRL
|TTYF_REDIRECT
)) >= 0)
531 ** write string to standard out.
533 ** We're a little paranoid here; if the string is very long, truncate it.
536 int21_09(regcontext_t
*REGS
)
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
++) {
548 tty_write(*addr
, TTYF_REDIRECT
);
560 int21_0a(regcontext_t
*REGS
)
566 /* pointer to buffer */
567 addr
= (unsigned char *)MAKEPTR(R_DS
, R_DX
);
569 /* capacity of buffer */
571 if (avail
== 0) /* no space */
573 nbytes
= 0; /* read nothing yet */
577 n
= tty_read((regcontext_t
*)®S
->sc
,
578 TTYF_BLOCK
|TTYF_CTRL
|TTYF_REDIRECT
);
579 if (n
< 0) /* end of input */
580 n
= '\r'; /* make like CR */
583 case '\r': /* done */
585 addr
[nbytes
+2] = '\r';
586 addr
[nbytes
+3] = '\0'; /* XXX is this necessary? */
588 case '\n': /* ignore */
591 case '\b': /* backspace */
594 tty_write('\b', TTYF_REDIRECT
);
595 if (addr
[nbytes
+2] < ' ')
596 tty_write('\b', TTYF_REDIRECT
);
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
);
608 tty_write(n
, TTYF_REDIRECT
);
620 ** This is a favorite for camping on, so we do some poll-counting
624 int21_0b(regcontext_t
*REGS
)
628 /* XXX this is pretty bogus, actually */
630 R_AL
= 0xff; /* no X mode, always claim data available */
633 /* XXX tty_peek is broken */
634 n
= tty_peek(REGS
, poll_cnt
? 0 : TTYF_POLL
) ? 0xff : 0;
635 if (n
< 0) /* control-break */
637 R_AL
= n
; /* will be 0 or 0xff */
646 ** flush stdin and read using other function
649 int21_0c(regcontext_t
*REGS
)
651 if (xmode
) /* XXX should always flush! */
654 switch(R_AL
) { /* which subfunction? */
656 return(int21_01(REGS
));
658 return(int21_06(REGS
));
660 return(int21_07(REGS
));
662 return(int21_08(REGS
));
664 return(int21_0a(REGS
));
672 ** select default drive
675 int21_0e(regcontext_t
*REGS
)
677 diskdrive
= R_DL
; /* XXX rangecheck? */
678 R_AL
= ndisks
+ 2; /* report actual limit */
688 int21_19(regcontext_t
*REGS
)
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
);
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.
715 int21_23(regcontext_t
*REGS
)
717 debug(D_HALF
, "Returning failure from get file size for fcb 21:23\n");
725 ** set interrupt vector
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
);
741 int21_26(regcontext_t
*REGS
)
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);
759 int21_2a(regcontext_t
*REGS
)
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;
783 int21_2b(regcontext_t
*REGS
)
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;
801 return (DATA_INVALID
);
803 delta_clock
= now
- tv
.tv_sec
; /* compute new offset? */
814 int21_2c(regcontext_t
*REGS
)
821 gettimeofday(&tv
, &tz
);
822 now
= tv
.tv_sec
+ delta_clock
;
823 tm
= *localtime(&now
);
828 R_DL
= tv
.tv_usec
/ 10000;
838 int21_2d(regcontext_t
*REGS
)
845 gettimeofday(&tv
, &tz
);
846 now
= tv
.tv_sec
+ delta_clock
;
847 tm
= *localtime(&now
);
852 tv
.tv_usec
= R_DL
* 10000;
856 return (DATA_INVALID
);
858 delta_clock
= now
- tv
.tv_sec
;
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
);
879 ** get DOS version number.
881 ** XXX begging for a rewrite
884 int21_30(regcontext_t
*REGS
)
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.
899 cmd
+= *(short *)cmd
+ 1;
900 while (cmd
[-1] && cmd
[-1] != '\\' && cmd
[-1] != ':')
903 /* get the version we're pretending to be for this sucker */
905 R_AL
= (v
/ 100) & 0xff;
916 int21_33_5(regcontext_t
*REGS
)
918 R_DL
= 3; /* always booted from C */
925 ** get true DOS version
928 int21_33_6(regcontext_t
*REGS
)
933 R_BL
= (v
/ 100) & 0xff;
943 ** extended break checking
946 int21_33(regcontext_t
*REGS
)
964 unknown_int3(0x21, 0x33, R_AL
, REGS
);
965 return(FUNC_NUM_IVALID
);
973 ** Get address of InDos flag
975 ** XXX check interrupt list WRT location of critical error flag too.
978 int21_34(regcontext_t
*REGS
)
980 PUTVEC(R_ES
, R_BX
, (u_long
)InDOS
);
987 ** get interrupt vector
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
);
1000 ** switch character manipulation
1004 int21_37(regcontext_t
*REGS
)
1007 case 0: /* get switch character */
1011 case 1: /* set switch character (normally /) */
1012 /* ignored by most versions of DOS */
1015 unknown_int3(0x21, 0x37, R_AL
, REGS
);
1016 return (FUNC_NUM_IVALID
);
1025 ** country code information
1027 ** XXX internat guru?
1030 int21_38(regcontext_t
*REGS
)
1034 if (R_DX
== 0xffff) {
1035 debug(D_HALF
, "warning: set country code ignored");
1038 addr
= (char *)MAKEPTR(R_DS
, R_DX
);
1039 PUTVEC(countryinfo
.ciCaseMapSegment
, countryinfo
.ciCaseMapOffset
,
1041 memcpy(addr
, &countryinfo
, sizeof(countryinfo
));
1051 ** mkdir, rmdir, unlink, rename
1054 int21_dirfn(regcontext_t
*REGS
)
1057 char fname
[PATH_MAX
],tname
[PATH_MAX
];
1060 error
= translate_filename((u_char
*)MAKEPTR(R_DS
, R_DX
), fname
, &drive
);
1064 if (dos_readonly(drive
))
1065 return (WRITE_PROT_DISK
);
1069 debug(D_FILE_OPS
, "mkdir(%s)\n", fname
);
1070 error
= mkdir(fname
, 0777);
1073 debug(D_FILE_OPS
, "rmdir(%s)\n", fname
);
1074 error
= rmdir(fname
);
1077 debug(D_FILE_OPS
, "unlink(%s)\n", fname
);
1078 error
= unlink(fname
);
1080 case 0x56: /* rename - some extra work */
1081 error
= translate_filename((u_char
*)MAKEPTR(R_ES
, R_DI
), tname
, &drive
);
1085 debug(D_FILE_OPS
, "rename(%s, %s)\n", fname
, tname
);
1086 error
= rename(fname
, tname
);
1090 fatal("call to int21_dirfn for unknown function %x\n",R_AH
);
1096 return (PATH_NOT_FOUND
);
1098 return (NOT_SAME_DEV
);
1100 return (ACCESS_DENIED
);
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
)));
1123 ** open, creat, creat new, multipurpose creat
1126 int21_open(regcontext_t
*REGS
)
1129 char fname
[PATH_MAX
];
1131 int mode
,action
,status
;
1137 case 0x3c: /* creat */
1138 pname
= (char *)MAKEPTR(R_DS
, R_DX
);
1139 action
= 0x12; /* create/truncate regardless */
1141 debug(D_FILE_OPS
, "creat");
1144 case 0x3d: /* open */
1145 pname
= (char *)MAKEPTR(R_DS
, R_DX
);
1146 action
= 0x01; /* fail if not exist, open if exists */
1158 return (FUNC_NUM_IVALID
);
1160 debug(D_FILE_OPS
, "open");
1163 case 0x5b: /* creat new */
1164 pname
= (char *)MAKEPTR(R_DS
, R_DL
);
1165 action
= 0x10; /* create if not exist, fail if exists */
1167 debug(D_FILE_OPS
, "creat_new");
1170 case 0x6c: /* multipurpose */
1171 pname
= (char *)MAKEPTR(R_DS
, R_SI
);
1184 return (FUNC_NUM_IVALID
);
1186 debug(D_FILE_OPS
, "mopen");
1190 fatal("called int21_creat for unknown function %x\n",R_AH
);
1192 if (action
& 0x02) /* replace/open mode */
1195 /* consider proposed name */
1196 error
= translate_filename(pname
, fname
, &drive
);
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? */
1208 mode
|= O_CREAT
; /* have to create as we go */
1209 status
= 0x02; /* file created */
1211 return(FILE_NOT_FOUND
);
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 */
1224 status
= 0x01; /* just open it */
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 */
1238 R_AX
= fd
; /* return fd */
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
);
1268 int21_3f(regcontext_t
*REGS
)
1274 addr
= (char *)MAKEPTR(R_DS
, R_DX
);
1276 debug(D_FILE_OPS
, "read(%d, %d)\n", R_BX
, R_CX
);
1280 n
= read (R_BX
, addr
, R_CX
);
1284 avail
= tty_read(REGS
, TTYF_BLOCK
|TTYF_CTRL
|TTYF_ECHONL
);
1287 if ((addr
[n
++] = avail
) == '\r')
1292 n
= read (R_BX
, addr
, R_CX
);
1295 return (READ_FAULT
);
1307 write_or_truncate(int fd
, char *addr
, int len
)
1312 offset
= lseek(fd
, 0, SEEK_CUR
);
1316 return ftruncate(fd
, offset
);
1318 return write(fd
, addr
, len
);
1323 int21_40(regcontext_t
*REGS
)
1328 addr
= (char *)MAKEPTR(R_DS
, R_DX
);
1331 debug(D_FILE_OPS
, "write(%d, %d)\n", R_BX
, nbytes
);
1336 n
= write_or_truncate(R_BX
, addr
, nbytes
);
1340 while (nbytes
-- > 0)
1341 tty_write(*addr
++, -1);
1345 n
= write_or_truncate(R_BX
, addr
, nbytes
);
1349 while (nbytes
-- > 0)
1350 tty_write(*addr
++, -1);
1354 n
= write_or_truncate(R_BX
, addr
, nbytes
);
1358 while (nbytes
-- > 0)
1359 tty_write(*addr
++, -1);
1362 n
= write_or_truncate(R_BX
, addr
, nbytes
);
1366 return (WRITE_FAULT
);
1378 int21_42(regcontext_t
*REGS
)
1383 offset
= (off_t
) ((int) (R_CX
<< 16) + R_DX
);
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) {
1402 return (HANDLE_INVALID
);
1404 return (SEEK_ERROR
);
1407 R_DX
= (offset
>> 16) & 0xffff;
1408 R_AX
= offset
& 0xffff;
1415 ** get/set attributes
1418 int21_43(regcontext_t
*REGS
)
1421 char fname
[PATH_MAX
];
1426 error
= translate_filename((u_char
*)MAKEPTR(R_DS
, R_DX
), fname
, &drive
);
1430 debug(D_FILE_OPS
, "get/set attributes: %s, cx=%x, al=%d\n",
1433 if (stat(fname
, &sb
) < 0) {
1434 debug(D_FILE_OPS
, "stat failed for %s\n", fname
);
1435 return (FILE_NOT_FOUND
);
1439 case 0: /* get attributes */
1441 if (dos_readonly(drive
) || access(fname
, W_OK
))
1443 if (S_ISDIR(sb
.st_mode
))
1448 case 1: /* set attributes - XXX ignored */
1450 return (ACCESS_DENIED
);
1454 return (FUNC_NUM_IVALID
);
1462 ** ioctl - get device info
1464 ** XXX it would be nice to detect EOF.
1467 int21_44_0(regcontext_t
*REGS
)
1469 debug(D_FILE_OPS
, "ioctl get %d\n", R_BX
);
1473 R_DX
= 0x80 | 0x01; /* is device, is standard output */
1476 R_DX
= 0x80 | 0x02; /* is device, is standard input */
1479 R_DX
= 0x80; /* is device */
1483 R_DX
= 0x80; /* is a device */
1485 R_DX
= 0; /* is a file */
1494 ** ioctl - set device info
1497 int21_44_1(regcontext_t
*REGS
)
1499 debug(D_FILE_OPS
, "ioctl set device info %d flags %x (ignored)\n",
1507 ** Get output status
1510 int21_44_7(regcontext_t
*REGS
)
1512 /* XXX Should really check to see if BX is open or not */
1520 ** test for removable block device
1523 int21_44_8(regcontext_t
*REGS
)
1525 R_AX
= 1; /* fixed */
1532 ** test for remote device (disallow direct I/O)
1535 int21_44_9(regcontext_t
*REGS
)
1537 R_DX
= 0x1200; /* disk is remote, direct I/O not allowed */
1547 int21_45(regcontext_t
*REGS
)
1551 debug(D_FILE_OPS
, "dup(%d)\n", R_BX
);
1553 if ((nfd
= dup(R_BX
)) < 0) {
1555 return (HANDLE_INVALID
);
1557 return (TOO_MANY_OPEN_FILES
);
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
);
1577 return (HANDLE_INVALID
);
1588 int21_47(regcontext_t
*REGS
)
1597 p
= (char *)dos_getcwd(n
) + 1;
1598 addr
= (char *)MAKEPTR(R_DS
, R_SI
);
1604 memcpy(addr
, p
, nbytes
);
1615 int21_48(regcontext_t
*REGS
)
1619 memseg
= mem_alloc(R_BX
, pspseg
, &avail
);
1636 int21_49(regcontext_t
*REGS
)
1638 if (mem_adjust(R_ES
, 0, NULL
) < 0)
1639 return (MEM_BLK_ADDR_IVALID
);
1646 ** resize memory block
1649 int21_4a(regcontext_t
*REGS
)
1653 if ((n
= mem_adjust(R_ES
, R_BX
, &avail
)) < 0) {
1658 return (MEM_BLK_ADDR_IVALID
);
1671 int21_4b(regcontext_t
*REGS
)
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
);
1685 param
= (u_short
*)MAKEPTR(R_ES
, R_BX
);
1688 case 0x00: /* load and execute */
1689 exec_command(REGS
, 1, fd
, cmdname
, param
);
1693 case 0x01: /* just load */
1694 exec_command(REGS
, 0, fd
, cmdname
, param
);
1698 case 0x03: /* load overlay */
1699 load_overlay(fd
, param
[0], param
[1]);
1704 unknown_int3(0x21, 0x4b, R_AL
, REGS
);
1705 return (FUNC_NUM_IVALID
);
1716 int21_4c(regcontext_t
*REGS
)
1718 return_status
= R_AL
;
1726 ** get return code of child
1729 int21_4d(regcontext_t
*REGS
)
1731 R_AX
= return_status
;
1739 ** find first, find next
1742 int21_find(regcontext_t
*REGS
)
1748 dta
= (find_block_t
*)VECPTR(disk_transfer_addr
);
1751 case 0x4e: /* find first */
1752 error
= find_first((u_char
*)MAKEPTR(R_DS
, R_DX
), R_CX
, &dosdir
, dta
);
1755 error
= find_next(&dosdir
, dta
);
1758 fatal("called int21_find for unknown function %x\n",R_AH
);
1761 dosdir_to_dta(&dosdir
, dta
);
1773 int21_50(regcontext_t
*REGS
)
1782 ** get mtime for handle
1785 int21_57_0(regcontext_t
*REGS
)
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
);
1801 ** set mtime for handle
1804 int21_57_1(regcontext_t
*REGS __unused
)
1806 #ifdef __NetBSD__ /* XXX need futimes() */
1808 struct timeval tv
[2];
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
);
1825 ** get/set memory strategy
1826 ** get/set UMB link state
1829 int21_58(regcontext_t
*REGS
)
1832 case 0x00: /* get memory strategy */
1833 R_AX
= memory_strategy
;
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;
1840 case 0x02: /* get UMB link state */
1841 R_AL
= 0; /* UMBs not in link chain */
1843 default: /* includes set, which is invalid */
1844 unknown_int3(0x21, 0x58, R_AL
, REGS
);
1845 return (FUNC_NUM_IVALID
);
1853 ** get extended error information
1856 int21_59(regcontext_t
*REGS
)
1868 R_BH
= 7; /* application error */
1875 R_BH
= 8; /* not found */
1880 R_BH
= 1; /* out of resource */
1884 R_BH
= 12; /* already exists */
1887 R_BL
= 6; /* always ignore! */
1888 R_CH
= 1; /* unknown/inappropriate */
1895 ** create temporary file
1898 int21_5a(regcontext_t
*REGS
)
1900 char fname
[PATH_MAX
];
1907 /* get and check proposed path */
1908 pname
= (char *)MAKEPTR(R_DS
, R_DX
);
1909 error
= translate_filename(pname
, fname
, &drive
);
1913 debug(D_FILE_OPS
, "tempname(%s)\n", fname
);
1915 if (dos_readonly(drive
))
1916 return (WRITE_PROT_DISK
);
1919 strcat(fname
,"__dostmp.XXX");
1920 fd
= mkstemp(fname
);
1922 return (ACCESS_DENIED
);
1924 strcat(pname
, fname
+ n
); /* give back the full name */
1932 ** canonicalise name
1935 int21_60(regcontext_t
*REGS
)
1937 return(dos_makepath((char *)MAKEPTR(R_DS
, R_SI
),
1938 (char *)MAKEPTR(R_ES
, R_DI
)));
1947 int21_62(regcontext_t
*REGS
)
1957 ** (mostly for humour value 8)
1960 int21_65_23(regcontext_t
*REGS
)
1963 case 'n': /* no, nein, non, nyet */
1977 default: /* maybe */
1988 ** fflush/commit file
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
);
2000 /******************************************************************************
2001 ** 21:0f 21:10 21:11 21:12 21:16 21:27 21:28:21:29
2006 openfcb(struct fcb
*fcbp
)
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
);
2017 encode_dos_file_time(statb
.st_mtime
,
2018 &fcbp
->fcbFileDate
, &fcbp
->fcbFileTime
);
2019 fcbp
->fcbFileSize
= statb
.st_size
;
2023 getfcb_rec(struct fcb
*fcbp
, int nrec
)
2027 n
= fcbp
->fcbRandomRecNo
;
2028 if (fcbp
->fcbRecSize
>= 64)
2030 fcbp
->fcbCurRecNo
= n
% 128;
2031 fcbp
->fcbCurBlockNo
= n
/ 128;
2032 if (lseek(fcbp
->fcb_fd
, n
* fcbp
->fcbRecSize
, SEEK_SET
) < 0)
2034 return (nrec
* fcbp
->fcbRecSize
);
2039 setfcb_rec(struct fcb
*fcbp
, int n
)
2043 total
= fcbp
->fcbRandomRecNo
;
2044 if (fcbp
->fcbRecSize
>= 64)
2046 recs
= (n
+fcbp
->fcbRecSize
-1) / fcbp
->fcbRecSize
;
2049 fcbp
->fcbRandomRecNo
= total
;
2050 fcbp
->fcbCurRecNo
= total
% 128;
2051 fcbp
->fcbCurBlockNo
= total
/ 128;
2057 fcb_to_string(fcbp
, buf
)
2062 if (fcbp
->fcbDriveID
!= 0x00) {
2063 *buf
++ = drntol(fcbp
->fcbDriveID
- 1);
2066 pack_name(fcbp
->fcbFileName
, buf
);
2071 int21_fcb(regcontext_t
*REGS
)
2074 char fname
[PATH_MAX
];
2086 fcbp
= (struct fcb
*)MAKEPTR(R_DS
, R_DX
);
2090 case 0x0f: /* open file with FCB */
2091 fcb_to_string(fcbp
, buf
);
2092 error
= translate_filename(buf
, fname
, &drive
);
2096 debug(D_FILE_OPS
, "open FCB(%s)\n", fname
);
2098 if (ustat(fname
, &sb
) < 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
);
2111 return (ACCESS_DENIED
);
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
);
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
);
2137 dosdir_to_dta(&dosdir
, dta
);
2141 case 0x12: /* find_next with FCB */
2142 dta
= (find_block_t
*)VECPTR(disk_transfer_addr
);
2144 error
= find_next(&dosdir
, dta
);
2148 dosdir_to_dta(&dosdir
, dta
);
2152 case 0x16: /* create file with FCB */
2153 fcb_to_string(fcbp
, buf
);
2154 error
= translate_filename(buf
, fname
, &drive
);
2158 debug(D_FILE_OPS
, "creat FCB(%s)\n", fname
);
2160 if (ustat(fname
, &sb
) < 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
);
2177 case 0x27: /* random block read */
2178 addr
= (u_char
*)VECPTR(disk_transfer_addr
);
2179 nbytes
= getfcb_rec(fcbp
, R_CX
);
2182 return (READ_FAULT
);
2183 n
= read(fcbp
->fcb_fd
, addr
, nbytes
);
2185 return (READ_FAULT
);
2186 R_CX
= setfcb_rec(fcbp
, n
);
2188 nbytes
= n
% fcbp
->fcbRecSize
;
2192 bzero(addr
+ n
, fcbp
->fcbRecSize
- nbytes
);
2200 case 0x28: /* random block write */
2201 addr
= (u_char
*)VECPTR(disk_transfer_addr
);
2202 nbytes
= getfcb_rec(fcbp
, R_CX
);
2205 return (WRITE_FAULT
);
2206 n
= write(fcbp
->fcb_fd
, addr
, nbytes
);
2208 return (WRITE_FAULT
);
2209 R_CX
= setfcb_rec(fcbp
, n
);
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
),
2224 debug(D_FILE_OPS
, "%d %s, FCB: %d, %.11s\n",
2226 (char *)MAKEPTR(R_DS
, R_SI
),
2227 *(int *)MAKEPTR(R_ES
, R_DI
),
2228 (char *)MAKEPTR(R_ES
, R_DI
) + 1);
2234 fatal("called int21_fcb with unknown function %x\n",R_AH
);
2244 ** network functions
2248 int21_net(regcontext_t
*REGS
)
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");
2260 case 0x09: /* Flush redirected printer output */
2263 unknown_int3(0x21, 0x5d, R_AL
, REGS
);
2264 return (FUNC_NUM_IVALID
);
2270 unknown_int2(0x21, R_AH
, REGS
);
2271 return (FUNC_NUM_IVALID
);
2273 fatal("called int21_net with unknown function %x\n",R_AH
);
2281 ** Unknown/unsupported
2284 int21_NOFUNC(regcontext_t
*REGS
)
2286 unknown_int2(0x21, R_AH
, REGS
);
2287 return (FUNC_NUM_IVALID
);
2293 ** Null function; no error, no action
2296 int21_NULLFUNC(regcontext_t
*REGS
)
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"},
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
[] = {
2438 "TOO_MANY_OPEN_FILES",
2443 "MEM_BLK_ADDR_IVALID",
2446 "ACCESS_CODE_INVALID",
2449 "DISK_DRIVE_INVALID",
2454 "UNKNOWN_UNIT_CERR",
2458 "BAD_REQ_STRUCT_LEN",
2460 "UNKNOWN_MEDIA_TYPE",
2462 "PRINTER_OUT_OF_PAPER",
2468 const int dos_ret_size
= (sizeof(dos_return
) / sizeof(char *));
2471 ** for want of anywhere better to go
2474 int20(regcontext_t
*REGS
)
2476 /* int 20 = exit(0) */
2481 int29(regcontext_t
*REGS
)
2483 tty_write(R_AL
, TTYF_REDIRECT
);
2486 /******************************************************************************
2487 ** entrypoint for MS-DOS functions
2490 int21(regcontext_t
*REGS
)
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 */
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");
2515 /* XXX is this entirely legitimate? */
2527 int67(regcontext_t
*REGS
)
2532 static u_char upcase_trampoline
[] = {
2538 ** initialise thyself
2546 vec
= insert_softint_trampoline();
2548 register_callback(vec
, int20
, "int 20");
2550 vec
= insert_softint_trampoline();
2552 register_callback(vec
, int21
, "int 21");
2554 vec
= insert_softint_trampoline();
2556 register_callback(vec
, int29
, "int 29");
2558 vec
= insert_softint_trampoline();
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
);