Move int08 stub and LOL to upper memory.
[wine] / dlls / winedos / int21.c
1 /*
2  * DOS interrupt 21h handler
3  *
4  * Copyright 1993, 1994 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  * Copyright 1997 Andreas Mohr
7  * Copyright 1998 Uwe Bonnes
8  * Copyright 1998, 1999 Ove Kaaven
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "config.h"
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winternl.h"
30 #include "wine/winbase16.h"
31 #include "dosexe.h"
32 #include "miscemu.h"
33 #include "msdos.h"
34 #include "file.h"
35 #include "winerror.h"
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
38
39 /*
40  * FIXME: Delete this reference when all int21 code has been moved to winedos.
41  */
42 extern void WINAPI INT_Int21Handler( CONTEXT86 *context );
43
44 WINE_DEFAULT_DEBUG_CHANNEL(int21);
45
46
47 /***********************************************************************
48  *           INT21_GetPSP
49  *
50  * Handler for functions 0x51 and 0x62.
51  */
52 static void INT21_GetPSP( CONTEXT86 *context )
53 {
54     TRACE( "GET CURRENT PSP ADDRESS (%02x)\n", AH_reg(context) );
55
56     /*
57      * FIXME: should we return the original DOS PSP upon
58      *        Windows startup ? 
59      */
60     if (!ISV86(context) && DOSVM_IsWin16())
61         SET_BX( context, LOWORD(GetCurrentPDB16()) );
62     else
63         SET_BX( context, DOSVM_psp );
64 }
65
66
67 /***********************************************************************
68  *           INT21_Ioctl
69  *
70  * Handler for function 0x44.
71  */
72 static void INT21_Ioctl( CONTEXT86 *context )
73 {
74   static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
75   const DOS_DEVICE *dev = DOSFS_GetDeviceByHandle(
76       DosFileHandleToWin32Handle(BX_reg(context)) );
77
78   if (dev && !strcmpiW( dev->name, emmxxxx0W )) {
79     EMS_Ioctl_Handler(context);
80     return;
81   }
82
83   switch (AL_reg(context))
84   {
85   case 0x0b: /* SET SHARING RETRY COUNT */
86       TRACE("IOCTL - SET SHARING RETRY COUNT pause %d retries %d\n",
87            CX_reg(context), DX_reg(context));
88       if (!CX_reg(context))
89       {
90          SET_AX( context, 1 );
91          SET_CFLAG(context);
92          break;
93       }
94       DOSMEM_LOL()->sharing_retry_delay = CX_reg(context);
95       if (!DX_reg(context))
96          DOSMEM_LOL()->sharing_retry_count = DX_reg(context);
97       RESET_CFLAG(context);
98       break;
99   default:
100       INT_Int21Handler( context );
101   }
102 }
103
104
105 /***********************************************************************
106  *           INT21_GetExtendedError
107  */
108 static void INT21_GetExtendedError( CONTEXT86 *context )
109 {
110     BYTE class, action, locus;
111     WORD error = GetLastError();
112
113     switch(error)
114     {
115     case ERROR_SUCCESS:
116         class = action = locus = 0;
117         break;
118     case ERROR_DIR_NOT_EMPTY:
119         class  = EC_Exists;
120         action = SA_Ignore;
121         locus  = EL_Disk;
122         break;
123     case ERROR_ACCESS_DENIED:
124         class  = EC_AccessDenied;
125         action = SA_Abort;
126         locus  = EL_Disk;
127         break;
128     case ERROR_CANNOT_MAKE:
129         class  = EC_AccessDenied;
130         action = SA_Abort;
131         locus  = EL_Unknown;
132         break;
133     case ERROR_DISK_FULL:
134     case ERROR_HANDLE_DISK_FULL:
135         class  = EC_MediaError;
136         action = SA_Abort;
137         locus  = EL_Disk;
138         break;
139     case ERROR_FILE_EXISTS:
140     case ERROR_ALREADY_EXISTS:
141         class  = EC_Exists;
142         action = SA_Abort;
143         locus  = EL_Disk;
144         break;
145     case ERROR_FILE_NOT_FOUND:
146         class  = EC_NotFound;
147         action = SA_Abort;
148         locus  = EL_Disk;
149         break;
150     case ER_GeneralFailure:
151         class  = EC_SystemFailure;
152         action = SA_Abort;
153         locus  = EL_Unknown;
154         break;
155     case ERROR_INVALID_DRIVE:
156         class  = EC_MediaError;
157         action = SA_Abort;
158         locus  = EL_Disk;
159         break;
160     case ERROR_INVALID_HANDLE:
161         class  = EC_ProgramError;
162         action = SA_Abort;
163         locus  = EL_Disk;
164         break;
165     case ERROR_LOCK_VIOLATION:
166         class  = EC_AccessDenied;
167         action = SA_Abort;
168         locus  = EL_Disk;
169         break;
170     case ERROR_NO_MORE_FILES:
171         class  = EC_MediaError;
172         action = SA_Abort;
173         locus  = EL_Disk;
174         break;
175     case ER_NoNetwork:
176         class  = EC_NotFound;
177         action = SA_Abort;
178         locus  = EL_Network;
179         break;
180     case ERROR_NOT_ENOUGH_MEMORY:
181         class  = EC_OutOfResource;
182         action = SA_Abort;
183         locus  = EL_Memory;
184         break;
185     case ERROR_PATH_NOT_FOUND:
186         class  = EC_NotFound;
187         action = SA_Abort;
188         locus  = EL_Disk;
189         break;
190     case ERROR_SEEK:
191         class  = EC_NotFound;
192         action = SA_Ignore;
193         locus  = EL_Disk;
194         break;
195     case ERROR_SHARING_VIOLATION:
196         class  = EC_Temporary;
197         action = SA_Retry;
198         locus  = EL_Disk;
199         break;
200     case ERROR_TOO_MANY_OPEN_FILES:
201         class  = EC_ProgramError;
202         action = SA_Abort;
203         locus  = EL_Disk;
204         break;
205     default:
206         FIXME("Unknown error %d\n", error );
207         class  = EC_SystemFailure;
208         action = SA_Abort;
209         locus  = EL_Unknown;
210         break;
211     }
212     TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
213            error, class, action, locus );
214     SET_AX( context, error );
215     SET_BH( context, class );
216     SET_BL( context, action );
217     SET_CH( context, locus );
218 }
219
220
221 /***********************************************************************
222  *           DOSVM_Int21Handler (WINEDOS16.133)
223  *
224  * Interrupt 0x21 handler.
225  */
226 void WINAPI DOSVM_Int21Handler( CONTEXT86 *context )
227 {
228     BOOL bSetDOSExtendedError = FALSE;
229
230     TRACE( "AX=%04x BX=%04x CX=%04x DX=%04x "
231            "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
232            AX_reg(context), BX_reg(context), 
233            CX_reg(context), DX_reg(context),
234            SI_reg(context), DI_reg(context),
235            (WORD)context->SegDs, (WORD)context->SegEs,
236            context->EFlags );
237
238    /*
239     * Extended error is used by (at least) functions 0x2f to 0x62.
240     * Function 0x59 returns extended error and error should not
241     * be cleared before handling the function.
242     */
243     if (AH_reg(context) >= 0x2f && AH_reg(context) != 0x59) 
244         SetLastError(0);
245
246     RESET_CFLAG(context); /* Not sure if this is a good idea. */
247
248     switch(AH_reg(context))
249     {
250     case 0x00: /* TERMINATE PROGRAM */
251         TRACE("TERMINATE PROGRAM\n");
252         if (DOSVM_IsWin16())
253             ExitThread( 0 );
254         else
255             MZ_Exit( context, FALSE, 0 );
256         break;
257
258     case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
259         {
260             BYTE ascii;
261             TRACE("DIRECT CHARACTER INPUT WITH ECHO\n");
262             DOSVM_Int16ReadChar(&ascii, NULL, FALSE);
263             SET_AL( context, ascii );
264             DOSVM_PutChar(AL_reg(context));
265         }
266         break;
267
268     case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
269         TRACE("Write Character to Standard Output\n");
270         DOSVM_PutChar(DL_reg(context));
271         break;
272
273     case 0x03: /* READ CHARACTER FROM STDAUX  */
274     case 0x04: /* WRITE CHARACTER TO STDAUX */
275     case 0x05: /* WRITE CHARACTER TO PRINTER */
276         INT_BARF( context, 0x21 );
277         break;
278
279     case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
280         /* FIXME: Use DOSDEV_Peek/Read/Write(DOSDEV_Console(),...) !! */
281         if (DL_reg(context) == 0xff) {
282             static char scan = 0;
283             TRACE("Direct Console Input\n");
284             if (scan) {
285                 /* return pending scancode */
286                 SET_AL( context, scan );
287                 RESET_ZFLAG(context);
288                 scan = 0;
289             } else {
290                 BYTE ascii;
291                 if (DOSVM_Int16ReadChar(&ascii,&scan,TRUE)) {
292                     DOSVM_Int16ReadChar(&ascii,&scan,FALSE);
293                     /* return ASCII code */
294                     SET_AL( context, ascii );
295                     RESET_ZFLAG(context);
296                     /* return scan code on next call only if ascii==0 */
297                     if (ascii) scan = 0;
298                 } else {
299                     /* nothing pending, clear everything */
300                     SET_AL( context, 0 );
301                     SET_ZFLAG(context);
302                     scan = 0; /* just in case */
303                 }
304             }
305         } else {
306             TRACE("Direct Console Output\n");
307             DOSVM_PutChar(DL_reg(context));
308         }
309         break;
310
311     case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
312         {
313             BYTE ascii;
314             /* FIXME: Use DOSDEV_Peek/Read(DOSDEV_Console(),...) !! */
315             TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n");
316             DOSVM_Int16ReadChar(&ascii, NULL, FALSE);
317             SET_AL( context, ascii );
318         }
319         break;
320
321     case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
322         {
323             BYTE ascii;
324             /* FIXME: Use DOSDEV_Peek/Read(DOSDEV_Console(),...) !! */
325             TRACE("CHARACTER INPUT WITHOUT ECHO\n");
326             DOSVM_Int16ReadChar(&ascii, NULL, FALSE);
327             SET_AL( context, ascii );
328         }
329         break;
330
331     case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
332         INT_Int21Handler( context );
333         break;
334
335     case 0x0a: /* BUFFERED INPUT */
336         INT_Int21Handler( context );
337         break;
338
339     case 0x0b: /* GET STDIN STATUS */
340         TRACE( "GET STDIN STATUS\n" );
341         {
342             BIOSDATA *data = BIOS_DATA;
343             if(data->FirstKbdCharPtr == data->NextKbdCharPtr)
344                 SET_AL( context, 0 );
345             else
346                 SET_AL( context, 0xff );
347         }
348         break;
349
350     case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
351         {
352             BYTE al = AL_reg(context); /* Input function to execute after flush. */
353
354             TRACE( "FLUSH BUFFER AND READ STANDARD INPUT - 0x%02x\n", al );
355
356             /* FIXME: buffers are not flushed */
357
358             /*
359              * If AL is one of 0x01, 0x06, 0x07, 0x08, or 0x0a,
360              * int21 function identified by AL will be called.
361              */
362             if(al == 0x01 || al == 0x06 || al == 0x07 || al == 0x08 || al == 0x0a)
363             {
364                 SET_AH( context, al );
365                 DOSVM_Int21Handler( context );
366             }
367         }
368         break;
369
370     case 0x0d: /* DISK BUFFER FLUSH */
371         TRACE("DISK BUFFER FLUSH ignored\n");
372         break;
373
374     case 0x0e: /* SELECT DEFAULT DRIVE */
375         INT_Int21Handler( context );
376         break;
377
378     case 0x0f: /* OPEN FILE USING FCB */
379     case 0x10: /* CLOSE FILE USING FCB */
380         INT_BARF( context, 0x21 );
381         break;
382
383     case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
384     case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
385     case 0x13: /* DELETE FILE USING FCB */
386         INT_Int21Handler( context );
387         break;
388
389     case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
390     case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
391     case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
392         INT_BARF( context, 0x21 );
393         break;
394
395     case 0x17: /* RENAME FILE USING FCB */
396         INT_Int21Handler( context );
397         break;
398
399     case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
400         SET_AL( context, 0 );
401         break;
402
403     case 0x19: /* GET CURRENT DEFAULT DRIVE */
404     case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
405     case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */        
406     case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
407         INT_Int21Handler( context );
408         break;
409
410     case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
411     case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
412         SET_AL( context, 0 );
413         break;
414
415     case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
416         INT_Int21Handler( context );
417         break;
418
419     case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
420         SET_AL( context, 0 );
421         break;
422
423     case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
424     case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
425     case 0x23: /* GET FILE SIZE FOR FCB */
426     case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
427         INT_BARF( context, 0x21 );
428         break;
429
430     case 0x25: /* SET INTERRUPT VECTOR */
431         TRACE("SET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
432         {
433             FARPROC16 ptr = (FARPROC16)MAKESEGPTR( context->SegDs, DX_reg(context) );
434             if (!ISV86(context) && DOSVM_IsWin16())
435                 DOSVM_SetPMHandler16(  AL_reg(context), ptr );
436             else
437                 DOSVM_SetRMHandler( AL_reg(context), ptr );
438         }
439         break;
440
441     case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
442     case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
443     case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
444         INT_BARF( context, 0x21 );
445         break;
446
447     case 0x29: /* PARSE FILENAME INTO FCB */
448         INT_Int21Handler( context );
449         break;
450
451     case 0x2a: /* GET SYSTEM DATE */
452         TRACE( "GET SYSTEM DATE\n" );
453         {
454             SYSTEMTIME systime;
455             GetLocalTime( &systime );
456             SET_CX( context, systime.wYear );
457             SET_DH( context, systime.wMonth );
458             SET_DL( context, systime.wDay );
459             SET_AL( context, systime.wDayOfWeek );
460         }
461         break;
462
463     case 0x2b: /* SET SYSTEM DATE */
464         FIXME("SetSystemDate(%02d/%02d/%04d): not allowed\n",
465               DL_reg(context), DH_reg(context), CX_reg(context) );
466         SET_AL( context, 0 );  /* Let's pretend we succeeded */
467         break;
468
469     case 0x2c: /* GET SYSTEM TIME */
470         TRACE( "GET SYSTEM TIME\n" );
471         {
472             SYSTEMTIME systime;
473             GetLocalTime( &systime );
474             SET_CL( context, systime.wHour );
475             SET_CH( context, systime.wMinute );
476             SET_DH( context, systime.wSecond );
477             SET_DL( context, systime.wMilliseconds / 10 );
478         }
479         break;
480
481     case 0x2d: /* SET SYSTEM TIME */
482         FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
483               CH_reg(context), CL_reg(context),
484               DH_reg(context), DL_reg(context) );
485         SET_AL( context, 0 );  /* Let's pretend we succeeded */
486         break;
487
488     case 0x2e: /* SET VERIFY FLAG */
489         TRACE("SET VERIFY FLAG ignored\n");
490         /* we cannot change the behaviour anyway, so just ignore it */
491         break;
492
493     case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
494     case 0x30: /* GET DOS VERSION */
495         INT_Int21Handler( context );
496         break;
497
498     case 0x31: /* TERMINATE AND STAY RESIDENT */
499         FIXME("TERMINATE AND STAY RESIDENT stub\n");
500         break;
501
502     case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
503     case 0x33: /* MULTIPLEXED */
504     case 0x34: /* GET ADDRESS OF INDOS FLAG */
505         INT_Int21Handler( context );
506         break;
507
508     case 0x35: /* GET INTERRUPT VECTOR */
509         TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
510         {
511             FARPROC16 addr;
512             if (!ISV86(context) && DOSVM_IsWin16())
513                 addr = DOSVM_GetPMHandler16( AL_reg(context) );
514             else
515                 addr = DOSVM_GetRMHandler( AL_reg(context) );
516             context->SegEs = SELECTOROF(addr);
517             SET_BX( context, OFFSETOF(addr) );
518         }
519         break;
520
521     case 0x36: /* GET FREE DISK SPACE */
522     case 0x37: /* SWITCHAR */
523         INT_Int21Handler( context );
524         break;
525
526     case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
527         TRACE( "GET COUNTRY-SPECIFIC INFORMATION for country 0x%02x\n",
528                AL_reg(context) );
529         SET_AX( context, 0x02 ); /* no country support available */
530         SET_CFLAG( context );
531         break;
532
533     case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
534     case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
535     case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
536     case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
537     case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
538     case 0x3e: /* "CLOSE" - CLOSE FILE */
539     case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
540         INT_Int21Handler( context );
541         break;
542
543     case 0x40:  /* "WRITE" - WRITE TO FILE OR DEVICE */
544         /* Writes to stdout are handled here. */
545         if (!DOSVM_IsWin16() && BX_reg(context) == 1) {
546           BYTE *ptr = CTX_SEG_OFF_TO_LIN(context,
547                                          context->SegDs,
548                                          context->Edx);
549           int i;
550
551           for(i=0; i<CX_reg(context); i++)
552             DOSVM_PutChar(ptr[i]);
553         } else
554           INT_Int21Handler( context );
555         break;
556
557     case 0x41: /* "UNLINK" - DELETE FILE */
558     case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
559     case 0x43: /* FILE ATTRIBUTES */
560         INT_Int21Handler( context );
561         break;
562
563     case 0x44: /* IOCTL */
564         INT21_Ioctl( context );
565         break;
566
567     case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
568     case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
569     case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
570         INT_Int21Handler( context );
571         break;
572
573     case 0x48: /* ALLOCATE MEMORY */
574         TRACE( "ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context) );
575         {
576             WORD  selector = 0;
577             DWORD bytes = (DWORD)BX_reg(context) << 4;
578
579             if (!ISV86(context) && DOSVM_IsWin16())
580             {
581                 DWORD rv = GlobalDOSAlloc16( bytes );
582                 selector = LOWORD( rv );
583             }
584             else
585                 DOSMEM_GetBlock( bytes, &selector );
586                
587             if (selector)
588                 SET_AX( context, selector );
589             else
590             {
591                 SET_CFLAG(context);
592                 SET_AX( context, 0x0008 ); /* insufficient memory */
593                 SET_BX( context, DOSMEM_Available() >> 4 );
594             }
595         }
596         break;
597
598     case 0x49: /* FREE MEMORY */
599         TRACE( "FREE MEMORY segment %04lX\n", context->SegEs );
600         {
601             BOOL ok;
602             
603             if (!ISV86(context) && DOSVM_IsWin16())
604             {
605                 ok = !GlobalDOSFree16( context->SegEs );
606
607                 /* If we don't reset ES_reg, we will fail in the relay code */
608                 if (ok)
609                     context->SegEs = 0;
610             }
611             else
612                 ok = DOSMEM_FreeBlock( (void*)((DWORD)context->SegEs << 4) );
613
614             if (!ok)
615             {
616                 TRACE("FREE MEMORY failed\n");
617                 SET_CFLAG(context);
618                 SET_AX( context, 0x0009 ); /* memory block address invalid */
619             }
620         }
621         break;
622
623     case 0x4a: /* RESIZE MEMORY BLOCK */
624         INT_Int21Handler( context );
625         break;
626
627     case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
628         if(DOSVM_IsWin16()) {
629             INT_Int21Handler( context );
630             break;
631         }
632
633         TRACE("EXEC %s\n", (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx ));
634         if (!MZ_Exec( context, CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx),
635                       AL_reg(context), CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx) ))
636         {
637             SET_AX( context, GetLastError() );
638             SET_CFLAG(context);
639         }
640         break;
641
642     case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
643         TRACE( "EXIT with return code %d\n", AL_reg(context) );
644         if (DOSVM_IsWin16())
645             ExitThread( AL_reg(context) );
646         else
647             MZ_Exit( context, FALSE, AL_reg(context) );
648         break;
649
650     case 0x4d: /* GET RETURN CODE */
651         TRACE("GET RETURN CODE (ERRORLEVEL)\n");
652         SET_AX( context, DOSVM_retval );
653         DOSVM_retval = 0;
654         break;
655
656     case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
657     case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
658         INT_Int21Handler( context );
659         break;
660
661     case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
662         TRACE("SET CURRENT PROCESS ID (SET PSP ADDRESS)\n");
663         DOSVM_psp = BX_reg(context);
664         break;
665
666     case 0x51: /* GET PSP ADDRESS */
667         INT21_GetPSP( context );
668         break;
669
670     case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
671         if (!ISV86(context) && DOSVM_IsWin16())
672         {
673             SEGPTR ptr = DOSMEM_LOL()->wine_pm_lol;
674             context->SegEs = SELECTOROF(ptr);
675             SET_BX( context, OFFSETOF(ptr) );
676         }
677         else
678         {
679             SEGPTR ptr = DOSMEM_LOL()->wine_rm_lol;
680             context->SegEs = SELECTOROF(ptr);
681             SET_BX( context, OFFSETOF(ptr) );
682         }
683         break;
684
685     case 0x54: /* Get Verify Flag */
686         TRACE("Get Verify Flag - Not Supported\n");
687         SET_AL( context, 0x00 );  /* pretend we can tell. 00h = off 01h = on */
688         break;
689
690     case 0x56: /* "RENAME" - RENAME FILE */
691     case 0x57: /* FILE DATE AND TIME */
692         INT_Int21Handler( context );
693         break;
694
695     case 0x58: /* GET OR SET MEMORY ALLOCATION STRATEGY */
696         TRACE( "GET OR SET MEMORY ALLOCATION STRATEGY, subfunction %d\n", 
697                AL_reg(context) );
698         switch (AL_reg(context))
699         {
700         case 0x00: /* GET ALLOCATION STRATEGY */
701             SET_AX( context, 1 ); /* low memory best fit */
702             break;
703
704         case 0x01: /* SET ALLOCATION STRATEGY */
705             TRACE( "Set allocation strategy to %d - ignored\n",
706                    BL_reg(context) );
707             break;
708
709         default:
710             INT_BARF( context, 0x21 );
711             break;
712         }
713         break;
714
715     case 0x59: /* GET EXTENDED ERROR INFO */
716         INT21_GetExtendedError( context );
717         break;
718
719     case 0x5a: /* CREATE TEMPORARY FILE */
720     case 0x5b: /* CREATE NEW FILE */ 
721     case 0x5c: /* "FLOCK" - RECORD LOCKING */
722     case 0x5d: /* NETWORK 5D */
723     case 0x5e: /* NETWORK 5E */
724     case 0x5f: /* NETWORK 5F */
725     case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
726     case 0x61: /* UNUSED */
727         INT_Int21Handler( context );
728         break;
729
730     case 0x62: /* GET PSP ADDRESS */
731         INT21_GetPSP( context );
732         break;
733
734     case 0x63: /* MISC. LANGUAGE SUPPORT */
735         INT_Int21Handler( context );
736         break;
737
738     case 0x64: /* OS/2 DOS BOX */
739         INT_BARF( context, 0x21 );
740         SET_CFLAG(context);
741         break;
742
743     case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
744     case 0x66: /* GLOBAL CODE PAGE TABLE */
745         INT_Int21Handler( context );
746         break;
747
748     case 0x67: /* SET HANDLE COUNT */
749         TRACE( "SET HANDLE COUNT to %d\n", BX_reg(context) );
750         if (SetHandleCount( BX_reg(context) ) < BX_reg(context) )
751             bSetDOSExtendedError = TRUE;
752         break;
753
754     case 0x68: /* "FFLUSH" - COMMIT FILE */
755     case 0x69: /* DISK SERIAL NUMBER */
756     case 0x6a: /* COMMIT FILE */
757         INT_Int21Handler( context );
758         break;
759
760     case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
761         SET_AL( context, 0 );
762         break;
763
764     case 0x6c: /* EXTENDED OPEN/CREATE */
765     case 0x70: /* MS-DOS 7 (Windows95) - ??? (country-specific?)*/
766     case 0x71: /* MS-DOS 7 (Windows95) - LONG FILENAME FUNCTIONS */
767     case 0x72: /* MS-DOS 7 (Windows95) - ??? */
768     case 0x73: /* MULTIPLEXED: Win95 OSR2/Win98 FAT32 calls */
769     case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
770     case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
771         INT_Int21Handler( context );
772         break;
773
774     default:
775         INT_BARF( context, 0x21 );
776         break;
777
778     } /* END OF SWITCH */
779
780     /* Set general error condition. */
781     if (bSetDOSExtendedError)
782     {
783         SET_AX( context, GetLastError() );
784         SET_CFLAG( context );
785     }
786
787     /* Print error code if carry flag is set. */
788     if (context->EFlags & 0x0001)
789         TRACE("failed, error %ld\n", GetLastError() );
790
791     TRACE( "returning: AX=%04x BX=%04x CX=%04x DX=%04x "
792            "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
793            AX_reg(context), BX_reg(context), 
794            CX_reg(context), DX_reg(context), 
795            SI_reg(context), DI_reg(context),
796            (WORD)context->SegDs, (WORD)context->SegEs,
797            context->EFlags );
798 }