Release 961023
[wine/multimedia.git] / miscemu / int21.c
blobfb5aff479bbe9036f4a7ac536ac5d7026a6bebd1
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 "drive.h"
20 #include "file.h"
21 #include "windows.h"
22 #include "msdos.h"
23 #include "ldt.h"
24 #include "task.h"
25 #include "options.h"
26 #include "miscemu.h"
27 #include "xmalloc.h"
28 #include "stddebug.h"
29 #include "debug.h"
30 #if defined(__svr4__) || defined(_SCO_DS)
31 /* SVR4 DOESNT do locking the same way must implement properly */
32 #define LOCK_EX 0
33 #define LOCK_SH 1
34 #define LOCK_NB 8
35 #endif
38 #define DOS_GET_DRIVE(reg) ((reg) ? (reg) - 1 : DRIVE_GetCurrentDrive())
40 /* Define the drive parameter block, as used by int21/1F
41 * and int21/32. This table can be accessed through the
42 * global 'dpb' pointer, which points into the local dos
43 * heap.
45 struct DPB
47 BYTE drive_num; /* 0=A, etc. */
48 BYTE unit_num; /* Drive's unit number (?) */
49 WORD sector_size; /* Sector size in bytes */
50 BYTE high_sector; /* Highest sector in a cluster */
51 BYTE shift; /* Shift count (?) */
52 WORD reserved; /* Number of reserved sectors at start */
53 BYTE num_FAT; /* Number of FATs */
54 WORD dir_entries; /* Number of root dir entries */
55 WORD first_data; /* First data sector */
56 WORD high_cluster; /* Highest cluster number */
57 WORD sectors_in_FAT; /* Number of sectors per FAT */
58 WORD start_dir; /* Starting sector of first dir */
59 DWORD driver_head; /* Address of device driver header (?) */
60 BYTE media_ID; /* Media ID */
61 BYTE access_flag; /* Prev. accessed flag (0=yes,0xFF=no) */
62 DWORD next; /* Pointer to next DPB in list */
63 WORD free_search; /* Free cluster search start */
64 WORD free_clusters; /* Number of free clusters (0xFFFF=unknown) */
67 WORD CodePage = 437;
68 struct DPB *dpb;
69 DWORD dpbsegptr;
71 struct DosHeap {
72 BYTE InDosFlag;
73 BYTE mediaID;
74 BYTE biosdate[8];
75 struct DPB dpb;
77 static struct DosHeap *heap;
78 static WORD DosHeapHandle;
80 WORD sharing_retries = 3; /* number of retries at sharing violation */
81 WORD sharing_pause = 1; /* pause between retries */
83 extern char TempDirectory[];
85 BYTE *GetCurrentDTA(void)
87 TDB *pTask = (TDB *)GlobalLock16( 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 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 int INT21_GetFreeDiskSpace( SIGCONTEXT *context )
138 DWORD cluster_sectors, sector_bytes, free_clusters, total_clusters;
139 char root[] = "A:\\";
141 *root += DOS_GET_DRIVE( DL_reg(context) );
142 if (!GetDiskFreeSpace32A( root, &cluster_sectors, &sector_bytes,
143 &free_clusters, &total_clusters )) return 0;
144 AX_reg(context) = cluster_sectors;
145 BX_reg(context) = free_clusters;
146 CX_reg(context) = sector_bytes;
147 DX_reg(context) = total_clusters;
148 return 1;
151 static int INT21_GetDriveAllocInfo( SIGCONTEXT *context )
153 if (!INT21_GetFreeDiskSpace( context )) return 0;
154 heap->mediaID = 0xf0;
155 DS_reg(context) = DosHeapHandle;
156 BX_reg(context) = (int)&heap->mediaID - (int)heap;
157 return 1;
160 static void GetDrivePB( SIGCONTEXT *context, int drive )
162 if(!DRIVE_IsValid(drive))
164 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
165 AX_reg(context) = 0x00ff;
167 else
169 dprintf_int(stddeb, "int21: GetDrivePB not fully implemented.\n");
171 /* FIXME: I have no idea what a lot of this information should
172 * say or whether it even really matters since we're not allowing
173 * direct block access. However, some programs seem to depend on
174 * getting at least _something_ back from here. The 'next' pointer
175 * does worry me, though. Should we have a complete table of
176 * separate DPBs per drive? Probably, but I'm lazy. :-) -CH
178 dpb->drive_num = dpb->unit_num = drive; /* The same? */
179 dpb->sector_size = 512;
180 dpb->high_sector = 1;
181 dpb->shift = drive < 2 ? 0 : 6; /* 6 for HD, 0 for floppy */
182 dpb->reserved = 0;
183 dpb->num_FAT = 1;
184 dpb->dir_entries = 2;
185 dpb->first_data = 2;
186 dpb->high_cluster = 64000;
187 dpb->sectors_in_FAT = 1;
188 dpb->start_dir = 1;
189 dpb->driver_head = 0;
190 dpb->media_ID = (drive > 1) ? 0xF8 : 0xF0;
191 dpb->access_flag = 0;
192 dpb->next = 0;
193 dpb->free_search = 0;
194 dpb->free_clusters = 0xFFFF; /* unknown */
196 AL_reg(context) = 0x00;
197 DS_reg(context) = SELECTOROF(dpbsegptr);
198 BX_reg(context) = OFFSETOF(dpbsegptr);
203 static void ioctlGetDeviceInfo( SIGCONTEXT *context )
205 dprintf_int (stddeb, "int21: ioctl (%d, GetDeviceInfo)\n", BX_reg(context));
207 DX_reg(context) = 0x0942;
208 /* bits 0-5 are current drive
209 * bit 6 - file has NOT been written..FIXME: correct?
210 * bit 8 - generate int24 if no diskspace on write/ read past end of file
211 * bit 11 - media not removable
213 RESET_CFLAG(context);
216 static void ioctlGenericBlkDevReq( SIGCONTEXT *context )
218 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
219 int drive = DOS_GET_DRIVE( BL_reg(context) );
221 if (!DRIVE_IsValid(drive))
223 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
224 AX_reg(context) = DOS_ExtendedError;
225 SET_CFLAG(context);
226 return;
229 if (CH_reg(context) != 0x08)
231 INT_BARF( context, 0x21 );
232 return;
234 switch (CL_reg(context)) {
235 case 0x4a: /* lock logical volume */
236 dprintf_int(stddeb,"int21: lock logical volume (%d) level %d mode %d\n",drive,BH_reg(context),DX_reg(context));
237 return;
238 case 0x60: /* get device parameters */
239 /* used by w4wgrp's winfile */
240 memset(dataptr, 0, 0x26);
241 dataptr[0] = 0x04;
242 dataptr[6] = 0; /* media type */
243 if (drive > 1)
245 dataptr[1] = 0x05; /* fixed disk */
246 setword(&dataptr[2], 0x01); /* non removable */
247 setword(&dataptr[4], 0x300); /* # of cylinders */
249 else
251 dataptr[1] = 0x07; /* block dev, floppy */
252 setword(&dataptr[2], 0x02); /* removable */
253 setword(&dataptr[4], 80); /* # of cylinders */
255 CreateBPB(drive, &dataptr[7]);
256 RESET_CFLAG(context);
257 return;
258 case 0x66:/* get disk serial number */
259 { char label[12],fsname[9],path[4];
260 DWORD serial;
262 strcpy(path,"x:\\");path[0]=drive+'A';
263 GetVolumeInformation32A(
264 path,label,12,&serial,NULL,NULL,fsname,9
266 *(WORD*)dataptr = 0;
267 memcpy(dataptr+2,&serial,4);
268 memcpy(dataptr+6,label ,11);
269 memcpy(dataptr+17,fsname,8);
270 return;
272 case 0x6a:
273 dprintf_int(stddeb,"int21: logical volume %d unlocked.\n",drive);
274 return;
275 default:
276 INT_BARF( context, 0x21 );
280 static void GetSystemDate( SIGCONTEXT *context )
282 struct tm *now;
283 time_t ltime;
285 ltime = time(NULL);
286 now = localtime(&ltime);
288 CX_reg(context) = now->tm_year + 1900;
289 DX_reg(context) = ((now->tm_mon + 1) << 8) | now->tm_mday;
290 AX_reg(context) = now->tm_wday;
293 static void INT21_GetSystemTime( SIGCONTEXT *context )
295 struct tm *now;
296 struct timeval tv;
297 time_t seconds;
299 gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */
300 seconds = tv.tv_sec;
301 now = localtime(&seconds);
303 CX_reg(context) = (now->tm_hour<<8) | now->tm_min;
304 DX_reg(context) = (now->tm_sec<<8) | tv.tv_usec/10000;
305 /* Note hundredths of seconds */
308 static void INT21_CreateFile( SIGCONTEXT *context )
310 AX_reg(context) = _lcreat( PTR_SEG_OFF_TO_LIN( DS_reg(context),
311 DX_reg(context) ), CX_reg(context) );
312 if (AX_reg(context) == (WORD)HFILE_ERROR)
314 AX_reg(context) = DOS_ExtendedError;
315 SET_CFLAG(context);
320 void OpenExistingFile( SIGCONTEXT *context )
322 AX_reg(context) = _lopen( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)),
323 AL_reg(context) );
324 if (AX_reg(context) == (WORD)HFILE_ERROR)
326 AX_reg(context) = DOS_ExtendedError;
327 SET_CFLAG(context);
329 #if 0
330 int handle;
331 int mode;
332 int lock;
334 dprintf_int (stddeb, "int21: open (%s, %d) = %d\n",
335 DOS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),
336 DX_reg(context))), mode, handle);
338 switch (AX_reg(context) & 0x0070)
340 case 0x00: /* compatability mode */
341 case 0x40: /* DENYNONE */
342 lock = -1;
343 break;
345 case 0x30: /* DENYREAD */
346 dprintf_int(stddeb,
347 "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
348 (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)));
349 case 0x10: /* DENYALL */
350 lock = LOCK_EX;
351 break;
353 case 0x20: /* DENYWRITE */
354 lock = LOCK_SH;
355 break;
357 default:
358 lock = -1;
361 if (lock != -1)
364 int result,retries=sharing_retries;
366 #if defined(__svr4__) || defined(_SCO_DS)
367 printf("Should call flock and needs porting to lockf\n");
368 result = 0;
369 retries = 0;
370 #else
371 result = flock(handle, lock | LOCK_NB);
372 #endif
373 if ( retries && (!result) )
375 int i;
376 for(i=0;i<32768*((int)sharing_pause);i++)
377 result++; /* stop the optimizer */
378 for(i=0;i<32768*((int)sharing_pause);i++)
379 result--;
382 while( (!result) && (!(retries--)) );
384 if(result)
386 errno_to_doserr();
387 AX_reg(context) = ExtendedError;
388 close(handle);
389 SET_CFLAG(context);
390 return;
395 Error (0,0,0);
396 AX_reg(context) = handle;
397 RESET_CFLAG(context);
398 #endif
401 static void CloseFile( SIGCONTEXT *context )
403 if ((AX_reg(context) = _lclose( BX_reg(context) )) != 0)
405 AX_reg(context) = DOS_ExtendedError;
406 SET_CFLAG(context);
410 void ExtendedOpenCreateFile(SIGCONTEXT *context )
412 BYTE action=DL_reg(context);
413 dprintf_int(stddeb, "int21: extended open/create: file= %s \n",
414 DOSFS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context)),FALSE));
415 /* Shuffle arguments to call OpenExistingFile */
416 AL_reg(context) = BL_reg(context);
417 DX_reg(context) = SI_reg(context);
418 /* BX,CX and DX should be preserved */
419 OpenExistingFile(context);
420 if ((EFL_reg(context) & 0x0001)==0)
421 { /* It exists */
422 dprintf_int(stddeb, "int21: extended open/create %s exists \n",
423 DOSFS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context)),TRUE));
424 /* Now decide what do do */
425 if ((action & 0x07)== 0)
427 BX_reg(context) = AX_reg(context);
428 CloseFile(context);
429 AX_reg(context) = 0x0050;/*File exists*/
430 CX_reg(context) = 0;
431 SET_CFLAG(context);
432 dprintf_int(stddeb, "int21: extended open/create: failed because file exixts \n");
433 return;
435 if ((action & 0x07)== 2) {
436 /* Truncate it, but first check if opend for write */
437 if ((BL_reg(context) & 0x0007)== 0) {
438 BX_reg(context) = AX_reg(context);
439 CloseFile(context);
440 dprintf_int(stddeb, "int21: extended open/create: failed, trunc on ro file");
441 AX_reg(context) = 0x000C;/*Access code invalid*/
442 CX_reg(context) = 0;
443 SET_CFLAG(context);
444 return;
446 /* Shuffle arguments to call CloseFile */
447 dprintf_int(stddeb, "int21: extended open/create: Closing before truncate\n");
448 BX_reg(context) = AX_reg(context);
449 /* BX and DX should be preserved */
450 CloseFile(context);
451 if (EFL_reg(context) & 0x0001) {
452 dprintf_int(stddeb, "int21: extended open/create: close before trunc failed");
453 AX_reg(context) = 0x0019;/*Seek Error*/
454 CX_reg(context) = 0;
455 SET_CFLAG(context);
457 /* Shuffle arguments to call CreateFile */
458 dprintf_int(stddeb, "int21: extended open/create: Truncating\n");
459 AL_reg(context) = BL_reg(context);
460 /* CX is still the same */
461 DX_reg(context) = SI_reg(context);
462 INT21_CreateFile(context);
463 if (EFL_reg(context) & 0x0001) { /*no file open, flags set */
464 dprintf_int(stddeb, "int21: extended open/create: truncfailed");
465 return;
467 CX_reg(context) = 3;
468 return;
470 CX_reg(context) = 1;
471 return;
473 else /* file does not exist */
475 RESET_CFLAG(context); /* was set by OpenExistingFile(context) */
476 dprintf_int(stddeb, "int21: extended open/create %s dosen't exists \n",
477 DOSFS_GetUnixFileName(PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context)),FALSE));
478 if ((action & 0xF0)== 0) {
479 CX_reg(context) = 0;
480 SET_CFLAG(context);
481 dprintf_int(stddeb, "int21: extended open/create: failed, file dosen't exist\n");
482 return;
484 /* Shuffle arguments to call CreateFile */
485 dprintf_int(stddeb, "int21: extended open/create: Creating\n");
486 AL_reg(context) = BL_reg(context);
487 /* CX should still be the same */
488 DX_reg(context) = SI_reg(context);
489 INT21_CreateFile(context);
490 if (EFL_reg(context) & 0x0001) { /*no file open, flags set */
491 dprintf_int(stddeb, "int21: extended open/create: create failed\n");
492 return;
494 CX_reg(context) = 2;
495 return;
500 static int INT21_RenameFile( SIGCONTEXT *context )
502 const char *newname, *oldname;
503 char *buffer;
505 /* FIXME: should not rename over an existing file */
506 dprintf_int(stddeb,"int21: renaming %s to %s\n",
507 (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)),
508 (char *)PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context)));
510 oldname = DOSFS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),
511 DX_reg(context)), TRUE );
512 if (!oldname) return 0;
513 buffer = xstrdup( oldname );
514 newname = DOSFS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(ES_reg(context),
515 DI_reg(context)), FALSE );
516 if (!newname)
518 free( buffer );
519 return 0;
522 if (rename( buffer, newname) == -1)
524 FILE_SetDosError();
525 free( buffer );
526 return 0;
528 free( buffer );
529 return 1;
533 static void INT21_ChangeDir( SIGCONTEXT *context )
535 int drive;
536 char *dirname = PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context));
538 dprintf_int(stddeb,"int21: changedir %s\n", dirname);
539 if (dirname[0] && (dirname[1] == ':'))
541 drive = toupper(dirname[0]) - 'A';
542 dirname += 2;
544 else drive = DRIVE_GetCurrentDrive();
545 if (!DRIVE_Chdir( drive, dirname ))
547 AX_reg(context) = DOS_ExtendedError;
548 SET_CFLAG(context);
553 static int INT21_FindFirst( SIGCONTEXT *context )
555 const char *path, *unixPath, *mask;
556 char *p;
557 FINDFILE_DTA *dta = (FINDFILE_DTA *)GetCurrentDTA();
559 path = (const char *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
560 dta->unixPath = NULL;
561 if (!(unixPath = DOSFS_GetUnixFileName( path, FALSE )))
563 AX_reg(context) = DOS_ExtendedError;
564 SET_CFLAG(context);
565 return 0;
567 dta->unixPath = xstrdup( unixPath );
568 p = strrchr( dta->unixPath, '/' );
569 *p = '\0';
570 if (!(mask = DOSFS_ToDosFCBFormat( p + 1 )))
572 free( dta->unixPath );
573 dta->unixPath = NULL;
574 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
575 AX_reg(context) = ER_FileNotFound;
576 SET_CFLAG(context);
577 return 0;
579 memcpy( dta->mask, mask, sizeof(dta->mask) );
580 dta->drive = (path[0] && (path[1] == ':')) ? toupper(path[0]) - 'A'
581 : DRIVE_GetCurrentDrive();
582 dta->count = 0;
583 dta->search_attr = CL_reg(context);
584 return 1;
588 static int INT21_FindNext( SIGCONTEXT *context )
590 FINDFILE_DTA *dta = (FINDFILE_DTA *)GetCurrentDTA();
591 DOS_DIRENT entry;
592 int count;
594 if (!dta->unixPath) return 0;
595 if (!(count = DOSFS_FindNext( dta->unixPath, dta->mask, dta->drive,
596 dta->search_attr, dta->count, &entry )))
598 free( dta->unixPath );
599 dta->unixPath = NULL;
600 return 0;
602 if ((int)dta->count + count > 0xffff)
604 fprintf( stderr, "Too many directory entries in %s\n", dta->unixPath );
605 free( dta->unixPath );
606 dta->unixPath = NULL;
607 return 0;
609 dta->count += count;
610 dta->fileattr = entry.attr;
611 dta->filetime = entry.time;
612 dta->filedate = entry.date;
613 dta->filesize = entry.size;
614 strcpy( dta->filename, DOSFS_ToDosDTAFormat( entry.name ) );
615 return 1;
619 static int INT21_CreateTempFile( SIGCONTEXT *context )
621 static int counter = 0;
622 char *name = PTR_SEG_OFF_TO_LIN( DS_reg(context), DX_reg(context) );
623 char *p = name + strlen(name);
625 for (;;)
627 sprintf( p, "wine%04x.%03d", (int)getpid(), counter );
628 counter = (counter + 1) % 1000;
630 if ((AX_reg(context) = _lcreat_uniq( name, 0 )) != (WORD)HFILE_ERROR)
632 dprintf_int( stddeb, "INT21_CreateTempFile: created %s\n", name );
633 return 1;
635 if (DOS_ExtendedError != ER_FileExists) return 0;
640 static int INT21_GetCurrentDirectory( SIGCONTEXT *context )
642 int drive = DOS_GET_DRIVE( DL_reg(context) );
643 char *ptr = (char *)PTR_SEG_OFF_TO_LIN( DS_reg(context), SI_reg(context) );
645 if (!DRIVE_IsValid(drive))
647 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
648 return 0;
651 lstrcpyn32A( ptr, DRIVE_GetDosCwd(drive), 64 );
652 if (!ptr[0]) strcpy( ptr, "\\" );
653 return 1;
657 static int INT21_GetDiskSerialNumber( SIGCONTEXT *context )
659 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
660 int drive = DOS_GET_DRIVE( BL_reg(context) );
662 if (!DRIVE_IsValid(drive))
664 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
665 return 0;
668 *(WORD *)dataptr = 0;
669 *(DWORD *)(dataptr + 2) = DRIVE_GetSerialNumber( drive );
670 memcpy( dataptr + 6, DRIVE_GetLabel( drive ), 11 );
671 strncpy(dataptr + 0x11, "FAT16 ", 8);
672 return 1;
676 static int INT21_SetDiskSerialNumber( SIGCONTEXT *context )
678 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
679 int drive = DOS_GET_DRIVE( BL_reg(context) );
681 if (!DRIVE_IsValid(drive))
683 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
684 return 0;
687 DRIVE_SetSerialNumber( drive, *(DWORD *)(dataptr + 2) );
688 return 1;
692 /* microsoft's programmers should be shot for using CP/M style int21
693 calls in Windows for Workgroup's winfile.exe */
695 static int INT21_FindFirstFCB( SIGCONTEXT *context )
697 BYTE *fcb = (BYTE *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
698 FINDFILE_FCB *pFCB;
699 BYTE attr;
700 char buffer[] = "A:.";
701 const char *unixPath;
703 if (*fcb == 0xff)
705 attr = fcb[6];
706 pFCB = (FINDFILE_FCB *)(fcb + 7);
708 else
710 attr = 0;
711 pFCB = (FINDFILE_FCB *)fcb;
714 buffer[0] += DOS_GET_DRIVE( pFCB->drive );
715 pFCB->unixPath = NULL;
716 if (!(unixPath = DOSFS_GetUnixFileName( buffer, TRUE ))) return 0;
717 pFCB->unixPath = xstrdup( unixPath );
718 pFCB->count = 0;
719 return 1;
723 static int INT21_FindNextFCB( SIGCONTEXT *context )
725 BYTE *fcb = (BYTE *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
726 FINDFILE_FCB *pFCB;
727 DOS_DIRENTRY_LAYOUT *pResult = (DOS_DIRENTRY_LAYOUT *)GetCurrentDTA();
728 DOS_DIRENT entry;
729 BYTE attr;
730 int count;
732 if (*fcb == 0xff)
734 attr = fcb[6];
735 pFCB = (FINDFILE_FCB *)(fcb + 7);
737 else
739 attr = 0;
740 pFCB = (FINDFILE_FCB *)fcb;
743 if (!pFCB->unixPath) return 0;
744 if (!(count = DOSFS_FindNext( pFCB->unixPath, pFCB->filename,
745 DOS_GET_DRIVE( pFCB->drive ), attr,
746 pFCB->count, &entry )))
748 free( pFCB->unixPath );
749 pFCB->unixPath = NULL;
750 return 0;
752 pFCB->count += count;
754 memcpy( pResult->filename, entry.name, sizeof(pResult->filename) );
755 pResult->fileattr = entry.attr;
756 memset( pResult->reserved, 0, sizeof(pResult->reserved) );
757 pResult->filetime = entry.time;
758 pResult->filedate = entry.date;
759 pResult->cluster = 0; /* what else? */
760 pResult->filesize = entry.size;
761 return 1;
765 static void DeleteFileFCB( SIGCONTEXT *context )
767 fprintf( stderr, "DeleteFileFCB: not implemented yet\n" );
768 #if 0
769 BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
770 struct dosdirent *dp;
771 char temp[256], *ptr;
772 int drive = DOS_GET_DRIVE( *fcb );
774 DumpFCB( fcb );
776 temp[0] = '\\';
777 strcpy(temp+1, DRIVE_GetDosCwd(drive));
778 strcat(temp, "\\");
779 strncat(temp, fcb + 1, 8);
780 ChopOffWhiteSpace(temp);
781 strncat(temp, fcb + 9, 3);
782 ChopOffWhiteSpace(temp);
784 if ((dp = DOS_opendir(temp)) == NULL) {
785 Error(InvalidDrive, EC_MediaError , EL_Disk);
786 AX_reg(context) = 0xff;
787 return;
790 temp[0] = '\\';
791 strcpy(temp+1, DRIVE_GetDosCwd(drive) );
792 strcat(temp, "\\");
794 ptr = temp + strlen(temp);
796 while (DOS_readdir(dp) != NULL)
798 strcpy(ptr, dp->filename);
799 dprintf_int(stddeb, "int21: delete file %s\n", temp);
800 /* unlink(DOS_GetUnixFileName(temp)); */
802 DOS_closedir(dp);
803 AX_reg(context) = 0;
804 #endif
807 static void RenameFileFCB( SIGCONTEXT *context )
809 fprintf( stderr, "RenameFileFCB: not implemented yet\n" );
810 #if 0
811 BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
812 struct dosdirent *dp;
813 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
814 int drive = DOS_GET_DRIVE( *fcb );
816 DumpFCB( fcb );
818 temp[0] = '\\';
819 strcpy(temp+1, DRIVE_GetDosCwd(drive) );
820 strcat(temp, "\\");
821 strncat(temp, fcb + 1, 8);
822 ChopOffWhiteSpace(temp);
823 strncat(temp, fcb + 9, 3);
824 ChopOffWhiteSpace(temp);
826 if ((dp = DOS_opendir(temp)) == NULL) {
827 Error(InvalidDrive, EC_MediaError , EL_Disk);
828 AX_reg(context) = 0xff;
829 return;
832 oldname[0] = '\\';
833 strcpy(oldname+1, DRIVE_GetDosCwd(drive) );
834 strcat(oldname, "\\");
835 strcpy( newname, oldname );
836 oldnameptr = oldname + strlen(oldname);
837 newnameptr = newname + strlen(newname);
839 while (DOS_readdir(dp) != NULL)
841 strcpy(oldnameptr, dp->filename);
842 strcpy(newnameptr, fcb + 1);
843 dprintf_int(stddeb, "int21: renamefile %s -> %s\n",
844 oldname, newname);
846 DOS_closedir(dp);
847 AX_reg(context) = 0;
848 #endif
853 static void fLock( SIGCONTEXT * context )
855 #if 0
856 struct flock f;
857 int result,retries=sharing_retries;
859 f.l_start = MAKELONG(DX_reg(context),CX_reg(context));
860 f.l_len = MAKELONG(DI_reg(context),SI_reg(context));
861 f.l_whence = 0;
862 f.l_pid = 0;
864 switch ( AX_reg(context) & 0xff )
866 case 0x00: /* LOCK */
867 f.l_type = F_WRLCK;
868 break;
870 case 0x01: /* UNLOCK */
871 f.l_type = F_UNLCK;
872 break;
874 default:
875 AX_reg(context) = 0x0001;
876 SET_CFLAG(context);
877 return;
881 result = fcntl(BX_reg(context),F_SETLK,&f);
882 if ( retries && (!result) )
884 int i;
885 for(i=0;i<32768*((int)sharing_pause);i++)
886 result++; /* stop the optimizer */
887 for(i=0;i<32768*((int)sharing_pause);i++)
888 result--;
891 while( (!result) && (!(retries--)) );
893 if(result)
895 FILE_SetDosError();
896 AX_reg(context) = DOS_ExtendedError;
897 SET_CFLAG(context);
898 return;
900 #endif
904 static int INT21_GetFileAttribute( SIGCONTEXT * context )
906 const char *unixName;
908 unixName = DOSFS_GetUnixFileName( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)), TRUE );
909 if (!unixName) return 0;
910 if (!FILE_Stat( unixName, &CL_reg(context), NULL, NULL, NULL )) return 0;
911 CH_reg(context) = 0;
912 dprintf_int( stddeb, "INT21_GetFileAttributes(%s) = 0x%x\n",
913 unixName, CX_reg(context) );
914 return 1;
918 extern void LOCAL_PrintHeap (WORD ds);
920 /***********************************************************************
921 * DOS3Call (KERNEL.102)
923 void DOS3Call( SIGCONTEXT *context )
925 dprintf_int( stddeb, "int21: AX=%04x BX=%04x CX=%04x DX=%04x "
926 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
927 AX_reg(context), BX_reg(context), CX_reg(context),
928 DX_reg(context), SI_reg(context), DI_reg(context),
929 DS_reg(context), ES_reg(context), EFL_reg(context));
931 if (AH_reg(context) == 0x59) /* Get extended error info */
933 AX_reg(context) = DOS_ExtendedError;
934 BH_reg(context) = DOS_ErrorClass;
935 BL_reg(context) = DOS_ErrorAction;
936 CH_reg(context) = DOS_ErrorLocus;
937 return;
940 DOS_ERROR( 0, 0, 0, 0 );
941 RESET_CFLAG(context); /* Not sure if this is a good idea */
943 switch(AH_reg(context))
945 case 0x00: /* TERMINATE PROGRAM */
946 TASK_KillCurrentTask( 0 );
947 break;
949 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
950 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
951 case 0x03: /* READ CHARACTER FROM STDAUX */
952 case 0x04: /* WRITE CHARACTER TO STDAUX */
953 case 0x05: /* WRITE CHARACTER TO PRINTER */
954 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
955 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
956 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
957 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
958 case 0x0a: /* BUFFERED INPUT */
959 case 0x0b: /* GET STDIN STATUS */
960 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
961 case 0x0f: /* OPEN FILE USING FCB */
962 case 0x10: /* CLOSE FILE USING FCB */
963 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
964 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
965 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
966 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
967 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
968 case 0x23: /* GET FILE SIZE FOR FCB */
969 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
970 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
971 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
972 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
973 case 0x29: /* PARSE FILENAME INTO FCB */
974 case 0x2e: /* SET VERIFY FLAG */
975 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
976 "SWITCHAR" - SET SWITCH CHARACTER
977 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
978 case 0x54: /* GET VERIFY FLAG */
979 INT_BARF( context, 0x21 );
980 break;
982 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
983 case 0x1d:
984 case 0x1e:
985 case 0x20:
986 case 0x6b: /* NULL FUNCTION */
987 AL_reg(context) = 0;
988 break;
990 case 0x5c: /* "FLOCK" - RECORD LOCKING */
991 fLock(context);
992 break;
994 case 0x0d: /* DISK BUFFER FLUSH */
995 RESET_CFLAG(context); /* dos 6+ only */
996 break;
998 case 0x0e: /* SELECT DEFAULT DRIVE */
999 DRIVE_SetCurrentDrive( DL_reg(context) );
1000 AL_reg(context) = MAX_DOS_DRIVES;
1001 break;
1003 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1004 if (!INT21_FindFirstFCB(context))
1006 AL_reg(context) = 0xff;
1007 break;
1009 /* else fall through */
1011 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1012 AL_reg(context) = INT21_FindNextFCB(context) ? 0x00 : 0xff;
1013 break;
1015 case 0x13: /* DELETE FILE USING FCB */
1016 DeleteFileFCB(context);
1017 break;
1019 case 0x17: /* RENAME FILE USING FCB */
1020 RenameFileFCB(context);
1021 break;
1023 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1024 AL_reg(context) = DRIVE_GetCurrentDrive();
1025 break;
1027 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1029 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
1030 pTask->dta = PTR_SEG_OFF_TO_SEGPTR(DS_reg(context),DX_reg(context));
1031 dprintf_int(stddeb, "int21: Set DTA: %08lx\n", pTask->dta);
1033 break;
1035 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1036 DL_reg(context) = 0;
1037 if (!INT21_GetDriveAllocInfo(context)) AX_reg(context) = 0xffff;
1038 break;
1040 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1041 if (!INT21_GetDriveAllocInfo(context)) AX_reg(context) = 0xffff;
1042 break;
1044 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1045 GetDrivePB(context, DRIVE_GetCurrentDrive());
1046 break;
1048 case 0x25: /* SET INTERRUPT VECTOR */
1049 INT_SetHandler( AL_reg(context),
1050 (FARPROC16)PTR_SEG_OFF_TO_SEGPTR( DS_reg(context),
1051 DX_reg(context)));
1052 break;
1054 case 0x2a: /* GET SYSTEM DATE */
1055 GetSystemDate(context);
1056 break;
1058 case 0x2b: /* SET SYSTEM DATE */
1059 fprintf( stdnimp, "SetSystemDate(%02d/%02d/%04d): not allowed\n",
1060 DL_reg(context), DH_reg(context), CX_reg(context) );
1061 AL_reg(context) = 0; /* Let's pretend we succeeded */
1062 break;
1064 case 0x2c: /* GET SYSTEM TIME */
1065 INT21_GetSystemTime(context);
1066 break;
1068 case 0x2d: /* SET SYSTEM TIME */
1069 fprintf( stdnimp, "SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
1070 CH_reg(context), CL_reg(context),
1071 DH_reg(context), DL_reg(context) );
1072 AL_reg(context) = 0; /* Let's pretend we succeeded */
1073 break;
1075 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1077 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
1078 ES_reg(context) = SELECTOROF( pTask->dta );
1079 BX_reg(context) = OFFSETOF( pTask->dta );
1081 break;
1083 case 0x30: /* GET DOS VERSION */
1084 AX_reg(context) = DOSVERSION;
1085 BX_reg(context) = 0x0012; /* 0x123456 is Wine's serial # */
1086 CX_reg(context) = 0x3456;
1087 break;
1089 case 0x31: /* TERMINATE AND STAY RESIDENT */
1090 INT_BARF( context, 0x21 );
1091 break;
1093 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1094 GetDrivePB(context, DOS_GET_DRIVE( DL_reg(context) ) );
1095 break;
1097 case 0x33: /* MULTIPLEXED */
1098 switch (AL_reg(context))
1100 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1101 DL_reg(context) = 0;
1102 break;
1104 case 0x01: /* SET EXTENDED BREAK STATE */
1105 break;
1107 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1108 DL_reg(context) = 0;
1109 break;
1111 case 0x05: /* GET BOOT DRIVE */
1112 DL_reg(context) = 2;
1113 /* c: is Wine's bootdrive */
1114 break;
1116 case 0x06: /* GET TRUE VERSION NUMBER */
1117 BX_reg(context) = DOSVERSION;
1118 DX_reg(context) = 0x00;
1119 break;
1121 default:
1122 INT_BARF( context, 0x21 );
1123 break;
1125 break;
1127 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1128 ES_reg(context) = DosHeapHandle;
1129 BX_reg(context) = (int)&heap->InDosFlag - (int)heap;
1130 break;
1132 case 0x35: /* GET INTERRUPT VECTOR */
1134 FARPROC16 addr = INT_GetHandler( AL_reg(context) );
1135 ES_reg(context) = SELECTOROF(addr);
1136 BX_reg(context) = OFFSETOF(addr);
1138 break;
1140 case 0x36: /* GET FREE DISK SPACE */
1141 if (!INT21_GetFreeDiskSpace(context)) AX_reg(context) = 0xffff;
1142 break;
1144 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1145 AX_reg(context) = 0x02; /* no country support available */
1146 SET_CFLAG(context);
1147 break;
1149 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1150 if (!CreateDirectory16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1151 DX_reg(context) ), NULL))
1153 AX_reg(context) = DOS_ExtendedError;
1154 SET_CFLAG(context);
1156 break;
1158 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1159 if (!RemoveDirectory16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1160 DX_reg(context) )))
1162 AX_reg(context) = DOS_ExtendedError;
1163 SET_CFLAG(context);
1165 break;
1167 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1168 INT21_ChangeDir(context);
1169 break;
1171 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1172 AX_reg(context) = _lcreat( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1173 DX_reg(context) ), CX_reg(context) );
1174 if (AX_reg(context) == (WORD)HFILE_ERROR)
1176 AX_reg(context) = DOS_ExtendedError;
1177 SET_CFLAG(context);
1179 break;
1181 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1182 OpenExistingFile(context);
1183 break;
1185 case 0x3e: /* "CLOSE" - CLOSE FILE */
1186 if ((AX_reg(context) = _lclose( BX_reg(context) )) != 0)
1188 AX_reg(context) = DOS_ExtendedError;
1189 SET_CFLAG(context);
1191 break;
1193 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1195 LONG result = WIN16_hread( BX_reg(context),
1196 PTR_SEG_OFF_TO_SEGPTR( DS_reg(context),
1197 DX_reg(context) ),
1198 CX_reg(context) );
1199 if (result == -1)
1201 AX_reg(context) = DOS_ExtendedError;
1202 SET_CFLAG(context);
1204 else AX_reg(context) = (WORD)result;
1206 break;
1208 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1210 LONG result = _hwrite( BX_reg(context),
1211 PTR_SEG_OFF_TO_LIN( DS_reg(context),
1212 DX_reg(context) ),
1213 CX_reg(context) );
1214 if (result == -1)
1216 AX_reg(context) = DOS_ExtendedError;
1217 SET_CFLAG(context);
1219 else AX_reg(context) = (WORD)result;
1221 break;
1223 case 0x41: /* "UNLINK" - DELETE FILE */
1224 if (!DeleteFile32A( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1225 DX_reg(context) )))
1227 AX_reg(context) = DOS_ExtendedError;
1228 SET_CFLAG(context);
1230 break;
1232 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1234 LONG status = _llseek( BX_reg(context),
1235 MAKELONG(DX_reg(context),CX_reg(context)),
1236 AL_reg(context) );
1237 if (status == HFILE_ERROR)
1239 AX_reg(context) = DOS_ExtendedError;
1240 SET_CFLAG(context);
1241 break;
1243 AX_reg(context) = LOWORD(status);
1244 DX_reg(context) = HIWORD(status);
1246 break;
1248 case 0x43: /* FILE ATTRIBUTES */
1249 switch (AL_reg(context))
1251 case 0x00:
1252 if (!INT21_GetFileAttribute(context))
1254 AX_reg(context) = DOS_ExtendedError;
1255 SET_CFLAG(context);
1257 break;
1258 case 0x01:
1259 RESET_CFLAG(context);
1260 break;
1262 break;
1264 case 0x44: /* IOCTL */
1265 switch (AL_reg(context))
1267 case 0x00:
1268 ioctlGetDeviceInfo(context);
1269 break;
1271 case 0x01:
1272 break;
1273 case 0x05:{ /* IOCTL - WRITE TO BLOCK DEVICE CONTROL CHANNEL */
1274 BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context));
1275 int i;
1276 int drive = DOS_GET_DRIVE(BL_reg(context));
1278 fprintf(stdnimp,"int21: program tried to write to block device control channel of drive %d:\n",drive);
1279 for (i=0;i<CX_reg(context);i++)
1280 fprintf(stdnimp,"%02x ",dataptr[i]);
1281 fprintf(stdnimp,"\n");
1282 AX_reg(context)=CX_reg(context);
1283 break;
1285 case 0x08: /* Check if drive is removable. */
1286 switch(GetDriveType16( DOS_GET_DRIVE( BL_reg(context) )))
1288 case DRIVE_CANNOTDETERMINE:
1289 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
1290 AX_reg(context) = ER_InvalidDrive;
1291 SET_CFLAG(context);
1292 break;
1293 case DRIVE_REMOVABLE:
1294 AX_reg(context) = 0; /* removable */
1295 break;
1296 default:
1297 AX_reg(context) = 1; /* not removable */
1298 break;
1300 break;
1302 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1303 switch(GetDriveType16( DOS_GET_DRIVE( BL_reg(context) )))
1305 case DRIVE_CANNOTDETERMINE:
1306 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
1307 AX_reg(context) = ER_InvalidDrive;
1308 SET_CFLAG(context);
1309 break;
1310 case DRIVE_REMOTE:
1311 DX_reg(context) = (1<<9) | (1<<12); /* remote */
1312 break;
1313 default:
1314 DX_reg(context) = 0; /* FIXME: use driver attr here */
1315 break;
1317 break;
1319 case 0x0a: /* check if handle (BX) is remote */
1320 /* returns DX, bit 15 set if remote, bit 14 set if date/time
1321 * not set on close
1323 DX_reg(context) = 0;
1324 break;
1326 case 0x0b: /* SET SHARING RETRY COUNT */
1327 if (!CX_reg(context))
1329 AX_reg(context) = 1;
1330 SET_CFLAG(context);
1331 break;
1333 sharing_pause = CX_reg(context);
1334 if (!DX_reg(context))
1335 sharing_retries = DX_reg(context);
1336 RESET_CFLAG(context);
1337 break;
1339 case 0x0d:
1340 ioctlGenericBlkDevReq(context);
1341 break;
1342 case 0x0e: /* get logical drive mapping */
1343 AL_reg(context) = 0; /* drive has no mapping */
1344 break;
1346 case 0x0F: /* Set logical drive mapping */
1347 /* FIXME: Not implemented at the moment, always returns error
1349 INT_BARF( context, 0x21 );
1350 AX_reg(context) = 0x0001; /* invalid function */
1351 SET_CFLAG(context);
1352 break;
1354 default:
1355 INT_BARF( context, 0x21 );
1356 break;
1358 break;
1360 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1361 if ((AX_reg(context) = FILE_Dup(BX_reg(context))) == (WORD)HFILE_ERROR)
1363 AX_reg(context) = DOS_ExtendedError;
1364 SET_CFLAG(context);
1366 break;
1368 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1369 if (FILE_Dup2( BX_reg(context), CX_reg(context) ) == HFILE_ERROR)
1371 AX_reg(context) = DOS_ExtendedError;
1372 SET_CFLAG(context);
1374 break;
1376 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1377 if (!INT21_GetCurrentDirectory(context))
1379 AX_reg(context) = DOS_ExtendedError;
1380 SET_CFLAG(context);
1382 else AX_reg(context) = 0x0100;
1383 /* intlist: many Microsoft products for Windows rely on this */
1384 break;
1386 case 0x48: /* ALLOCATE MEMORY */
1387 case 0x49: /* FREE MEMORY */
1388 case 0x4a: /* RESIZE MEMORY BLOCK */
1389 INT_BARF( context, 0x21 );
1390 break;
1392 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1393 AX_reg(context) = WinExec( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1394 DX_reg(context) ),
1395 SW_NORMAL );
1396 if (AX_reg(context) < 32) SET_CFLAG(context);
1397 break;
1399 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1400 TASK_KillCurrentTask( AL_reg(context) );
1401 break;
1403 case 0x4d: /* GET RETURN CODE */
1404 AX_reg(context) = 0; /* normal exit */
1405 break;
1407 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1408 if (!INT21_FindFirst(context)) break;
1409 /* fall through */
1411 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1412 if (!INT21_FindNext(context))
1414 DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1415 AX_reg(context) = ER_NoMoreFiles;
1416 SET_CFLAG(context);
1418 break;
1420 case 0x51: /* GET PSP ADDRESS */
1421 case 0x62: /* GET PSP ADDRESS */
1422 /* FIXME: should we return the original DOS PSP upon */
1423 /* Windows startup ? */
1424 BX_reg(context) = GetCurrentPDB();
1425 break;
1427 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1428 ES_reg(context) = 0x0;
1429 BX_reg(context) = 0x0;
1430 INT_BARF( context, 0x21 );
1431 break;
1433 case 0x56: /* "RENAME" - RENAME FILE */
1434 if (!INT21_RenameFile(context))
1436 AX_reg(context) = DOS_ExtendedError;
1437 SET_CFLAG(context);
1439 break;
1441 case 0x57: /* FILE DATE AND TIME */
1442 switch (AL_reg(context))
1444 case 0x00: /* Get */
1445 if (!FILE_GetDateTime( BX_reg(context), &DX_reg(context),
1446 &CX_reg(context), TRUE ))
1448 AX_reg(context) = DOS_ExtendedError;
1449 SET_CFLAG(context);
1451 break;
1453 case 0x01: /* Set */
1454 if (!FILE_SetDateTime( BX_reg(context), DX_reg(context),
1455 CX_reg(context) ))
1457 AX_reg(context) = DOS_ExtendedError;
1458 SET_CFLAG(context);
1460 break;
1462 break;
1464 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1465 switch (AL_reg(context))
1467 case 0x00:
1468 AX_reg(context) = 1;
1469 break;
1470 case 0x02:
1471 AX_reg(context) = 0;
1472 break;
1473 case 0x01:
1474 case 0x03:
1475 break;
1477 RESET_CFLAG(context);
1478 break;
1480 case 0x5a: /* CREATE TEMPORARY FILE */
1481 if (!INT21_CreateTempFile(context))
1483 AX_reg(context) = DOS_ExtendedError;
1484 SET_CFLAG(context);
1486 break;
1488 case 0x5b: /* CREATE NEW FILE */
1489 if ((AX_reg(context) = _lcreat_uniq( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)), 0 )) == (WORD)HFILE_ERROR)
1491 AX_reg(context) = DOS_ExtendedError;
1492 SET_CFLAG(context);
1494 break;
1496 case 0x5d: /* NETWORK */
1497 case 0x5e:
1498 /* network software not installed */
1499 DOS_ERROR( ER_NoNetwork, EC_NotFound, SA_Abort, EL_Network );
1500 AX_reg(context) = DOS_ExtendedError;
1501 SET_CFLAG(context);
1502 break;
1504 case 0x5f: /* NETWORK */
1505 switch (AL_reg(context))
1507 case 0x07: /* ENABLE DRIVE */
1508 if (!DRIVE_Enable( DL_reg(context) ))
1510 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
1511 AX_reg(context) = DOS_ExtendedError;
1512 SET_CFLAG(context);
1514 break;
1516 case 0x08: /* DISABLE DRIVE */
1517 if (!DRIVE_Disable( DL_reg(context) ))
1519 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
1520 AX_reg(context) = DOS_ExtendedError;
1521 SET_CFLAG(context);
1523 break;
1525 default:
1526 /* network software not installed */
1527 DOS_ERROR( ER_NoNetwork, EC_NotFound, SA_Abort, EL_Network );
1528 AX_reg(context) = DOS_ExtendedError;
1529 SET_CFLAG(context);
1530 break;
1532 break;
1534 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1536 const char *truename = DOSFS_GetDosTrueName( PTR_SEG_OFF_TO_LIN(DS_reg(context),SI_reg(context) ), FALSE );
1537 if (!truename)
1539 AX_reg(context) = DOS_ExtendedError;
1540 SET_CFLAG(context);
1542 else
1544 lstrcpyn32A( PTR_SEG_OFF_TO_LIN( ES_reg(context),
1545 DI_reg(context) ),
1546 truename, 128 );
1547 AX_reg(context) = 0;
1550 break;
1552 case 0x61: /* UNUSED */
1553 case 0x63: /* UNUSED */
1554 case 0x64: /* OS/2 DOS BOX */
1555 INT_BARF( context, 0x21 );
1556 SET_CFLAG(context);
1557 break;
1558 case 0x65:{/* GET EXTENDED COUNTRY INFORMATION */
1559 extern WORD WINE_LanguageId;
1560 BYTE *dataptr=PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context));;
1561 switch (AL_reg(context)) {
1562 case 0x01:
1563 dataptr[0] = 0x1;
1564 *(WORD*)(dataptr+1) = 41;
1565 *(WORD*)(dataptr+3) = WINE_LanguageId;
1566 *(WORD*)(dataptr+5) = CodePage;
1567 break;
1568 case 0x06: {
1569 extern DWORD DOSMEM_CollateTable;
1571 dataptr[0] = 0x06;
1572 *(DWORD*)(dataptr+1) = MAKELONG(DOSMEM_CollateTable & 0xFFFF,DOSMEM_AllocSelector(DOSMEM_CollateTable>>16));
1573 CX_reg(context) = 258;/*FIXME: size of table?*/
1574 break;
1576 default:
1577 INT_BARF( context, 0x21 );
1578 SET_CFLAG(context);
1579 break;
1581 break;
1583 case 0x66: /* GLOBAL CODE PAGE TABLE */
1584 switch (AL_reg(context))
1586 case 0x01:
1587 DX_reg(context) = BX_reg(context) = CodePage;
1588 RESET_CFLAG(context);
1589 break;
1590 case 0x02:
1591 CodePage = BX_reg(context);
1592 RESET_CFLAG(context);
1593 break;
1595 break;
1597 case 0x67: /* SET HANDLE COUNT */
1598 SetHandleCount16( BX_reg(context) );
1599 if (DOS_ExtendedError)
1601 AX_reg(context) = DOS_ExtendedError;
1602 SET_CFLAG(context);
1604 break;
1606 case 0x68: /* "FFLUSH" - COMMIT FILE */
1607 case 0x6a: /* COMMIT FILE */
1608 if (!FlushFileBuffers( BX_reg(context) ))
1610 AX_reg(context) = DOS_ExtendedError;
1611 SET_CFLAG(context);
1613 break;
1615 case 0x69: /* DISK SERIAL NUMBER */
1616 switch (AL_reg(context))
1618 case 0x00:
1619 if (!INT21_GetDiskSerialNumber(context))
1621 AX_reg(context) = DOS_ExtendedError;
1622 SET_CFLAG(context);
1624 else AX_reg(context) = 0;
1625 break;
1626 case 0x01:
1627 if (!INT21_SetDiskSerialNumber(context))
1629 AX_reg(context) = DOS_ExtendedError;
1630 SET_CFLAG(context);
1632 else AX_reg(context) = 1;
1633 break;
1635 break;
1637 case 0x6C: /* Extended Open/Create*/
1638 ExtendedOpenCreateFile(context);
1639 break;
1641 case 0x71: /* MS-DOS 7 (Windows95) - LONG FILENAME FUNCTIONS */
1642 switch(AL_reg(context))
1644 case 0x39: /* Create directory */
1645 if (!CreateDirectory32A( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1646 DX_reg(context) ), NULL))
1648 AX_reg(context) = DOS_ExtendedError;
1649 SET_CFLAG(context);
1651 break;
1652 case 0x3a: /* Remove directory */
1653 if (!RemoveDirectory32A( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1654 DX_reg(context) )))
1656 AX_reg(context) = DOS_ExtendedError;
1657 SET_CFLAG(context);
1659 break;
1660 case 0xa0:
1661 break;
1662 case 0x3b: /* Change directory */
1663 case 0x41: /* Delete file */
1664 case 0x43: /* Get/Set file attributes */
1665 case 0x47: /* Get current directory */
1666 case 0x4e: /* Find first file */
1667 case 0x4f: /* Find next file */
1668 case 0x56: /* Move (rename) file */
1669 case 0x6c: /* Create/Open file */
1670 default:
1671 fprintf( stderr, "Unimplemented int21 long file name function:\n");
1672 INT_BARF( context, 0x21 );
1673 SET_CFLAG(context);
1674 AL_reg(context) = 0;
1675 break;
1677 break;
1679 case 0x70: /* MS-DOS 7 (Windows95) - ??? (country-specific?)*/
1680 case 0x72: /* MS-DOS 7 (Windows95) - ??? */
1681 case 0x73: /* MS-DOS 7 (Windows95) - DRIVE LOCKING ??? */
1682 dprintf_int(stddeb,"int21: windows95 function AX %04x\n",
1683 AX_reg(context));
1684 dprintf_int(stddeb, " returning unimplemented\n");
1685 SET_CFLAG(context);
1686 AL_reg(context) = 0;
1687 break;
1689 case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
1690 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1691 break;
1693 default:
1694 INT_BARF( context, 0x21 );
1695 break;
1697 dprintf_int( stddeb, "ret21: AX=%04x BX=%04x CX=%04x DX=%04x "
1698 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
1699 AX_reg(context), BX_reg(context), CX_reg(context),
1700 DX_reg(context), SI_reg(context), DI_reg(context),
1701 DS_reg(context), ES_reg(context), EFL_reg(context));
1705 BOOL32 INT21_Init(void)
1707 if (!(DosHeapHandle = GlobalAlloc16(GMEM_FIXED,sizeof(struct DosHeap))))
1709 fprintf( stderr, "INT21_Init: Out of memory\n");
1710 return FALSE;
1712 heap = (struct DosHeap *) GlobalLock16(DosHeapHandle);
1714 dpb = &heap->dpb;
1715 dpbsegptr = PTR_SEG_OFF_TO_SEGPTR(DosHeapHandle,(int)&heap->dpb-(int)heap);
1716 heap->InDosFlag = 0;
1717 strcpy(heap->biosdate, "01/01/80");
1718 return TRUE;