Added include protection for unistd.h and sys/time.h.
[wine] / msdos / int21.c
1 /*
2  * DOS interrupt 21h handler
3  *
4  * Copyright 1993, 1994 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  * Copyright 1997 Andreas Mohr
7  * Copyright 1998 Uwe Bonnes
8  * Copyright 1998, 1999 Ove Kaaven
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "config.h"
26
27 #include <time.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #ifdef HAVE_SYS_FILE_H
33 # include <sys/file.h>
34 #endif
35 #include <string.h>
36 #ifdef HAVE_SYS_TIME_H
37 # include <sys/time.h>
38 #endif
39 #include <sys/types.h>
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #include <utime.h>
44 #include <ctype.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "ntddk.h"
48 #include "wingdi.h"
49 #include "winuser.h" /* SW_NORMAL */
50 #include "wine/winbase16.h"
51 #include "winerror.h"
52 #include "drive.h"
53 #include "file.h"
54 #include "callback.h"
55 #include "msdos.h"
56 #include "miscemu.h"
57 #include "task.h"
58 #include "wine/debug.h"
59
60 WINE_DEFAULT_DEBUG_CHANNEL(int21);
61 #if defined(__svr4__) || defined(_SCO_DS)
62 /* SVR4 DOESNT do locking the same way must implement properly */
63 #define LOCK_EX 0
64 #define LOCK_SH  1
65 #define LOCK_NB  8
66 #endif
67
68
69 #define DOS_GET_DRIVE(reg) ((reg) ? (reg) - 1 : DRIVE_GetCurrentDrive())
70
71 /* Define the drive parameter block, as used by int21/1F
72  * and int21/32.  This table can be accessed through the
73  * global 'dpb' pointer, which points into the local dos
74  * heap.
75  */
76 struct DPB
77 {
78     BYTE drive_num;         /* 0=A, etc. */
79     BYTE unit_num;          /* Drive's unit number (?) */
80     WORD sector_size;       /* Sector size in bytes */
81     BYTE high_sector;       /* Highest sector in a cluster */
82     BYTE shift;             /* Shift count (?) */
83     WORD reserved;          /* Number of reserved sectors at start */
84     BYTE num_FAT;           /* Number of FATs */
85     WORD dir_entries;       /* Number of root dir entries */
86     WORD first_data;        /* First data sector */
87     WORD high_cluster;      /* Highest cluster number */
88     WORD sectors_in_FAT;    /* Number of sectors per FAT */
89     WORD start_dir;         /* Starting sector of first dir */
90     DWORD driver_head;      /* Address of device driver header (?) */
91     BYTE media_ID;          /* Media ID */
92     BYTE access_flag;       /* Prev. accessed flag (0=yes,0xFF=no) */
93     DWORD next;             /* Pointer to next DPB in list */
94     WORD free_search;       /* Free cluster search start */
95     WORD free_clusters;     /* Number of free clusters (0xFFFF=unknown) */
96 };
97
98 struct EDPB                     /* FAT32 extended Drive Parameter Block */
99 {                               /* from Ralf Brown's Interrupt List */
100         struct DPB dpb;         /* first 24 bytes = original DPB */
101
102         BYTE edpb_flags;        /* undocumented/unknown flags */
103         DWORD next_edpb;        /* pointer to next EDPB */
104         WORD free_cluster;      /* cluster to start search for free space on write, typically
105                                    the last cluster allocated */
106         WORD clusters_free;     /* number of free clusters on drive or FFFF = unknown */
107         WORD clusters_free_hi;  /* hiword of clusters_free */
108         WORD mirroring_flags;   /* mirroring flags: bit 7 set = do not mirror active FAT */
109                                 /* bits 0-3 = 0-based number of the active FAT */
110         WORD info_sector;       /* sector number of file system info sector, or FFFF for none */
111         WORD spare_boot_sector; /* sector number of backup boot sector, or FFFF for none */
112         DWORD first_cluster;    /* sector number of the first cluster */
113         DWORD max_cluster;      /* sector number of the last cluster */
114         DWORD fat_clusters;     /* number of clusters occupied by FAT */
115         DWORD root_cluster;     /* cluster number of start of root directory */
116         DWORD free_cluster2;    /* same as free_cluster: cluster at which to start
117                                    search for free space when writing */
118
119 };
120
121 WORD CodePage = 437;
122 DWORD dpbsegptr;
123
124 struct DosHeap {
125         BYTE InDosFlag;
126         BYTE mediaID;
127         BYTE biosdate[8];
128         struct DPB dpb;
129         BYTE DummyDBCSLeadTable[6];
130 };
131 static struct DosHeap *heap;
132 static WORD DosHeapHandle;
133
134 extern char TempDirectory[];
135
136 static void INT21_ReadConfigSys(void)
137 {
138     static int done;
139     if (!done) DOSCONF_ReadConfig();
140     done = 1;
141 }
142
143 static BOOL INT21_CreateHeap(void)
144 {
145     if (!(DosHeapHandle = GlobalAlloc16(GMEM_FIXED,sizeof(struct DosHeap))))
146     {
147         WARN("Out of memory\n");
148         return FALSE;
149     }
150     heap = (struct DosHeap *) GlobalLock16(DosHeapHandle);
151     dpbsegptr = MAKESEGPTR(DosHeapHandle,(int)&heap->dpb-(int)heap);
152     heap->InDosFlag = 0;
153     strcpy(heap->biosdate, "01/01/80");
154     memset(heap->DummyDBCSLeadTable, 0, 6);
155     return TRUE;
156 }
157
158 static BYTE *GetCurrentDTA( CONTEXT86 *context )
159 {
160     TDB *pTask = TASK_GetCurrent();
161
162     /* FIXME: This assumes DTA was set correctly! */
163     return (BYTE *)CTX_SEG_OFF_TO_LIN( context, SELECTOROF(pTask->dta),
164                                                 (DWORD)OFFSETOF(pTask->dta) );
165 }
166
167
168 void CreateBPB(int drive, BYTE *data, BOOL16 limited)
169 /* limited == TRUE is used with INT 0x21/0x440d */
170 {
171         if (drive > 1) {
172                 setword(data, 512);
173                 data[2] = 2;
174                 setword(&data[3], 0);
175                 data[5] = 2;
176                 setword(&data[6], 240);
177                 setword(&data[8], 64000);
178                 data[0x0a] = 0xf8;
179                 setword(&data[0x0b], 40);
180                 setword(&data[0x0d], 56);
181                 setword(&data[0x0f], 2);
182                 setword(&data[0x11], 0);
183                 if (!limited) {
184                     setword(&data[0x1f], 800);
185                     data[0x21] = 5;
186                     setword(&data[0x22], 1);
187                 }
188         } else { /* 1.44mb */
189                 setword(data, 512);
190                 data[2] = 2;
191                 setword(&data[3], 0);
192                 data[5] = 2;
193                 setword(&data[6], 240);
194                 setword(&data[8], 2880);
195                 data[0x0a] = 0xf8;
196                 setword(&data[0x0b], 6);
197                 setword(&data[0x0d], 18);
198                 setword(&data[0x0f], 2);
199                 setword(&data[0x11], 0);
200                 if (!limited) {
201                     setword(&data[0x1f], 80);
202                     data[0x21] = 7;
203                     setword(&data[0x22], 2);
204                 }
205         }
206 }
207
208 static int INT21_GetFreeDiskSpace( CONTEXT86 *context )
209 {
210     DWORD cluster_sectors, sector_bytes, free_clusters, total_clusters;
211     char root[] = "A:\\";
212
213     *root += DOS_GET_DRIVE( DL_reg(context) );
214     if (!GetDiskFreeSpaceA( root, &cluster_sectors, &sector_bytes,
215                               &free_clusters, &total_clusters )) return 0;
216     AX_reg(context) = cluster_sectors;
217     BX_reg(context) = free_clusters;
218     CX_reg(context) = sector_bytes;
219     DX_reg(context) = total_clusters;
220     return 1;
221 }
222
223 static int INT21_GetDriveAllocInfo( CONTEXT86 *context )
224 {
225     if (!INT21_GetFreeDiskSpace( context )) return 0;
226     if (!heap && !INT21_CreateHeap()) return 0;
227     heap->mediaID = 0xf0;
228     context->SegDs = DosHeapHandle;
229     BX_reg(context) = (int)&heap->mediaID - (int)heap;
230     return 1;
231 }
232
233 static int FillInDrivePB( int drive )
234 {
235         if(!DRIVE_IsValid(drive))
236         {
237             SetLastError( ERROR_INVALID_DRIVE );
238                         return 0;
239         }
240         else if (heap || INT21_CreateHeap())
241         {
242                 /* FIXME: I have no idea what a lot of this information should
243                  * say or whether it even really matters since we're not allowing
244                  * direct block access.  However, some programs seem to depend on
245                  * getting at least _something_ back from here.  The 'next' pointer
246                  * does worry me, though.  Should we have a complete table of
247                  * separate DPBs per drive?  Probably, but I'm lazy. :-)  -CH
248                  */
249                 heap->dpb.drive_num = heap->dpb.unit_num = drive; /*The same?*/
250                 heap->dpb.sector_size = 512;
251                 heap->dpb.high_sector = 1;
252                 heap->dpb.shift = drive < 2 ? 0 : 6; /*6 for HD, 0 for floppy*/
253                 heap->dpb.reserved = 0;
254                 heap->dpb.num_FAT = 1;
255                 heap->dpb.dir_entries = 2;
256                 heap->dpb.first_data = 2;
257                 heap->dpb.high_cluster = 64000;
258                 heap->dpb.sectors_in_FAT = 1;
259                 heap->dpb.start_dir = 1;
260                 heap->dpb.driver_head = 0;
261                 heap->dpb.media_ID = (drive > 1) ? 0xF8 : 0xF0;
262                 heap->dpb.access_flag = 0;
263                 heap->dpb.next = 0;
264                 heap->dpb.free_search = 0;
265                 heap->dpb.free_clusters = 0xFFFF;    /* unknown */
266                                 return 1;
267                 }
268
269                 return 0;
270 }
271
272 static void GetDrivePB( CONTEXT86 *context, int drive )
273 {
274         if (FillInDrivePB( drive ))
275         {
276                 AL_reg(context) = 0x00;
277                 context->SegDs = SELECTOROF(dpbsegptr);
278                 BX_reg(context) = OFFSETOF(dpbsegptr);
279         }
280         else
281         {
282         AX_reg(context) = 0x00ff;
283         }
284 }
285
286
287 static void ioctlGetDeviceInfo( CONTEXT86 *context )
288 {
289     int curr_drive;
290     const DOS_DEVICE *dev;
291
292     TRACE("(%d)\n", BX_reg(context));
293
294     RESET_CFLAG(context);
295
296     /* DOS device ? */
297     if ((dev = DOSFS_GetDeviceByHandle( DosFileHandleToWin32Handle(BX_reg(context)) )))
298     {
299         DX_reg(context) = dev->flags;
300         return;
301     }
302
303     /* it seems to be a file */
304     curr_drive = DRIVE_GetCurrentDrive();
305     DX_reg(context) = 0x0140 + curr_drive + ((curr_drive > 1) ? 0x0800 : 0);
306     /* no floppy */
307     /* bits 0-5 are current drive
308      * bit 6 - file has NOT been written..FIXME: correct?
309      * bit 8 - generate int24 if no diskspace on write/ read past end of file
310      * bit 11 - media not removable
311      * bit 14 - don't set file date/time on closing
312      * bit 15 - file is remote
313      */
314 }
315
316 static BOOL ioctlGenericBlkDevReq( CONTEXT86 *context )
317 {
318         BYTE *dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
319         int drive = DOS_GET_DRIVE( BL_reg(context) );
320
321         if (!DRIVE_IsValid(drive))
322         {
323             SetLastError( ERROR_FILE_NOT_FOUND );
324             return TRUE;
325         }
326
327         if (CH_reg(context) != 0x08)
328         {
329             INT_BARF( context, 0x21 );
330             return FALSE;
331         }
332
333         switch (CL_reg(context))
334         {
335                 case 0x4a: /* lock logical volume */
336                         TRACE("lock logical volume (%d) level %d mode %d\n",drive,BH_reg(context),DX_reg(context));
337                         break;
338
339                 case 0x60: /* get device parameters */
340                            /* used by w4wgrp's winfile */
341                         memset(dataptr, 0, 0x20); /* DOS 6.22 uses 0x20 bytes */
342                         dataptr[0] = 0x04;
343                         dataptr[6] = 0; /* media type */
344                         if (drive > 1)
345                         {
346                                 dataptr[1] = 0x05; /* fixed disk */
347                                 setword(&dataptr[2], 0x01); /* non removable */
348                                 setword(&dataptr[4], 0x300); /* # of cylinders */
349                         }
350                         else
351                         {
352                                 dataptr[1] = 0x07; /* block dev, floppy */
353                                 setword(&dataptr[2], 0x02); /* removable */
354                                 setword(&dataptr[4], 80); /* # of cylinders */
355                         }
356                         CreateBPB(drive, &dataptr[7], TRUE);
357                         RESET_CFLAG(context);
358                         break;
359
360                 case 0x41: /* write logical device track */
361                 case 0x61: /* read logical device track */
362                         {
363                                 BYTE drive = BL_reg(context) ?
364                                                 BL_reg(context) : DRIVE_GetCurrentDrive();
365                                 WORD head   = *(WORD *)dataptr+1;
366                                 WORD cyl    = *(WORD *)dataptr+3;
367                                 WORD sect   = *(WORD *)dataptr+5;
368                                 WORD nrsect = *(WORD *)dataptr+7;
369                                 BYTE *data  =  (BYTE *)dataptr+9;
370                                 int (*raw_func)(BYTE, DWORD, DWORD, BYTE *, BOOL);
371
372                                 raw_func = (CL_reg(context) == 0x41) ?
373                                                                 DRIVE_RawWrite : DRIVE_RawRead;
374
375                                 if (raw_func(drive, head*cyl*sect, nrsect, data, FALSE))
376                                         RESET_CFLAG(context);
377                                 else
378                                 {
379                                         AX_reg(context) = 0x1e; /* read fault */
380                                         SET_CFLAG(context);
381                                 }
382                         }
383                         break;
384                 case 0x66:/*  get disk serial number */
385                         {
386                                 char    label[12],fsname[9],path[4];
387                                 DWORD   serial;
388
389                                 strcpy(path,"x:\\");path[0]=drive+'A';
390                                 GetVolumeInformationA(
391                                         path,label,12,&serial,NULL,NULL,fsname,9
392                                 );
393                                 *(WORD*)dataptr         = 0;
394                                 memcpy(dataptr+2,&serial,4);
395                                 memcpy(dataptr+6,label  ,11);
396                                 memcpy(dataptr+17,fsname,8);
397                         }
398                         break;
399
400                 case 0x6a:
401                         TRACE("logical volume %d unlocked.\n",drive);
402                         break;
403
404                 case 0x6f:
405                         memset(dataptr+1, '\0', dataptr[0]-1);
406                         dataptr[1] = dataptr[0];
407                         dataptr[2] = 0x07; /* protected mode driver; no eject; no notification */
408                         dataptr[3] = 0xFF; /* no physical drive */
409                         break;
410
411                 case 0x72:
412                         /* Trail on error implementation */
413                         AX_reg(context) = GetDriveType16(BL_reg(context)) == DRIVE_UNKNOWN ? 0x0f : 0x01;
414                         SET_CFLAG(context);     /* Seems to be set all the time */
415                         break;
416
417                 default:
418                         INT_BARF( context, 0x21 );
419         }
420         return FALSE;
421 }
422
423 static void INT21_ParseFileNameIntoFCB( CONTEXT86 *context )
424 {
425     char *filename =
426         CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi );
427     char *fcb =
428         CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi );
429     char *buffer, *s, *d;
430
431     AL_reg(context) = 0xff; /* failed */
432
433     TRACE("filename: '%s'\n", filename);
434
435     buffer = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + 1);
436
437     s = filename;
438     d = buffer;
439     while (*s)
440     {
441         if ((*s != ' ') && (*s != '\r') && (*s != '\n'))
442             *d++ = *s++;
443         else
444             break;
445     }
446
447     *d = '\0';
448     DOSFS_ToDosFCBFormat(buffer, fcb + 1);
449     *fcb = 0;
450     TRACE("FCB: '%s'\n", ((CHAR *)fcb + 1));
451
452     HeapFree( GetProcessHeap(), 0, buffer);
453
454     AL_reg(context) =
455         ((strchr(filename, '*')) || (strchr(filename, '$'))) != 0;
456
457     /* point DS:SI to first unparsed character */
458     SI_reg(context) += (int)s - (int)filename;
459 }
460
461 static void INT21_GetSystemDate( CONTEXT86 *context )
462 {
463     SYSTEMTIME systime;
464     GetLocalTime( &systime );
465     CX_reg(context) = systime.wYear;
466     DX_reg(context) = (systime.wMonth << 8) | systime.wDay;
467     AX_reg(context) = systime.wDayOfWeek;
468 }
469
470 static void INT21_GetSystemTime( CONTEXT86 *context )
471 {
472     SYSTEMTIME systime;
473     GetLocalTime( &systime );
474     CX_reg(context) = (systime.wHour << 8) | systime.wMinute;
475     DX_reg(context) = (systime.wSecond << 8) | (systime.wMilliseconds / 10);
476 }
477
478 /* Many calls translate a drive argument like this:
479    drive number (00h = default, 01h = A:, etc)
480    */
481 static char drivestring[]="default";
482
483 char *INT21_DriveName(int drive)
484 {
485
486     if(drive >0)
487       {
488         drivestring[0]= (unsigned char)drive + '@';
489         drivestring[1]=':';
490         drivestring[2]=0;
491       }
492     return drivestring;
493 }
494 static BOOL INT21_CreateFile( CONTEXT86 *context )
495 {
496     AX_reg(context) = _lcreat16( CTX_SEG_OFF_TO_LIN(context, context->SegDs,
497                                           context->Edx ), CX_reg(context) );
498     return (AX_reg(context) == (WORD)HFILE_ERROR16);
499 }
500
501 static HFILE16 _lcreat16_uniq( LPCSTR path, INT attr )
502 {
503     /* Mask off all flags not explicitly allowed by the doc */
504     attr &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
505     return Win32HandleToDosFileHandle( CreateFileA( path, GENERIC_READ | GENERIC_WRITE,
506                                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
507                                              CREATE_NEW, attr, 0 ));
508 }
509
510 static void OpenExistingFile( CONTEXT86 *context )
511 {
512     AX_reg(context) = _lopen16( CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx),
513                                AL_reg(context) );
514     if (AX_reg(context) == (WORD)HFILE_ERROR16)
515     {
516         AX_reg(context) = GetLastError();
517         SET_CFLAG(context);
518     }
519 }
520
521 static BOOL INT21_ExtendedOpenCreateFile(CONTEXT86 *context )
522 {
523   BOOL bExtendedError = FALSE;
524   BYTE action = DL_reg(context);
525
526   /* Shuffle arguments to call OpenExistingFile */
527   AL_reg(context) = BL_reg(context);
528   DX_reg(context) = SI_reg(context);
529   /* BX,CX and DX should be preserved */
530   OpenExistingFile(context);
531
532   if ((context->EFlags & 0x0001) == 0) /* File exists */
533   {
534       UINT16    uReturnCX = 0;
535
536       /* Now decide what do do */
537
538       if ((action & 0x07) == 0)
539       {
540           _lclose16( AX_reg(context) );
541           AX_reg(context) = 0x0050;     /*File exists*/
542           SET_CFLAG(context);
543           WARN("extended open/create: failed because file exists \n");
544       }
545       else if ((action & 0x07) == 2)
546       {
547         /* Truncate it, but first check if opened for write */
548         if ((BL_reg(context) & 0x0007)== 0)
549         {
550             _lclose16( AX_reg(context) );
551             WARN("extended open/create: failed, trunc on ro file\n");
552             AX_reg(context) = 0x000C;   /*Access code invalid*/
553             SET_CFLAG(context);
554         }
555         else
556         {
557                 TRACE("extended open/create: Closing before truncate\n");
558                 if (_lclose16( AX_reg(context) ))
559                 {
560                    WARN("extended open/create: close before trunc failed\n");
561                    AX_reg(context) = 0x0019;    /*Seek Error*/
562                    CX_reg(context) = 0;
563                    SET_CFLAG(context);
564                 }
565                 /* Shuffle arguments to call CreateFile */
566
567                 TRACE("extended open/create: Truncating\n");
568                 AL_reg(context) = BL_reg(context);
569                 /* CX is still the same */
570                 DX_reg(context) = SI_reg(context);
571                 bExtendedError = INT21_CreateFile(context);
572
573                 if (context->EFlags & 0x0001)   /*no file open, flags set */
574                 {
575                     WARN("extended open/create: trunc failed\n");
576                     return bExtendedError;
577                 }
578                 uReturnCX = 0x3;
579         }
580       }
581       else uReturnCX = 0x1;
582
583       CX_reg(context) = uReturnCX;
584   }
585   else /* file does not exist */
586   {
587       RESET_CFLAG(context); /* was set by OpenExistingFile(context) */
588       if ((action & 0xF0)== 0)
589       {
590         CX_reg(context) = 0;
591         SET_CFLAG(context);
592         WARN("extended open/create: failed, file dosen't exist\n");
593       }
594       else
595       {
596         /* Shuffle arguments to call CreateFile */
597         TRACE("extended open/create: Creating\n");
598         AL_reg(context) = BL_reg(context);
599         /* CX should still be the same */
600         DX_reg(context) = SI_reg(context);
601         bExtendedError = INT21_CreateFile(context);
602         if (context->EFlags & 0x0001)  /*no file open, flags set */
603         {
604             WARN("extended open/create: create failed\n");
605             return bExtendedError;
606         }
607         CX_reg(context) = 2;
608       }
609   }
610
611   return bExtendedError;
612 }
613
614
615 static BOOL INT21_ChangeDir( CONTEXT86 *context )
616 {
617     int drive;
618     char *dirname = CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx);
619
620     TRACE("changedir %s\n", dirname);
621     if (dirname[0] && (dirname[1] == ':'))
622     {
623         drive = toupper(dirname[0]) - 'A';
624         dirname += 2;
625     }
626     else drive = DRIVE_GetCurrentDrive();
627     return DRIVE_Chdir( drive, dirname );
628 }
629
630
631 static int INT21_FindFirst( CONTEXT86 *context )
632 {
633     char *p;
634     const char *path;
635     DOS_FULL_NAME full_name;
636     FINDFILE_DTA *dta = (FINDFILE_DTA *)GetCurrentDTA(context);
637
638     path = (const char *)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
639     dta->unixPath = NULL;
640     if (!DOSFS_GetFullName( path, FALSE, &full_name ))
641     {
642         AX_reg(context) = GetLastError();
643         SET_CFLAG(context);
644         return 0;
645     }
646     dta->unixPath = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
647     strcpy( dta->unixPath, full_name.long_name );
648     p = strrchr( dta->unixPath, '/' );
649     *p = '\0';
650
651     /* Note: terminating NULL in dta->mask overwrites dta->search_attr
652      *       (doesn't matter as it is set below anyway)
653      */
654     if (!DOSFS_ToDosFCBFormat( p + 1, dta->mask ))
655     {
656         HeapFree( GetProcessHeap(), 0, dta->unixPath );
657         dta->unixPath = NULL;
658         SetLastError( ERROR_FILE_NOT_FOUND );
659         AX_reg(context) = ERROR_FILE_NOT_FOUND;
660         SET_CFLAG(context);
661         return 0;
662     }
663     dta->drive = (path[0] && (path[1] == ':')) ? toupper(path[0]) - 'A'
664                                                : DRIVE_GetCurrentDrive();
665     dta->count = 0;
666     dta->search_attr = CL_reg(context);
667     return 1;
668 }
669
670
671 static int INT21_FindNext( CONTEXT86 *context )
672 {
673     FINDFILE_DTA *dta = (FINDFILE_DTA *)GetCurrentDTA(context);
674     WIN32_FIND_DATAA entry;
675     int count;
676
677     if (!dta->unixPath) return 0;
678     if (!(count = DOSFS_FindNext( dta->unixPath, dta->mask, NULL, dta->drive,
679                                   dta->search_attr, dta->count, &entry )))
680     {
681         HeapFree( GetProcessHeap(), 0, dta->unixPath );
682         dta->unixPath = NULL;
683         return 0;
684     }
685     if ((int)dta->count + count > 0xffff)
686     {
687         WARN("Too many directory entries in %s\n", dta->unixPath );
688         HeapFree( GetProcessHeap(), 0, dta->unixPath );
689         dta->unixPath = NULL;
690         return 0;
691     }
692     dta->count += count;
693     dta->fileattr = entry.dwFileAttributes;
694     dta->filesize = entry.nFileSizeLow;
695     FileTimeToDosDateTime( &entry.ftLastWriteTime,
696                            &dta->filedate, &dta->filetime );
697     strcpy( dta->filename, entry.cAlternateFileName );
698     if (!memchr(dta->mask,'?',11)) {
699         /* wildcardless search, release resources in case no findnext will
700          * be issued, and as a workaround in case file creation messes up
701          * findnext, as sometimes happens with pkunzip */
702         HeapFree( GetProcessHeap(), 0, dta->unixPath );
703         dta->unixPath = NULL;
704     }
705     return 1;
706 }
707
708
709 static BOOL INT21_CreateTempFile( CONTEXT86 *context )
710 {
711     static int counter = 0;
712     char *name = CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx );
713     char *p = name + strlen(name);
714
715     /* despite what Ralf Brown says, some programs seem to call without
716      * ending backslash (DOS accepts that, so we accept it too) */
717     if ((p == name) || (p[-1] != '\\')) *p++ = '\\';
718
719     for (;;)
720     {
721         sprintf( p, "wine%04x.%03d", (int)getpid(), counter );
722         counter = (counter + 1) % 1000;
723
724         if ((AX_reg(context) = _lcreat16_uniq( name, 0 )) != (WORD)HFILE_ERROR16)
725         {
726             TRACE("created %s\n", name );
727             return TRUE;
728         }
729         if (GetLastError() != ERROR_FILE_EXISTS) return FALSE;
730     }
731 }
732
733
734 static BOOL INT21_GetCurrentDirectory( CONTEXT86 *context )
735 {
736     int drive = DOS_GET_DRIVE( DL_reg(context) );
737     char *ptr = (char *)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Esi );
738
739     if (!DRIVE_IsValid(drive))
740     {
741         SetLastError( ERROR_INVALID_DRIVE );
742         return FALSE;
743     }
744     lstrcpynA( ptr, DRIVE_GetDosCwd(drive), 64 );
745     AX_reg(context) = 0x0100;                        /* success return code */
746     return TRUE;
747 }
748
749
750 static void INT21_GetDBCSLeadTable( CONTEXT86 *context )
751 {
752     if (heap || INT21_CreateHeap())
753     { /* return an empty table just as DOS 4.0+ does */
754         context->SegDs = DosHeapHandle;
755         SI_reg(context) = (int)&heap->DummyDBCSLeadTable - (int)heap;
756     }
757     else
758     {
759         AX_reg(context) = 0x1; /* error */
760         SET_CFLAG(context);
761     }
762 }
763
764
765 static int INT21_GetDiskSerialNumber( CONTEXT86 *context )
766 {
767     BYTE *dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
768     int drive = DOS_GET_DRIVE( BL_reg(context) );
769
770     if (!DRIVE_IsValid(drive))
771     {
772         SetLastError( ERROR_INVALID_DRIVE );
773         return 0;
774     }
775
776     *(WORD *)dataptr = 0;
777     *(DWORD *)(dataptr + 2) = DRIVE_GetSerialNumber( drive );
778     memcpy( dataptr + 6, DRIVE_GetLabel( drive ), 11 );
779     strncpy(dataptr + 0x11, "FAT16   ", 8);
780     return 1;
781 }
782
783
784 static int INT21_SetDiskSerialNumber( CONTEXT86 *context )
785 {
786     BYTE *dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
787     int drive = DOS_GET_DRIVE( BL_reg(context) );
788
789     if (!DRIVE_IsValid(drive))
790     {
791         SetLastError( ERROR_INVALID_DRIVE );
792         return 0;
793     }
794
795     DRIVE_SetSerialNumber( drive, *(DWORD *)(dataptr + 2) );
796     return 1;
797 }
798
799
800 /* microsoft's programmers should be shot for using CP/M style int21
801    calls in Windows for Workgroup's winfile.exe */
802
803 static int INT21_FindFirstFCB( CONTEXT86 *context )
804 {
805     BYTE *fcb = (BYTE *)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
806     FINDFILE_FCB *pFCB;
807     LPCSTR root, cwd;
808     int drive;
809
810     if (*fcb == 0xff) pFCB = (FINDFILE_FCB *)(fcb + 7);
811     else pFCB = (FINDFILE_FCB *)fcb;
812     drive = DOS_GET_DRIVE( pFCB->drive );
813     if (!DRIVE_IsValid( drive )) return 0;
814     root = DRIVE_GetRoot( drive );
815     cwd  = DRIVE_GetUnixCwd( drive );
816     pFCB->unixPath = HeapAlloc( GetProcessHeap(), 0,
817                                 strlen(root)+strlen(cwd)+2 );
818     if (!pFCB->unixPath) return 0;
819     strcpy( pFCB->unixPath, root );
820     strcat( pFCB->unixPath, "/" );
821     strcat( pFCB->unixPath, cwd );
822     pFCB->count = 0;
823     return 1;
824 }
825
826
827 static int INT21_FindNextFCB( CONTEXT86 *context )
828 {
829     BYTE *fcb = (BYTE *)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
830     FINDFILE_FCB *pFCB;
831     DOS_DIRENTRY_LAYOUT *pResult = (DOS_DIRENTRY_LAYOUT *)GetCurrentDTA(context);
832     WIN32_FIND_DATAA entry;
833     BYTE attr;
834     int count;
835
836     if (*fcb == 0xff) /* extended FCB ? */
837     {
838         attr = fcb[6];
839         pFCB = (FINDFILE_FCB *)(fcb + 7);
840     }
841     else
842     {
843         attr = 0;
844         pFCB = (FINDFILE_FCB *)fcb;
845     }
846
847     if (!pFCB->unixPath) return 0;
848     if (!(count = DOSFS_FindNext( pFCB->unixPath, pFCB->filename, NULL,
849                                   DOS_GET_DRIVE( pFCB->drive ), attr,
850                                   pFCB->count, &entry )))
851     {
852         HeapFree( GetProcessHeap(), 0, pFCB->unixPath );
853         pFCB->unixPath = NULL;
854         return 0;
855     }
856     pFCB->count += count;
857
858     if (*fcb == 0xff) { /* place extended FCB header before pResult if called with extended FCB */
859         *(BYTE *)pResult = 0xff;
860         (BYTE *)pResult +=6; /* leave reserved field behind */
861         *(BYTE *)pResult = entry.dwFileAttributes;
862         ((BYTE *)pResult)++;
863     }
864     *(BYTE *)pResult = DOS_GET_DRIVE( pFCB->drive ); /* DOS_DIRENTRY_LAYOUT after current drive number */
865     ((BYTE *)pResult)++;
866     pResult->fileattr = entry.dwFileAttributes;
867     pResult->cluster  = 0;  /* what else? */
868     pResult->filesize = entry.nFileSizeLow;
869     memset( pResult->reserved, 0, sizeof(pResult->reserved) );
870     FileTimeToDosDateTime( &entry.ftLastWriteTime,
871                            &pResult->filedate, &pResult->filetime );
872
873     /* Convert file name to FCB format */
874
875     memset( pResult->filename, ' ', sizeof(pResult->filename) );
876     if (!strcmp( entry.cAlternateFileName, "." )) pResult->filename[0] = '.';
877     else if (!strcmp( entry.cAlternateFileName, ".." ))
878         pResult->filename[0] = pResult->filename[1] = '.';
879     else
880     {
881         char *p = strrchr( entry.cAlternateFileName, '.' );
882         if (p && p[1] && (p != entry.cAlternateFileName))
883         {
884             memcpy( pResult->filename, entry.cAlternateFileName,
885                     min( (p - entry.cAlternateFileName), 8 ) );
886             memcpy( pResult->filename + 8, p + 1, min( strlen(p), 3 ) );
887         }
888         else
889             memcpy( pResult->filename, entry.cAlternateFileName,
890                     min( strlen(entry.cAlternateFileName), 8 ) );
891     }
892     return 1;
893 }
894
895
896 static void DeleteFileFCB( CONTEXT86 *context )
897 {
898     FIXME("(%p): stub\n", context);
899 }
900
901 static void RenameFileFCB( CONTEXT86 *context )
902 {
903     FIXME("(%p): stub\n", context);
904 }
905
906
907
908 static void fLock( CONTEXT86 * context )
909 {
910
911     switch ( AX_reg(context) & 0xff )
912     {
913         case 0x00: /* LOCK */
914           TRACE("lock handle %d offset %ld length %ld\n",
915                 BX_reg(context),
916                 MAKELONG(DX_reg(context),CX_reg(context)),
917                 MAKELONG(DI_reg(context),SI_reg(context))) ;
918           if (!LockFile(DosFileHandleToWin32Handle(BX_reg(context)),
919                         MAKELONG(DX_reg(context),CX_reg(context)), 0,
920                         MAKELONG(DI_reg(context),SI_reg(context)), 0)) {
921             AX_reg(context) = GetLastError();
922             SET_CFLAG(context);
923           }
924           break;
925
926         case 0x01: /* UNLOCK */
927           TRACE("unlock handle %d offset %ld length %ld\n",
928                 BX_reg(context),
929                 MAKELONG(DX_reg(context),CX_reg(context)),
930                 MAKELONG(DI_reg(context),SI_reg(context))) ;
931           if (!UnlockFile(DosFileHandleToWin32Handle(BX_reg(context)),
932                           MAKELONG(DX_reg(context),CX_reg(context)), 0,
933                           MAKELONG(DI_reg(context),SI_reg(context)), 0)) {
934             AX_reg(context) = GetLastError();
935             SET_CFLAG(context);
936           }
937           return;
938         default:
939           AX_reg(context) = 0x0001;
940           SET_CFLAG(context);
941           return;
942      }
943 }
944
945 static BOOL
946 INT21_networkfunc (CONTEXT86 *context)
947 {
948      switch (AL_reg(context)) {
949      case 0x00: /* Get machine name. */
950      {
951           char *dst = CTX_SEG_OFF_TO_LIN (context,context->SegDs,context->Edx);
952           TRACE("getting machine name to %p\n", dst);
953           if (gethostname (dst, 15))
954           {
955                WARN("failed!\n");
956                SetLastError( ER_NoNetwork );
957                return TRUE;
958           } else {
959                int len = strlen (dst);
960                while (len < 15)
961                     dst[len++] = ' ';
962                dst[15] = 0;
963                CH_reg(context) = 1; /* Valid */
964                CL_reg(context) = 1; /* NETbios number??? */
965                TRACE("returning %s\n", debugstr_an (dst, 16));
966                return FALSE;
967           }
968      }
969
970      default:
971           SetLastError( ER_NoNetwork );
972           return TRUE;
973      }
974 }
975
976
977 static void ASPI_DOS_HandleInt( CONTEXT86 *context )
978 {
979     if (!Dosvm.ASPIHandler && !DPMI_LoadDosSystem())
980     {
981         ERR("could not setup ASPI handler\n");
982         return;
983     }
984     Dosvm.ASPIHandler( context );
985 }
986
987 /***********************************************************************
988  *           INT21_GetExtendedError
989  */
990 static void INT21_GetExtendedError( CONTEXT86 *context )
991 {
992     BYTE class, action, locus;
993     WORD error = GetLastError();
994
995     switch(error)
996     {
997     case ERROR_SUCCESS:
998         class = action = locus = 0;
999         break;
1000     case ERROR_DIR_NOT_EMPTY:
1001         class  = EC_Exists;
1002         action = SA_Ignore;
1003         locus  = EL_Disk;
1004         break;
1005     case ERROR_ACCESS_DENIED:
1006         class  = EC_AccessDenied;
1007         action = SA_Abort;
1008         locus  = EL_Disk;
1009         break;
1010     case ERROR_CANNOT_MAKE:
1011         class  = EC_AccessDenied;
1012         action = SA_Abort;
1013         locus  = EL_Unknown;
1014         break;
1015     case ERROR_DISK_FULL:
1016     case ERROR_HANDLE_DISK_FULL:
1017         class  = EC_MediaError;
1018         action = SA_Abort;
1019         locus  = EL_Disk;
1020         break;
1021     case ERROR_FILE_EXISTS:
1022     case ERROR_ALREADY_EXISTS:
1023         class  = EC_Exists;
1024         action = SA_Abort;
1025         locus  = EL_Disk;
1026         break;
1027     case ERROR_FILE_NOT_FOUND:
1028         class  = EC_NotFound;
1029         action = SA_Abort;
1030         locus  = EL_Disk;
1031         break;
1032     case ER_GeneralFailure:
1033         class  = EC_SystemFailure;
1034         action = SA_Abort;
1035         locus  = EL_Unknown;
1036         break;
1037     case ERROR_INVALID_DRIVE:
1038         class  = EC_MediaError;
1039         action = SA_Abort;
1040         locus  = EL_Disk;
1041         break;
1042     case ERROR_INVALID_HANDLE:
1043         class  = EC_ProgramError;
1044         action = SA_Abort;
1045         locus  = EL_Disk;
1046         break;
1047     case ERROR_LOCK_VIOLATION:
1048         class  = EC_AccessDenied;
1049         action = SA_Abort;
1050         locus  = EL_Disk;
1051         break;
1052     case ERROR_NO_MORE_FILES:
1053         class  = EC_MediaError;
1054         action = SA_Abort;
1055         locus  = EL_Disk;
1056         break;
1057     case ER_NoNetwork:
1058         class  = EC_NotFound;
1059         action = SA_Abort;
1060         locus  = EL_Network;
1061         break;
1062     case ERROR_NOT_ENOUGH_MEMORY:
1063         class  = EC_OutOfResource;
1064         action = SA_Abort;
1065         locus  = EL_Memory;
1066         break;
1067     case ERROR_PATH_NOT_FOUND:
1068         class  = EC_NotFound;
1069         action = SA_Abort;
1070         locus  = EL_Disk;
1071         break;
1072     case ERROR_SEEK:
1073         class  = EC_NotFound;
1074         action = SA_Ignore;
1075         locus  = EL_Disk;
1076         break;
1077     case ERROR_SHARING_VIOLATION:
1078         class  = EC_Temporary;
1079         action = SA_Retry;
1080         locus  = EL_Disk;
1081         break;
1082     case ERROR_TOO_MANY_OPEN_FILES:
1083         class  = EC_ProgramError;
1084         action = SA_Abort;
1085         locus  = EL_Disk;
1086         break;
1087     default:
1088         FIXME("Unknown error %d\n", error );
1089         class  = EC_SystemFailure;
1090         action = SA_Abort;
1091         locus  = EL_Unknown;
1092         break;
1093     }
1094     TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
1095            error, class, action, locus );
1096     AX_reg(context) = error;
1097     BH_reg(context) = class;
1098     BL_reg(context) = action;
1099     CH_reg(context) = locus;
1100 }
1101
1102 /***********************************************************************
1103  *           DOS3Call         (KERNEL.102)
1104  *           INT_Int21Handler (WPROCS.133)
1105  */
1106 void WINAPI DOS3Call( CONTEXT86 *context )
1107 {
1108     BOOL        bSetDOSExtendedError = FALSE;
1109
1110
1111     TRACE("AX=%04x BX=%04x CX=%04x DX=%04x "
1112           "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
1113           AX_reg(context), BX_reg(context), CX_reg(context), DX_reg(context),
1114           SI_reg(context), DI_reg(context),
1115           (WORD)context->SegDs, (WORD)context->SegEs,
1116           context->EFlags );
1117
1118
1119     if (AH_reg(context) == 0x59)  /* Get extended error info */
1120     {
1121         INT21_GetExtendedError( context );
1122         return;
1123     }
1124
1125     if (AH_reg(context) == 0x0C)  /* Flush buffer and read standard input */
1126     {
1127         TRACE("FLUSH BUFFER AND READ STANDARD INPUT\n");
1128         /* no flush here yet */
1129         AH_reg(context) = AL_reg(context);
1130     }
1131
1132     if (AH_reg(context)>=0x2f) {
1133         /* extended error is used by (at least) functions 0x2f to 0x62 */
1134         SetLastError(0);
1135     }
1136     RESET_CFLAG(context);  /* Not sure if this is a good idea */
1137
1138     switch(AH_reg(context))
1139     {
1140     case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1141     case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1142     case 0x03: /* READ CHARACTER FROM STDAUX  */
1143     case 0x04: /* WRITE CHARACTER TO STDAUX */
1144     case 0x05: /* WRITE CHARACTER TO PRINTER */
1145     case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1146     case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
1147     case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
1148     case 0x0b: /* GET STDIN STATUS */
1149     case 0x0f: /* OPEN FILE USING FCB */
1150     case 0x10: /* CLOSE FILE USING FCB */
1151     case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
1152     case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
1153     case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
1154     case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
1155     case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
1156     case 0x23: /* GET FILE SIZE FOR FCB */
1157     case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
1158     case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
1159     case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
1160     case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
1161     case 0x4d: /* GET RETURN CODE */
1162     case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
1163         INT_BARF( context, 0x21 );
1164         break;
1165
1166     case 0x00: /* TERMINATE PROGRAM */
1167         TRACE("TERMINATE PROGRAM\n");
1168         ExitThread( 0 );
1169         break;
1170
1171     case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
1172         TRACE("WRITE '$'-terminated string from %04lX:%04X to stdout\n",
1173               context->SegDs,DX_reg(context) );
1174         {
1175             LPSTR data = CTX_SEG_OFF_TO_LIN(context,context->SegDs,context->Edx);
1176             LPSTR p = data;
1177             /* do NOT use strchr() to calculate the string length,
1178             as '\0' is valid string content, too !
1179             Maybe we should check for non-'$' strings, but DOS doesn't. */
1180             while (*p != '$') p++;
1181             _hwrite16( 1, data, (int)p - (int)data);
1182             AL_reg(context) = '$'; /* yes, '$' (0x24) gets returned in AL */
1183         }
1184         break;
1185
1186     case 0x0a: /* BUFFERED INPUT */
1187       {
1188         char *buffer = ((char *)CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
1189                                                    context->Edx ));
1190         int res;
1191
1192         TRACE("BUFFERED INPUT (size=%d)\n",buffer[0]);
1193         if (buffer[1])
1194           TRACE("Handle old chars in buffer!\n");
1195         res=_lread16( 0, buffer+2,buffer[0]);
1196         buffer[1]=res;
1197         if(buffer[res+1] == '\n')
1198           buffer[res+1] = '\r';
1199         break;
1200       }
1201
1202     case 0x2e: /* SET VERIFY FLAG */
1203         TRACE("SET VERIFY FLAG ignored\n");
1204         /* we cannot change the behaviour anyway, so just ignore it */
1205         break;
1206
1207     case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
1208     case 0x1d:
1209     case 0x1e:
1210     case 0x20:
1211     case 0x6b: /* NULL FUNCTION */
1212         AL_reg(context) = 0;
1213         break;
1214
1215     case 0x5c: /* "FLOCK" - RECORD LOCKING */
1216         fLock(context);
1217         break;
1218
1219     case 0x0d: /* DISK BUFFER FLUSH */
1220         TRACE("DISK BUFFER FLUSH ignored\n");
1221         RESET_CFLAG(context); /* dos 6+ only */
1222         break;
1223
1224     case 0x0e: /* SELECT DEFAULT DRIVE */
1225         TRACE("SELECT DEFAULT DRIVE %d\n", DL_reg(context));
1226         DRIVE_SetCurrentDrive( DL_reg(context) );
1227         AL_reg(context) = MAX_DOS_DRIVES;
1228         break;
1229
1230     case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1231         TRACE("FIND FIRST MATCHING FILE USING FCB %p\n",
1232               CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx));
1233         if (!INT21_FindFirstFCB(context))
1234         {
1235             AL_reg(context) = 0xff;
1236             break;
1237         }
1238         /* else fall through */
1239
1240     case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1241         AL_reg(context) = INT21_FindNextFCB(context) ? 0x00 : 0xff;
1242         break;
1243
1244     case 0x13: /* DELETE FILE USING FCB */
1245         DeleteFileFCB(context);
1246         break;
1247
1248     case 0x17: /* RENAME FILE USING FCB */
1249         RenameFileFCB(context);
1250         break;
1251
1252     case 0x19: /* GET CURRENT DEFAULT DRIVE */
1253         AL_reg(context) = DRIVE_GetCurrentDrive();
1254         break;
1255
1256     case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1257         {
1258             TDB *pTask = TASK_GetCurrent();
1259             pTask->dta = MAKESEGPTR(context->SegDs,DX_reg(context));
1260             TRACE("Set DTA: %08lx\n", pTask->dta);
1261         }
1262         break;
1263
1264     case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1265         DL_reg(context) = 0;
1266         if (!INT21_GetDriveAllocInfo(context)) AX_reg(context) = 0xffff;
1267         break;
1268
1269     case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1270         if (!INT21_GetDriveAllocInfo(context)) AX_reg(context) = 0xffff;
1271         break;
1272
1273     case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1274         GetDrivePB(context, DRIVE_GetCurrentDrive());
1275         break;
1276
1277     case 0x25: /* SET INTERRUPT VECTOR */
1278         INT_SetPMHandler( AL_reg(context), (FARPROC16)MAKESEGPTR( context->SegDs, DX_reg(context)));
1279         break;
1280
1281     case 0x29: /* PARSE FILENAME INTO FCB */
1282         INT21_ParseFileNameIntoFCB(context);
1283         break;
1284
1285     case 0x2a: /* GET SYSTEM DATE */
1286         INT21_GetSystemDate(context);
1287         break;
1288
1289     case 0x2b: /* SET SYSTEM DATE */
1290         FIXME("SetSystemDate(%02d/%02d/%04d): not allowed\n",
1291               DL_reg(context), DH_reg(context), CX_reg(context) );
1292         AL_reg(context) = 0;  /* Let's pretend we succeeded */
1293         break;
1294
1295     case 0x2c: /* GET SYSTEM TIME */
1296         INT21_GetSystemTime(context);
1297         break;
1298
1299     case 0x2d: /* SET SYSTEM TIME */
1300         FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
1301               CH_reg(context), CL_reg(context),
1302               DH_reg(context), DL_reg(context) );
1303         AL_reg(context) = 0;  /* Let's pretend we succeeded */
1304         break;
1305
1306     case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1307         TRACE("GET DISK TRANSFER AREA ADDRESS\n");
1308         {
1309             TDB *pTask = TASK_GetCurrent();
1310             context->SegEs = SELECTOROF( pTask->dta );
1311             BX_reg(context) = OFFSETOF( pTask->dta );
1312         }
1313         break;
1314
1315     case 0x30: /* GET DOS VERSION */
1316         TRACE("GET DOS VERSION %s requested\n",
1317               (AL_reg(context) == 0x00)?"OEM number":"version flag");
1318         AX_reg(context) = (HIWORD(GetVersion16()) >> 8) |
1319                           (HIWORD(GetVersion16()) << 8);
1320 #if 0
1321         AH_reg(context) = 0x7;
1322         AL_reg(context) = 0xA;
1323 #endif
1324
1325         BX_reg(context) = 0x00FF;     /* 0x123456 is Wine's serial # */
1326         CX_reg(context) = 0x0000;
1327         break;
1328
1329     case 0x31: /* TERMINATE AND STAY RESIDENT */
1330         FIXME("TERMINATE AND STAY RESIDENT stub\n");
1331         break;
1332
1333     case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1334         TRACE("GET DOS DRIVE PARAMETER BLOCK FOR DRIVE %s\n",
1335               INT21_DriveName( DL_reg(context)));
1336         GetDrivePB(context, DOS_GET_DRIVE( DL_reg(context) ) );
1337         break;
1338
1339     case 0x33: /* MULTIPLEXED */
1340         switch (AL_reg(context))
1341         {
1342               case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1343                 TRACE("GET CURRENT EXTENDED BREAK STATE\n");
1344                 INT21_ReadConfigSys();
1345                 DL_reg(context) = DOSCONF_config.brk_flag;
1346                 break;
1347
1348               case 0x01: /* SET EXTENDED BREAK STATE */
1349                 TRACE("SET CURRENT EXTENDED BREAK STATE\n");
1350                 INT21_ReadConfigSys();
1351                 DOSCONF_config.brk_flag = (DL_reg(context) > 0);
1352                 break;
1353
1354               case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1355                 TRACE("GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE\n");
1356                 INT21_ReadConfigSys();
1357                 /* ugly coding in order to stay reentrant */
1358                 if (DL_reg(context))
1359                 {
1360                     DL_reg(context) = DOSCONF_config.brk_flag;
1361                     DOSCONF_config.brk_flag = 1;
1362                 }
1363                 else
1364                 {
1365                     DL_reg(context) = DOSCONF_config.brk_flag;
1366                     DOSCONF_config.brk_flag = 0;
1367                 }
1368                 break;
1369
1370               case 0x05: /* GET BOOT DRIVE */
1371                 TRACE("GET BOOT DRIVE\n");
1372                 DL_reg(context) = 3;
1373                 /* c: is Wine's bootdrive (a: is 1)*/
1374                 break;
1375
1376               case 0x06: /* GET TRUE VERSION NUMBER */
1377                 TRACE("GET TRUE VERSION NUMBER\n");
1378                 BX_reg(context) = (HIWORD(GetVersion16() >> 8)) |
1379                                   (HIWORD(GetVersion16() << 8));
1380                 DX_reg(context) = 0x00;
1381                 break;
1382
1383               default:
1384                 INT_BARF( context, 0x21 );
1385                 break;
1386         }
1387         break;
1388
1389     case 0x34: /* GET ADDRESS OF INDOS FLAG */
1390         TRACE("GET ADDRESS OF INDOS FLAG\n");
1391         if (!heap) INT21_CreateHeap();
1392         context->SegEs = DosHeapHandle;
1393         BX_reg(context) = (int)&heap->InDosFlag - (int)heap;
1394         break;
1395
1396     case 0x35: /* GET INTERRUPT VECTOR */
1397         TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
1398         {
1399             FARPROC16 addr = INT_GetPMHandler( AL_reg(context) );
1400             context->SegEs = SELECTOROF(addr);
1401             BX_reg(context) = OFFSETOF(addr);
1402         }
1403         break;
1404
1405     case 0x36: /* GET FREE DISK SPACE */
1406         TRACE("GET FREE DISK SPACE FOR DRIVE %s\n",
1407               INT21_DriveName( DL_reg(context)));
1408         if (!INT21_GetFreeDiskSpace(context)) AX_reg(context) = 0xffff;
1409         break;
1410
1411     case 0x37:
1412       {
1413         unsigned char switchchar='/';
1414         switch (AL_reg(context))
1415         {
1416         case 0x00: /* "SWITCHAR" - GET SWITCH CHARACTER */
1417           TRACE("SWITCHAR - GET SWITCH CHARACTER\n");
1418           AL_reg(context) = 0x00; /* success*/
1419           DL_reg(context) = switchchar;
1420           break;
1421         case 0x01: /*"SWITCHAR" - SET SWITCH CHARACTER*/
1422           TRACE("SWITCHAR - SET SWITCH CHARACTER\n");
1423           switchchar = DL_reg(context);
1424           AL_reg(context) = 0x00; /* success*/
1425           break;
1426         default: /*"AVAILDEV" - SPECIFY \DEV\ PREFIX USE*/
1427           INT_BARF( context, 0x21 );
1428           break;
1429         }
1430         break;
1431       }
1432
1433     case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1434         TRACE("GET COUNTRY-SPECIFIC INFORMATION for country 0x%02x\n",
1435               AL_reg(context));
1436         AX_reg(context) = 0x02; /* no country support available */
1437         SET_CFLAG(context);
1438         break;
1439
1440     case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1441         TRACE("MKDIR %s\n",
1442               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1443         bSetDOSExtendedError = (!CreateDirectory16( CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
1444                                                            context->Edx ), NULL));
1445         /* FIXME: CreateDirectory's LastErrors will clash with the ones
1446          * used by dos. AH=39 only returns 3 (path not found) and 5 (access
1447          * denied), while CreateDirectory return several ones. remap some of
1448          * them. -Marcus
1449          */
1450         if (bSetDOSExtendedError) {
1451                 switch (GetLastError()) {
1452                 case ERROR_ALREADY_EXISTS:
1453                 case ERROR_FILENAME_EXCED_RANGE:
1454                 case ERROR_DISK_FULL:
1455                         SetLastError(ERROR_ACCESS_DENIED);
1456                         break;
1457                 default: break;
1458                 }
1459         }
1460         break;
1461
1462     case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1463         TRACE("RMDIR %s\n",
1464               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1465         bSetDOSExtendedError = (!RemoveDirectory16( CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
1466                                                                  context->Edx )));
1467         break;
1468
1469     case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1470         TRACE("CHDIR %s\n",
1471               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1472         bSetDOSExtendedError = !INT21_ChangeDir(context);
1473         break;
1474
1475     case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1476         TRACE("CREAT flag 0x%02x %s\n",CX_reg(context),
1477               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1478         bSetDOSExtendedError = INT21_CreateFile( context );
1479         break;
1480
1481     case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1482         TRACE("OPEN mode 0x%02x %s\n",AL_reg(context),
1483               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1484         OpenExistingFile(context);
1485         break;
1486
1487     case 0x3e: /* "CLOSE" - CLOSE FILE */
1488         TRACE("CLOSE handle %d\n",BX_reg(context));
1489         bSetDOSExtendedError = ((AX_reg(context) = _lclose16( BX_reg(context) )) != 0);
1490         break;
1491
1492     case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1493         TRACE("READ from %d to %04lX:%04X for %d byte\n",BX_reg(context),
1494               context->SegDs,DX_reg(context),CX_reg(context) );
1495         {
1496             LONG result;
1497             if (ISV86(context))
1498                 result = _hread16( BX_reg(context),
1499                                    CTX_SEG_OFF_TO_LIN(context, context->SegDs,
1500                                                                context->Edx ),
1501                                    CX_reg(context) );
1502             else
1503                 result = WIN16_hread( BX_reg(context),
1504                                       MAKESEGPTR( context->SegDs, context->Edx ),
1505                                       CX_reg(context) );
1506             if (result == -1) bSetDOSExtendedError = TRUE;
1507             else AX_reg(context) = (WORD)result;
1508         }
1509         break;
1510
1511     case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1512         TRACE("WRITE from %04lX:%04X to handle %d for %d byte\n",
1513               context->SegDs,DX_reg(context),BX_reg(context),CX_reg(context) );
1514         {
1515             LONG result = _hwrite16( BX_reg(context),
1516                                      CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
1517                                                          context->Edx ),
1518                                      CX_reg(context) );
1519             if (result == -1) bSetDOSExtendedError = TRUE;
1520             else AX_reg(context) = (WORD)result;
1521         }
1522         break;
1523
1524     case 0x41: /* "UNLINK" - DELETE FILE */
1525         TRACE("UNLINK %s\n",
1526               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1527         bSetDOSExtendedError = (!DeleteFileA( CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
1528                                                              context->Edx )));
1529         break;
1530
1531     case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1532         TRACE("LSEEK handle %d offset %ld from %s\n",
1533               BX_reg(context), MAKELONG(DX_reg(context),CX_reg(context)),
1534               (AL_reg(context)==0)?"start of file":(AL_reg(context)==1)?
1535               "current file position":"end of file");
1536         {
1537             LONG status = _llseek16( BX_reg(context),
1538                                      MAKELONG(DX_reg(context),CX_reg(context)),
1539                                      AL_reg(context) );
1540             if (status == -1) bSetDOSExtendedError = TRUE;
1541             else
1542             {
1543                 AX_reg(context) = LOWORD(status);
1544                 DX_reg(context) = HIWORD(status);
1545             }
1546         }
1547         break;
1548
1549     case 0x43: /* FILE ATTRIBUTES */
1550         switch (AL_reg(context))
1551         {
1552         case 0x00:
1553             TRACE("GET FILE ATTRIBUTES for %s\n",
1554                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1555             AX_reg(context) = (WORD)GetFileAttributesA(
1556                                           CTX_SEG_OFF_TO_LIN(context, context->SegDs,
1557                                                              context->Edx));
1558             if (AX_reg(context) == 0xffff) bSetDOSExtendedError = TRUE;
1559             else CX_reg(context) = AX_reg(context);
1560             break;
1561
1562         case 0x01:
1563             TRACE("SET FILE ATTRIBUTES 0x%02x for %s\n", CX_reg(context),
1564                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1565             bSetDOSExtendedError =
1566                 (!SetFileAttributesA( CTX_SEG_OFF_TO_LIN(context, context->SegDs,
1567                                                            context->Edx),
1568                                                            CX_reg(context) ));
1569             break;
1570         case 0x02:
1571             FIXME("GET COMPRESSED FILE SIZE for %s stub\n",
1572                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1573         }
1574         break;
1575
1576     case 0x44: /* IOCTL */
1577         switch (AL_reg(context))
1578         {
1579         case 0x00:
1580             ioctlGetDeviceInfo(context);
1581             break;
1582
1583         case 0x01:
1584             break;
1585         case 0x02:{
1586             const DOS_DEVICE *dev;
1587             if ((dev = DOSFS_GetDeviceByHandle( DosFileHandleToWin32Handle(BX_reg(context)) )) &&
1588                 !strcasecmp( dev->name, "SCSIMGR$" ))
1589             {
1590                 ASPI_DOS_HandleInt(context);
1591             }
1592             break;
1593        }
1594         case 0x05:{     /* IOCTL - WRITE TO BLOCK DEVICE CONTROL CHANNEL */
1595             /*BYTE *dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx);*/
1596             int drive = DOS_GET_DRIVE(BL_reg(context));
1597
1598             FIXME("program tried to write to block device control channel of drive %d:\n",drive);
1599             /* for (i=0;i<CX_reg(context);i++)
1600                 fprintf(stdnimp,"%02x ",dataptr[i]);
1601             fprintf(stdnimp,"\n");*/
1602             AX_reg(context)=CX_reg(context);
1603             break;
1604         }
1605         case 0x08:   /* Check if drive is removable. */
1606             TRACE("IOCTL - CHECK IF BLOCK DEVICE REMOVABLE for drive %s\n",
1607                  INT21_DriveName( BL_reg(context)));
1608             switch(GetDriveType16( DOS_GET_DRIVE( BL_reg(context) )))
1609             {
1610             case DRIVE_UNKNOWN:
1611                 SetLastError( ERROR_INVALID_DRIVE );
1612                 AX_reg(context) = ERROR_INVALID_DRIVE;
1613                 SET_CFLAG(context);
1614                 break;
1615             case DRIVE_REMOVABLE:
1616                 AX_reg(context) = 0;      /* removable */
1617                 break;
1618             default:
1619                 AX_reg(context) = 1;   /* not removable */
1620                 break;
1621             }
1622             break;
1623
1624         case 0x09:   /* CHECK IF BLOCK DEVICE REMOTE */
1625             TRACE("IOCTL - CHECK IF BLOCK DEVICE REMOTE for drive %s\n",
1626                  INT21_DriveName( BL_reg(context)));
1627             switch(GetDriveType16( DOS_GET_DRIVE( BL_reg(context) )))
1628             {
1629             case DRIVE_UNKNOWN:
1630                 SetLastError( ERROR_INVALID_DRIVE );
1631                 AX_reg(context) = ERROR_INVALID_DRIVE;
1632                 SET_CFLAG(context);
1633                 break;
1634             case DRIVE_REMOTE:
1635                 DX_reg(context) = (1<<9) | (1<<12);  /* remote */
1636                 break;
1637             default:
1638                 DX_reg(context) = 0;  /* FIXME: use driver attr here */
1639                 break;
1640             }
1641             break;
1642
1643         case 0x0a: /* check if handle (BX) is remote */
1644             TRACE("IOCTL - CHECK IF HANDLE %d IS REMOTE\n",BX_reg(context));
1645             /* returns DX, bit 15 set if remote, bit 14 set if date/time
1646              * not set on close
1647              */
1648             DX_reg(context) = 0;
1649             break;
1650
1651         case 0x0d:
1652             TRACE("IOCTL - GENERIC BLOCK DEVICE REQUEST %s\n",
1653                   INT21_DriveName( BL_reg(context)));
1654             bSetDOSExtendedError = ioctlGenericBlkDevReq(context);
1655             break;
1656
1657         case 0x0e: /* get logical drive mapping */
1658             TRACE("IOCTL - GET LOGICAL DRIVE MAP for drive %s\n",
1659                   INT21_DriveName( BL_reg(context)));
1660             AL_reg(context) = 0; /* drive has no mapping - FIXME: may be wrong*/
1661             break;
1662
1663         case 0x0F:   /* Set logical drive mapping */
1664             {
1665             int drive;
1666             TRACE("IOCTL - SET LOGICAL DRIVE MAP for drive %s\n",
1667                   INT21_DriveName( BL_reg(context)));
1668             drive = DOS_GET_DRIVE ( BL_reg(context) );
1669             if ( ! DRIVE_SetLogicalMapping ( drive, drive+1 ) )
1670             {
1671                 SET_CFLAG(context);
1672                 AX_reg(context) = 0x000F;  /* invalid drive */
1673             }
1674             break;
1675             }
1676
1677         case 0xe0:  /* Sun PC-NFS API */
1678             /* not installed */
1679             break;
1680
1681         case 0x52:  /* DR-DOS version */
1682             /* This is not DR-DOS */
1683
1684             TRACE("GET DR-DOS VERSION requested\n");
1685
1686             AX_reg(context) = 0x0001; /* Invalid function */
1687             SET_CFLAG(context);       /* Error */
1688             SetLastError( ERROR_INVALID_FUNCTION );
1689             break;
1690
1691         default:
1692             INT_BARF( context, 0x21 );
1693             break;
1694         }
1695         break;
1696
1697     case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1698         {
1699             HANDLE handle;
1700             TRACE("DUP - DUPLICATE FILE HANDLE %d\n",BX_reg(context));
1701             if ((bSetDOSExtendedError = !DuplicateHandle( GetCurrentProcess(),
1702                                                           DosFileHandleToWin32Handle(BX_reg(context)),
1703                                                           GetCurrentProcess(), &handle,
1704                                                           0, TRUE, DUPLICATE_SAME_ACCESS )))
1705                 AX_reg(context) = HFILE_ERROR16;
1706             else
1707                 AX_reg(context) = Win32HandleToDosFileHandle(handle);
1708             break;
1709         }
1710
1711     case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1712         TRACE("FORCEDUP - FORCE DUPLICATE FILE HANDLE %d to %d\n",
1713               BX_reg(context),CX_reg(context));
1714         bSetDOSExtendedError = (FILE_Dup2( BX_reg(context), CX_reg(context) ) == HFILE_ERROR16);
1715         break;
1716
1717     case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1718         TRACE("CWD - GET CURRENT DIRECTORY for drive %s\n",
1719               INT21_DriveName( DL_reg(context)));
1720         bSetDOSExtendedError = !INT21_GetCurrentDirectory(context);
1721         break;
1722
1723     case 0x48: /* ALLOCATE MEMORY */
1724         TRACE("ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context));
1725         {
1726             LPVOID *mem;
1727             if (ISV86(context))
1728               {
1729                 mem= DOSMEM_GetBlock((DWORD)BX_reg(context)<<4,NULL);
1730             if (mem)
1731                 AX_reg(context) = DOSMEM_MapLinearToDos(mem)>>4;
1732               }
1733             else
1734               {
1735                 mem = (LPVOID)GlobalDOSAlloc16(BX_reg(context)<<4);
1736                 if (mem)
1737                   AX_reg(context) = (DWORD)mem&0xffff;
1738               }
1739             if (!mem)
1740               {
1741                 SET_CFLAG(context);
1742                 AX_reg(context) = 0x0008; /* insufficient memory */
1743                 BX_reg(context) = DOSMEM_Available()>>4;
1744             }
1745         }
1746         break;
1747
1748     case 0x49: /* FREE MEMORY */
1749         TRACE("FREE MEMORY segment %04lX\n", context->SegEs);
1750         {
1751           BOOL ret;
1752           if (ISV86(context))
1753             ret= DOSMEM_FreeBlock(DOSMEM_MapDosToLinear(context->SegEs<<4));
1754           else
1755             {
1756               ret = !GlobalDOSFree16(context->SegEs);
1757               /* If we don't reset ES_reg, we will fail in the relay code */
1758               context->SegEs=ret;
1759             }
1760           if (!ret)
1761             {
1762               TRACE("FREE MEMORY failed\n");
1763             SET_CFLAG(context);
1764             AX_reg(context) = 0x0009; /* memory block address invalid */
1765         }
1766         }
1767         break;
1768
1769     case 0x4a: /* RESIZE MEMORY BLOCK */
1770         TRACE("RESIZE MEMORY segment %04lX to %d paragraphs\n", context->SegEs, BX_reg(context));
1771         if (!ISV86(context))
1772           FIXME("RESIZE MEMORY probably insufficient implementation. Expect crash soon\n");
1773         {
1774             LPVOID *mem = DOSMEM_ResizeBlock(DOSMEM_MapDosToLinear(context->SegEs<<4),
1775                                              BX_reg(context)<<4,NULL);
1776             if (mem)
1777                 AX_reg(context) = DOSMEM_MapLinearToDos(mem)>>4;
1778             else {
1779                 SET_CFLAG(context);
1780                 AX_reg(context) = 0x0008; /* insufficient memory */
1781                 BX_reg(context) = DOSMEM_Available()>>4; /* not quite right */
1782             }
1783         }
1784         break;
1785
1786     case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1787         TRACE("EXEC %s\n", (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx ));
1788         AX_reg(context) = WinExec16( CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx ),
1789                                      SW_NORMAL );
1790         if (AX_reg(context) < 32) SET_CFLAG(context);
1791         break;
1792
1793     case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1794         TRACE("EXIT with return code %d\n",AL_reg(context));
1795         ExitThread( AL_reg(context) );
1796         break;
1797
1798     case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1799         TRACE("FINDFIRST mask 0x%04x spec %s\n",CX_reg(context),
1800               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1801         if (!INT21_FindFirst(context)) break;
1802         /* fall through */
1803
1804     case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1805         TRACE("FINDNEXT\n");
1806         if (!INT21_FindNext(context))
1807         {
1808             SetLastError( ERROR_NO_MORE_FILES );
1809             AX_reg(context) = ERROR_NO_MORE_FILES;
1810             SET_CFLAG(context);
1811         }
1812         else AX_reg(context) = 0;  /* OK */
1813         break;
1814     case 0x51: /* GET PSP ADDRESS */
1815         TRACE("GET CURRENT PROCESS ID (GET PSP ADDRESS)\n");
1816         /* FIXME: should we return the original DOS PSP upon */
1817         /*        Windows startup ? */
1818         BX_reg(context) = GetCurrentPDB16();
1819         break;
1820     case 0x62: /* GET PSP ADDRESS */
1821         /* FIXME: should we return the original DOS PSP upon */
1822         /*        Windows startup ? */
1823         BX_reg(context) = GetCurrentPDB16();
1824         break;
1825
1826     case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1827         TRACE("SYSVARS - GET LIST OF LISTS\n");
1828         {
1829             context->SegEs = LOWORD(DOS_LOLSeg);
1830             BX_reg(context) = FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB);
1831         }
1832         break;
1833
1834     case 0x54: /* Get Verify Flag */
1835         TRACE("Get Verify Flag - Not Supported\n");
1836         AL_reg(context) = 0x00;  /* pretend we can tell. 00h = off 01h = on */
1837         break;
1838
1839     case 0x56: /* "RENAME" - RENAME FILE */
1840         TRACE("RENAME %s to %s\n",
1841               (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx),
1842               (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegEs,context->Edi));
1843         bSetDOSExtendedError =
1844                 (!MoveFileA( CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx),
1845                                CTX_SEG_OFF_TO_LIN(context, context->SegEs,context->Edi)));
1846         break;
1847
1848     case 0x57: /* FILE DATE AND TIME */
1849         switch (AL_reg(context))
1850         {
1851         case 0x00:  /* Get */
1852             {
1853                 FILETIME filetime;
1854                 TRACE("GET FILE DATE AND TIME for handle %d\n",
1855                       BX_reg(context));
1856                 if (!GetFileTime( DosFileHandleToWin32Handle(BX_reg(context)), NULL, NULL, &filetime ))
1857                      bSetDOSExtendedError = TRUE;
1858                 else FileTimeToDosDateTime( &filetime, &DX_reg(context),
1859                                             &CX_reg(context) );
1860             }
1861             break;
1862
1863         case 0x01:  /* Set */
1864             {
1865                 FILETIME filetime;
1866                 TRACE("SET FILE DATE AND TIME for handle %d\n",
1867                       BX_reg(context));
1868                 DosDateTimeToFileTime( DX_reg(context), CX_reg(context),
1869                                        &filetime );
1870                 bSetDOSExtendedError =
1871                         (!SetFileTime( DosFileHandleToWin32Handle(BX_reg(context)),
1872                                       NULL, NULL, &filetime ));
1873             }
1874             break;
1875         }
1876         break;
1877
1878     case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1879         TRACE("GET OR SET MEMORY/UMB ALLOCATION STRATEGY subfunction %d\n",
1880               AL_reg(context));
1881         switch (AL_reg(context))
1882         {
1883         case 0x00:
1884             AX_reg(context) = 1;
1885             break;
1886         case 0x02:
1887             AX_reg(context) = 0;
1888             break;
1889         case 0x01:
1890         case 0x03:
1891             break;
1892         }
1893         RESET_CFLAG(context);
1894         break;
1895
1896     case 0x5a: /* CREATE TEMPORARY FILE */
1897         TRACE("CREATE TEMPORARY FILE\n");
1898         bSetDOSExtendedError = !INT21_CreateTempFile(context);
1899         break;
1900
1901     case 0x5b: /* CREATE NEW FILE */
1902         TRACE("CREATE NEW FILE 0x%02x for %s\n", CX_reg(context),
1903               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
1904         bSetDOSExtendedError = ((AX_reg(context) =
1905                _lcreat16_uniq( CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx),
1906                                CX_reg(context) )) == (WORD)HFILE_ERROR16);
1907         break;
1908
1909     case 0x5d: /* NETWORK */
1910         FIXME("Function 0x%04x not implemented.\n", AX_reg (context));
1911         /* Fix the following while you're at it.  */
1912         SetLastError( ER_NoNetwork );
1913         bSetDOSExtendedError = TRUE;
1914         break;
1915
1916     case 0x5e:
1917         bSetDOSExtendedError = INT21_networkfunc (context);
1918         break;
1919
1920     case 0x5f: /* NETWORK */
1921         switch (AL_reg(context))
1922         {
1923         case 0x07: /* ENABLE DRIVE */
1924             TRACE("ENABLE DRIVE %c:\n",(DL_reg(context)+'A'));
1925             if (!DRIVE_Enable( DL_reg(context) ))
1926             {
1927                 SetLastError( ERROR_INVALID_DRIVE );
1928                 bSetDOSExtendedError = TRUE;
1929             }
1930             break;
1931
1932         case 0x08: /* DISABLE DRIVE */
1933             TRACE("DISABLE DRIVE %c:\n",(DL_reg(context)+'A'));
1934             if (!DRIVE_Disable( DL_reg(context) ))
1935             {
1936                 SetLastError( ERROR_INVALID_DRIVE );
1937                 bSetDOSExtendedError = TRUE;
1938             }
1939             break;
1940
1941         default:
1942             /* network software not installed */
1943             TRACE("NETWORK function AX=%04x not implemented\n",AX_reg(context));
1944             SetLastError( ER_NoNetwork );
1945             bSetDOSExtendedError = TRUE;
1946             break;
1947         }
1948         break;
1949
1950     case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1951         TRACE("TRUENAME %s\n",
1952               (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Esi));
1953         {
1954             if (!GetFullPathNameA( CTX_SEG_OFF_TO_LIN(context, context->SegDs,
1955                                                         context->Esi), 128,
1956                                      CTX_SEG_OFF_TO_LIN(context, context->SegEs,
1957                                                         context->Edi),NULL))
1958                 bSetDOSExtendedError = TRUE;
1959             else AX_reg(context) = 0;
1960         }
1961         break;
1962
1963     case 0x61: /* UNUSED */
1964     case 0x63: /* misc. language support */
1965         switch (AL_reg(context)) {
1966         case 0x00: /* GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
1967             INT21_GetDBCSLeadTable(context);
1968             break;
1969         }
1970         break;
1971     case 0x64: /* OS/2 DOS BOX */
1972         INT_BARF( context, 0x21 );
1973         SET_CFLAG(context);
1974         break;
1975
1976     case 0x65:{/* GET EXTENDED COUNTRY INFORMATION */
1977         BYTE    *dataptr=CTX_SEG_OFF_TO_LIN(context, context->SegEs,context->Edi);
1978         TRACE("GET EXTENDED COUNTRY INFORMATION code page %d country %d\n",
1979               BX_reg(context), DX_reg(context));
1980         switch (AL_reg(context)) {
1981         case 0x01:
1982             TRACE("\tget general internationalization info\n");
1983             dataptr[0] = 0x1;
1984             *(WORD*)(dataptr+1) = 41;
1985             *(WORD*)(dataptr+3) = GetSystemDefaultLangID();
1986             *(WORD*)(dataptr+5) = CodePage;
1987             *(DWORD*)(dataptr+0x19) = 0; /* FIXME: ptr to case map routine */
1988             break;
1989         case 0x06:
1990             TRACE("\tget pointer to collating sequence table\n");
1991             dataptr[0] = 0x06;
1992             *(DWORD*)(dataptr+1) = MAKELONG(DOSMEM_CollateTable & 0xFFFF,DOSMEM_AllocSelector(DOSMEM_CollateTable>>16));
1993             CX_reg(context)         = 258;/*FIXME: size of table?*/
1994             break;
1995         case 0x20:
1996             TRACE("\tConvert char to uppercase\n");
1997             DL_reg(context) = toupper(DL_reg(context));
1998             break;
1999         case 0x21:
2000             TRACE("\tconvert string to uppercase with length\n");
2001             {
2002                 char *ptr = (char *)CTX_SEG_OFF_TO_LIN(context,context->SegDs,context->Edx);
2003                 WORD len = CX_reg(context);
2004                 while (len--) { *ptr = toupper(*ptr); ptr++; }
2005             }
2006             break;
2007         case 0x22:
2008             TRACE("\tConvert ASCIIZ string to uppercase\n");
2009             _strupr( (LPSTR)CTX_SEG_OFF_TO_LIN(context,context->SegDs,context->Edx) );
2010             break;
2011         default:
2012             TRACE("\tunimplemented function %d\n",AL_reg(context));
2013             INT_BARF( context, 0x21 );
2014             SET_CFLAG(context);
2015             break;
2016         }
2017         break;
2018     }
2019     case 0x66: /* GLOBAL CODE PAGE TABLE */
2020         switch (AL_reg(context))
2021         {
2022         case 0x01:
2023             TRACE("GET GLOBAL CODE PAGE TABLE\n");
2024             DX_reg(context) = BX_reg(context) = CodePage;
2025             RESET_CFLAG(context);
2026             break;
2027         case 0x02:
2028             TRACE("SET GLOBAL CODE PAGE TABLE active page %d system page %d\n",
2029                   BX_reg(context),DX_reg(context));
2030             CodePage = BX_reg(context);
2031             RESET_CFLAG(context);
2032             break;
2033         }
2034         break;
2035
2036     case 0x67: /* SET HANDLE COUNT */
2037         TRACE("SET HANDLE COUNT to %d\n",BX_reg(context) );
2038         SetHandleCount16( BX_reg(context) );
2039         if (GetLastError()) bSetDOSExtendedError = TRUE;
2040         break;
2041
2042     case 0x68: /* "FFLUSH" - COMMIT FILE */
2043     case 0x6a: /* COMMIT FILE */
2044         TRACE("FFLUSH/COMMIT handle %d\n",BX_reg(context));
2045         bSetDOSExtendedError = (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ));
2046         break;
2047
2048     case 0x69: /* DISK SERIAL NUMBER */
2049         switch (AL_reg(context))
2050         {
2051         case 0x00:
2052             TRACE("GET DISK SERIAL NUMBER for drive %s\n",
2053                   INT21_DriveName(BL_reg(context)));
2054             if (!INT21_GetDiskSerialNumber(context)) bSetDOSExtendedError = TRUE;
2055             else AX_reg(context) = 0;
2056             break;
2057
2058         case 0x01:
2059             TRACE("SET DISK SERIAL NUMBER for drive %s\n",
2060                   INT21_DriveName(BL_reg(context)));
2061             if (!INT21_SetDiskSerialNumber(context)) bSetDOSExtendedError = TRUE;
2062             else AX_reg(context) = 1;
2063             break;
2064         }
2065         break;
2066
2067     case 0x6C: /* Extended Open/Create*/
2068         TRACE("EXTENDED OPEN/CREATE %s\n",
2069               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edi));
2070         bSetDOSExtendedError = INT21_ExtendedOpenCreateFile(context);
2071         break;
2072
2073     case 0x71: /* MS-DOS 7 (Windows95) - LONG FILENAME FUNCTIONS */
2074         if ((GetVersion()&0xC0000004)!=0xC0000004) {
2075             /* not supported on anything but Win95 */
2076             TRACE("LONG FILENAME functions supported only by win95\n");
2077             SET_CFLAG(context);
2078             AL_reg(context) = 0;
2079         } else
2080         switch(AL_reg(context))
2081         {
2082         case 0x39:  /* Create directory */
2083             TRACE("LONG FILENAME - MAKE DIRECTORY %s\n",
2084                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs,context->Edx));
2085             bSetDOSExtendedError = (!CreateDirectoryA(
2086                                         CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
2087                                                   context->Edx ), NULL));
2088             /* FIXME: CreateDirectory's LastErrors will clash with the ones
2089              * used by dos. AH=39 only returns 3 (path not found) and 5 (access
2090              * denied), while CreateDirectory return several ones. remap some of
2091              * them. -Marcus
2092              */
2093             if (bSetDOSExtendedError) {
2094                     switch (GetLastError()) {
2095                     case ERROR_ALREADY_EXISTS:
2096                     case ERROR_FILENAME_EXCED_RANGE:
2097                     case ERROR_DISK_FULL:
2098                             SetLastError(ERROR_ACCESS_DENIED);
2099                             break;
2100                     default: break;
2101                     }
2102             }
2103             break;
2104         case 0x3a:  /* Remove directory */
2105             TRACE("LONG FILENAME - REMOVE DIRECTORY %s\n",
2106                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs,context->Edx));
2107             bSetDOSExtendedError = (!RemoveDirectoryA(
2108                                         CTX_SEG_OFF_TO_LIN(context,  context->SegDs,
2109                                                         context->Edx )));
2110             break;
2111         case 0x43:  /* Get/Set file attributes */
2112           TRACE("LONG FILENAME -EXTENDED GET/SET FILE ATTRIBUTES %s\n",
2113                 (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs,context->Edx));
2114         switch (BL_reg(context))
2115         {
2116         case 0x00: /* Get file attributes */
2117             TRACE("\tretrieve attributes\n");
2118             CX_reg(context) = (WORD)GetFileAttributesA(
2119                                           CTX_SEG_OFF_TO_LIN(context, context->SegDs,
2120                                                              context->Edx));
2121             if (CX_reg(context) == 0xffff) bSetDOSExtendedError = TRUE;
2122             break;
2123         case 0x01:
2124             TRACE("\tset attributes 0x%04x\n",CX_reg(context));
2125             bSetDOSExtendedError = (!SetFileAttributesA(
2126                                         CTX_SEG_OFF_TO_LIN(context, context->SegDs,
2127                                                            context->Edx),
2128                                         CX_reg(context)  ) );
2129             break;
2130         default:
2131           FIXME("Unimplemented long file name function:\n");
2132           INT_BARF( context, 0x21 );
2133           SET_CFLAG(context);
2134           AL_reg(context) = 0;
2135           break;
2136         }
2137         break;
2138         case 0x47:  /* Get current directory */
2139             TRACE(" LONG FILENAME - GET CURRENT DIRECTORY for drive %s\n",
2140                   INT21_DriveName(DL_reg(context)));
2141             bSetDOSExtendedError = !INT21_GetCurrentDirectory(context);
2142             break;
2143
2144         case 0x4e:  /* Find first file */
2145             TRACE(" LONG FILENAME - FIND FIRST MATCHING FILE for %s\n",
2146                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx));
2147             /* FIXME: use attributes in CX */
2148             if ((AX_reg(context) = FindFirstFile16(
2149                    CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx),
2150                    (WIN32_FIND_DATAA *)CTX_SEG_OFF_TO_LIN(context, context->SegEs,
2151                                                             context->Edi)))
2152                 == INVALID_HANDLE_VALUE16)
2153                 bSetDOSExtendedError = TRUE;
2154             break;
2155         case 0x4f:  /* Find next file */
2156             TRACE("LONG FILENAME - FIND NEXT MATCHING FILE for handle %d\n",
2157                   BX_reg(context));
2158             if (!FindNextFile16( BX_reg(context),
2159                     (WIN32_FIND_DATAA *)CTX_SEG_OFF_TO_LIN(context, context->SegEs,
2160                                                              context->Edi)))
2161                 bSetDOSExtendedError = TRUE;
2162             break;
2163         case 0xa0:
2164             {
2165                 LPCSTR driveroot = (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs,context->Edx);
2166                 LPSTR buffer = (LPSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegEs,context->Edi);
2167                 DWORD filename_len, flags;
2168
2169                 TRACE("LONG FILENAME - GET VOLUME INFORMATION for drive having root dir '%s'.\n", driveroot);
2170                 AX_reg(context) = 0;
2171                 if (!GetVolumeInformationA( driveroot, NULL, 0, NULL, &filename_len,
2172                                             &flags, buffer, 8 ))
2173                 {
2174                     INT_BARF( context, 0x21 );
2175                     SET_CFLAG(context);
2176                     break;
2177                 }
2178                 BX_reg(context) = flags | 0x4000; /* support for LFN functions */
2179                 CX_reg(context) = filename_len;
2180                 DX_reg(context) = MAX_PATH; /* FIXME: which len if DRIVE_SHORT_NAMES ? */
2181             }
2182             break;
2183         case 0xa1:  /* Find close */
2184             TRACE("LONG FILENAME - FINDCLOSE for handle %d\n",
2185                   BX_reg(context));
2186             bSetDOSExtendedError = (!FindClose16( BX_reg(context) ));
2187             break;
2188         case 0x60:
2189           switch(CL_reg(context))
2190           {
2191             case 0x01:  /* Get short filename or path */
2192               if (!GetShortPathNameA
2193                   ( CTX_SEG_OFF_TO_LIN(context, context->SegDs,
2194                                        context->Esi),
2195                     CTX_SEG_OFF_TO_LIN(context, context->SegEs,
2196                                        context->Edi), 67))
2197                 bSetDOSExtendedError = TRUE;
2198               else AX_reg(context) = 0;
2199               break;
2200             case 0x02:  /* Get canonical long filename or path */
2201               if (!GetFullPathNameA
2202                   ( CTX_SEG_OFF_TO_LIN(context, context->SegDs,
2203                                        context->Esi), 128,
2204                     CTX_SEG_OFF_TO_LIN(context, context->SegEs,
2205                                        context->Edi),NULL))
2206                 bSetDOSExtendedError = TRUE;
2207               else AX_reg(context) = 0;
2208               break;
2209             default:
2210               FIXME("Unimplemented long file name function:\n");
2211               INT_BARF( context, 0x21 );
2212               SET_CFLAG(context);
2213               AL_reg(context) = 0;
2214               break;
2215           }
2216           break;
2217         case 0x6c:  /* Create or open file */
2218             TRACE("LONG FILENAME - CREATE OR OPEN FILE %s\n",
2219                  (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Esi));
2220           /* translate Dos 7 action to Dos 6 action */
2221             bSetDOSExtendedError = INT21_ExtendedOpenCreateFile(context);
2222             break;
2223
2224         case 0x3b:  /* Change directory */
2225             TRACE("LONG FILENAME - CHANGE DIRECTORY %s\n",
2226                  (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
2227             if (!SetCurrentDirectoryA(CTX_SEG_OFF_TO_LIN(context,
2228                                                 context->SegDs,
2229                                                 context->Edx
2230                                         ))
2231             ) {
2232                 SET_CFLAG(context);
2233                 AL_reg(context) = GetLastError();
2234             }
2235             break;
2236         case 0x41:  /* Delete file */
2237             TRACE("LONG FILENAME - DELETE FILE %s\n",
2238                  (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
2239             if (!DeleteFileA(CTX_SEG_OFF_TO_LIN(context,
2240                                         context->SegDs,
2241                                         context->Edx)
2242             )) {
2243                 SET_CFLAG(context);
2244                 AL_reg(context) = GetLastError();
2245             }
2246             break;
2247         case 0x56:  /* Move (rename) file */
2248             {
2249                 LPCSTR fn1 = (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx);
2250                 LPCSTR fn2 = (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegEs, context->Edi);
2251                 TRACE("LONG FILENAME - RENAME FILE %s to %s\n", fn1, fn2);
2252                 if (!MoveFileA(fn1, fn2))
2253                 {
2254                     SET_CFLAG(context);
2255                     AL_reg(context) = GetLastError();
2256                 }
2257             }
2258             break;
2259         default:
2260             FIXME("Unimplemented long file name function:\n");
2261             INT_BARF( context, 0x21 );
2262             SET_CFLAG(context);
2263             AL_reg(context) = 0;
2264             break;
2265         }
2266         break;
2267
2268     case 0x70: /* MS-DOS 7 (Windows95) - ??? (country-specific?)*/
2269     case 0x72: /* MS-DOS 7 (Windows95) - ??? */
2270         TRACE("windows95 function AX %04x\n",
2271                     AX_reg(context));
2272         WARN("        returning unimplemented\n");
2273         SET_CFLAG(context);
2274         AL_reg(context) = 0;
2275         break;
2276
2277     case 0x73: /* MULTIPLEXED: Win95 OSR2/Win98 FAT32 calls */
2278         TRACE("windows95 function AX %04x\n",
2279                     AX_reg(context));
2280
2281         switch (AL_reg(context))
2282                 {
2283                 case 0x02:      /* Get Extended Drive Parameter Block for specific drive */
2284                                 /* ES:DI points to word with length of data (should be 0x3d) */
2285                         {
2286                                 WORD *buffer;
2287                                 struct EDPB *edpb;
2288                             DWORD cluster_sectors, sector_bytes, free_clusters, total_clusters;
2289                             char root[] = "A:\\";
2290
2291                                 buffer = (WORD *)CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi);
2292
2293                                 TRACE("Get Extended DPB: linear buffer address is %p\n", buffer);
2294
2295                                 /* validate passed-in buffer lengths */
2296                                 if ((*buffer != 0x3d) || (context->Ecx != 0x3f))
2297                                 {
2298                                         WARN("Get Extended DPB: buffer lengths incorrect\n");
2299                                         WARN("CX = %lx, buffer[0] = %x\n", context->Ecx, *buffer);
2300                                         SET_CFLAG(context);
2301                                         AL_reg(context) = 0x18;         /* bad buffer length */
2302                                 }
2303
2304                                 /* buffer checks out */
2305                                 buffer++;               /* skip over length word now */
2306                                 if (FillInDrivePB( DX_reg(context) ) )
2307                                 {
2308                                         edpb = (struct EDPB *)buffer;
2309
2310                                         /* copy down the old-style DPB portion first */
2311                                         memcpy(&edpb->dpb, &heap->dpb, sizeof(struct DPB));
2312
2313                                         /* now fill in the extended entries */
2314                                         edpb->edpb_flags = 0;
2315                                         edpb->next_edpb = 0;
2316                                         edpb->free_cluster = edpb->free_cluster2 = 0;
2317
2318                                         /* determine free disk space */
2319                                     *root += DOS_GET_DRIVE( DX_reg(context) );
2320                                     GetDiskFreeSpaceA( root, &cluster_sectors, &sector_bytes,
2321                               &free_clusters, &total_clusters );
2322
2323                                         edpb->clusters_free = (free_clusters&0xffff);
2324
2325                                         edpb->clusters_free_hi = free_clusters >> 16;
2326                                         edpb->mirroring_flags = 0;
2327                                         edpb->info_sector = 0xffff;
2328                                         edpb->spare_boot_sector = 0xffff;
2329                                         edpb->first_cluster = 0;
2330                                         edpb->max_cluster = total_clusters;
2331                                         edpb->fat_clusters = 32;        /* made-up value */
2332                                         edpb->root_cluster = 0;
2333
2334                                         RESET_CFLAG(context);   /* clear carry */
2335                                         AX_reg(context) = 0;
2336                                 }
2337                                 else
2338                                 {
2339                                 AX_reg(context) = 0x00ff;
2340                                         SET_CFLAG(context);
2341                                 }
2342                         }
2343                         break;
2344
2345                 case 0x03:      /* Get Extended free space on drive */
2346                 case 0x04:  /* Set DPB for formatting */
2347                 case 0x05:  /* extended absolute disk read/write */
2348                         FIXME("Unimplemented FAT32 int32 function %04x\n", AX_reg(context));
2349                         SET_CFLAG(context);
2350                         AL_reg(context) = 0;
2351                         break;
2352                 }
2353
2354                 break;
2355
2356
2357     case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
2358     case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
2359         break;
2360
2361     default:
2362         INT_BARF( context, 0x21 );
2363         break;
2364
2365     } /* END OF SWITCH */
2366
2367     if( bSetDOSExtendedError )          /* set general error condition */
2368     {
2369         AX_reg(context) = GetLastError();
2370         SET_CFLAG(context);
2371     }
2372
2373     if ((context->EFlags & 0x0001))
2374         TRACE("failed, error %ld\n", GetLastError() );
2375
2376     TRACE("returning: AX=%04x BX=%04x CX=%04x DX=%04x "
2377                  "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
2378                  AX_reg(context), BX_reg(context), CX_reg(context),
2379                  DX_reg(context), SI_reg(context), DI_reg(context),
2380                  (WORD)context->SegDs, (WORD)context->SegEs,
2381                  context->EFlags);
2382 }
2383
2384 /***********************************************************************
2385  *              GetSetKernelDOSProc (KERNEL.311)
2386  */
2387 FARPROC16 WINAPI GetSetKernelDOSProc16(FARPROC16 DosProc)
2388 {
2389         FIXME("(DosProc=0x%08x): stub\n", (UINT)DosProc);
2390         return NULL;
2391 }
2392