Release 951226
[wine/multimedia.git] / miscemu / int21.c
blob143e04aba0f2b0a9d5070c845d53c823bb936fd0
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 "dos_fs.h"
19 #include "windows.h"
20 #include "msdos.h"
21 #include "registers.h"
22 #include "ldt.h"
23 #include "task.h"
24 #include "options.h"
25 #include "miscemu.h"
26 #include "stddebug.h"
27 #include "debug.h"
28 #ifdef __svr4__
29 /* SVR4 DOESNTdo locking the same way must implement properly */
30 #define LOCK_EX 0
31 #define LOCK_SH 1
32 #define LOCK_NB 8
33 #endif
34 /* Define the drive parameter block, as used by int21/1F
35 * and int21/32. This table can be accessed through the
36 * global 'dpb' pointer, which points into the local dos
37 * heap.
39 struct DPB
41 BYTE drive_num; /* 0=A, etc. */
42 BYTE unit_num; /* Drive's unit number (?) */
43 WORD sector_size; /* Sector size in bytes */
44 BYTE high_sector; /* Highest sector in a cluster */
45 BYTE shift; /* Shift count (?) */
46 WORD reserved; /* Number of reserved sectors at start */
47 BYTE num_FAT; /* Number of FATs */
48 WORD dir_entries; /* Number of root dir entries */
49 WORD first_data; /* First data sector */
50 WORD high_cluster; /* Highest cluster number */
51 WORD sectors_in_FAT; /* Number of sectors per FAT */
52 WORD start_dir; /* Starting sector of first dir */
53 DWORD driver_head; /* Address of device driver header (?) */
54 BYTE media_ID; /* Media ID */
55 BYTE access_flag; /* Prev. accessed flag (0=yes,0xFF=no) */
56 DWORD next; /* Pointer to next DPB in list */
57 WORD free_search; /* Free cluster search start */
58 WORD free_clusters; /* Number of free clusters (0xFFFF=unknown) */
61 WORD CodePage = 437;
62 struct DPB *dpb;
63 DWORD dpbsegptr;
65 struct DosHeap {
66 BYTE InDosFlag;
67 BYTE mediaID;
68 BYTE biosdate[8];
69 struct DPB dpb;
71 static struct DosHeap *heap;
72 static WORD DosHeapHandle;
74 WORD sharing_retries = 3; /* number of retries at sharing violation */
75 WORD sharing_pause = 1; /* pause between retries */
77 extern char TempDirectory[];
79 static int Error(int e, int class, int el)
81 return DOS_Error(e,class,el);
85 BYTE *GetCurrentDTA(void)
87 TDB *pTask = (TDB *)GlobalLock( GetCurrentTask() );
88 return (BYTE *)PTR_SEG_TO_LIN( pTask->dta );
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 (DL_reg(context) == 0) drive = DOS_GetDefaultDrive();
142 else drive = DL_reg(context) - 1;
144 if (!DOS_ValidDrive(drive)) {
145 Error(InvalidDrive, EC_MediaError , EL_Disk);
146 AX_reg(context) = 0xffff;
147 return;
150 if (!DOS_GetFreeSpace(drive, &size, &available)) {
151 Error(GeneralFailure, EC_MediaError , EL_Disk);
152 AX_reg(context) = 0xffff;
153 return;
156 AX_reg(context) = (drive < 2) ? 1 : 64; /* 64 for hard-disks, 1 for diskettes */
157 CX_reg(context) = 512;
159 BX_reg(context) = (available / (CX_reg(context) * AX_reg(context)));
160 DX_reg(context) = (size / (CX_reg(context) * AX_reg(context)));
161 Error (0,0,0);
164 static void GetDriveAllocInfo(struct sigcontext_struct *context)
166 int drive;
167 long size, available;
169 if (DL_reg(context) == 0) drive = DOS_GetDefaultDrive();
170 else drive = DL_reg(context) - 1;
172 if (!DOS_ValidDrive(drive))
174 AX_reg(context) = 4;
175 CX_reg(context) = 512;
176 DX_reg(context) = 0;
177 Error (InvalidDrive, EC_MediaError, EL_Disk);
178 return;
181 if (!DOS_GetFreeSpace(drive, &size, &available)) {
182 Error(GeneralFailure, EC_MediaError , EL_Disk);
183 AX_reg(context) = 0xffff;
184 return;
187 AX_reg(context) = (drive < 2) ? 1 : 64; /* 64 for hard-disks, 1 for diskettes */
188 CX_reg(context) = 512;
189 DX_reg(context) = (size / (CX_reg(context) * AX_reg(context)));
191 heap->mediaID = 0xf0;
193 DS_reg(context) = DosHeapHandle;
194 BX_reg(context) = (int)&heap->mediaID - (int)heap;
195 Error (0,0,0);
198 static void GetDrivePB(struct sigcontext_struct *context, int drive)
200 if(!DOS_ValidDrive(drive))
202 Error (InvalidDrive, EC_MediaError, EL_Disk);
203 AX_reg(context) = 0x00ff;
205 else
207 dprintf_int(stddeb, "int21: GetDrivePB not fully implemented.\n");
209 /* FIXME: I have no idea what a lot of this information should
210 * say or whether it even really matters since we're not allowing
211 * direct block access. However, some programs seem to depend on
212 * getting at least _something_ back from here. The 'next' pointer
213 * does worry me, though. Should we have a complete table of
214 * separate DPBs per drive? Probably, but I'm lazy. :-) -CH
216 dpb->drive_num = dpb->unit_num = drive; /* The same? */
217 dpb->sector_size = 512;
218 dpb->high_sector = 1;
219 dpb->shift = drive < 2 ? 0 : 6; /* 6 for HD, 0 for floppy */
220 dpb->reserved = 0;
221 dpb->num_FAT = 1;
222 dpb->dir_entries = 2;
223 dpb->first_data = 2;
224 dpb->high_cluster = 64000;
225 dpb->sectors_in_FAT = 1;
226 dpb->start_dir = 1;
227 dpb->driver_head = 0;
228 dpb->media_ID = (drive > 1) ? 0xF8 : 0xF0;
229 dpb->access_flag = 0;
230 dpb->next = 0;
231 dpb->free_search = 0;
232 dpb->free_clusters = 0xFFFF; /* unknown */
234 AL_reg(context) = 0x00;
235 DS_reg(context) = SELECTOROF(dpbsegptr);
236 BX_reg(context) = OFFSETOF(dpbsegptr);
240 static void ReadFile(struct sigcontext_struct *context)
242 char *ptr;
243 int size;
245 /* can't read from stdout / stderr */
246 if ((BX_reg(context) == 1) || (BX_reg(context) == 2)) {
247 Error (InvalidHandle, EL_Unknown, EC_Unknown);
248 AX_reg(context) = InvalidHandle;
249 SET_CFLAG(context);
250 dprintf_int(stddeb, "int21: read (%d, void *, 0x%x) = EBADF\n",
251 BX_reg(context), CX_reg(context));
252 return;
255 ptr = PTR_SEG_OFF_TO_LIN (DS_reg(context),DX_reg(context));
256 if (BX_reg(context) == 0) {
257 *ptr = EOF;
258 Error (0,0,0);
259 AX_reg(context) = 1;
260 RESET_CFLAG(context);
261 dprintf_int(stddeb, "int21: read (%d, void *, 0x%x) = EOF\n",
262 BX_reg(context), CX_reg(context));
263 return;
264 } else {
265 size = read(BX_reg(context), ptr, CX_reg(context));
266 dprintf_int(stddeb,"int21: read(%d, %04x:%04x, 0x%x) = 0x%x\n",
267 BX_reg(context), DS_reg(context), DX_reg(context),
268 CX_reg(context), size );
269 if (size == -1) {
270 errno_to_doserr();
271 AX_reg(context) = ExtendedError;
272 SET_CFLAG(context);
273 return;
275 Error (0,0,0);
276 AX_reg(context) = size;
277 RESET_CFLAG(context);
281 static void WriteFile(struct sigcontext_struct *context)
283 char *ptr;
284 int x,size;
286 ptr = PTR_SEG_OFF_TO_LIN (DS_reg(context),DX_reg(context));
288 if (BX_reg(context) == 0) {
289 Error (InvalidHandle, EC_Unknown, EL_Unknown);
290 EAX_reg(context) = InvalidHandle;
291 SET_CFLAG(context);
292 return;
295 if (BX_reg(context) < 3) {
296 for (x = 0;x != CX_reg(context);x++) {
297 dprintf_int(stddeb, "%c", *ptr++);
299 fflush(stddeb);
301 Error (0,0,0);
302 AX_reg(context) = CX_reg(context);
303 RESET_CFLAG(context);
304 } else {
305 /* well, this function already handles everything we need */
306 size = _lwrite(BX_reg(context),ptr,CX_reg(context));
307 if (size == -1) { /* HFILE_ERROR == -1 */
308 errno_to_doserr();
309 AX_reg(context) = ExtendedError;
310 SET_CFLAG(context);
311 return;
313 Error (0,0,0);
314 AX_reg(context) = size;
315 RESET_CFLAG(context);
319 static void SeekFile(struct sigcontext_struct *context)
321 off_t status, fileoffset;
323 switch (AL_reg(context))
325 case 1: fileoffset = SEEK_CUR;
326 break;
327 case 2: fileoffset = SEEK_END;
328 break;
329 default:
330 case 0: fileoffset = SEEK_SET;
331 break;
333 status = lseek(BX_reg(context), (CX_reg(context) << 16) + DX_reg(context), fileoffset);
335 dprintf_int (stddeb, "int21: seek (%d, 0x%x, %d) = 0x%lx\n",
336 BX_reg(context), (CX_reg(context) << 16) + DX_reg(context), AL_reg(context), status);
338 if (status == -1)
340 errno_to_doserr();
341 AX_reg(context) = ExtendedError;
342 SET_CFLAG(context);
343 return;
345 Error (0,0,0);
346 AX_reg(context) = LOWORD(status);
347 DX_reg(context) = HIWORD(status);
348 RESET_CFLAG(context);
351 static void ioctlGetDeviceInfo(struct sigcontext_struct *context)
354 dprintf_int (stddeb, "int21: ioctl (%d, GetDeviceInfo)\n", BX_reg(context));
356 switch (BX_reg(context))
358 case 0:
359 case 1:
360 case 2:
361 DX_reg(context) = 0x80d0 | (1 << (BX_reg(context) != 0));
362 RESET_CFLAG(context);
363 break;
365 default:
367 struct stat sbuf;
369 if (fstat(BX_reg(context), &sbuf) < 0)
371 INT_BARF( context, 0x21 );
372 SET_CFLAG(context);
373 return;
376 DX_reg(context) = 0x0943;
377 /* bits 0-5 are current drive
378 * bit 6 - file has NOT been written..FIXME: correct?
379 * bit 8 - generate int24 if no diskspace on write/ read past end of file
380 * bit 11 - media not removable
384 RESET_CFLAG(context);
387 static void ioctlGenericBlkDevReq(struct sigcontext_struct *context)
389 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
390 int drive;
392 if (BL_reg(context) == 0) drive = DOS_GetDefaultDrive();
393 else drive = BL_reg(context) - 1;
395 if (!DOS_ValidDrive(drive))
397 Error( FileNotFound, EC_NotFound, EL_Disk );
398 AX_reg(context) = FileNotFound;
399 SET_CFLAG(context);
400 return;
403 if (CH_reg(context) != 0x08)
405 INT_BARF( context, 0x21 );
406 return;
408 switch (CL_reg(context)) {
409 case 0x60: /* get device parameters */
410 /* used by w4wgrp's winfile */
411 memset(dataptr, 0, 0x26);
412 dataptr[0] = 0x04;
413 dataptr[6] = 0; /* media type */
414 if (drive > 1)
416 dataptr[1] = 0x05; /* fixed disk */
417 setword(&dataptr[2], 0x01); /* non removable */
418 setword(&dataptr[4], 0x300); /* # of cylinders */
420 else
422 dataptr[1] = 0x07; /* block dev, floppy */
423 setword(&dataptr[2], 0x02); /* removable */
424 setword(&dataptr[4], 80); /* # of cylinders */
426 CreateBPB(drive, &dataptr[7]);
427 RESET_CFLAG(context);
428 return;
429 default:
430 INT_BARF( context, 0x21 );
434 static void GetSystemDate(struct sigcontext_struct *context)
436 struct tm *now;
437 time_t ltime;
439 ltime = time(NULL);
440 now = localtime(&ltime);
442 CX_reg(context) = now->tm_year + 1900;
443 DX_reg(context) = ((now->tm_mon + 1) << 8) | now->tm_mday;
444 AX_reg(context) = now->tm_wday;
447 static void INT21_GetSystemTime(struct sigcontext_struct *context)
449 struct tm *now;
450 struct timeval tv;
451 time_t seconds;
453 gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */
454 seconds = tv.tv_sec;
455 now = localtime(&seconds);
457 CX_reg(context) = (now->tm_hour<<8) | now->tm_min;
458 DX_reg(context) = (now->tm_sec<<8) | tv.tv_usec/10000;
459 /* Note hundredths of seconds */
462 static void GetExtendedErrorInfo(struct sigcontext_struct *context)
464 AX_reg(context) = ExtendedError;
465 BX_reg(context) = (ErrorClass << 8) | Action;
466 CH_reg(context) = ErrorLocus;
469 static void CreateFile(struct sigcontext_struct *context)
471 int handle;
472 handle = open(DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),
473 DX_reg(context))),
474 O_CREAT | O_TRUNC | O_RDWR, 0666 );
475 if (handle == -1) {
476 errno_to_doserr();
477 AX_reg(context) = ExtendedError;
478 SET_CFLAG(context);
479 return;
481 Error (0,0,0);
482 AX_reg(context) = handle;
483 RESET_CFLAG(context);
486 void OpenExistingFile(struct sigcontext_struct *context)
488 int handle;
489 int mode;
490 int lock;
492 switch (AX_reg(context) & 0x0007)
494 case 0:
495 mode = O_RDONLY;
496 break;
498 case 1:
499 mode = O_WRONLY;
500 break;
502 default:
503 mode = O_RDWR;
504 break;
507 if ((handle = open(DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))),
508 mode)) == -1)
510 if( Options.allowReadOnly )
511 handle = open( DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))),
512 O_RDONLY );
513 if( handle == -1 )
515 dprintf_int (stddeb, "int21: open (%s, %d) = -1\n",
516 DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context))), mode);
517 errno_to_doserr();
518 AX_reg(context) = ExtendedError;
519 SET_CFLAG(context);
520 return;
524 dprintf_int (stddeb, "int21: open (%s, %d) = %d\n",
525 DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),
526 DX_reg(context))), mode, handle);
528 switch (AX_reg(context) & 0x0070)
530 case 0x00: /* compatability mode */
531 case 0x40: /* DENYNONE */
532 lock = -1;
533 break;
535 case 0x30: /* DENYREAD */
536 dprintf_int(stddeb,
537 "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
538 (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)));
539 case 0x10: /* DENYALL */
540 lock = LOCK_EX;
541 break;
543 case 0x20: /* DENYWRITE */
544 lock = LOCK_SH;
545 break;
547 default:
548 lock = -1;
551 if (lock != -1)
554 int result,retries=sharing_retries;
556 #ifdef __svr4__
557 printf("Should call flock and needs porting to lockf\n");
558 result = 0;
559 retries = 0;
560 #else
561 result = flock(handle, lock | LOCK_NB);
562 #endif
563 if ( retries && (!result) )
565 int i;
566 for(i=0;i<32768*((int)sharing_pause);i++)
567 result++; /* stop the optimizer */
568 for(i=0;i<32768*((int)sharing_pause);i++)
569 result--;
572 while( (!result) && (!(retries--)) );
574 if(result)
576 errno_to_doserr();
577 AX_reg(context) = ExtendedError;
578 close(handle);
579 SET_CFLAG(context);
580 return;
585 Error (0,0,0);
586 AX_reg(context) = handle;
587 RESET_CFLAG(context);
590 static void CloseFile(struct sigcontext_struct *context)
592 dprintf_int (stddeb, "int21: close (%d)\n", BX_reg(context));
594 if (_lclose( BX_reg(context) ))
596 errno_to_doserr();
597 AX_reg(context) = ExtendedError;
598 SET_CFLAG(context);
599 return;
601 Error (0,0,0);
602 AX_reg(context) = NoError;
603 RESET_CFLAG(context);
606 void ExtendedOpenCreateFile(struct sigcontext_struct *context)
608 dprintf_int(stddeb, "int21: extended open/create: file= %s \n",
609 DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context))));
610 /* Shuffle arguments to call OpenExistingFile */
611 AL_reg(context) = BL_reg(context);
612 DX_reg(context) = SI_reg(context);
613 /* BX,CX and DX should be preserved */
614 OpenExistingFile(context);
615 if ((EFL_reg(context) & 0x0001)==0)
616 { /* It exists */
617 dprintf_int(stddeb, "int21: extended open/create %s exists \n",
618 DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context))));
619 /* Now decide what do do */
620 if ((DL_reg(context) & 0x0007)== 0)
622 BX_reg(context) = AX_reg(context);
623 CloseFile(context);
624 AX_reg(context) = 0x0050;/*File exists*/
625 CX_reg(context) = 0;
626 SET_CFLAG(context);
627 dprintf_int(stddeb, "int21: extended open/create: failed because file exixts \n");
628 return;
630 if ((DL_reg(context) & 0x0007)== 2) {
631 /* Truncate it, but first check if opend for write */
632 if ((BL_reg(context) & 0x0007)== 0) {
633 BX_reg(context) = AX_reg(context);
634 CloseFile(context);
635 dprintf_int(stddeb, "int21: extended open/create: failed, trunc on ro file");
636 AX_reg(context) = 0x000C;/*Access code invalid*/
637 CX_reg(context) = 0;
638 SET_CFLAG(context);
639 return;
641 /* Shuffle arguments to call CloseFile */
642 dprintf_int(stddeb, "int21: extended open/create: Closing before truncate\n");
643 BX_reg(context) = AX_reg(context);
644 /* BX and DX should be preserved */
645 CloseFile(context);
646 if (EFL_reg(context) & 0x0001) {
647 dprintf_int(stddeb, "int21: extended open/create: close before trunc failed");
648 AX_reg(context) = 0x0019;/*Seek Error*/
649 CX_reg(context) = 0;
650 SET_CFLAG(context);
652 /* Shuffle arguments to call CreateFile */
653 dprintf_int(stddeb, "int21: extended open/create: Truncating\n");
654 AL_reg(context) = BL_reg(context);
655 /* CX is still the same */
656 DX_reg(context) = SI_reg(context);
657 CreateFile(context);
658 if (EFL_reg(context) & 0x0001) { /*no file open, flags set */
659 dprintf_int(stddeb, "int21: extended open/create: truncfailed");
660 return;
662 CX_reg(context) = 3;
663 return;
665 CX_reg(context) = 1;
666 return;
668 else /* file does not exist */
670 dprintf_int(stddeb, "int21: extended open/create %s dosen't exists \n",
671 DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context))));
672 if ((DL_reg(context) & 0x00F0)== 0) {
673 CX_reg(context) = 0;
674 SET_CFLAG(context);
675 dprintf_int(stddeb, "int21: extended open/create: failed, file dosen't exist\n");
676 return;
678 /* Shuffle arguments to call CreateFile */
679 dprintf_int(stddeb, "int21: extended open/create: Creating\n");
680 AL_reg(context) = BL_reg(context);
681 /* CX should still be the same */
682 DX_reg(context) = SI_reg(context);
683 CreateFile(context);
684 if (EFL_reg(context) & 0x0001) { /*no file open, flags set */
685 dprintf_int(stddeb, "int21: extended open/create: create failed\n");
686 return;
688 CX_reg(context) = 2;
689 return;
692 static void RenameFile(struct sigcontext_struct *context)
694 char *newname, *oldname;
696 /* FIXME: should not rename over an existing file */
697 dprintf_int(stddeb,"int21: renaming %s to %s\n",
698 (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)),
699 (char *)PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context)));
701 oldname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),
702 DX_reg(context)) );
703 newname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(ES_reg(context),
704 DI_reg(context)) );
706 rename( oldname, newname);
707 RESET_CFLAG(context);
711 static void MakeDir(struct sigcontext_struct *context)
713 char *dirname;
715 dprintf_int(stddeb,"int21: makedir %s\n", (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) );
717 if ((dirname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),
718 DX_reg(context)) ))== NULL) {
719 Error( CanNotMakeDir, EC_AccessDenied, EL_Disk );
720 AX_reg(context) = CanNotMakeDir;
721 SET_CFLAG(context);
722 return;
725 if ((mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == -1) && errno!=EEXIST) {
726 Error( CanNotMakeDir, EC_AccessDenied, EL_Disk );
727 AX_reg(context) = CanNotMakeDir;
728 SET_CFLAG(context);
729 return;
731 RESET_CFLAG(context);
734 static void ChangeDir(struct sigcontext_struct *context)
736 int drive;
737 char *dirname = PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context));
738 drive = DOS_GetDefaultDrive();
739 dprintf_int(stddeb,"int21: changedir %s\n", dirname);
740 if (dirname != NULL && dirname[1] == ':') {
741 drive = toupper(dirname[0]) - 'A';
742 dirname += 2;
744 if (!DOS_ChangeDir(drive, dirname))
746 SET_CFLAG(context);
747 AX_reg(context)=0x03;
751 static void RemoveDir(struct sigcontext_struct *context)
753 char *dirname;
755 dprintf_int(stddeb,"int21: removedir %s\n", (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) );
757 if ((dirname = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) ))== NULL) {
758 Error( PathNotFound, EC_NotFound, EL_Disk );
759 AX_reg(context) = PathNotFound;
760 SET_CFLAG(context);
761 return;
765 if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) {
766 AL = CanNotRemoveCwd;
767 SET_CFLAG(context);
770 if (rmdir(dirname) == -1) {
771 Error( AccessDenied, EC_AccessDenied, EL_Disk );
772 AX_reg(context) = AccessDenied;
773 SET_CFLAG(context);
774 return;
776 RESET_CFLAG(context);
779 static void FindNext(struct sigcontext_struct *context)
781 struct dosdirent *dp;
782 struct tm *t;
783 BYTE *dta = GetCurrentDTA();
785 memcpy(&dp, dta+0x11, sizeof(dp));
787 dprintf_int(stddeb, "int21: FindNext, dta %p, dp %p\n", dta, dp);
788 do {
789 if ((dp = DOS_readdir(dp)) == NULL) {
790 Error(NoMoreFiles, EC_MediaError , EL_Disk);
791 AX_reg(context) = NoMoreFiles;
792 SET_CFLAG(context);
793 return;
795 } /* while (*(dta + 0x0c) != dp->attribute);*/
796 while ( ( dp->search_attribute & dp->attribute) != dp->attribute);
798 *(dta + 0x15) = dp->attribute;
799 setword(&dta[0x0d], dp->entnum);
801 t = localtime(&(dp->filetime));
802 setword(&dta[0x16], (t->tm_hour << 11) + (t->tm_min << 5) +
803 (t->tm_sec / 2)); /* time */
804 setword(&dta[0x18], ((t->tm_year - 80) << 9) + (t->tm_mon << 5) +
805 (t->tm_mday)); /* date */
806 setdword(&dta[0x1a], dp->filesize);
807 strncpy(dta + 0x1e, dp->filename, 12);
808 *(dta + 0x1e + 13) = 0;
809 AnsiUpper(dta+0x1e);
811 AX_reg(context) = 0;
812 RESET_CFLAG(context);
814 dprintf_int(stddeb, "int21: FindNext -- (%s) index=%d size=%ld\n", dp->filename, dp->entnum, dp->filesize);
817 static void FindFirst(struct sigcontext_struct *context)
819 BYTE drive, *path = PTR_SEG_OFF_TO_LIN(DS_reg(context),
820 DX_reg(context));
821 struct dosdirent *dp;
823 BYTE *dta = GetCurrentDTA();
825 dprintf_int(stddeb, "int21: FindFirst path = %s\n", path);
827 if ((*path)&&(path[1] == ':')) {
828 drive = (islower(*path) ? toupper(*path) : *path) - 'A';
830 if (!DOS_ValidDrive(drive)) {
831 Error(InvalidDrive, EC_MediaError , EL_Disk);
832 AX_reg(context) = InvalidDrive;
833 SET_CFLAG(context);
834 return;
836 } else
837 drive = DOS_GetDefaultDrive();
839 *dta = drive;
840 memset(dta + 1 , '?', 11);
841 *(dta + 0x0c) = ECX_reg(context) & (FA_LABEL | FA_DIREC);
843 if ((dp = DOS_opendir(path)) == NULL) {
844 Error(PathNotFound, EC_MediaError, EL_Disk);
845 AX_reg(context) = FileNotFound;
846 SET_CFLAG(context);
847 return;
850 dp->search_attribute = ECX_reg(context) & (FA_LABEL | FA_DIREC);
851 memcpy(dta + 0x11, &dp, sizeof(dp));
852 FindNext(context);
856 static void GetFileDateTime(struct sigcontext_struct *context)
858 struct stat filestat;
859 struct tm *now;
861 fstat( BX_reg(context), &filestat );
862 now = localtime (&filestat.st_mtime);
864 CX_reg(context) = ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2);
865 DX_reg(context) = (((now->tm_year + 1900 - 1980) * 0x200) +
866 (now->tm_mon * 0x20) + now->tm_mday);
868 RESET_CFLAG(context);
871 static void SetFileDateTime(struct sigcontext_struct *context)
873 char *filename;
874 struct utimbuf filetime;
876 /* FIXME: Argument isn't the name of the file in DS:DX,
877 but the file handle in BX */
878 filename = DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),
879 DX_reg(context)) );
881 filetime.actime = 0L;
882 filetime.modtime = filetime.actime;
884 utime(filename, &filetime);
885 RESET_CFLAG(context);
888 static void CreateTempFile(struct sigcontext_struct *context)
890 char temp[256];
891 int handle;
892 static int counter = 0;
894 sprintf(temp,"%s\\win%d.%03d",TempDirectory,(int) getpid(), counter);
895 counter = (counter + 1) % 1000;
897 dprintf_int(stddeb,"CreateTempFile %s\n",temp);
899 handle = open(DOS_GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR, 0666);
901 if (handle == -1) {
902 Error( WriteProtected, EC_AccessDenied, EL_Disk );
903 AX_reg(context) = WriteProtected;
904 SET_CFLAG(context);
905 return;
908 strcpy(PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)), temp);
910 AX_reg(context) = handle;
911 RESET_CFLAG(context);
914 static void CreateNewFile(struct sigcontext_struct *context)
916 int handle;
918 if ((handle = open(DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)) ),
919 O_CREAT | O_EXCL | O_RDWR, 0666)) == -1)
921 Error( WriteProtected, EC_AccessDenied, EL_Disk );
922 AX_reg(context) = WriteProtected;
923 SET_CFLAG(context);
924 return;
927 AX_reg(context) = handle;
928 RESET_CFLAG(context);
931 static void GetCurrentDirectory(struct sigcontext_struct *context)
933 int drive;
935 if (DL_reg(context) == 0) drive = DOS_GetDefaultDrive();
936 else drive = DL_reg(context) - 1;
938 if (!DOS_ValidDrive(drive))
940 Error( InvalidDrive, EC_NotFound, EL_Disk );
941 AX_reg(context) = InvalidDrive;
942 SET_CFLAG(context);
943 return;
946 strcpy(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context)),
947 DOS_GetCurrentDir(drive) );
948 RESET_CFLAG(context);
951 static void GetDiskSerialNumber(struct sigcontext_struct *context)
953 int drive;
954 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
955 DWORD serialnumber;
957 if (BL_reg(context) == 0) drive = DOS_GetDefaultDrive();
958 else drive = BL_reg(context) - 1;
960 if (!DOS_ValidDrive(drive))
962 Error( InvalidDrive, EC_NotFound, EL_Disk );
963 AX_reg(context) = InvalidDrive;
964 SET_CFLAG(context);
965 return;
968 DOS_GetSerialNumber(drive, &serialnumber);
970 setword(dataptr, 0);
971 setdword(&dataptr[2], serialnumber);
972 strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8);
973 strncpy(dataptr + 0x11, "FAT16 ", 8);
975 AX_reg(context) = 0;
976 RESET_CFLAG(context);
979 static void SetDiskSerialNumber(struct sigcontext_struct *context)
981 int drive;
982 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
983 DWORD serialnumber;
985 if (BL_reg(context) == 0) drive = DOS_GetDefaultDrive();
986 else drive = BL_reg(context) - 1;
988 if (!DOS_ValidDrive(drive))
990 Error( InvalidDrive, EC_NotFound, EL_Disk );
991 AX_reg(context) = InvalidDrive;
992 SET_CFLAG(context);
993 return;
996 serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) +
997 (dataptr[4] << 24);
999 DOS_SetSerialNumber(drive, serialnumber);
1000 AX_reg(context) = 1;
1001 RESET_CFLAG(context);
1004 static void DumpFCB(BYTE *fcb)
1006 int x, y;
1008 fcb -= 7;
1010 for (y = 0; y !=2 ; y++) {
1011 for (x = 0; x!=15;x++)
1012 dprintf_int(stddeb, "%02x ", *fcb++);
1013 dprintf_int(stddeb,"\n");
1017 /* microsoft's programmers should be shot for using CP/M style int21
1018 calls in Windows for Workgroup's winfile.exe */
1020 static void FindFirstFCB(struct sigcontext_struct *context)
1022 BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
1023 struct fcb *standard_fcb;
1024 struct fcb *output_fcb;
1025 int drive;
1026 char path[12];
1028 BYTE *dta = GetCurrentDTA();
1030 DumpFCB( fcb );
1032 if ((*fcb) == 0xff)
1034 standard_fcb = (struct fcb *)(fcb + 7);
1035 output_fcb = (struct fcb *)(dta + 7);
1036 *dta = 0xff;
1038 else
1040 standard_fcb = (struct fcb *)fcb;
1041 output_fcb = (struct fcb *)dta;
1044 if (standard_fcb->drive)
1046 drive = standard_fcb->drive - 1;
1047 if (!DOS_ValidDrive(drive))
1049 Error (InvalidDrive, EC_MediaError, EL_Disk);
1050 AX_reg(context) = 0xff;
1051 return;
1054 else
1055 drive = DOS_GetDefaultDrive();
1057 output_fcb->drive = drive;
1059 if (*(fcb) == 0xff)
1061 if (*(fcb+6) & FA_LABEL) /* return volume label */
1063 *(dta+6) = FA_LABEL;
1064 memset(&output_fcb->name, ' ', 11);
1065 if (DOS_GetVolumeLabel(drive) != NULL)
1067 strncpy(output_fcb->name, DOS_GetVolumeLabel(drive), 11);
1068 AX_reg(context) = 0x00;
1069 return;
1074 strncpy(output_fcb->name, standard_fcb->name, 11);
1075 if (*fcb == 0xff)
1076 *(dta+6) = ( *(fcb+6) & (!FA_DIREC));
1078 sprintf(path,"%c:*.*",drive+'A');
1079 if ((output_fcb->directory = DOS_opendir(path))==NULL)
1081 Error (PathNotFound, EC_MediaError, EL_Disk);
1082 AX_reg(context) = 0xff;
1083 return;
1089 static void DeleteFileFCB(struct sigcontext_struct *context)
1091 BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
1092 int drive;
1093 struct dosdirent *dp;
1094 char temp[256], *ptr;
1096 DumpFCB( fcb );
1098 if (*fcb)
1099 drive = *fcb - 1;
1100 else
1101 drive = DOS_GetDefaultDrive();
1103 strcpy(temp, DOS_GetCurrentDir(drive));
1104 strcat(temp, "\\");
1105 strncat(temp, fcb + 1, 8);
1106 ChopOffWhiteSpace(temp);
1107 strncat(temp, fcb + 9, 3);
1108 ChopOffWhiteSpace(temp);
1110 if ((dp = DOS_opendir(temp)) == NULL) {
1111 Error(InvalidDrive, EC_MediaError , EL_Disk);
1112 AX_reg(context) = 0xff;
1113 return;
1116 strcpy(temp, DOS_GetCurrentDir(drive) );
1117 strcat(temp, "\\");
1119 ptr = temp + strlen(temp);
1121 while (DOS_readdir(dp) != NULL)
1123 strcpy(ptr, dp->filename);
1124 dprintf_int(stddeb, "int21: delete file %s\n", temp);
1125 /* unlink(DOS_GetUnixFileName(temp)); */
1127 DOS_closedir(dp);
1128 AX_reg(context) = 0;
1131 static void RenameFileFCB(struct sigcontext_struct *context)
1133 BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
1134 int drive;
1135 struct dosdirent *dp;
1136 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
1138 DumpFCB( fcb );
1140 if (*fcb)
1141 drive = *fcb - 1;
1142 else
1143 drive = DOS_GetDefaultDrive();
1145 strcpy(temp, DOS_GetCurrentDir(drive));
1146 strcat(temp, "\\");
1147 strncat(temp, fcb + 1, 8);
1148 ChopOffWhiteSpace(temp);
1149 strncat(temp, fcb + 9, 3);
1150 ChopOffWhiteSpace(temp);
1152 if ((dp = DOS_opendir(temp)) == NULL) {
1153 Error(InvalidDrive, EC_MediaError , EL_Disk);
1154 AX_reg(context) = 0xff;
1155 return;
1158 strcpy(oldname, DOS_GetCurrentDir(drive) );
1159 strcat(oldname, "\\");
1160 oldnameptr = oldname + strlen(oldname);
1162 strcpy(newname, DOS_GetCurrentDir(drive) );
1163 strcat(newname, "\\");
1164 newnameptr = newname + strlen(newname);
1166 while (DOS_readdir(dp) != NULL)
1168 strcpy(oldnameptr, dp->filename);
1169 strcpy(newnameptr, fcb + 1);
1170 dprintf_int(stddeb, "int21: renamefile %s -> %s\n",
1171 oldname, newname);
1173 DOS_closedir(dp);
1174 AX_reg(context) = 0;
1179 static void fLock (struct sigcontext_struct * context)
1181 struct flock f;
1182 int result,retries=sharing_retries;
1184 f.l_start = MAKELONG(DX_reg(context),CX_reg(context));
1185 f.l_len = MAKELONG(DI_reg(context),SI_reg(context));
1186 f.l_whence = 0;
1187 f.l_pid = 0;
1189 switch ( AX_reg(context) & 0xff )
1191 case 0x00: /* LOCK */
1192 f.l_type = F_WRLCK;
1193 break;
1195 case 0x01: /* UNLOCK */
1196 f.l_type = F_UNLCK;
1197 break;
1199 default:
1200 AX_reg(context) = 0x0001;
1201 SET_CFLAG(context);
1202 return;
1206 result = fcntl(BX_reg(context),F_SETLK,&f);
1207 if ( retries && (!result) )
1209 int i;
1210 for(i=0;i<32768*((int)sharing_pause);i++)
1211 result++; /* stop the optimizer */
1212 for(i=0;i<32768*((int)sharing_pause);i++)
1213 result--;
1216 while( (!result) && (!(retries--)) );
1218 if(result)
1220 errno_to_doserr();
1221 AX_reg(context) = ExtendedError;
1222 SET_CFLAG(context);
1223 return;
1226 Error (0,0,0);
1227 RESET_CFLAG(context);
1231 static void GetFileAttribute (struct sigcontext_struct * context)
1233 char *filename = PTR_SEG_OFF_TO_LIN (DS_reg(context),DX_reg(context));
1234 struct stat s;
1235 int res;
1237 res = stat(DOS_GetUnixFileName(filename), &s);
1238 if (res==-1)
1240 errno_to_doserr();
1241 AX_reg(context) = ExtendedError;
1242 SET_CFLAG(context);
1243 return;
1246 CX_reg(context) = 0;
1247 if (S_ISDIR(s.st_mode))
1248 CX_reg(context) |= 0x10;
1249 if ((S_IWRITE & s.st_mode) != S_IWRITE)
1250 CX_reg(context) |= 0x01;
1252 dprintf_int (stddeb, "int21: GetFileAttributes (%s) = 0x%x\n",
1253 filename, CX_reg(context) );
1255 RESET_CFLAG(context);
1256 Error (0,0,0);
1260 extern void LOCAL_PrintHeap (WORD ds);
1262 /***********************************************************************
1263 * DOS3Call (KERNEL.102)
1265 void DOS3Call( struct sigcontext_struct context )
1267 int drive;
1269 if (AH_reg(&context) == 0x59)
1271 GetExtendedErrorInfo(&context);
1272 return;
1274 else
1276 Error (0,0,0);
1277 switch(AH_reg(&context))
1279 case 0x00: /* TERMINATE PROGRAM */
1280 TASK_KillCurrentTask( 0 );
1281 break;
1283 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1284 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1285 case 0x03: /* READ CHARACTER FROM STDAUX */
1286 case 0x04: /* WRITE CHARACTER TO STDAUX */
1287 case 0x05: /* WRITE CHARACTER TO PRINTER */
1288 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1289 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
1290 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
1291 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
1292 case 0x0a: /* BUFFERED INPUT */
1293 case 0x0b: /* GET STDIN STATUS */
1294 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
1295 case 0x0f: /* OPEN FILE USING FCB */
1296 case 0x10: /* CLOSE FILE USING FCB */
1297 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1298 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
1299 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
1300 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
1301 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
1302 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
1303 case 0x23: /* GET FILE SIZE FOR FCB */
1304 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
1305 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
1306 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
1307 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
1308 case 0x29: /* PARSE FILENAME INTO FCB */
1309 case 0x2e: /* SET VERIFY FLAG */
1310 INT_BARF( &context, 0x21 );
1311 break;
1313 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
1314 "SWITCHAR" - SET SWITCH CHARACTER
1315 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
1316 case 0x54: /* GET VERIFY FLAG */
1317 INT_BARF( &context, 0x21 );
1318 break;
1320 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
1321 case 0x1d:
1322 case 0x1e:
1323 case 0x20:
1324 case 0x6b: /* NULL FUNCTION */
1325 AL_reg(&context) = 0;
1326 break;
1328 case 0x5c: /* "FLOCK" - RECORD LOCKING */
1329 fLock(&context);
1330 break;
1332 case 0x0d: /* DISK BUFFER FLUSH */
1333 RESET_CFLAG(&context); /* dos 6+ only */
1334 break;
1336 case 0x0e: /* SELECT DEFAULT DRIVE */
1337 if (!DOS_ValidDrive(DL_reg(&context)))
1338 Error (InvalidDrive, EC_MediaError, EL_Disk);
1339 else
1341 DOS_SetDefaultDrive(DL_reg(&context));
1342 Error(0,0,0);
1344 AL_reg(&context) = MAX_DOS_DRIVES;
1345 break;
1347 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1348 FindFirstFCB(&context);
1349 break;
1351 case 0x13: /* DELETE FILE USING FCB */
1352 DeleteFileFCB(&context);
1353 break;
1355 case 0x17: /* RENAME FILE USING FCB */
1356 RenameFileFCB(&context);
1357 break;
1359 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1360 AL_reg(&context) = DOS_GetDefaultDrive();
1361 Error (0,0,0);
1362 break;
1364 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1366 TDB *pTask = (TDB *)GlobalLock( GetCurrentTask() );
1367 pTask->dta = MAKELONG( DX_reg(&context), DS_reg(&context) );
1368 dprintf_int(stddeb, "int21: Set DTA: %08lx\n", pTask->dta);
1370 break;
1372 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1373 DL_reg(&context) = 0;
1374 GetDriveAllocInfo(&context);
1375 break;
1377 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1378 GetDriveAllocInfo(&context);
1379 break;
1381 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1382 GetDrivePB(&context, DOS_GetDefaultDrive());
1383 break;
1385 case 0x25: /* SET INTERRUPT VECTOR */
1386 INT_SetHandler( AL_reg(&context),
1387 MAKELONG( DX_reg(&context), DS_reg(&context) ) );
1388 break;
1390 case 0x2a: /* GET SYSTEM DATE */
1391 GetSystemDate(&context);
1392 break;
1394 case 0x2b: /* SET SYSTEM DATE */
1395 fprintf( stdnimp, "SetSystemDate(%02d/%02d/%04d): not allowed\n",
1396 DL_reg(&context), DH_reg(&context), CX_reg(&context) );
1397 AL_reg(&context) = 0; /* Let's pretend we succeeded */
1398 break;
1400 case 0x2c: /* GET SYSTEM TIME */
1401 INT21_GetSystemTime(&context);
1402 break;
1404 case 0x2d: /* SET SYSTEM TIME */
1405 fprintf( stdnimp, "SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
1406 CH_reg(&context), CL_reg(&context),
1407 DH_reg(&context), DL_reg(&context) );
1408 AL_reg(&context) = 0; /* Let's pretend we succeeded */
1409 break;
1411 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1413 TDB *pTask = (TDB *)GlobalLock( GetCurrentTask() );
1414 ES_reg(&context) = SELECTOROF( pTask->dta );
1415 BX_reg(&context) = OFFSETOF( pTask->dta );
1417 break;
1419 case 0x30: /* GET DOS VERSION */
1420 AX_reg(&context) = DOSVERSION;
1421 BX_reg(&context) = 0x0012; /* 0x123456 is Wine's serial # */
1422 CX_reg(&context) = 0x3456;
1423 break;
1425 case 0x31: /* TERMINATE AND STAY RESIDENT */
1426 INT_BARF( &context, 0x21 );
1427 break;
1429 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1430 GetDrivePB(&context, (DL_reg(&context) == 0) ?
1431 (DOS_GetDefaultDrive()) : (DL_reg(&context)-1));
1432 break;
1434 case 0x33: /* MULTIPLEXED */
1435 switch (AL_reg(&context))
1437 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1438 DL_reg(&context) = 0;
1439 break;
1441 case 0x01: /* SET EXTENDED BREAK STATE */
1442 break;
1444 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1445 DL_reg(&context) = 0;
1446 break;
1448 case 0x05: /* GET BOOT DRIVE */
1449 DL_reg(&context) = 2;
1450 /* c: is Wine's bootdrive */
1451 break;
1453 case 0x06: /* GET TRUE VERSION NUMBER */
1454 BX_reg(&context) = DOSVERSION;
1455 DX_reg(&context) = 0x00;
1456 break;
1458 default:
1459 INT_BARF( &context, 0x21 );
1460 break;
1462 break;
1464 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1465 ES_reg(&context) = DosHeapHandle;
1466 BX_reg(&context) = (int)&heap->InDosFlag - (int)heap;
1467 break;
1469 case 0x35: /* GET INTERRUPT VECTOR */
1471 SEGPTR addr = INT_GetHandler( AL_reg(&context) );
1472 ES_reg(&context) = SELECTOROF(addr);
1473 BX_reg(&context) = OFFSETOF(addr);
1475 break;
1477 case 0x36: /* GET FREE DISK SPACE */
1478 GetFreeDiskSpace(&context);
1479 break;
1481 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1482 AX_reg(&context) = 0x02; /* no country support available */
1483 SET_CFLAG(&context);
1484 break;
1486 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1487 MakeDir(&context);
1488 break;
1490 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1491 RemoveDir(&context);
1492 break;
1494 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1495 ChangeDir(&context);
1496 break;
1498 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1499 CreateFile(&context);
1500 break;
1502 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1503 OpenExistingFile(&context);
1504 break;
1506 case 0x3e: /* "CLOSE" - CLOSE FILE */
1507 CloseFile(&context);
1508 break;
1510 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1511 ReadFile(&context);
1512 break;
1514 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1515 WriteFile(&context);
1516 break;
1518 case 0x41: /* "UNLINK" - DELETE FILE */
1519 if (unlink( DOS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(&context),DX_reg(&context))) ) == -1) {
1520 errno_to_doserr();
1521 AX_reg(&context) = ExtendedError;
1522 SET_CFLAG(&context);
1523 break;
1525 Error(0,0,0);
1526 RESET_CFLAG(&context);
1527 break;
1529 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1530 SeekFile(&context);
1531 break;
1533 case 0x43: /* FILE ATTRIBUTES */
1534 switch (AL_reg(&context))
1536 case 0x00:
1537 GetFileAttribute(&context);
1538 break;
1539 case 0x01:
1540 RESET_CFLAG(&context);
1541 break;
1543 break;
1545 case 0x44: /* IOCTL */
1546 switch (AL_reg(&context))
1548 case 0x00:
1549 ioctlGetDeviceInfo(&context);
1550 break;
1552 case 0x01:
1554 break;
1555 case 0x08: /* Check if drive is removable. */
1556 drive = BL_reg(&context) ? (BL_reg(&context) - 1)
1557 : DOS_GetDefaultDrive();
1558 if(!DOS_ValidDrive(drive))
1560 Error( InvalidDrive, EC_NotFound, EL_Disk );
1561 AX_reg(&context) = InvalidDrive;
1562 SET_CFLAG(&context);
1564 else
1566 if (drive > 1)
1567 AX_reg(&context) = 1; /* not removable */
1568 else
1569 AX_reg(&context) = 0; /* removable */
1570 RESET_CFLAG(&context);
1572 break;
1574 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1575 drive = BL_reg(&context) ? (BL_reg(&context) - 1)
1576 : DOS_GetDefaultDrive();
1577 if(!DOS_ValidDrive(drive))
1579 Error( InvalidDrive, EC_NotFound, EL_Disk );
1580 AX_reg(&context) = InvalidDrive;
1581 SET_CFLAG(&context);
1583 else
1585 DX_reg(&context) = (1<<9) | (1<<12) | (1<<15);
1586 RESET_CFLAG(&context);
1588 break;
1589 case 0x0a: /* check if handle (BX) is remote */
1590 /* returns DX, bit 15 set if remote, bit 14 set if date/time
1591 * not set on close
1593 DX_reg(&context) = 0;
1594 RESET_CFLAG(&context);
1595 break;
1596 case 0x0b: /* SET SHARING RETRY COUNT */
1597 if (!CX_reg(&context))
1599 AX_reg(&context) = 1;
1600 SET_CFLAG(&context);
1601 break;
1603 sharing_pause = CX_reg(&context);
1604 if (!DX_reg(&context))
1605 sharing_retries = DX_reg(&context);
1606 RESET_CFLAG(&context);
1607 break;
1609 case 0x0d:
1610 ioctlGenericBlkDevReq(&context);
1611 break;
1613 case 0x0F: /* Set logical drive mapping */
1614 /* FIXME: Not implemented at the moment, always returns error
1616 fprintf(stdnimp,"Attempt to map drive %02x\n",BL_reg(&context));
1617 AX_reg(&context) = 0x0001; /* invalid function */
1618 SET_CFLAG(&context);
1619 break;
1621 default:
1622 INT_BARF( &context, 0x21 );
1623 break;
1625 break;
1627 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1628 if ((AX_reg(&context) = dup(BX_reg(&context))) == 0xffff)
1630 errno_to_doserr();
1631 AX_reg(&context) = ExtendedError;
1632 SET_CFLAG(&context);
1634 else RESET_CFLAG(&context);
1635 break;
1637 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1638 if (dup2( BX_reg(&context), CX_reg(&context) ) == -1)
1640 errno_to_doserr();
1641 AX_reg(&context) = ExtendedError;
1642 SET_CFLAG(&context);
1644 else RESET_CFLAG(&context);
1645 break;
1647 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1648 GetCurrentDirectory(&context);
1649 AX_reg(&context) = 0x0100;
1650 /* intlist: many Microsoft products for Windows rely on this */
1651 break;
1653 case 0x48: /* ALLOCATE MEMORY */
1654 case 0x49: /* FREE MEMORY */
1655 case 0x4a: /* RESIZE MEMORY BLOCK */
1656 INT_BARF( &context, 0x21 );
1657 break;
1659 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1660 WinExec( PTR_SEG_OFF_TO_LIN(DS_reg(&context),DX_reg(&context)),
1661 SW_NORMAL );
1662 break;
1664 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1665 TASK_KillCurrentTask( AL_reg(&context) );
1666 break;
1668 case 0x4d: /* GET RETURN CODE */
1669 AX_reg(&context) = NoError; /* normal exit */
1670 break;
1672 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1673 FindFirst(&context);
1674 break;
1676 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1677 FindNext(&context);
1678 break;
1680 case 0x51: /* GET PSP ADDRESS */
1681 case 0x62: /* GET PSP ADDRESS */
1682 /* FIXME: should we return the original DOS PSP upon */
1683 /* Windows startup ? */
1684 BX_reg(&context) = GetCurrentPDB();
1685 break;
1687 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1688 ES_reg(&context) = 0x0;
1689 BX_reg(&context) = 0x0;
1690 INT_BARF( &context, 0x21 );
1691 break;
1693 case 0x56: /* "RENAME" - RENAME FILE */
1694 RenameFile(&context);
1695 break;
1697 case 0x57: /* FILE DATE AND TIME */
1698 switch (AL_reg(&context))
1700 case 0x00:
1701 GetFileDateTime(&context);
1702 break;
1703 case 0x01:
1704 SetFileDateTime(&context);
1705 break;
1707 break;
1709 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1710 switch (AL_reg(&context))
1712 case 0x00:
1713 AX_reg(&context) = 1;
1714 break;
1715 case 0x02:
1716 AX_reg(&context) = 0;
1717 break;
1718 case 0x01:
1719 case 0x03:
1720 break;
1722 RESET_CFLAG(&context);
1723 break;
1725 case 0x5a: /* CREATE TEMPORARY FILE */
1726 CreateTempFile(&context);
1727 break;
1729 case 0x5b: /* CREATE NEW FILE */
1730 CreateNewFile(&context);
1731 break;
1733 case 0x5d: /* NETWORK */
1734 case 0x5e:
1735 /* network software not installed */
1736 AX_reg(&context) = NoNetwork;
1737 SET_CFLAG(&context);
1738 break;
1740 case 0x5f: /* NETWORK */
1741 switch (AL_reg(&context))
1743 case 0x07: /* ENABLE DRIVE */
1744 if (!DOS_EnableDrive(DL_reg(&context)))
1746 Error(InvalidDrive, EC_MediaError , EL_Disk);
1747 AX_reg(&context) = InvalidDrive;
1748 SET_CFLAG(&context);
1749 break;
1751 else
1753 RESET_CFLAG(&context);
1754 break;
1756 case 0x08: /* DISABLE DRIVE */
1757 if (!DOS_DisableDrive(DL_reg(&context)))
1759 Error(InvalidDrive, EC_MediaError , EL_Disk);
1760 AX_reg(&context) = InvalidDrive;
1761 SET_CFLAG(&context);
1762 break;
1764 else
1766 RESET_CFLAG(&context);
1767 break;
1769 default:
1770 /* network software not installed */
1771 AX_reg(&context) = NoNetwork;
1772 SET_CFLAG(&context);
1773 break;
1775 break;
1777 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1778 strncpy(PTR_SEG_OFF_TO_LIN(ES_reg(&context),DI_reg(&context)),
1779 PTR_SEG_OFF_TO_LIN(DS_reg(&context),SI_reg(&context)),
1780 strlen(PTR_SEG_OFF_TO_LIN(DS_reg(&context),SI_reg(&context))) & 0x7f);
1781 RESET_CFLAG(&context);
1782 break;
1784 case 0x61: /* UNUSED */
1785 case 0x63: /* UNUSED */
1786 case 0x64: /* OS/2 DOS BOX */
1787 case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
1788 INT_BARF( &context, 0x21 );
1789 break;
1791 case 0x66: /* GLOBAL CODE PAGE TABLE */
1792 switch (AL_reg(&context))
1794 case 0x01:
1795 DX_reg(&context) = BX_reg(&context) = CodePage;
1796 RESET_CFLAG(&context);
1797 break;
1798 case 0x02:
1799 CodePage = BX_reg(&context);
1800 RESET_CFLAG(&context);
1801 break;
1803 break;
1805 case 0x67: /* SET HANDLE COUNT */
1806 RESET_CFLAG(&context);
1807 break;
1809 case 0x68: /* "FFLUSH" - COMMIT FILE */
1810 case 0x6a: /* COMMIT FILE */
1811 fsync( BX_reg(&context) );
1812 RESET_CFLAG(&context);
1813 break;
1815 case 0x69: /* DISK SERIAL NUMBER */
1816 switch (AL_reg(&context))
1818 case 0x00:
1819 GetDiskSerialNumber(&context);
1820 break;
1821 case 0x01:
1822 SetDiskSerialNumber(&context);
1823 break;
1825 break;
1827 case 0x6C: /* Extended Open/Create*/
1828 ExtendedOpenCreateFile(&context);
1829 break;
1831 case 0x70: /* MS-DOS 7 (Windows95) - ??? (country-specific?)*/
1832 case 0x71: /* MS-DOS 7 (Chicago) - LONG FILENAME FUNCTIONS */
1833 case 0x72: /* MS-DOS 7 (Windows95) - ??? */
1834 case 0x73: /* MS-DOS 7 (Windows95) - DRIVE LOCKING ??? */
1835 dprintf_int(stddeb,"int21: windows95 function AX %04x\n",
1836 AX_reg(&context));
1837 dprintf_int(stddeb, " returning unimplemented\n");
1838 SET_CFLAG(&context);
1839 AL_reg(&context) = 0;
1840 break;
1842 case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
1843 break;
1845 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1846 break;
1848 default:
1849 INT_BARF( &context, 0x21 );
1850 break;
1853 dprintf_int(stddeb,"ret21: AX %04x, BX %04x, CX %04x, DX %04x, "
1854 "SI %04x, DI %04x, DS %04x, ES %04x EFL %08lx\n",
1855 AX_reg(&context), BX_reg(&context), CX_reg(&context),
1856 DX_reg(&context), SI_reg(&context), DI_reg(&context),
1857 DS_reg(&context), ES_reg(&context), EFL_reg(&context));
1861 void INT21_Init(void)
1863 if ((DosHeapHandle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0)
1865 fprintf( stderr, "INT21_Init: Out of memory\n");
1866 exit(1);
1868 heap = (struct DosHeap *) GlobalLock(DosHeapHandle);
1870 dpb = &heap->dpb;
1871 dpbsegptr = MAKELONG( (int)&heap->dpb - (int)heap, DosHeapHandle );
1872 heap->InDosFlag = 0;
1873 strcpy(heap->biosdate, "01/01/80");