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