Release 940912
[wine/multimedia.git] / miscemu / int21.c
blob7596aa49d23865487aa7c3773d84a7954a219d58
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 <sys/file.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <utime.h>
17 #include <ctype.h>
18 #include "prototypes.h"
19 #include "regfunc.h"
20 #include "windows.h"
21 #include "wine.h"
22 #include "msdos.h"
23 #include "registers.h"
24 #include "options.h"
26 WORD ExtendedError, CodePage = 437;
27 BYTE ErrorClass, Action, ErrorLocus;
28 BYTE *dta;
30 struct DosHeap {
31 BYTE dta[256];
32 BYTE InDosFlag;
33 BYTE biosdate[8];
35 static struct DosHeap *heap;
37 WORD sharing_retries = 3; /* number of retries at sharing violation */
38 WORD sharing_pause = 1; /* pause between retries */
40 extern char TempDirectory[];
42 static int Error(int e, int class, int el)
44 ErrorClass = class;
45 Action = SA_Ask4Retry;
46 ErrorLocus = el;
47 ExtendedError = e;
49 return e;
52 void errno_to_doserr(void)
54 switch (errno) {
55 case EAGAIN:
56 Error (ShareViolation, EC_Temporary, EL_Unknown);
57 break;
58 case EBADF:
59 Error (InvalidHandle, EC_AppError, EL_Unknown);
60 break;
61 case ENOSPC:
62 Error (DiskFull, EC_MediaError, EL_Disk);
63 break;
64 case EACCES:
65 case EPERM:
66 case EROFS:
67 Error (WriteProtected, EC_AccessDenied, EL_Unknown);
68 break;
69 case EBUSY:
70 Error (LockViolation, EC_AccessDenied, EL_Unknown);
71 break;
72 case ENOENT:
73 Error (FileNotFound, EC_NotFound, EL_Unknown);
74 break;
75 case EISDIR:
76 Error (CanNotMakeDir, EC_AccessDenied, EL_Unknown);
77 break;
78 case ENFILE:
79 case EMFILE:
80 Error (NoMoreFiles, EC_MediaError, EL_Unknown);
81 break;
82 case EEXIST:
83 Error (FileExists, EC_Exists, EL_Disk);
84 break;
85 default:
86 fprintf(stderr, "int21: unknown errno %d!\n", errno);
87 Error (GeneralFailure, EC_SystemFailure, EL_Unknown);
88 break;
92 static void Barf(struct sigcontext_struct *context)
94 fprintf(stderr, "int21: unknown/not implemented parameters:\n");
95 fprintf(stderr, "int21: AX %04x, BX %04x, CX %04x, DX %04x, "
96 "SI %04x, DI %04x, DS %04x, ES %04x\n",
97 AX, BX, CX, DX, SI, DI, DS, ES);
100 void ChopOffWhiteSpace(char *string)
102 int length;
104 for (length = strlen(string) ; length ; length--)
105 if (string[length] == ' ')
106 string[length] = '\0';
109 static void CreateBPB(int drive, BYTE *data)
111 if (drive > 1) {
112 setword(data, 512);
113 data[2] = 2;
114 setword(&data[3], 0);
115 data[5] = 2;
116 setword(&data[6], 240);
117 setword(&data[8], 64000);
118 data[0x0a] = 0xf8;
119 setword(&data[0x0b], 40);
120 setword(&data[0x0d], 56);
121 setword(&data[0x0f], 2);
122 setword(&data[0x11], 0);
123 setword(&data[0x1f], 800);
124 data[0x21] = 5;
125 setword(&data[0x22], 1);
126 } else { /* 1.44mb */
127 setword(data, 512);
128 data[2] = 2;
129 setword(&data[3], 0);
130 data[5] = 2;
131 setword(&data[6], 240);
132 setword(&data[8], 2880);
133 data[0x0a] = 0xf8;
134 setword(&data[0x0b], 6);
135 setword(&data[0x0d], 18);
136 setword(&data[0x0f], 2);
137 setword(&data[0x11], 0);
138 setword(&data[0x1f], 80);
139 data[0x21] = 7;
140 setword(&data[0x22], 2);
144 static void GetFreeDiskSpace(struct sigcontext_struct *context)
146 int drive;
147 long size,available;
149 if (DL == 0)
150 drive = DOS_GetDefaultDrive();
151 else
152 drive = DL - 1;
154 if (!DOS_ValidDrive(drive)) {
155 Error(InvalidDrive, EC_MediaError , EL_Disk);
156 AX = 0xffff;
157 return;
160 if (!DOS_GetFreeSpace(drive, &size, &available)) {
161 Error(GeneralFailure, EC_MediaError , EL_Disk);
162 AX = 0xffff;
163 return;
166 AX = 4;
167 CX = 512;
169 BX = (available / (CX * AX));
170 DX = (size / (CX * AX));
171 Error (0,0,0);
174 static void GetDriveAllocInfo(struct sigcontext_struct *context)
176 int drive;
177 long size, available;
178 BYTE mediaID;
180 if (!DOS_ValidDrive(DL)) {
181 AX = 4;
182 CX = 512;
183 DX = 0;
184 Error (InvalidDrive, EC_MediaError, EL_Disk);
185 return;
188 if (!DOS_GetFreeSpace(DL, &size, &available)) {
189 Error(GeneralFailure, EC_MediaError , EL_Disk);
190 AX = 0xffff;
191 return;
194 EAX = 4;
195 ECX = 512;
196 EDX = (size / (CX * AX));
198 mediaID = 0xf0;
200 DS = segment(mediaID);
201 BX = offset(mediaID);
202 Error (0,0,0);
205 static void GetDefDriveAllocInfo(struct sigcontext_struct *context)
207 DX = DOS_GetDefaultDrive();
208 GetDriveAllocInfo(context);
211 static void GetDrivePB(struct sigcontext_struct *context)
213 Error (InvalidDrive, EC_MediaError, EL_Disk);
214 AX = 0x00ff;
215 /* I'm sorry but I only got networked drives :-) */
218 static void ReadFile(struct sigcontext_struct *context)
220 char *ptr;
221 int size;
223 /* can't read from stdout / stderr */
224 if ((BX == 1) || (BX == 2)) {
225 Error (InvalidHandle, EL_Unknown, EC_Unknown);
226 AX = InvalidHandle;
227 SetCflag;
228 return;
231 ptr = pointer (DS,DX);
232 if (BX == 0) {
233 *ptr = EOF;
234 Error (0,0,0);
235 AX = 1;
236 ResetCflag;
237 return;
238 } else {
239 size = read(BX, ptr, CX);
240 if (size == -1) {
241 errno_to_doserr();
242 AL = ExtendedError;
243 SetCflag;
244 return;
246 Error (0,0,0);
247 AX = size;
248 ResetCflag;
252 static void WriteFile(struct sigcontext_struct *context)
254 char *ptr;
255 int x,size;
257 ptr = pointer (DS,DX);
259 if (BX == 0) {
260 Error (InvalidHandle, EC_Unknown, EL_Unknown);
261 EAX = InvalidHandle;
262 SetCflag;
263 return;
266 if (BX < 3) {
267 for (x = 0;x != CX;x++) {
268 fprintf(stderr, "%c", *ptr++);
270 fflush(stderr);
272 Error (0,0,0);
273 AL = CX;
274 ResetCflag;
275 } else {
276 size = write(BX, ptr , CX);
277 if (size == 0) {
278 Error (WriteFault, EC_Unknown, EL_Unknown);
279 AL = ExtendedError;
280 return;
283 if (size == -1) {
284 errno_to_doserr();
285 AL = ExtendedError;
286 SetCflag;
287 return;
289 Error (0,0,0);
290 AX = size;
291 ResetCflag;
295 static void SeekFile(struct sigcontext_struct *context)
297 off_t status, fileoffset;
299 switch (AL) {
300 case 1: fileoffset = SEEK_CUR;
301 break;
302 case 2: fileoffset = SEEK_END;
303 break;
304 default:
305 case 0: fileoffset = SEEK_SET;
306 break;
308 status = lseek(BX, (CX * 0x100) + DX, fileoffset);
309 if (status == -1) {
310 errno_to_doserr();
311 AL = ExtendedError; SetCflag;
312 return;
314 Error (0,0,0);
315 AX = (status & 0xffff);
316 DX = ((status >> 16) & 0xffff);
318 ResetCflag;
321 static void ioctlGetDeviceInfo(struct sigcontext_struct *context)
323 switch (BX) {
324 case 0:
325 case 1:
326 case 2:
327 DX = 0x80d3;
328 break;
330 default:
332 struct stat sbuf;
334 if (fstat(BX, &sbuf) < 0)
336 IntBarf(0x21, context);
337 DX = 0x50;
338 SetCflag;
339 return;
342 /* This isn't the right answer, but should be close enough. */
343 DX = 0x0943;
346 ResetCflag;
349 static void ioctlGenericBlkDevReq(struct sigcontext_struct *context)
351 BYTE *dataptr = pointer(DS, DX);
352 int drive;
354 if (BL == 0)
355 drive = DOS_GetDefaultDrive();
356 else
357 drive = BL - 1;
359 if (CH != 0x08) {
360 IntBarf(0x21, context);
361 return;
363 switch (CL) {
364 case 0x60: /* get device parameters */
365 /* used by w4wgrp's winfile */
366 dataptr[0] = 0x04;
367 dataptr[6] = 0; /* media type */
368 if (drive > 1)
370 dataptr[1] = 0x05; /* fixed disk */
371 setword(&dataptr[2], 0x01); /* non removable */
372 setword(&dataptr[4], 0x300); /* # of cylinders */
374 else
376 dataptr[1] = 0x07; /* block dev, floppy */
377 setword(&dataptr[2], 0x02); /* removable */
378 setword(&dataptr[4], 80); /* # of cylinders */
380 CreateBPB(drive, &dataptr[7]);
381 AL = 0;
382 ResetCflag;
383 return;
384 default:
385 IntBarf(0x21, context);
389 static void GetSystemDate(struct sigcontext_struct *context)
391 struct tm *now;
392 time_t ltime;
394 ltime = time(NULL);
395 now = localtime(&ltime);
397 CX = now->tm_year + 1900;
398 DX = ((now->tm_mon + 1) << 8) | now->tm_mday;
399 AX = now->tm_wday;
402 static void GetSystemTime(struct sigcontext_struct *context)
404 struct tm *now;
405 struct timeval tv;
407 gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */
408 now = localtime(&tv.tv_sec);
410 CX = (now->tm_hour<<8) | now->tm_min;
411 DX = (now->tm_sec<<8) | tv.tv_usec/10000;
412 /* Note hundredths of seconds */
415 static void GetExtendedErrorInfo(struct sigcontext_struct *context)
417 AL = ExtendedError;
418 BX = (ErrorClass << 8) | Action;
419 CH = ErrorLocus << 8;
422 static void CreateFile(struct sigcontext_struct *context)
424 int handle;
426 if ((handle = open(GetUnixFileName( pointer(DS,DX)),
427 O_CREAT | O_TRUNC | O_RDWR )) == -1) {
428 errno_to_doserr();
429 AL = ExtendedError;
430 SetCflag;
431 return;
433 Error (0,0,0);
434 EAX = (EAX & 0xffff0000) | handle;
435 ResetCflag;
438 void OpenExistingFile(struct sigcontext_struct *context)
440 int handle;
441 int mode;
442 int lock;
444 switch (AX & 0x0007)
446 case 0:
447 mode = O_RDONLY;
448 break;
450 case 1:
451 mode = O_WRONLY;
452 break;
454 default:
455 mode = O_RDWR;
456 break;
459 if ((handle = open(GetUnixFileName(pointer(DS,DX)), mode)) == -1) {
460 errno_to_doserr();
461 AL = ExtendedError;
462 SetCflag;
463 return;
466 switch (AX & 0x0070)
468 case 0x00: /* compatability mode */
469 case 0x40: /* DENYNONE */
470 lock = -1;
471 break;
473 case 0x30: /* DENYREAD */
474 fprintf(stderr,
475 "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
476 pointer(DS,DX));
477 case 0x10: /* DENYALL */
478 lock = LOCK_EX;
479 break;
481 case 0x20: /* DENYWRITE */
482 lock = LOCK_SH;
483 break;
485 default:
486 lock = -1;
489 if (lock != -1)
492 int result,retries=sharing_retries;
494 result = flock(handle, lock | LOCK_NB);
495 if ( retries && (!result) )
497 int i;
498 for(i=0;i<32768*((int)sharing_pause);i++)
499 result++; /* stop the optimizer */
500 for(i=0;i<32768*((int)sharing_pause);i++)
501 result--;
504 while( (!result) && (!(retries--)) );
506 if(result)
508 errno_to_doserr();
509 EAX = (EAX & 0xffffff00) | ExtendedError;
510 close(handle);
511 SetCflag;
512 return;
517 Error (0,0,0);
518 EAX = (EAX & 0xffff0000) | handle;
519 ResetCflag;
522 static void CloseFile(struct sigcontext_struct *context)
524 if (close(BX) == -1) {
525 errno_to_doserr();
526 AL = ExtendedError;
527 SetCflag;
528 return;
530 Error (0,0,0);
531 AL = NoError;
532 ResetCflag;
535 static void RenameFile(struct sigcontext_struct *context)
537 char *newname, *oldname;
539 fprintf(stderr,"int21: renaming %s to %s\n",
540 pointer(DS,DX), pointer(ES,DI) );
542 oldname = GetUnixFileName( pointer(DS,DX) );
543 newname = GetUnixFileName( pointer(ES,DI) );
545 rename( oldname, newname);
546 ResetCflag;
550 static void MakeDir(struct sigcontext_struct *context)
552 char *dirname;
554 fprintf(stderr,"int21: makedir %s\n", pointer(DS,DX) );
556 if ((dirname = GetUnixFileName( pointer(DS,DX) ))== NULL) {
557 AL = CanNotMakeDir;
558 SetCflag;
559 return;
562 if (mkdir(dirname,0) == -1) {
563 AL = CanNotMakeDir;
564 SetCflag;
565 return;
567 ResetCflag;
570 static void ChangeDir(struct sigcontext_struct *context)
572 int drive;
573 char *dirname = pointer(DS,DX);
574 drive = DOS_GetDefaultDrive();
575 fprintf(stderr,"int21: changedir %s\n", dirname);
576 if (dirname != NULL && dirname[1] == ':') {
577 drive = toupper(dirname[0]) - 'A';
578 dirname += 2;
580 DOS_ChangeDir(drive, dirname);
583 static void RemoveDir(struct sigcontext_struct *context)
585 char *dirname;
587 fprintf(stderr,"int21: removedir %s\n", pointer(DS,DX) );
589 if ((dirname = GetUnixFileName( pointer(DS,DX) ))== NULL) {
590 AL = CanNotMakeDir;
591 SetCflag;
592 return;
596 if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) {
597 AL = CanNotRemoveCwd;
598 SetCflag;
601 if (rmdir(dirname) == -1) {
602 AL = CanNotMakeDir;
603 SetCflag;
605 ResetCflag;
608 static void ExecProgram(struct sigcontext_struct *context)
610 execl("wine", GetUnixFileName( pointer(DS,DX)) );
613 static void FindNext(struct sigcontext_struct *context)
615 struct dosdirent *dp;
617 memcpy(&dp, dta+0x0d, sizeof(dp));
619 do {
620 if ((dp = DOS_readdir(dp)) == NULL) {
621 Error(NoMoreFiles, EC_MediaError , EL_Disk);
622 AL = NoMoreFiles;
623 SetCflag;
624 return;
626 } while (*(dta + 0x0c) != dp->attribute);
628 setword(&dta[0x16], 0x1234); /* time */
629 setword(&dta[0x18], 0x1234); /* date */
630 setdword(&dta[0x1a], dp->filesize);
631 strncpy(dta + 0x1e, dp->filename, 13);
634 ResetCflag;
635 return;
638 static void FindFirst(struct sigcontext_struct *context)
640 BYTE drive, *path = pointer(DS, DX);
641 struct dosdirent *dp;
643 if (path[1] == ':') {
644 drive = (islower(*path) ? toupper(*path) : *path) - 'A';
646 if (!DOS_ValidDrive(drive)) {
647 Error(InvalidDrive, EC_MediaError , EL_Disk);
648 AL = InvalidDrive;
649 SetCflag;
650 return;
652 } else
653 drive = DOS_GetDefaultDrive();
655 *dta = drive;
656 memset(dta + 1 , '?', 11);
657 *(dta + 0x0c) = ECX & (FA_LABEL | FA_DIREC);
659 if (ECX & FA_LABEL) {
660 /* return volume label */
662 if (DOS_GetVolumeLabel(drive) != NULL)
663 strncpy(dta + 0x1e, DOS_GetVolumeLabel(drive), 8);
665 AL = 0;
666 ResetCflag;
667 return;
670 if ((dp = DOS_opendir(path)) == NULL) {
671 Error(PathNotFound, EC_MediaError, EL_Disk);
672 AL = FileNotFound;
673 SetCflag;
674 return;
677 memcpy(dta + 0x0d, &dp, sizeof(dp));
678 FindNext(context);
681 static void GetFileDateTime(struct sigcontext_struct *context)
683 char *filename;
684 struct stat filestat;
685 struct tm *now;
687 if ((filename = GetUnixFileName( pointer(DS,DX) ))== NULL) {
688 AL = FileNotFound;
689 SetCflag;
690 return;
692 stat(filename, &filestat);
694 now = localtime (&filestat.st_mtime);
696 CX = ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2);
697 DX = ((now->tm_year * 0x200) + (now->tm_mon * 0x20) + now->tm_mday);
699 ResetCflag;
702 static void SetFileDateTime(struct sigcontext_struct *context)
704 char *filename;
705 struct utimbuf filetime;
707 filename = GetUnixFileName( pointer(DS,DX) );
709 filetime.actime = 0L;
710 filetime.modtime = filetime.actime;
712 utime(filename, &filetime);
713 ResetCflag;
716 static void CreateTempFile(struct sigcontext_struct *context)
718 char temp[256];
719 int handle;
721 sprintf(temp,"%s\\win%d.tmp",TempDirectory,(int) getpid());
723 fprintf(stderr,"CreateTempFile %s\n",temp);
725 handle = open(GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR);
727 if (handle == -1) {
728 AL = WriteProtected;
729 SetCflag;
730 return;
733 strcpy(pointer(DS,DX), temp);
735 AX = handle;
736 ResetCflag;
739 static void CreateNewFile(struct sigcontext_struct *context)
741 int handle;
743 if ((handle = open(GetUnixFileName( pointer(DS,DX) ), O_CREAT | O_EXCL | O_RDWR)) == -1) {
744 AL = WriteProtected;
745 SetCflag;
746 return;
749 AX = handle;
750 ResetCflag;
753 static void GetCurrentDirectory(struct sigcontext_struct *context)
755 int drive;
757 if (DL == 0)
758 drive = DOS_GetDefaultDrive();
759 else
760 drive = DL - 1;
762 if (!DOS_ValidDrive(drive)) {
763 AL = InvalidDrive;
764 SetCflag;
765 return;
768 strcpy(pointer(DS,SI), DOS_GetCurrentDir(drive) );
769 ResetCflag;
772 static void GetDiskSerialNumber(struct sigcontext_struct *context)
774 int drive;
775 BYTE *dataptr = pointer(DS, DX);
776 DWORD serialnumber;
778 if (BL == 0)
779 drive = DOS_GetDefaultDrive();
780 else
781 drive = BL - 1;
783 if (!DOS_ValidDrive(drive)) {
784 AL =InvalidDrive;
785 SetCflag;
786 return;
789 DOS_GetSerialNumber(drive, &serialnumber);
791 setword(dataptr, 0);
792 setdword(&dataptr[2], serialnumber);
793 strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8);
794 strncpy(dataptr + 0x11, "FAT16 ", 8);
797 ResetCflag;
800 static void SetDiskSerialNumber(struct sigcontext_struct *context)
802 int drive;
803 BYTE *dataptr = pointer(DS, DX);
804 DWORD serialnumber;
806 if (BL == 0)
807 drive = DOS_GetDefaultDrive();
808 else
809 drive = BL - 1;
811 if (!DOS_ValidDrive(drive)) {
812 AL = InvalidDrive;
813 SetCflag;
814 return;
817 serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) +
818 (dataptr[4] << 24);
820 DOS_SetSerialNumber(drive, serialnumber);
821 AL = 1L;
822 ResetCflag;
825 static void DumpFCB(BYTE *fcb)
827 int x, y;
829 fcb -= 7;
831 for (y = 0; y !=2 ; y++) {
832 for (x = 0; x!=15;x++)
833 fprintf(stderr, "%02x ", *fcb++);
834 fprintf(stderr,"\n");
838 /* microsoft's programmers should be shot for using CP/M style int21
839 calls in Windows for Workgroup's winfile.exe */
841 static void FindFirstFCB(struct sigcontext_struct *context)
843 BYTE *fcb = pointer(DS, DX);
844 struct fcb *standard_fcb;
845 struct fcb *output_fcb;
846 int drive;
847 char path[12];
849 DumpFCB( fcb );
851 if ((*fcb) == 0xff)
853 standard_fcb = fcb + 7;
854 output_fcb = dta + 7;
855 *dta = 0xff;
857 else
859 standard_fcb = fcb;
860 output_fcb = dta;
863 if (standard_fcb->drive)
865 drive = standard_fcb->drive - 1;
866 if (!DOS_ValidDrive(drive))
868 Error (InvalidDrive, EC_MediaError, EL_Disk);
869 AL = 0xff;
870 return;
873 else
874 drive = DOS_GetDefaultDrive();
876 output_fcb->drive = drive;
878 if (*(fcb) == 0xff)
880 if (*(fcb+6) & FA_LABEL) /* return volume label */
882 *(dta+6) = FA_LABEL;
883 memset(&output_fcb->name, ' ', 11);
884 if (DOS_GetVolumeLabel(drive) != NULL)
886 strncpy(&output_fcb->name, DOS_GetVolumeLabel(drive), 11);
887 AL = 0x00;
888 return;
893 strncpy(&(output_fcb->name),&(standard_fcb->name),11);
894 if (*fcb == 0xff)
895 *(dta+6) = ( *(fcb+6) & (!FA_DIREC));
897 sprintf(path,"%c:*.*",drive+'A');
898 if ((output_fcb->directory = DOS_opendir(path))==NULL)
900 Error (PathNotFound, EC_MediaError, EL_Disk);
901 AL = 0xff;
902 return;
908 static void DeleteFileFCB(struct sigcontext_struct *context)
910 BYTE *fcb = pointer(DS, DX);
911 int drive;
912 struct dosdirent *dp;
913 char temp[256], *ptr;
915 DumpFCB( fcb );
917 if (*fcb)
918 drive = *fcb - 1;
919 else
920 drive = DOS_GetDefaultDrive();
922 strcpy(temp, DOS_GetCurrentDir(drive));
923 strcat(temp, "\\");
924 strncat(temp, fcb + 1, 8);
925 ChopOffWhiteSpace(temp);
926 strncat(temp, fcb + 9, 3);
927 ChopOffWhiteSpace(temp);
929 if ((dp = DOS_opendir(temp)) == NULL) {
930 Error(InvalidDrive, EC_MediaError , EL_Disk);
931 AL = 0xffL;
932 return;
935 strcpy(temp, DOS_GetCurrentDir(drive) );
936 strcat(temp, "\\");
938 ptr = temp + strlen(temp);
940 while (DOS_readdir(dp) != NULL)
942 strcpy(ptr, dp->filename);
943 fprintf(stderr, "int21: delete file %s\n", temp);
944 /* unlink(GetUnixFileName(temp)); */
946 DOS_closedir(dp);
950 static void RenameFileFCB(struct sigcontext_struct *context)
952 BYTE *fcb = pointer(DS, DX);
953 int drive;
954 struct dosdirent *dp;
955 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
957 DumpFCB( fcb );
959 if (*fcb)
960 drive = *fcb - 1;
961 else
962 drive = DOS_GetDefaultDrive();
964 strcpy(temp, DOS_GetCurrentDir(drive));
965 strcat(temp, "\\");
966 strncat(temp, fcb + 1, 8);
967 ChopOffWhiteSpace(temp);
968 strncat(temp, fcb + 9, 3);
969 ChopOffWhiteSpace(temp);
971 if ((dp = DOS_opendir(temp)) == NULL) {
972 Error(InvalidDrive, EC_MediaError , EL_Disk);
973 AL = 0xffL;
974 return;
977 strcpy(oldname, DOS_GetCurrentDir(drive) );
978 strcat(oldname, "\\");
979 oldnameptr = oldname + strlen(oldname);
981 strcpy(newname, DOS_GetCurrentDir(drive) );
982 strcat(newname, "\\");
983 newnameptr = newname + strlen(newname);
985 while (DOS_readdir(dp) != NULL)
987 strcpy(oldnameptr, dp->filename);
988 strcpy(newnameptr, fcb + 1);
989 fprintf(stderr, "int21: renamefile %s -> %s\n", oldname, newname);
991 DOS_closedir(dp);
997 static void fLock (struct sigcontext_struct * context)
999 struct flock f;
1000 int result,retries=sharing_retries;
1002 f.l_start = MAKELONG(DX,CX);
1003 f.l_len = MAKELONG(DI,SI);
1004 f.l_whence = 0;
1005 f.l_pid = 0;
1007 switch ( AX & 0xff )
1009 case 0x00: /* LOCK */
1010 f.l_type = F_WRLCK;
1011 break;
1013 case 0x01: /* UNLOCK */
1014 f.l_type = F_UNLCK;
1015 break;
1017 default:
1018 EAX = (EAX & 0xffff0000) | 0x0001;
1019 SetCflag;
1020 return;
1024 result = fcntl(BX,F_SETLK,&f);
1025 if ( retries && (!result) )
1027 int i;
1028 for(i=0;i<32768*((int)sharing_pause);i++)
1029 result++; /* stop the optimizer */
1030 for(i=0;i<32768*((int)sharing_pause);i++)
1031 result--;
1034 while( (!result) && (!(retries--)) );
1036 if(result)
1038 errno_to_doserr();
1039 EAX = (EAX & 0xffffff00) | ExtendedError;
1040 SetCflag;
1041 return;
1044 Error (0,0,0);
1045 ResetCflag;
1049 static void GetFileAttribute (struct sigcontext_struct * context)
1051 char *filename = pointer (DS,DX);
1052 struct stat s;
1053 int res,cx;
1055 res = stat(GetUnixFileName(filename), &s);
1056 if (res==-1)
1058 errno_to_doserr();
1059 EAX = (EAX & 0xffffff00) | ExtendedError;
1060 SetCflag;
1061 return;
1064 cx = 0;
1065 if (S_ISDIR(s.st_mode))
1066 cx|=0x10;
1067 if ((S_IWRITE & s.st_mode) != S_IWRITE)
1068 cx|=0x01;
1070 ECX = (ECX & 0xffff0000) | cx;
1071 ResetCflag;
1072 Error (0,0,0);
1077 /************************************************************************/
1079 int do_int21(struct sigcontext_struct * context)
1081 if (Options.relay_debug)
1083 printf("int21: AX %04x, BX %04x, CX %04x, DX %04x, "
1084 "SI %04x, DI %04x, DS %04x, ES %04x\n",
1085 AX, BX, CX, DX, SI, DI, DS, ES);
1088 if (AH == 0x59)
1090 GetExtendedErrorInfo(context);
1091 return 1;
1093 else
1095 Error (0,0,0);
1096 switch(AH)
1098 case 0x00: /* TERMINATE PROGRAM */
1099 exit(0);
1101 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1102 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1103 case 0x03: /* READ CHARACTER FROM STDAUX */
1104 case 0x04: /* WRITE CHARACTER TO STDAUX */
1105 case 0x05: /* WRITE CHARACTER TO PRINTER */
1106 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1107 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
1108 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
1109 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
1110 case 0x0a: /* BUFFERED INPUT */
1111 case 0x0b: /* GET STDIN STATUS */
1112 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
1113 case 0x0f: /* OPEN FILE USING FCB */
1114 case 0x10: /* CLOSE FILE USING FCB */
1115 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1116 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
1117 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
1118 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
1119 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
1120 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
1121 case 0x23: /* GET FILE SIZE FOR FCB */
1122 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
1123 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
1124 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
1125 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
1126 case 0x29: /* PARSE FILENAME INTO FCB */
1127 case 0x2e: /* SET VERIFY FLAG */
1128 IntBarf(0x21, context);
1129 break;
1131 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
1132 case 0x1d:
1133 case 0x1e:
1134 case 0x20:
1135 case 0x2b: /* SET SYSTEM DATE */
1136 case 0x2d: /* SET SYSTEM TIME */
1137 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
1138 "SWITCHAR" - SET SWITCH CHARACTER
1139 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
1140 case 0x54: /* GET VERIFY FLAG */
1141 case 0x6b: /* NULL FUNCTION */
1142 IntBarf(0x21, context);
1143 EAX &= 0xff00;
1144 break;
1146 case 0x5c: /* "FLOCK" - RECORD LOCKING */
1147 fLock(context);
1148 break;
1150 case 0x0d: /* DISK BUFFER FLUSH */
1151 ResetCflag; /* dos 6+ only */
1152 break;
1154 case 0x0e: /* SELECT DEFAULT DRIVE */
1155 if (!DOS_ValidDrive(DL)) {
1156 Error (InvalidDrive, EC_MediaError, EL_Disk);
1157 return;
1158 } else {
1159 DOS_SetDefaultDrive(DL);
1160 AX = MAX_DOS_DRIVES;
1161 Error(0,0,0);
1163 break;
1165 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1166 FindFirstFCB(context);
1167 break;
1169 case 0x13: /* DELETE FILE USING FCB */
1170 DeleteFileFCB(context);
1171 break;
1173 case 0x17: /* RENAME FILE USING FCB */
1174 RenameFileFCB(context);
1175 break;
1177 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1178 AL = DOS_GetDefaultDrive();
1179 Error (0,0,0);
1180 break;
1182 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1183 dta = pointer(DS, DX);
1184 break;
1186 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1187 GetDefDriveAllocInfo(context);
1188 break;
1190 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1191 GetDriveAllocInfo(context);
1192 break;
1194 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1195 GetDrivePB(context);
1196 break;
1198 case 0x25: /* SET INTERRUPT VECTOR */
1199 /* Ignore any attempt to set a segment vector */
1200 fprintf(stderr, "int21: set interrupt vector %2x (%04x:%04x)\n", AL, DS, DX);
1201 break;
1203 case 0x2a: /* GET SYSTEM DATE */
1204 GetSystemDate(context);
1205 break;
1207 case 0x2c: /* GET SYSTEM TIME */
1208 GetSystemTime(context);
1209 break;
1211 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1212 ES = segment(dta);
1213 BX = offset(dta);
1214 break;
1216 case 0x30: /* GET DOS VERSION */
1217 AX = DOSVERSION;
1218 BX = 0x0012; /* 0x123456 is Wine's serial # */
1219 CX = 0x3456;
1220 break;
1222 case 0x31: /* TERMINATE AND STAY RESIDENT */
1223 IntBarf(0x21, context);
1224 break;
1226 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1227 GetDrivePB(context);
1228 break;
1230 case 0x33: /* MULTIPLEXED */
1231 switch (AL) {
1232 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1233 if (!(AL))
1234 EDX &= 0xff00L;
1235 break;
1237 case 0x01: /* SET EXTENDED BREAK STATE */
1238 break;
1240 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1241 DL = 0;
1242 break;
1244 case 0x05: /* GET BOOT DRIVE */
1245 DL = 2;
1246 /* c: is Wine's bootdrive */
1247 break;
1249 case 0x06: /* GET TRUE VERSION NUMBER */
1250 EBX = DOSVERSION;
1251 EDX = 0x00;
1252 break;
1254 default:
1255 IntBarf(0x21, context);
1256 break;
1258 break;
1260 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1261 ES = segment(heap->InDosFlag);
1262 BX = offset(heap->InDosFlag);
1263 break;
1265 case 0x35: /* GET INTERRUPT VECTOR */
1266 /* Return a NULL segment selector - this will bomb,
1267 if anyone ever tries to use it */
1268 fprintf(stderr, "int21: get interrupt vector %2x\n", AX & 0xff);
1269 ES = 0;
1270 BX = 0;
1271 break;
1273 case 0x36: /* GET FREE DISK SPACE */
1274 GetFreeDiskSpace(context);
1275 break;
1277 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1278 AX = 0x02; /* no country support available */
1279 SetCflag;
1280 break;
1282 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1283 MakeDir(context);
1284 break;
1286 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1287 RemoveDir(context);
1288 break;
1290 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1291 ChangeDir(context);
1292 break;
1294 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1295 CreateFile(context);
1296 break;
1298 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1299 OpenExistingFile(context);
1300 break;
1302 case 0x3e: /* "CLOSE" - CLOSE FILE */
1303 CloseFile(context);
1304 break;
1306 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1307 ReadFile(context);
1308 break;
1310 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1311 WriteFile(context);
1312 break;
1314 case 0x41: /* "UNLINK" - DELETE FILE */
1315 if (unlink( GetUnixFileName( pointer(DS,DX)) ) == -1) {
1316 errno_to_doserr();
1317 AL = ExtendedError;
1318 SetCflag;
1319 return;
1321 Error(0,0,0);
1322 ResetCflag;
1323 break;
1325 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1326 SeekFile(context);
1327 break;
1329 case 0x43: /* FILE ATTRIBUTES */
1330 switch (AL)
1332 case 0x00:
1333 GetFileAttribute(context);
1334 break;
1335 case 0x01:
1336 ResetCflag;
1337 break;
1339 break;
1341 case 0x44: /* IOCTL */
1342 switch (AL)
1344 case 0x00:
1345 ioctlGetDeviceInfo(context);
1346 break;
1348 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1349 EDX = (EDX & 0xffff0000) | (1<<9) | (1<<12) | (1<<15);
1350 ResetCflag;
1351 break;
1353 case 0x0b: /* SET SHARING RETRY COUNT */
1354 if (!CX)
1356 EAX = (EAX & 0xffff0000) | 0x0001;
1357 SetCflag;
1358 break;
1360 sharing_pause = CX;
1361 if (!DX)
1362 sharing_retries = DX;
1363 ResetCflag;
1364 break;
1366 case 0x0d:
1367 ioctlGenericBlkDevReq(context);
1368 break;
1370 default:
1371 IntBarf(0x21, context);
1372 break;
1374 break;
1376 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1377 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1378 AX = dup(BX);
1379 ResetCflag;
1380 break;
1382 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1383 GetCurrentDirectory(context);
1384 AX = 0x0100;
1385 /* intlist: many Microsoft products for Windows rely on this */
1386 break;
1388 case 0x48: /* ALLOCATE MEMORY */
1389 case 0x49: /* FREE MEMORY */
1390 case 0x4a: /* RESIZE MEMORY BLOCK */
1391 IntBarf(0x21, context);
1392 break;
1394 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1395 ExecProgram(context);
1396 break;
1398 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1399 exit(AL);
1400 break;
1402 case 0x4d: /* GET RETURN CODE */
1403 AL = NoError; /* normal exit */
1404 break;
1406 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1407 FindFirst(context);
1408 break;
1410 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1411 FindNext(context);
1412 break;
1414 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1415 ES = 0x0;
1416 BX = 0x0;
1417 IntBarf(0x21, context);
1418 break;
1420 case 0x56: /* "RENAME" - RENAME FILE */
1421 RenameFile(context);
1422 break;
1424 case 0x57: /* FILE DATE AND TIME */
1425 switch (AL)
1427 case 0x00:
1428 GetFileDateTime(context);
1429 break;
1430 case 0x01:
1431 SetFileDateTime(context);
1432 break;
1434 break;
1436 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1437 switch (AL)
1439 case 0x00:
1440 AL = 0x01L;
1441 break;
1442 case 0x02:
1443 EAX &= 0xff00L;
1444 break;
1445 case 0x01:
1446 case 0x03:
1447 break;
1449 ResetCflag;
1450 break;
1452 case 0x5a: /* CREATE TEMPORARY FILE */
1453 CreateTempFile(context);
1454 break;
1456 case 0x5b: /* CREATE NEW FILE */
1457 CreateNewFile(context);
1458 break;
1460 case 0x5d: /* NETWORK */
1461 case 0x5e:
1462 /* network software not installed */
1463 AL = NoNetwork;
1464 SetCflag;
1465 break;
1467 case 0x5f: /* NETWORK */
1468 switch (AL)
1470 case 0x07: /* ENABLE DRIVE */
1471 if (!DOS_EnableDrive(DL))
1473 Error(InvalidDrive, EC_MediaError , EL_Disk);
1474 AL = InvalidDrive;
1475 SetCflag;
1476 break;
1478 else
1480 ResetCflag;
1481 break;
1483 case 0x08: /* DISABLE DRIVE */
1484 if (!DOS_DisableDrive(DL))
1486 Error(InvalidDrive, EC_MediaError , EL_Disk);
1487 AL = InvalidDrive;
1488 SetCflag;
1489 break;
1491 else
1493 ResetCflag;
1494 break;
1496 default:
1497 /* network software not installed */
1498 AL = NoNetwork;
1499 SetCflag;
1500 break;
1502 break;
1504 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1505 strncpy(pointer(ES,DI), pointer(DS,SI), strlen(pointer(DS,SI)) & 0x7f);
1506 ResetCflag;
1507 break;
1509 case 0x61: /* UNUSED */
1510 case 0x62: /* GET CURRENT PSP ADDRESS */
1511 case 0x63: /* UNUSED */
1512 case 0x64: /* OS/2 DOS BOX */
1513 case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
1514 IntBarf(0x21, context);
1515 break;
1517 case 0x66: /* GLOBAL CODE PAGE TABLE */
1518 switch (AL) {
1519 case 0x01:
1520 BX = CodePage;
1521 DX = BX;
1522 ResetCflag;
1523 break;
1524 case 0x02:
1525 CodePage = BX;
1526 ResetCflag;
1527 break;
1529 break;
1531 case 0x67: /* SET HANDLE COUNT */
1532 ResetCflag;
1533 break;
1535 case 0x68: /* "FFLUSH" - COMMIT FILE */
1536 ResetCflag;
1537 break;
1539 case 0x69: /* DISK SERIAL NUMBER */
1540 switch (AL)
1542 case 0x00:
1543 GetDiskSerialNumber(context);
1544 break;
1545 case 0x01:
1546 SetDiskSerialNumber(context);
1547 break;
1549 break;
1551 case 0x6a: /* COMMIT FILE */
1552 ResetCflag;
1553 break;
1555 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1556 break;
1558 default:
1559 IntBarf(0x21, context);
1560 return 1;
1563 return 1;
1566 /**********************************************************************
1567 * DOS3Call
1569 void DOS3Call()
1571 do_int21((struct sigcontext_struct *) _CONTEXT);
1572 ReturnFromRegisterFunc();
1575 void INT21_Init(void)
1577 int handle;
1578 MDESC *DosHeapDesc;
1580 if ((handle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0)
1581 myerror("out of memory");
1583 heap = (struct DosHeap *) GlobalLock(handle);
1584 HEAP_Init(&DosHeapDesc, heap, sizeof(struct DosHeap));
1586 dta = heap->dta;
1587 heap->InDosFlag = 0;
1588 strcpy(heap->biosdate, "01/01/80");