Release 940804
[wine/gsoc-2012-control.git] / miscemu / int21.c
blob2485f59a607bd38f7325e5648b2a155edda384c7
1 /*
2 * (c) 1993, 1994 Erik Bos
3 */
5 #include <time.h>
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <utime.h>
14 #include "prototypes.h"
15 #include "regfunc.h"
16 #include "windows.h"
17 #include "wine.h"
18 #include "msdos.h"
19 #include "options.h"
21 WORD ExtendedError, CodePage = 437;
22 BYTE ErrorClass, Action, ErrorLocus;
23 BYTE *dta;
25 struct DosHeap {
26 BYTE dta[256];
27 BYTE InDosFlag;
28 BYTE biosdate[8];
30 static struct DosHeap *heap;
32 extern char TempDirectory[];
34 static int Error(int e, int class, int el)
36 ErrorClass = class;
37 Action = SA_Ask4Retry;
38 ErrorLocus = el;
39 ExtendedError = e;
41 return e;
44 static void errno_to_doserr(void)
46 switch (errno) {
47 case EAGAIN:
48 Error (ShareViolation, EC_Temporary, EL_Unknown);
49 break;
50 case EBADF:
51 Error (InvalidHandle, EC_AppError, EL_Unknown);
52 break;
53 case ENOSPC:
54 Error (DiskFull, EC_MediaError, EL_Disk);
55 break;
56 case EACCES:
57 case EPERM:
58 case EROFS:
59 Error (WriteProtected, EC_AccessDenied, EL_Unknown);
60 break;
61 case EBUSY:
62 Error (LockViolation, EC_AccessDenied, EL_Unknown);
63 break;
64 case ENOENT:
65 Error (FileNotFound, EC_NotFound, EL_Unknown);
66 break;
67 case EISDIR:
68 Error (CanNotMakeDir, EC_AccessDenied, EL_Unknown);
69 break;
70 case ENFILE:
71 case EMFILE:
72 Error (NoMoreFiles, EC_MediaError, EL_Unknown);
73 break;
74 case EEXIST:
75 Error (FileExists, EC_Exists, EL_Disk);
76 break;
77 default:
78 fprintf(stderr, "int21: unknown errno %d!\n", errno);
79 Error (GeneralFailure, EC_SystemFailure, EL_Unknown);
80 break;
84 static void Barf(struct sigcontext_struct *context)
86 fprintf(stderr, "int21: unknown/not implemented parameters:\n");
87 fprintf(stderr, "int21: AX %04x, BX %04x, CX %04x, DX %04x, "
88 "SI %04x, DI %04x, DS %04x, ES %04x\n",
89 AX, BX, CX, DX, SI, DI, DS, ES);
92 void ChopOffWhiteSpace(char *string)
94 int length;
96 for (length = strlen(string) ; length ; length--)
97 if (string[length] == ' ')
98 string[length] = '\0';
101 static void CreateBPB(int drive, BYTE *data)
103 if (drive > 1) {
104 setword(data, 512);
105 data[2] = 2;
106 setword(&data[3], 0);
107 data[5] = 2;
108 setword(&data[6], 240);
109 setword(&data[8], 64000);
110 data[0x0a] = 0xf8;
111 setword(&data[0x0b], 40);
112 setword(&data[0x0d], 56);
113 setword(&data[0x0f], 2);
114 setword(&data[0x11], 0);
115 setword(&data[0x1f], 800);
116 data[0x21] = 5;
117 setword(&data[0x22], 1);
118 } else { /* 1.44mb */
119 setword(data, 512);
120 data[2] = 2;
121 setword(&data[3], 0);
122 data[5] = 2;
123 setword(&data[6], 240);
124 setword(&data[8], 2880);
125 data[0x0a] = 0xf8;
126 setword(&data[0x0b], 6);
127 setword(&data[0x0d], 18);
128 setword(&data[0x0f], 2);
129 setword(&data[0x11], 0);
130 setword(&data[0x1f], 80);
131 data[0x21] = 7;
132 setword(&data[0x22], 2);
136 static void GetFreeDiskSpace(struct sigcontext_struct *context)
138 int drive;
139 long size,available;
141 if (!(EDX & 0xff))
142 drive = DOS_GetDefaultDrive();
143 else
144 drive = (EDX & 0xff) - 1;
146 if (!DOS_ValidDrive(drive)) {
147 Error(InvalidDrive, EC_MediaError , EL_Disk);
148 EAX |= 0xffffL;
149 return;
152 if (!DOS_GetFreeSpace(drive, &size, &available)) {
153 Error(GeneralFailure, EC_MediaError , EL_Disk);
154 EAX |= 0xffffL;
155 return;
158 EAX = (EAX & 0xffff0000) | 4;
159 ECX = (ECX & 0xffff0000) | 512;
161 EBX = (EBX & 0xffff0000) | (available / (CX * AX));
162 EDX = (EDX & 0xffff0000) | (size / (CX * AX));
163 Error (0,0,0);
166 static void GetDriveAllocInfo(struct sigcontext_struct *context)
168 int drive;
169 long size, available;
170 BYTE mediaID;
172 drive = EDX & 0xffL;
174 if (!DOS_ValidDrive(drive)) {
175 EAX = (EAX & 0xffff0000) | 4;
176 ECX = (ECX & 0xffff0000) | 512;
177 EDX = (EDX & 0xffff0000);
178 Error (InvalidDrive, EC_MediaError, EL_Disk);
179 return;
182 if (!DOS_GetFreeSpace(drive, &size, &available)) {
183 Error(GeneralFailure, EC_MediaError , EL_Disk);
184 EAX |= 0xffffL;
185 return;
188 EAX = (EAX & 0xffff0000) | 4;
189 ECX = (ECX & 0xffff0000) | 512;
190 EDX = (EDX & 0xffff0000) | (size / (CX * AX));
192 mediaID = 0xf0;
194 DS = segment(mediaID);
195 EBX = offset(mediaID);
196 Error (0,0,0);
199 static void GetDefDriveAllocInfo(struct sigcontext_struct *context)
201 EDX = DOS_GetDefaultDrive();
202 GetDriveAllocInfo(context);
205 static void GetDrivePB(struct sigcontext_struct *context)
207 Error (InvalidDrive, EC_MediaError, EL_Disk);
208 EAX = (EAX & 0xffff0000) | 0xffL;
209 /* I'm sorry but I only got networked drives :-) */
212 static void ReadFile(struct sigcontext_struct *context)
214 char *ptr;
215 int size;
217 /* can't read from stdout / stderr */
218 if ((BX == 1) || (BX == 2)) {
219 Error (InvalidHandle, EL_Unknown, EC_Unknown);
220 EAX = (EAX & 0xffff0000) | InvalidHandle;
221 SetCflag;
222 return;
225 ptr = pointer (DS,DX);
226 if (BX == 0) {
227 *ptr = EOF;
228 Error (0,0,0);
229 EAX = (EAX & 0xffff0000) | 1;
230 ResetCflag;
231 return;
232 } else {
233 size = read(BX, ptr, CX);
234 if (size == -1) {
235 errno_to_doserr();
236 EAX = (EAX & 0xffffff00) | ExtendedError;
237 SetCflag;
238 return;
240 Error (0,0,0);
241 EAX = (EAX & 0xffff0000) | size;
242 ResetCflag;
246 static void WriteFile(struct sigcontext_struct *context)
248 char *ptr;
249 int x,size;
251 ptr = pointer (DS,DX);
253 if (BX == 0) {
254 Error (InvalidHandle, EC_Unknown, EL_Unknown);
255 EAX = InvalidHandle;
256 SetCflag;
257 return;
260 if (BX < 3) {
261 for (x = 0;x != CX;x++) {
262 fprintf(stderr, "%c", *ptr++);
264 fflush(stderr);
266 Error (0,0,0);
267 EAX = (EAX & 0xffffff00) | CX;
268 ResetCflag;
269 } else {
270 size = write(BX, ptr , CX);
271 if (size == 0) {
272 Error (WriteFault, EC_Unknown, EL_Unknown);
273 EAX = (EAX & 0xffffff00) | ExtendedError;
274 return;
277 if (size == -1) {
278 errno_to_doserr();
279 EAX = (EAX & 0xffffff00) | ExtendedError;
280 SetCflag;
281 return;
283 Error (0,0,0);
284 EAX = (EAX & 0xffff0000) | size;
285 ResetCflag;
289 static void SeekFile(struct sigcontext_struct *context)
291 off_t status, fileoffset;
293 switch (EAX & 0xff) {
294 case 1: fileoffset = SEEK_CUR;
295 break;
296 case 2: fileoffset = SEEK_END;
297 break;
298 default:
299 case 0: fileoffset = SEEK_SET;
300 break;
302 status = lseek(BX, (CX * 0x100) + DX, fileoffset);
303 if (status == -1) {
304 errno_to_doserr();
305 EAX = (EAX & 0xffffff00) | ExtendedError; SetCflag;
306 return;
308 Error (0,0,0);
309 EAX = (EAX & 0xffff0000L) | (status & 0xffff);
310 EDX = (EDX & 0xffff0000L) | ((status >> 16) & 0xffff);
312 ResetCflag;
315 static void ioctlGetDeviceInfo(struct sigcontext_struct *context)
317 WORD handle = EBX & 0xffff;
319 switch (handle) {
320 case 0:
321 case 1:
322 case 2:
323 EDX = (EDX & 0xffff0000) | 0x80d3;
324 break;
326 default:
328 struct stat sbuf;
330 if (fstat(handle, &sbuf) < 0)
332 IntBarf(0x21, context);
333 EDX = (EDX & 0xffff0000) | 0x50;
334 SetCflag;
335 return;
338 /* This isn't the right answer, but should be close enough. */
339 EDX = (EDX & 0xffff0000) | 0x0943;
342 ResetCflag;
345 static void ioctlGenericBlkDevReq(struct sigcontext_struct *context)
347 BYTE *dataptr = pointer(DS, DX);
348 int drive;
350 if (!(EBX & 0xff))
351 drive = DOS_GetDefaultDrive();
352 else
353 drive = (EBX & 0xff) - 1;
355 if ((ECX & 0xff00) != 0x0800) {
356 IntBarf(0x21, context);
357 return;
359 switch (ECX & 0xff) {
360 case 0x60: /* get device parameters */
361 /* used by w4wgrp's winfile */
362 dataptr[0] = 0x04;
363 dataptr[6] = 0; /* media type */
364 if (drive > 1)
366 dataptr[1] = 0x05; /* fixed disk */
367 setword(&dataptr[2], 0x01); /* non removable */
368 setword(&dataptr[4], 0x300); /* # of cylinders */
370 else
372 dataptr[1] = 0x07; /* block dev, floppy */
373 setword(&dataptr[2], 0x02); /* removable */
374 setword(&dataptr[4], 80); /* # of cylinders */
376 CreateBPB(drive, &dataptr[7]);
377 EAX = (EAX & 0xfffff00);
378 ResetCflag;
379 return;
380 default:
381 IntBarf(0x21, context);
385 static void GetSystemDate(struct sigcontext_struct *context)
387 struct tm *now;
388 time_t ltime;
390 ltime = time(NULL);
391 now = localtime(&ltime);
393 ECX = (ECX & 0xffff0000) | (now->tm_year + 1900);
394 EDX = (EDX & 0xffff0000) | ((now->tm_mon + 1) << 8) | now->tm_mday;
395 EAX = (EAX & 0xffff0000) | now->tm_wday;
398 static void GetSystemTime(struct sigcontext_struct *context)
400 struct tm *now;
401 time_t ltime;
403 ltime = time(NULL);
404 now = localtime(&ltime);
406 ECX = (ECX & 0xffffff00) | (now->tm_hour << 8) | now->tm_min;
407 EDX = (EDX & 0xffffff00) | now->tm_sec << 8;
410 static void GetExtendedErrorInfo(struct sigcontext_struct *context)
412 EAX = (EAX & 0xffffff00) | ExtendedError;
413 EBX = (EBX & 0xffff0000) | (ErrorClass << 8) | Action;
414 ECX = (ECX & 0xffff00ff) | (ErrorLocus << 8);
417 static void CreateFile(struct sigcontext_struct *context)
419 int handle;
421 if ((handle = open(GetUnixFileName( pointer(DS,DX)), O_CREAT | O_TRUNC)) == -1) {
422 errno_to_doserr();
423 EAX = (EAX & 0xffffff00) | ExtendedError;
424 SetCflag;
425 return;
427 Error (0,0,0);
428 EBX = (EBX & 0xffff0000) | handle;
429 EAX = (EAX & 0xffffff00) | NoError;
430 ResetCflag;
433 static void OpenExistingFile(struct sigcontext_struct *context)
435 int handle;
436 int mode;
438 switch (AX & 0x0007)
440 case 0:
441 mode = O_RDONLY;
442 break;
444 case 1:
445 mode = O_WRONLY;
446 break;
448 default:
449 mode = O_RDWR;
450 break;
453 fprintf(stderr,"OpenExistingFile (%s)\n", pointer(DS,DX));
455 if ((handle = open(GetUnixFileName(pointer(DS,DX)), mode)) == -1) {
456 errno_to_doserr();
457 EAX = (EAX & 0xffffff00) | ExtendedError;
458 SetCflag;
459 return;
461 Error (0,0,0);
462 EAX = (EBX & 0xffff0000) | handle;
463 ResetCflag;
466 static void CloseFile(struct sigcontext_struct *context)
468 if (close(BX) == -1) {
469 errno_to_doserr();
470 EAX = (EAX & 0xffffff00) | ExtendedError;
471 SetCflag;
472 return;
474 Error (0,0,0);
475 EAX = (EAX & 0xffffff00) | NoError;
476 ResetCflag;
479 static void RenameFile(struct sigcontext_struct *context)
481 char *newname, *oldname;
483 fprintf(stderr,"int21: renaming %s to %s\n",
484 pointer(DS,DX), pointer(ES,DI) );
486 oldname = GetUnixFileName( pointer(DS,DX) );
487 newname = GetUnixFileName( pointer(ES,DI) );
489 rename( oldname, newname);
490 ResetCflag;
494 static void MakeDir(struct sigcontext_struct *context)
496 char *dirname;
498 fprintf(stderr,"int21: makedir %s\n", pointer(DS,DX) );
500 if ((dirname = GetUnixFileName( pointer(DS,DX) ))== NULL) {
501 EAX = (EAX & 0xffffff00) | CanNotMakeDir;
502 SetCflag;
503 return;
506 if (mkdir(dirname,0) == -1) {
507 EAX = (EAX & 0xffffff00) | CanNotMakeDir;
508 SetCflag;
509 return;
511 ResetCflag;
514 static void ChangeDir(struct sigcontext_struct *context)
516 int drive;
517 char *dirname = pointer(DS,DX);
518 drive = DOS_GetDefaultDrive();
519 fprintf(stderr,"int21: changedir %s\n", dirname);
520 if (dirname != NULL && dirname[1] == ':') {
521 drive = toupper(dirname[0]) - 'A';
522 dirname += 2;
524 DOS_ChangeDir(drive, dirname);
527 static void RemoveDir(struct sigcontext_struct *context)
529 char *dirname;
531 fprintf(stderr,"int21: removedir %s\n", pointer(DS,DX) );
533 if ((dirname = GetUnixFileName( pointer(DS,DX) ))== NULL) {
534 EAX = (EAX & 0xffffff00) | CanNotMakeDir;
535 SetCflag;
536 return;
540 if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) {
541 EAX = (EAX & 0xffffff00) | CanNotRemoveCwd;
542 SetCflag;
545 if (rmdir(dirname) == -1) {
546 EAX = (EAX & 0xffffff00) | CanNotMakeDir;
547 SetCflag;
549 ResetCflag;
552 static void ExecProgram(struct sigcontext_struct *context)
554 execl("wine", GetUnixFileName( pointer(DS,DX)) );
557 static void FindNext(struct sigcontext_struct *context)
559 struct dosdirent *dp;
561 dp = (struct dosdirent *)(dta + 0x0d);
563 do {
564 if ((dp = DOS_readdir(dp)) == NULL) {
565 Error(NoMoreFiles, EC_MediaError , EL_Disk);
566 EAX = (EAX & 0xffffff00) | NoMoreFiles;
567 SetCflag;
568 return;
570 } while (*(dta + 0x0c) != dp->attribute);
572 setword(&dta[0x16], 0x1234); /* time */
573 setword(&dta[0x18], 0x1234); /* date */
574 setdword(&dta[0x1a], dp->filesize);
575 strncpy(dta + 0x1e, dp->filename, 13);
577 EAX = (EAX & 0xffffff00);
578 ResetCflag;
579 return;
582 static void FindFirst(struct sigcontext_struct *context)
584 BYTE drive, *path = pointer(DS, DX);
585 struct dosdirent *dp;
587 if (path[1] == ':') {
588 drive = (islower(*path) ? toupper(*path) : *path) - 'A';
590 if (!DOS_ValidDrive(drive)) {
591 Error(InvalidDrive, EC_MediaError , EL_Disk);
592 EAX = (EAX & 0xffffff00L) | InvalidDrive;
593 SetCflag;
594 return;
596 } else
597 drive = DOS_GetDefaultDrive();
599 *dta = drive;
600 memset(dta + 1 , '?', 11);
601 *(dta + 0x0c) = ECX & (FA_LABEL | FA_DIREC);
603 if (ECX & FA_LABEL) {
604 /* return volume label */
606 if (DOS_GetVolumeLabel(drive) != NULL)
607 strncpy(dta + 0x1e, DOS_GetVolumeLabel(drive), 8);
609 EAX = (EAX & 0xffffff00L);
610 ResetCflag;
611 return;
614 if ((dp = DOS_opendir(path)) == NULL) {
615 Error(PathNotFound, EC_MediaError, EL_Disk);
616 EAX = (EAX & 0xffffff00L) | FileNotFound;
617 SetCflag;
618 return;
621 memcpy(dta + 0x0d, &dp, sizeof(dp));
622 FindNext(context);
625 static void GetFileDateTime(struct sigcontext_struct *context)
627 char *filename;
628 struct stat filestat;
629 struct tm *now;
631 if ((filename = GetUnixFileName( pointer(DS,DX) ))== NULL) {
632 EAX = (EAX & 0xffffff00) | FileNotFound;
633 SetCflag;
634 return;
636 stat(filename, &filestat);
638 now = localtime (&filestat.st_mtime);
640 ECX = (ECX & 0xffff0000) | ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2);
641 EDX = (EDX & 0xffff0000) | ((now->tm_year * 0x200) + (now->tm_mon * 0x20) + now->tm_mday);
643 ResetCflag;
646 static void SetFileDateTime(struct sigcontext_struct *context)
648 char *filename;
649 struct utimbuf filetime;
651 filename = GetUnixFileName( pointer(DS,DX) );
653 filetime.actime = 0L;
654 filetime.modtime = filetime.actime;
656 utime(filename, &filetime);
657 ResetCflag;
660 static void CreateTempFile(struct sigcontext_struct *context)
662 char temp[256];
663 int handle;
665 sprintf(temp,"%s\\win%d.tmp",TempDirectory,(int) getpid());
667 fprintf(stderr,"CreateTempFile %s\n",temp);
669 handle = open(GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR);
671 if (handle == -1) {
672 EAX = (EAX & 0xffffff00) | WriteProtected;
673 SetCflag;
674 return;
677 strcpy(pointer(DS,DX), temp);
679 EAX = (EAX & 0xffff0000) | handle;
680 ResetCflag;
683 static void CreateNewFile(struct sigcontext_struct *context)
685 int handle;
687 if ((handle = open(GetUnixFileName( pointer(DS,DX) ), O_CREAT | O_TRUNC | O_RDWR)) == -1) {
688 EAX = (EAX & 0xffffff00) | WriteProtected;
689 SetCflag;
690 return;
693 EAX = (EAX & 0xffff0000) | handle;
694 ResetCflag;
697 static void GetCurrentDirectory(struct sigcontext_struct *context)
699 int drive;
701 if ((EDX & 0xff) == 0)
702 drive = DOS_GetDefaultDrive();
703 else
704 drive = (EDX & 0xff)-1;
706 if (!DOS_ValidDrive(drive)) {
707 EAX = (EAX & 0xffffff00) | InvalidDrive;
708 SetCflag;
709 return;
712 strcpy(pointer(DS,SI), DOS_GetCurrentDir(drive) );
713 ResetCflag;
716 static void GetDiskSerialNumber(struct sigcontext_struct *context)
718 int drive;
719 BYTE *dataptr = pointer(DS, DX);
720 DWORD serialnumber;
722 if ((EBX & 0xff) == 0)
723 drive = DOS_GetDefaultDrive();
724 else
725 drive = (EBX & 0xff) - 1;
727 if (!DOS_ValidDrive(drive)) {
728 EAX = (EAX & 0xffffff00) |InvalidDrive;
729 SetCflag;
730 return;
733 DOS_GetSerialNumber(drive, &serialnumber);
735 setword(dataptr, 0);
736 setdword(&dataptr[2], serialnumber);
737 strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8);
738 strncpy(dataptr + 0x11, "FAT16 ", 8);
740 EAX = (EAX & 0xffffff00);
741 ResetCflag;
744 static void SetDiskSerialNumber(struct sigcontext_struct *context)
746 int drive;
747 BYTE *dataptr = pointer(DS, DX);
748 DWORD serialnumber;
750 if ((EBX & 0xff) == 0)
751 drive = DOS_GetDefaultDrive();
752 else
753 drive = (EBX & 0xff) - 1;
755 if (!DOS_ValidDrive(drive)) {
756 EAX = (EAX & 0xffffff00) | InvalidDrive;
757 SetCflag;
758 return;
761 serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) +
762 (dataptr[4] << 24);
764 DOS_SetSerialNumber(drive, serialnumber);
765 EAX = (EAX & 0xffffff00) | 1L;
766 ResetCflag;
769 static void DumpFCB(BYTE *fcb)
771 int x, y;
773 fcb -= 7;
775 for (y = 0; y !=2 ; y++) {
776 for (x = 0; x!=15;x++)
777 fprintf(stderr, "%02x ", *fcb++);
778 fprintf(stderr,"\n");
782 /* microsoft's programmers should be shot for using CP/M style int21
783 calls in Windows for Workgroup's winfile.exe */
785 static void FindFirstFCB(struct sigcontext_struct *context)
787 BYTE *fcb = pointer(DS, DX);
788 int drive;
790 DumpFCB( fcb );
792 if (*fcb)
793 drive = *fcb - 1;
794 else
795 drive = DOS_GetDefaultDrive();
797 if (*(fcb - 7) == 0xff) {
798 if (*(fcb - 1) == FA_DIREC) {
799 /* return volume label */
801 memset(dta, ' ', 11);
802 if (DOS_GetVolumeLabel(drive) != NULL)
803 strncpy(dta, DOS_GetVolumeLabel(drive), 8);
804 *(dta + 0x0b) = FA_DIREC;
806 EAX = (EAX & 0xffffff00);
807 return;
810 IntBarf(0x21, context);
813 static void DeleteFileFCB(struct sigcontext_struct *context)
815 BYTE *fcb = pointer(DS, DX);
816 int drive;
817 struct dosdirent *dp;
818 char temp[256], *ptr;
820 DumpFCB( fcb );
822 if (*fcb)
823 drive = *fcb - 1;
824 else
825 drive = DOS_GetDefaultDrive();
827 strcpy(temp, DOS_GetCurrentDir(drive));
828 strcat(temp, "\\");
829 strncat(temp, fcb + 1, 8);
830 ChopOffWhiteSpace(temp);
831 strncat(temp, fcb + 9, 3);
832 ChopOffWhiteSpace(temp);
834 if ((dp = DOS_opendir(temp)) == NULL) {
835 Error(InvalidDrive, EC_MediaError , EL_Disk);
836 EAX = (EAX & 0xffffff00) | 0xffL;
837 return;
840 strcpy(temp, DOS_GetCurrentDir(drive) );
841 strcat(temp, "\\");
843 ptr = temp + strlen(temp);
845 while (DOS_readdir(dp) != NULL)
847 strcpy(ptr, dp->filename);
848 fprintf(stderr, "int21: delete file %s\n", temp);
849 /* unlink(GetUnixFileName(temp)); */
851 DOS_closedir(dp);
852 EAX = (EAX & 0xffffff00);
855 static void RenameFileFCB(struct sigcontext_struct *context)
857 BYTE *fcb = pointer(DS, DX);
858 int drive;
859 struct dosdirent *dp;
860 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
862 DumpFCB( fcb );
864 if (*fcb)
865 drive = *fcb - 1;
866 else
867 drive = DOS_GetDefaultDrive();
869 strcpy(temp, DOS_GetCurrentDir(drive));
870 strcat(temp, "\\");
871 strncat(temp, fcb + 1, 8);
872 ChopOffWhiteSpace(temp);
873 strncat(temp, fcb + 9, 3);
874 ChopOffWhiteSpace(temp);
876 if ((dp = DOS_opendir(temp)) == NULL) {
877 Error(InvalidDrive, EC_MediaError , EL_Disk);
878 EAX = (EAX & 0xffffff00) | 0xffL;
879 return;
882 strcpy(oldname, DOS_GetCurrentDir(drive) );
883 strcat(oldname, "\\");
884 oldnameptr = oldname + strlen(oldname);
886 strcpy(newname, DOS_GetCurrentDir(drive) );
887 strcat(newname, "\\");
888 newnameptr = newname + strlen(newname);
890 while (DOS_readdir(dp) != NULL)
892 strcpy(oldnameptr, dp->filename);
893 strcpy(newnameptr, fcb + 1);
894 fprintf(stderr, "int21: renamefile %s -> %s\n", oldname, newname);
896 DOS_closedir(dp);
897 EAX = (EAX & 0xffffff00);
900 /************************************************************************/
902 int do_int21(struct sigcontext_struct * context)
904 int ah;
906 if (Options.relay_debug)
908 printf("int21: AX %04x, BX %04x, CX %04x, DX %04x, "
909 "SI %04x, DI %04x, DS %04x, ES %04x\n",
910 AX, BX, CX, DX, SI, DI, DS, ES);
913 ah = (EAX >> 8) & 0xffL;
915 if (ah == 0x59)
917 GetExtendedErrorInfo(context);
918 return 1;
920 else
922 Error (0,0,0);
923 switch(ah)
925 case 0x00: /* TERMINATE PROGRAM */
926 exit(0);
928 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
929 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
930 case 0x03: /* READ CHARACTER FROM STDAUX */
931 case 0x04: /* WRITE CHARACTER TO STDAUX */
932 case 0x05: /* WRITE CHARACTER TO PRINTER */
933 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
934 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
935 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
936 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
937 case 0x0a: /* BUFFERED INPUT */
938 case 0x0b: /* GET STDIN STATUS */
939 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
940 case 0x0f: /* OPEN FILE USING FCB */
941 case 0x10: /* CLOSE FILE USING FCB */
942 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
943 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
944 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
945 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
946 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
947 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
948 case 0x23: /* GET FILE SIZE FOR FCB */
949 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
950 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
951 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
952 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
953 case 0x29: /* PARSE FILENAME INTO FCB */
954 case 0x2e: /* SET VERIFY FLAG */
955 IntBarf(0x21, context);
956 break;
958 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
959 case 0x1d:
960 case 0x1e:
961 case 0x20:
962 case 0x2b: /* SET SYSTEM DATE */
963 case 0x2d: /* SET SYSTEM TIME */
964 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
965 "SWITCHAR" - SET SWITCH CHARACTER
966 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
967 case 0x54: /* GET VERIFY FLAG */
968 case 0x6b: /* NULL FUNCTION */
969 IntBarf(0x21, context);
970 EAX &= 0xff00;
971 break;
973 case 0x0d: /* DISK BUFFER FLUSH */
974 ResetCflag; /* dos 6+ only */
975 break;
977 case 0x0e: /* SELECT DEFAULT DRIVE */
978 if (!DOS_ValidDrive(EDX & 0xff)) {
979 Error (InvalidDrive, EC_MediaError, EL_Disk);
980 return;
981 } else {
982 DOS_SetDefaultDrive(EDX & 0xff);
983 EAX = (EAX &0xffffff00) | MAX_DOS_DRIVES;
984 Error(0,0,0);
986 break;
988 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
989 FindFirstFCB(context);
990 break;
992 case 0x13: /* DELETE FILE USING FCB */
993 DeleteFileFCB(context);
994 break;
996 case 0x17: /* RENAME FILE USING FCB */
997 RenameFileFCB(context);
998 break;
1000 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1001 EAX = (EAX & 0xffffff00) | DOS_GetDefaultDrive();
1002 Error (0,0,0);
1003 break;
1005 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1006 dta = pointer(DS, DX);
1007 break;
1009 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1010 GetDefDriveAllocInfo(context);
1011 break;
1013 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1014 GetDriveAllocInfo(context);
1015 break;
1017 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1018 GetDrivePB(context);
1019 break;
1021 case 0x25: /* SET INTERRUPT VECTOR */
1022 /* Ignore any attempt to set a segment vector */
1023 fprintf(stderr, "int21: set interrupt vector %2x (%04x:%04x)\n", AX & 0xff, DS, DX);
1024 break;
1026 case 0x2a: /* GET SYSTEM DATE */
1027 GetSystemDate(context);
1028 break;
1030 case 0x2c: /* GET SYSTEM TIME */
1031 GetSystemTime(context);
1032 break;
1034 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1035 ES = segment(dta);
1036 EBX = (EBX & 0xffff0000) | offset(dta);
1037 break;
1039 case 0x30: /* GET DOS VERSION */
1040 EAX = (EAX & 0xffff0000) | DOSVERSION;
1041 EBX = (EBX & 0xffff0000) | 0x0012; /* 0x123456 is Wine's serial # */
1042 ECX = (ECX & 0xffff0000) | 0x3456;
1043 break;
1045 case 0x31: /* TERMINATE AND STAY RESIDENT */
1046 IntBarf(0x21, context);
1047 break;
1049 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1050 GetDrivePB(context);
1051 break;
1053 case 0x33: /* MULTIPLEXED */
1054 switch (EAX & 0xff) {
1055 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1056 if (!(EAX & 0xff))
1057 EDX &= 0xff00L;
1058 break;
1060 case 0x01: /* SET EXTENDED BREAK STATE */
1061 break;
1063 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1064 EDX &= 0xff00L;
1065 break;
1067 case 0x05: /* GET BOOT DRIVE */
1068 EDX = (EDX & 0xff00) | 2;
1069 /* c: is Wine's bootdrive */
1070 break;
1072 case 0x06: /* GET TRUE VERSION NUMBER */
1073 EBX = DOSVERSION;
1074 EDX = 0x00;
1075 break;
1077 default:
1078 IntBarf(0x21, context);
1079 break;
1081 break;
1083 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1084 ES = (ES & 0xffff0000) | segment(heap->InDosFlag);
1085 EBX = (EBX & 0xffff0000) | offset(heap->InDosFlag);
1086 break;
1088 case 0x35: /* GET INTERRUPT VECTOR */
1089 /* Return a NULL segment selector - this will bomb,
1090 if anyone ever tries to use it */
1091 fprintf(stderr, "int21: get interrupt vector %2x\n", AX & 0xff);
1092 ES = 0;
1093 EBX = 0;
1094 break;
1096 case 0x36: /* GET FREE DISK SPACE */
1097 GetFreeDiskSpace(context);
1098 break;
1100 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1101 EAX &= 0xff00;
1102 EAX |= 0x02; /* no country support available */
1103 SetCflag;
1104 break;
1106 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1107 MakeDir(context);
1108 break;
1110 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1111 RemoveDir(context);
1112 break;
1114 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1115 ChangeDir(context);
1116 break;
1118 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1119 CreateFile(context);
1120 break;
1122 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1123 OpenExistingFile(context);
1124 break;
1126 case 0x3e: /* "CLOSE" - CLOSE FILE */
1127 CloseFile(context);
1128 break;
1130 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1131 ReadFile(context);
1132 break;
1134 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1135 WriteFile(context);
1136 break;
1138 case 0x41: /* "UNLINK" - DELETE FILE */
1139 if (unlink( GetUnixFileName( pointer(DS,DX)) ) == -1) {
1140 errno_to_doserr();
1141 EAX = (EAX & 0xffffff00) | ExtendedError;
1142 SetCflag;
1143 return;
1145 Error(0,0,0);
1146 ResetCflag;
1147 break;
1149 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1150 SeekFile(context);
1151 break;
1153 case 0x43: /* FILE ATTRIBUTES */
1154 switch (EAX & 0xff)
1156 case 0x00:
1157 EAX &= 0xfffff00L;
1158 ResetCflag;
1159 break;
1160 case 0x01:
1161 ResetCflag;
1162 break;
1164 break;
1166 case 0x44: /* IOCTL */
1167 switch (EAX & 0xff)
1169 case 0x00:
1170 ioctlGetDeviceInfo(context);
1171 break;
1173 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1174 EDX = (EDX & 0xffff0000) | (1<<9) | (1<<12);
1175 ResetCflag;
1176 break;
1178 case 0x0b: /* SET SHARING RETRY COUNT */
1179 ResetCflag;
1180 break;
1182 case 0x0d:
1183 ioctlGenericBlkDevReq(context);
1184 break;
1186 default:
1187 IntBarf(0x21, context);
1188 break;
1190 break;
1192 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1193 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1194 EAX = (EAX & 0xffff0000) | dup(BX);
1195 ResetCflag;
1196 break;
1198 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1199 GetCurrentDirectory(context);
1200 EAX = (EAX & 0xffff0000) | 0x0100;
1201 /* intlist: many Microsoft products for Windows rely on this */
1202 break;
1204 case 0x48: /* ALLOCATE MEMORY */
1205 case 0x49: /* FREE MEMORY */
1206 case 0x4a: /* RESIZE MEMORY BLOCK */
1207 IntBarf(0x21, context);
1208 break;
1210 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1211 ExecProgram(context);
1212 break;
1214 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1215 exit(EAX & 0xff);
1216 break;
1218 case 0x4d: /* GET RETURN CODE */
1219 EAX = (EAX & 0xffffff00) | NoError; /* normal exit */
1220 break;
1222 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1223 FindFirst(context);
1224 break;
1226 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1227 FindNext(context);
1228 break;
1230 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1231 ES = 0x0;
1232 EBX = (EBX & 0xffff0000);
1233 IntBarf(0x21, context);
1234 break;
1236 case 0x56: /* "RENAME" - RENAME FILE */
1237 RenameFile(context);
1238 break;
1240 case 0x57: /* FILE DATE AND TIME */
1241 switch (EAX & 0xff)
1243 case 0x00:
1244 GetFileDateTime(context);
1245 break;
1246 case 0x01:
1247 SetFileDateTime(context);
1248 break;
1250 break;
1252 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1253 switch (EAX & 0xff)
1255 case 0x00:
1256 EAX = (EAX & 0xffffff00) | 0x01L;
1257 break;
1258 case 0x02:
1259 EAX &= 0xff00L;
1260 break;
1261 case 0x01:
1262 case 0x03:
1263 break;
1265 ResetCflag;
1266 break;
1268 case 0x5a: /* CREATE TEMPORARY FILE */
1269 CreateTempFile(context);
1270 break;
1272 case 0x5b: /* CREATE NEW FILE */
1273 CreateNewFile(context);
1274 break;
1276 case 0x5c: /* "FLOCK" - RECORD LOCKING */
1277 IntBarf(0x21, context);
1278 break;
1280 case 0x5d: /* NETWORK */
1281 case 0x5e:
1282 /* network software not installed */
1283 EAX = (EAX & 0xfffff00) | NoNetwork;
1284 SetCflag;
1285 break;
1287 case 0x5f: /* NETWORK */
1288 switch (EAX & 0xff)
1290 case 0x07: /* ENABLE DRIVE */
1291 if (!DOS_EnableDrive(EDX & 0xff))
1293 Error(InvalidDrive, EC_MediaError , EL_Disk);
1294 EAX = (EAX & 0xfffff00) | InvalidDrive;
1295 SetCflag;
1296 break;
1298 else
1300 ResetCflag;
1301 break;
1303 case 0x08: /* DISABLE DRIVE */
1304 if (!DOS_DisableDrive(EDX & 0xff))
1306 Error(InvalidDrive, EC_MediaError , EL_Disk);
1307 EAX = (EAX & 0xfffff00) | InvalidDrive;
1308 SetCflag;
1309 break;
1311 else
1313 ResetCflag;
1314 break;
1316 default:
1317 /* network software not installed */
1318 EAX = (EAX & 0xfffff00) | NoNetwork;
1319 SetCflag;
1320 break;
1322 break;
1324 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1325 strncpy(pointer(ES,DI), pointer(DS,SI), strlen(pointer(DS,SI)) & 0x7f);
1326 ResetCflag;
1327 break;
1329 case 0x61: /* UNUSED */
1330 case 0x62: /* GET CURRENT PSP ADDRESS */
1331 case 0x63: /* UNUSED */
1332 case 0x64: /* OS/2 DOS BOX */
1333 case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
1334 IntBarf(0x21, context);
1335 break;
1337 case 0x66: /* GLOBAL CODE PAGE TABLE */
1338 switch (EAX & 0xff)
1340 case 0x01:
1341 EBX = CodePage;
1342 EDX = BX;
1343 ResetCflag;
1344 break;
1345 case 0x02:
1346 CodePage = BX;
1347 ResetCflag;
1348 break;
1350 break;
1352 case 0x67: /* SET HANDLE COUNT */
1353 ResetCflag;
1354 break;
1356 case 0x68: /* "FFLUSH" - COMMIT FILE */
1357 ResetCflag;
1358 break;
1360 case 0x69: /* DISK SERIAL NUMBER */
1361 switch (EAX & 0xff)
1363 case 0x00:
1364 GetDiskSerialNumber(context);
1365 break;
1366 case 0x01:
1367 SetDiskSerialNumber(context);
1368 break;
1370 break;
1372 case 0x6a: /* COMMIT FILE */
1373 ResetCflag;
1374 break;
1376 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1377 break;
1379 default:
1380 IntBarf(0x21, context);
1381 return 1;
1384 return 1;
1387 /**********************************************************************
1388 * DOS3Call
1390 void DOS3Call()
1392 do_int21((struct sigcontext_struct *) _CONTEXT);
1393 ReturnFromRegisterFunc();
1396 void INT21_Init(void)
1398 int handle;
1399 MDESC *DosHeapDesc;
1401 if ((handle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0)
1402 myerror("out of memory");
1404 heap = (struct DosHeap *) GlobalLock(handle);
1405 HEAP_Init(&DosHeapDesc, heap, sizeof(struct DosHeap));
1407 dta = heap->dta;
1408 heap->InDosFlag = 0;
1409 strcpy(heap->biosdate, "01/01/80");