Clean up int21 handling. Move error handling to winedos.
[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_Ioctl
49  */
50 static void INT21_Ioctl( CONTEXT86 *context )
51 {
52   static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
53   const DOS_DEVICE *dev = DOSFS_GetDeviceByHandle(
54       DosFileHandleToWin32Handle(BX_reg(context)) );
55
56   if (dev && !strcmpiW( dev->name, emmxxxx0W )) {
57     EMS_Ioctl_Handler(context);
58     return;
59   }
60
61   switch (AL_reg(context))
62   {
63   case 0x0b: /* SET SHARING RETRY COUNT */
64       TRACE("IOCTL - SET SHARING RETRY COUNT pause %d retries %d\n",
65            CX_reg(context), DX_reg(context));
66       if (!CX_reg(context))
67       {
68          SET_AX( context, 1 );
69          SET_CFLAG(context);
70          break;
71       }
72       DOSMEM_LOL()->sharing_retry_delay = CX_reg(context);
73       if (!DX_reg(context))
74          DOSMEM_LOL()->sharing_retry_count = DX_reg(context);
75       RESET_CFLAG(context);
76       break;
77   default:
78       INT_Int21Handler( context );
79   }
80 }
81
82
83 /***********************************************************************
84  *           INT21_GetExtendedError
85  */
86 static void INT21_GetExtendedError( CONTEXT86 *context )
87 {
88     BYTE class, action, locus;
89     WORD error = GetLastError();
90
91     switch(error)
92     {
93     case ERROR_SUCCESS:
94         class = action = locus = 0;
95         break;
96     case ERROR_DIR_NOT_EMPTY:
97         class  = EC_Exists;
98         action = SA_Ignore;
99         locus  = EL_Disk;
100         break;
101     case ERROR_ACCESS_DENIED:
102         class  = EC_AccessDenied;
103         action = SA_Abort;
104         locus  = EL_Disk;
105         break;
106     case ERROR_CANNOT_MAKE:
107         class  = EC_AccessDenied;
108         action = SA_Abort;
109         locus  = EL_Unknown;
110         break;
111     case ERROR_DISK_FULL:
112     case ERROR_HANDLE_DISK_FULL:
113         class  = EC_MediaError;
114         action = SA_Abort;
115         locus  = EL_Disk;
116         break;
117     case ERROR_FILE_EXISTS:
118     case ERROR_ALREADY_EXISTS:
119         class  = EC_Exists;
120         action = SA_Abort;
121         locus  = EL_Disk;
122         break;
123     case ERROR_FILE_NOT_FOUND:
124         class  = EC_NotFound;
125         action = SA_Abort;
126         locus  = EL_Disk;
127         break;
128     case ER_GeneralFailure:
129         class  = EC_SystemFailure;
130         action = SA_Abort;
131         locus  = EL_Unknown;
132         break;
133     case ERROR_INVALID_DRIVE:
134         class  = EC_MediaError;
135         action = SA_Abort;
136         locus  = EL_Disk;
137         break;
138     case ERROR_INVALID_HANDLE:
139         class  = EC_ProgramError;
140         action = SA_Abort;
141         locus  = EL_Disk;
142         break;
143     case ERROR_LOCK_VIOLATION:
144         class  = EC_AccessDenied;
145         action = SA_Abort;
146         locus  = EL_Disk;
147         break;
148     case ERROR_NO_MORE_FILES:
149         class  = EC_MediaError;
150         action = SA_Abort;
151         locus  = EL_Disk;
152         break;
153     case ER_NoNetwork:
154         class  = EC_NotFound;
155         action = SA_Abort;
156         locus  = EL_Network;
157         break;
158     case ERROR_NOT_ENOUGH_MEMORY:
159         class  = EC_OutOfResource;
160         action = SA_Abort;
161         locus  = EL_Memory;
162         break;
163     case ERROR_PATH_NOT_FOUND:
164         class  = EC_NotFound;
165         action = SA_Abort;
166         locus  = EL_Disk;
167         break;
168     case ERROR_SEEK:
169         class  = EC_NotFound;
170         action = SA_Ignore;
171         locus  = EL_Disk;
172         break;
173     case ERROR_SHARING_VIOLATION:
174         class  = EC_Temporary;
175         action = SA_Retry;
176         locus  = EL_Disk;
177         break;
178     case ERROR_TOO_MANY_OPEN_FILES:
179         class  = EC_ProgramError;
180         action = SA_Abort;
181         locus  = EL_Disk;
182         break;
183     default:
184         FIXME("Unknown error %d\n", error );
185         class  = EC_SystemFailure;
186         action = SA_Abort;
187         locus  = EL_Unknown;
188         break;
189     }
190     TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
191            error, class, action, locus );
192     SET_AX( context, error );
193     SET_BH( context, class );
194     SET_BL( context, action );
195     SET_CH( context, locus );
196 }
197
198
199 /***********************************************************************
200  *           DOSVM_Int21Handler (WINEDOS16.133)
201  *
202  * Interrupt 0x21 handler.
203  */
204 void WINAPI DOSVM_Int21Handler( CONTEXT86 *context )
205 {
206     BOOL bSetDOSExtendedError = FALSE;
207
208     TRACE( "AX=%04x BX=%04x CX=%04x DX=%04x "
209            "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
210            AX_reg(context), BX_reg(context), 
211            CX_reg(context), DX_reg(context),
212            SI_reg(context), DI_reg(context),
213            (WORD)context->SegDs, (WORD)context->SegEs,
214            context->EFlags );
215
216    /*
217     * Extended error is used by (at least) functions 0x2f to 0x62.
218     * Function 0x59 returns extended error and error should not
219     * be cleared before handling the function.
220     */
221     if (AH_reg(context) >= 0x2f && AH_reg(context) != 0x59) 
222         SetLastError(0);
223
224     RESET_CFLAG(context); /* Not sure if this is a good idea. */
225
226     switch(AH_reg(context))
227     {
228     case 0x00: /* TERMINATE PROGRAM */
229         TRACE("TERMINATE PROGRAM\n");
230         if (DOSVM_IsWin16())
231             ExitThread( 0 );
232         else
233             MZ_Exit( context, FALSE, 0 );
234         break;
235
236     case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
237         {
238             BYTE ascii;
239             TRACE("DIRECT CHARACTER INPUT WITH ECHO\n");
240             DOSVM_Int16ReadChar(&ascii, NULL, FALSE);
241             SET_AL( context, ascii );
242             DOSVM_PutChar(AL_reg(context));
243         }
244         break;
245
246     case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
247         TRACE("Write Character to Standard Output\n");
248         DOSVM_PutChar(DL_reg(context));
249         break;
250
251     case 0x03: /* READ CHARACTER FROM STDAUX  */
252     case 0x04: /* WRITE CHARACTER TO STDAUX */
253     case 0x05: /* WRITE CHARACTER TO PRINTER */
254         INT_BARF( context, 0x21 );
255         break;
256
257     case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
258         /* FIXME: Use DOSDEV_Peek/Read/Write(DOSDEV_Console(),...) !! */
259         if (DL_reg(context) == 0xff) {
260             static char scan = 0;
261             TRACE("Direct Console Input\n");
262             if (scan) {
263                 /* return pending scancode */
264                 SET_AL( context, scan );
265                 RESET_ZFLAG(context);
266                 scan = 0;
267             } else {
268                 BYTE ascii;
269                 if (DOSVM_Int16ReadChar(&ascii,&scan,TRUE)) {
270                     DOSVM_Int16ReadChar(&ascii,&scan,FALSE);
271                     /* return ASCII code */
272                     SET_AL( context, ascii );
273                     RESET_ZFLAG(context);
274                     /* return scan code on next call only if ascii==0 */
275                     if (ascii) scan = 0;
276                 } else {
277                     /* nothing pending, clear everything */
278                     SET_AL( context, 0 );
279                     SET_ZFLAG(context);
280                     scan = 0; /* just in case */
281                 }
282             }
283         } else {
284             TRACE("Direct Console Output\n");
285             DOSVM_PutChar(DL_reg(context));
286         }
287         break;
288
289     case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
290         {
291             BYTE ascii;
292             /* FIXME: Use DOSDEV_Peek/Read(DOSDEV_Console(),...) !! */
293             TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n");
294             DOSVM_Int16ReadChar(&ascii, NULL, FALSE);
295             SET_AL( context, ascii );
296         }
297         break;
298
299     case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
300         {
301             BYTE ascii;
302             /* FIXME: Use DOSDEV_Peek/Read(DOSDEV_Console(),...) !! */
303             TRACE("CHARACTER INPUT WITHOUT ECHO\n");
304             DOSVM_Int16ReadChar(&ascii, NULL, FALSE);
305             SET_AL( context, ascii );
306         }
307         break;
308
309     case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
310         INT_Int21Handler( context );
311         break;
312
313     case 0x0a: /* BUFFERED INPUT */
314         INT_Int21Handler( context );
315         break;
316
317     case 0x0b: /* GET STDIN STATUS */
318         TRACE( "GET STDIN STATUS\n" );
319         {
320             BIOSDATA *data = BIOS_DATA;
321             if(data->FirstKbdCharPtr == data->NextKbdCharPtr)
322                 SET_AL( context, 0 );
323             else
324                 SET_AL( context, 0xff );
325         }
326         break;
327
328     case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
329         {
330             BYTE al = AL_reg(context); /* Input function to execute after flush. */
331
332             TRACE( "FLUSH BUFFER AND READ STANDARD INPUT - 0x%02x\n", al );
333
334             /* FIXME: buffers are not flushed */
335
336             /*
337              * If AL is one of 0x01, 0x06, 0x07, 0x08, or 0x0a,
338              * int21 function identified by AL will be called.
339              */
340             if(al == 0x01 || al == 0x06 || al == 0x07 || al == 0x08 || al == 0x0a)
341             {
342                 SET_AH( context, al );
343                 DOSVM_Int21Handler( context );
344             }
345         }
346         break;
347
348     case 0x0d: /* DISK BUFFER FLUSH */
349         TRACE("DISK BUFFER FLUSH ignored\n");
350         RESET_CFLAG( context ); /* dos 6+ only */
351         break;
352
353     case 0x0e: /* SELECT DEFAULT DRIVE */
354         INT_Int21Handler( context );
355         break;
356
357     case 0x0f: /* OPEN FILE USING FCB */
358     case 0x10: /* CLOSE FILE USING FCB */
359         INT_BARF( context, 0x21 );
360         break;
361
362     case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
363     case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
364     case 0x13: /* DELETE FILE USING FCB */
365         INT_Int21Handler( context );
366         break;
367
368     case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
369     case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
370     case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
371         INT_BARF( context, 0x21 );
372         break;
373
374     case 0x17: /* RENAME FILE USING FCB */
375         INT_Int21Handler( context );
376         break;
377
378     case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
379         SET_AL( context, 0 );
380         break;
381
382     case 0x19: /* GET CURRENT DEFAULT DRIVE */
383     case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
384     case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */        
385     case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
386         INT_Int21Handler( context );
387         break;
388
389     case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
390     case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
391         SET_AL( context, 0 );
392         break;
393
394     case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
395         INT_Int21Handler( context );
396         break;
397
398     case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
399         SET_AL( context, 0 );
400         break;
401
402     case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
403     case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
404     case 0x23: /* GET FILE SIZE FOR FCB */
405     case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
406         INT_BARF( context, 0x21 );
407         break;
408
409     case 0x25: /* SET INTERRUPT VECTOR */
410         TRACE("SET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
411         {
412             FARPROC16 ptr = (FARPROC16)MAKESEGPTR( context->SegDs, DX_reg(context) );
413             if (DOSVM_IsWin16())
414                 DOSVM_SetPMHandler16(  AL_reg(context), ptr );
415             else
416                 DOSVM_SetRMHandler( AL_reg(context), ptr );
417         }
418         break;
419
420     case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
421     case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
422     case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
423         INT_BARF( context, 0x21 );
424         break;
425
426     case 0x29: /* PARSE FILENAME INTO FCB */
427     case 0x2a: /* GET SYSTEM DATE */
428         INT_Int21Handler( context );
429         break;
430
431     case 0x2b: /* SET SYSTEM DATE */
432         FIXME("SetSystemDate(%02d/%02d/%04d): not allowed\n",
433               DL_reg(context), DH_reg(context), CX_reg(context) );
434         SET_AL( context, 0 );  /* Let's pretend we succeeded */
435         break;
436
437     case 0x2c: /* GET SYSTEM TIME */
438         INT_Int21Handler( context );
439         break;
440
441     case 0x2d: /* SET SYSTEM TIME */
442         FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
443               CH_reg(context), CL_reg(context),
444               DH_reg(context), DL_reg(context) );
445         SET_AL( context, 0 );  /* Let's pretend we succeeded */
446         break;
447
448     case 0x2e: /* SET VERIFY FLAG */
449         TRACE("SET VERIFY FLAG ignored\n");
450         /* we cannot change the behaviour anyway, so just ignore it */
451         break;
452
453     case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
454     case 0x30: /* GET DOS VERSION */
455         INT_Int21Handler( context );
456         break;
457
458     case 0x31: /* TERMINATE AND STAY RESIDENT */
459         FIXME("TERMINATE AND STAY RESIDENT stub\n");
460         break;
461
462     case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
463     case 0x33: /* MULTIPLEXED */
464     case 0x34: /* GET ADDRESS OF INDOS FLAG */
465         INT_Int21Handler( context );
466         break;
467
468     case 0x35: /* GET INTERRUPT VECTOR */
469         TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
470         {
471             FARPROC16 addr;
472             if (DOSVM_IsWin16())
473                 addr = DOSVM_GetPMHandler16( AL_reg(context) );
474             else
475                 addr = DOSVM_GetRMHandler( AL_reg(context) );
476             context->SegEs = SELECTOROF(addr);
477             SET_BX( context, OFFSETOF(addr) );
478         }
479         break;
480
481     case 0x36: /* GET FREE DISK SPACE */
482     case 0x37: /* SWITCHAR */
483         INT_Int21Handler( context );
484         break;
485
486     case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
487         TRACE( "GET COUNTRY-SPECIFIC INFORMATION for country 0x%02x\n",
488                AL_reg(context) );
489         SET_AX( context, 0x02 ); /* no country support available */
490         SET_CFLAG( context );
491         break;
492
493     case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
494     case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
495     case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
496     case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
497     case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
498     case 0x3e: /* "CLOSE" - CLOSE FILE */
499     case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
500         INT_Int21Handler( context );
501         break;
502
503     case 0x40:  /* "WRITE" - WRITE TO FILE OR DEVICE */
504         /* Writes to stdout are handled here. */
505         if (!DOSVM_IsWin16() && BX_reg(context) == 1) {
506           BYTE *ptr = CTX_SEG_OFF_TO_LIN(context,
507                                          context->SegDs,
508                                          context->Edx);
509           int i;
510
511           for(i=0; i<CX_reg(context); i++)
512             DOSVM_PutChar(ptr[i]);
513         } else
514           INT_Int21Handler( context );
515         break;
516
517     case 0x41: /* "UNLINK" - DELETE FILE */
518     case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
519     case 0x43: /* FILE ATTRIBUTES */
520         INT_Int21Handler( context );
521         break;
522
523     case 0x44: /* IOCTL */
524         INT21_Ioctl( context );
525         break;
526
527     case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
528     case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
529     case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
530     case 0x48: /* ALLOCATE MEMORY */
531     case 0x49: /* FREE MEMORY */
532     case 0x4a: /* RESIZE MEMORY BLOCK */
533         INT_Int21Handler( context );
534         break;
535
536     case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
537         if(DOSVM_IsWin16()) {
538             INT_Int21Handler( context );
539             break;
540         }
541
542         TRACE("EXEC %s\n", (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx ));
543         if (!MZ_Exec( context, CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx),
544                       AL_reg(context), CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx) ))
545         {
546             SET_AX( context, GetLastError() );
547             SET_CFLAG(context);
548         }
549         break;
550
551     case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
552         TRACE( "EXIT with return code %d\n", AL_reg(context) );
553         if (DOSVM_IsWin16())
554             ExitThread( AL_reg(context) );
555         else
556             MZ_Exit( context, FALSE, AL_reg(context) );
557         break;
558
559     case 0x4d: /* GET RETURN CODE */
560         TRACE("GET RETURN CODE (ERRORLEVEL)\n");
561         SET_AX( context, DOSVM_retval );
562         DOSVM_retval = 0;
563         break;
564
565     case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
566     case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
567         INT_Int21Handler( context );
568         break;
569
570     case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
571         TRACE("SET CURRENT PROCESS ID (SET PSP ADDRESS)\n");
572         DOSVM_psp = BX_reg(context);
573         break;
574
575     case 0x51: /* GET PSP ADDRESS */
576         if (DOSVM_IsWin16()) {
577             INT_Int21Handler( context );
578             break;
579         }
580
581         TRACE("GET CURRENT PROCESS ID (GET PSP ADDRESS)\n");
582         /* FIXME: should we return the original DOS PSP upon */
583         /*        Windows startup ? */
584         SET_BX( context, DOSVM_psp );
585         break;
586
587     case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
588         TRACE("SYSVARS - GET LIST OF LISTS\n");
589         if (DOSVM_IsWin16())
590         {
591             FIXME("LOLSeg broken for now\n");
592             context->SegEs = 0;
593             SET_BX( context, 0 );
594         }
595         else
596         {
597             context->SegEs = HIWORD(DOS_LOLSeg);
598             SET_BX( context, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
599         }
600         break;
601
602     case 0x54: /* Get Verify Flag */
603         TRACE("Get Verify Flag - Not Supported\n");
604         SET_AL( context, 0x00 );  /* pretend we can tell. 00h = off 01h = on */
605         break;
606
607     case 0x56: /* "RENAME" - RENAME FILE */
608     case 0x57: /* FILE DATE AND TIME */
609     case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
610         INT_Int21Handler( context );
611         break;
612
613     case 0x59: /* GET EXTENDED ERROR INFO */
614         INT21_GetExtendedError( context );
615         break;
616
617     case 0x5a: /* CREATE TEMPORARY FILE */
618     case 0x5b: /* CREATE NEW FILE */ 
619     case 0x5c: /* "FLOCK" - RECORD LOCKING */
620     case 0x5d: /* NETWORK 5D */
621     case 0x5e: /* NETWORK 5E */
622     case 0x5f: /* NETWORK 5F */
623     case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
624     case 0x61: /* UNUSED */
625         INT_Int21Handler( context );
626         break;
627
628     case 0x62: /* GET PSP ADDRESS */
629         if (DOSVM_IsWin16()) {
630             INT_Int21Handler( context );
631             break;
632         }
633
634         TRACE("GET CURRENT PSP ADDRESS\n");
635         /* FIXME: should we return the original DOS PSP upon */
636         /*        Windows startup ? */
637         SET_BX( context, DOSVM_psp );
638         break;
639
640     case 0x63: /* MISC. LANGUAGE SUPPORT */
641     case 0x64: /* OS/2 DOS BOX */
642     case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
643     case 0x66: /* GLOBAL CODE PAGE TABLE */
644     case 0x67: /* SET HANDLE COUNT */
645     case 0x68: /* "FFLUSH" - COMMIT FILE */
646     case 0x69: /* DISK SERIAL NUMBER */
647     case 0x6a: /* COMMIT FILE */
648         INT_Int21Handler( context );
649         break;
650
651     case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
652         SET_AL( context, 0 );
653         break;
654
655     case 0x6c: /* EXTENDED OPEN/CREATE */
656     case 0x70: /* MS-DOS 7 (Windows95) - ??? (country-specific?)*/
657     case 0x71: /* MS-DOS 7 (Windows95) - LONG FILENAME FUNCTIONS */
658     case 0x72: /* MS-DOS 7 (Windows95) - ??? */
659     case 0x73: /* MULTIPLEXED: Win95 OSR2/Win98 FAT32 calls */
660     case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
661     case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
662         INT_Int21Handler( context );
663         break;
664
665     default:
666         INT_BARF( context, 0x21 );
667         break;
668
669     } /* END OF SWITCH */
670
671     /* Set general error condition. */
672     if (bSetDOSExtendedError)
673     {
674         SET_AX( context, GetLastError() );
675         SET_CFLAG( context );
676     }
677
678     /* Print error code if carry flag is set. */
679     if (context->EFlags & 0x0001)
680         TRACE("failed, error %ld\n", GetLastError() );
681
682     TRACE( "returning: AX=%04x BX=%04x CX=%04x DX=%04x "
683            "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
684            AX_reg(context), BX_reg(context), 
685            CX_reg(context), DX_reg(context), 
686            SI_reg(context), DI_reg(context),
687            (WORD)context->SegDs, (WORD)context->SegEs,
688            context->EFlags );
689 }