Adapted to CreateProcess changes.
[wine] / msdos / int2f.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * DOS interrupt 2fh handler
4  *
5  *  Cdrom - device driver emulation - Audio features.
6  *      (c) 1998 Petr Tomasek <tomasek@etf.cuni.cz>
7  *      (c) 1999 Eric Pouech
8  */
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "wine/winbase16.h"
15 #include "ldt.h"
16 #include "drive.h"
17 #include "msdos.h"
18 #include "miscemu.h"
19 #include "module.h"
20 #include "task.h"
21 #include "dosexe.h"
22 #include "heap.h"
23 /* #define DEBUG_INT */
24 #include "debug.h"
25 #include "cdrom.h"
26
27 /* base WPROCS.DLL ordinal number for VxDs */
28 #define VXD_BASE 400
29
30 static void do_int2f_16( CONTEXT *context );
31 static void do_mscdex( CONTEXT *context );
32
33 /**********************************************************************
34  *          INT_Int2fHandler
35  *
36  * Handler for int 2fh (multiplex).
37  */
38 void WINAPI INT_Int2fHandler( CONTEXT *context )
39 {
40     TRACE(int,"Subfunction 0x%X\n", AX_reg(context));
41
42     switch(AH_reg(context))
43     {
44     case 0x10:
45         AL_reg(context) = 0xff; /* share is installed */
46         break;
47
48     case 0x11:  /* Network Redirector / IFSFUNC */
49         switch (AL_reg(context))
50         {
51         case 0x00:  /* Install check */
52             /* not installed */
53             break;
54         case 0x80:  /* Enhanced services - Install check */
55             /* not installed */
56             break;
57         default:
58             INT_BARF( context, 0x2f );
59             break;
60         }
61         break;
62
63     case 0x12:
64         switch (AL_reg(context))
65         {
66         case 0x2e: /* get or set DOS error table address */
67             switch (DL_reg(context))
68             {
69             /* Four tables: even commands are 'get', odd are 'set' */
70             /* DOS 5.0+ ignores "set" commands */
71             case 0x01:
72             case 0x03:
73             case 0x05:
74             case 0x07:
75             case 0x09:
76                 break; 
77             /* Instead of having a message table in DOS-space, */
78             /* we can use a special case for MS-DOS to force   */
79             /* the secondary interface.                        */
80             case 0x00:
81             case 0x02:
82             case 0x04:
83             case 0x06: 
84                 ES_reg(context) = 0x0001;
85                 DI_reg(context) = 0x0000;
86                 break;
87             case 0x08:
88                 FIXME(int, "No real-mode handler for errors yet! (bye!)");
89                 break;
90             default:
91                 INT_BARF(context, 0x2f);
92             }
93             break;
94         default:
95            INT_BARF(context, 0x2f);
96         }  
97         break;
98    
99     case 0x15: /* mscdex */
100         do_mscdex(context);
101         break;
102
103     case 0x16:
104         do_int2f_16( context );
105         break;
106
107     case 0x1a: /* ANSI.SYS / AVATAR.SYS Install Check */
108         /* Not supported yet, do nothing */
109         break;
110
111     case 0x43:
112 #if 1
113         switch (AL_reg(context))
114         {
115         case 0x00:   /* XMS v2+ installation check */
116             WARN(int,"XMS is not fully implemented\n");
117             AL_reg(context) = 0x80;
118             break;
119         case 0x10:   /* XMS v2+ get driver address */
120         {
121             TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
122             NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
123             GlobalUnlock16( GetCurrentTask() );
124 #ifdef MZ_SUPPORTED
125             if (pModule && pModule->lpDosTask)
126                 ES_reg(context) = pModule->lpDosTask->xms_seg;
127             else
128 #endif
129                 ES_reg(context) = 0;
130             BX_reg(context) = 0;
131             break;
132         }
133         default:
134             INT_BARF( context, 0x2f );
135         }
136 #else
137         FIXME(int,"check for XMS (not supported)\n");
138         AL_reg(context) = 0x42; /* != 0x80 */
139 #endif
140         break;
141
142     case 0x45:
143        switch (AL_reg(context)) 
144        {
145        case 0x00:
146        case 0x01:
147        case 0x02:
148        case 0x03:
149        case 0x04:
150        case 0x05:
151        case 0x06:
152        case 0x07:
153        case 0x08:
154            /* Microsoft Profiler - not installed */
155            break;
156        default:
157             INT_BARF( context, 0x2f );
158        }
159        break;
160
161     case 0x4a:
162         switch(AL_reg(context))
163         {
164         case 0x10:  /* smartdrv */
165             break;  /* not installed */
166         case 0x11:  /* dblspace */
167             break;  /* not installed */
168         case 0x12:  /* realtime compression interface */
169             break;  /* not installed */
170         case 0x32:  /* patch IO.SYS (???) */
171             break;  /* we have no IO.SYS, so we can't patch it :-/ */
172         default:
173             INT_BARF( context, 0x2f );
174         }
175         break;
176     case 0x56:  /* INTERLNK */
177         switch(AL_reg(context))
178         {
179         case 0x01:  /* check if redirected drive */
180             AL_reg(context) = 0; /* not redirected */
181             break;
182         default:
183             INT_BARF( context, 0x2f );
184         }
185         break;
186     case 0x7a:  /* NOVELL NetWare */
187         switch (AL_reg(context))
188         {
189         case 0x0:  /* Low-level Netware installation check AL=0 not installed.*/
190             AL_reg(context) = 0;    
191             break;
192         case 0x20:  /* Get VLM Call Address */
193             /* return nothing -> NetWare not installed */
194             break;
195         default:
196             INT_BARF( context, 0x2f );
197             break;
198         }
199         break;
200     case 0xb7:  /* append */
201         AL_reg(context) = 0; /* not installed */
202         break;
203     case 0xb8:  /* network */
204         switch (AL_reg(context))
205         {
206         case 0x00:  /* Install check */
207             /* not installed */
208             break;
209         default:
210             INT_BARF( context, 0x2f );
211             break;
212         }
213         break;
214     case 0xbd:  /* some Novell network install check ??? */
215         AX_reg(context) = 0xa5a5; /* pretend to have Novell IPX installed */
216         break;
217     case 0xbf:  /* REDIRIFS.EXE */
218         switch (AL_reg(context))
219         {
220         case 0x00:  /* Install check */
221             /* not installed */
222             break;
223         default:
224             INT_BARF( context, 0x2f );
225             break;
226         }
227         break;
228     case 0xd7:  /* Banyan Vines */
229         switch (AL_reg(context))
230         {
231         case 0x01:  /* Install check - Get Int Number */
232             /* not installed */
233             break;
234         default:
235             INT_BARF( context, 0x2f );
236             break;
237         }
238         break;
239     case 0xfa:  /* Watcom debugger check, returns 0x666 if installed */
240         break;
241     default:
242         INT_BARF( context, 0x2f );
243         break;
244     }
245 }
246
247
248 /**********************************************************************
249  *          do_int2f_16
250  */
251 static void do_int2f_16( CONTEXT *context )
252 {
253     DWORD addr;
254
255     switch(AL_reg(context))
256     {
257     case 0x00:  /* Windows enhanced mode installation check */
258         AX_reg(context) = (GetWinFlags16() & WF_ENHANCED) ?
259                                                   LOWORD(GetVersion16()) : 0;
260         break;
261         
262     case 0x0a:  /* Get Windows version and type */
263         AX_reg(context) = 0;
264         BX_reg(context) = (LOWORD(GetVersion16()) << 8) |
265                           (LOWORD(GetVersion16()) >> 8);
266         CX_reg(context) = (GetWinFlags16() & WF_ENHANCED) ? 3 : 2;
267         break;
268
269     case 0x0b:  /* Identify Windows-aware TSRs */
270         /* we don't have any pre-Windows TSRs */
271         break;
272
273     case 0x11:  /* Get Shell Parameters - (SHELL= in CONFIG.SYS) */
274         /* We can mock this up. But not today... */ 
275         FIXME(int, "Get Shell Parameters\n");       
276         break;
277
278     case 0x80:  /* Release time-slice */
279         AL_reg(context) = 0;
280         break;
281
282     case 0x81: /* Begin critical section.  */
283         /* FIXME? */
284         break;
285
286     case 0x82: /* End critical section.  */
287         /* FIXME? */
288         break;
289
290     case 0x83:  /* Return Current Virtual Machine ID */
291         /* Virtual Machines are usually created/destroyed when Windows runs
292          * DOS programs. Since we never do, we are always in the System VM.
293          * According to Ralf Brown's Interrupt List, never return 0. But it
294          * seems to work okay (returning 0), just to be sure we return 1.
295          */
296         BX_reg(context) = 1; /* VM 1 is probably the System VM */
297         break;
298
299     case 0x84:  /* Get device API entry point */
300         addr = (DWORD)NE_GetEntryPoint( GetModuleHandle16("WPROCS"),
301                                         VXD_BASE + BX_reg(context) );
302         if (!addr)  /* not supported */
303         {
304             ERR(int,"Accessing unknown VxD %04x - Expect a failure now.\n",
305                      BX_reg(context) );
306         }
307         ES_reg(context) = SELECTOROF(addr);
308         DI_reg(context) = OFFSETOF(addr);
309         break;
310
311     case 0x86:  /* DPMI detect mode */
312         AX_reg(context) = 0;  /* Running under DPMI */
313         break;
314
315     case 0x87: /* DPMI installation check */
316 #if 1   /* DPMI still breaks pkunzip */
317         if (ISV86(context)) break; /* so bail out for now if in v86 mode */
318 #endif
319         {
320             TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
321             NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
322             SYSTEM_INFO si;
323
324             GlobalUnlock16( GetCurrentTask() );
325             GetSystemInfo(&si);
326             AX_reg(context) = 0x0000; /* DPMI Installed */
327             BX_reg(context) = 0x0001; /* 32bits available */
328             CL_reg(context) = si.wProcessorLevel;
329             DX_reg(context) = 0x005a; /* DPMI major/minor 0.90 */
330             SI_reg(context) = 0;      /* # of para. of DOS extended private data */
331 #ifdef MZ_SUPPORTED                   /* ES:DI is DPMI switch entry point */
332             if (pModule && pModule->lpDosTask)
333                 ES_reg(context) = pModule->lpDosTask->dpmi_seg;
334             else
335 #endif
336                 ES_reg(context) = 0;
337             DI_reg(context) = 0;
338             break;
339         }
340     case 0x8a:  /* DPMI get vendor-specific API entry point. */
341         /* The 1.0 specs say this should work with all 0.9 hosts.  */
342         break;
343
344     default:
345         INT_BARF( context, 0x2f );
346     }
347 }
348
349 void do_mscdex( CONTEXT *context )
350 {
351     int         drive, count;
352     char*       p;
353
354     switch(AL_reg(context)) {
355     case 0x00: /* Installation check */
356         /* Count the number of contiguous CDROM drives
357          */
358         for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) {
359             if (DRIVE_GetType(drive) == TYPE_CDROM) {
360                 while (DRIVE_GetType(drive + count) == TYPE_CDROM) count++;
361                 break;
362             }
363         }
364         TRACE(int, "Installation check: %d cdroms, starting at %d\n", count, drive);
365         BX_reg(context) = count;
366         CX_reg(context) = (drive < MAX_DOS_DRIVES) ? drive : 0;
367         break;
368         
369     case 0x0B: /* drive check */
370         AX_reg(context) = (DRIVE_GetType(CX_reg(context)) == TYPE_CDROM);
371         BX_reg(context) = 0xADAD;
372         break;
373         
374     case 0x0C: /* get version */
375         BX_reg(context) = 0x020a;
376         break;
377         
378     case 0x0D: /* get drive letters */
379         p = CTX_SEG_OFF_TO_LIN(context, ES_reg(context), EBX_reg(context));
380         memset( p, 0, MAX_DOS_DRIVES );
381         for (drive = 0; drive < MAX_DOS_DRIVES; drive++) {
382             if (DRIVE_GetType(drive) == TYPE_CDROM) *p++ = drive;
383         }
384         break;
385         
386     case 0x10: /* direct driver acces */
387     {
388         static  WINE_CDAUDIO    wcda;
389         BYTE*   driver_request;
390         BYTE*   io_stru;        
391         u_char  Error = 255; /* No Error */ 
392         int     dorealmode = ISV86(context);
393         
394         driver_request = (dorealmode) ? 
395             DOSMEM_MapRealToLinear(MAKELONG(BX_reg(context),ES_reg(context))) : 
396             PTR_SEG_OFF_TO_LIN(ES_reg(context),BX_reg(context));
397         
398         if (!driver_request) {
399             /* FIXME - to be deleted ?? */
400             ERR(int,"   ES:BX==0 ! SEGFAULT ?\n");
401             ERR(int," -->BX=0x%04x, ES=0x%04lx, DS=0x%04lx, CX=0x%04x\n",
402                 BX_reg(context), ES_reg(context), DS_reg(context), CX_reg(context));
403         } else {
404             /* FIXME - would be best to open the device at the begining of the wine session .... 
405              */
406             if (wcda.unixdev <= 0) 
407                 CDAUDIO_Open(&wcda);
408         }
409         TRACE(int,"CDROM device driver -> command <%d>\n", (unsigned char)driver_request[2]);
410         
411         /* set status to 0 */
412         driver_request[3] = driver_request[4] = 0;
413         CDAUDIO_GetCDStatus(&wcda);
414         
415         switch (driver_request[2]) {
416         case 3:
417             io_stru = (dorealmode) ? 
418                 DOSMEM_MapRealToLinear(MAKELONG(*((WORD*)(driver_request + 14)), *((WORD*)(driver_request + 16)))) :
419                     PTR_SEG_OFF_TO_LIN(*((WORD*)(driver_request + 16)), *((WORD*)(driver_request + 18)));
420             
421             TRACE(int," --> IOCTL INPUT <%d>\n", io_stru[0]); 
422             switch (io_stru[0]) {
423 #if 0
424             case 0: /* Get device Header */
425             {
426                 static  LPSTR ptr = 0;
427                 if (ptr == 0)   {
428                     ptr = SEGPTR_ALLOC(22);
429                     *((DWORD*)(ptr     )) = ~1;         /* Next Device Driver */
430                     *((WORD* )(ptr +  4)) = 0xC800;     /* Device attributes  */
431                     *((WORD* )(ptr +  6)) = 0x1234;     /* Pointer to device strategy routine: FIXME */
432                     *((WORD* )(ptr +  8)) = 0x3142;     /* Pointer to device interrupt routine: FIXME */
433                     *((char*) (ptr + 10)) = 'W';        /* 8-byte character device name field */
434                     *((char*) (ptr + 11)) = 'I';
435                     *((char*) (ptr + 12)) = 'N';
436                     *((char*) (ptr + 13)) = 'E';
437                     *((char*) (ptr + 14)) = '_';
438                     *((char*) (ptr + 15)) = 'C';
439                     *((char*) (ptr + 16)) = 'D';
440                     *((char*) (ptr + 17)) = '_';
441                     *((WORD*) (ptr + 18)) = 0;          /* Reserved (must be zero) */
442                     *((BYTE*) (ptr + 20)) = 0;          /* Drive letter (must be zero) */
443                     *((BYTE*) (ptr + 21)) = 1;          /* Number of units supported (one or more) FIXME*/
444                 }
445                 ((DWORD*)io_stru+1)[0] = SEGPTR_GET(ptr);
446             }
447             break;
448 #endif
449             
450             case 1: /* location of head */
451                 if (io_stru[1] == 0) {
452                     /* FIXME: what if io_stru+2 is not DWORD aligned ? */
453                     ((DWORD*)io_stru+2)[0] = wcda.dwCurFrame;
454                     TRACE(int," ----> HEAD LOCATION <%ld>\n", ((DWORD*)io_stru+2)[0]); 
455                 } else {
456                     ERR(int,"CDRom-Driver: Unsupported addressing mode !!\n");
457                     Error = 0x0c;
458                 }
459                 break;
460                 
461             case 4: /* Audio channel info */
462                 io_stru[1] = 0;
463                 io_stru[2] = 0xff;
464                 io_stru[3] = 1;
465                 io_stru[4] = 0xff;
466                 io_stru[5] = 2;
467                 io_stru[6] = 0xff;
468                 io_stru[7] = 3;
469                 io_stru[8] = 0xff;
470                 TRACE(int," ----> AUDIO CHANNEL CONTROL\n"); 
471                 break;
472                 
473             case 6: /* device status */
474                 /* FIXME .. does this work properly ?? */
475                 io_stru[3] = io_stru[4] = 0;
476                 io_stru[2] = 1;  /* supports audio channels (?? FIXME ??) */
477                 io_stru[1] = 16; /* data read and plays audio tracks */
478                 if (wcda.cdaMode == WINE_CDA_OPEN)
479                     io_stru[1] |= 1;
480                 TRACE(int," ----> DEVICE STATUS <0x%08lx>\n", (DWORD)io_stru[1]); 
481                 break;
482                 
483             case 8: /* Volume size */
484                 *((DWORD*)(io_stru+1)) = wcda.dwTotalLen;
485                 TRACE(int," ----> VOLMUE SIZE <0x%08lx>\n", *((DWORD*)(io_stru+1))); 
486                 break;
487                 
488             case 9: /* media changed ? */
489                 /* answers don't know... -1/1 for yes/no would be better */
490                 io_stru[0] = 0; /* FIXME? 1? */
491                 break;
492                 
493             case 10: /* audio disk info */
494                 io_stru[1] = wcda.nFirstTrack; /* starting track of the disc */
495                 io_stru[2] = wcda.nLastTrack; /* ending track */
496                 ((DWORD*)io_stru+3)[0] = wcda.dwFirstOffset;
497                 TRACE(int," ----> AUDIO DISK INFO <%d-%d/%ld>\n",
498                       io_stru[1], io_stru[2], ((DWORD *)io_stru+3)[0]); 
499                 break;
500                 
501             case 11: /* audio track info */
502                 ((DWORD*)io_stru+2)[0] = wcda.lpdwTrackPos[io_stru[1]];
503                 /* starting point if the track */
504                 io_stru[6] = wcda.lpbTrackFlags[io_stru[1]];
505                 TRACE(int," ----> AUDIO TRACK INFO <track=%d>[%ld:%d]\n",
506                       io_stru[1],((DWORD *)io_stru+2)[0], io_stru[6]); 
507                 break;
508                 
509             case 12: /* get Q-Channel / Subchannel (??) info */
510                 io_stru[ 1] = wcda.lpbTrackFlags[wcda.nCurTrack];
511                 io_stru[ 2] = wcda.nCurTrack;
512                 io_stru[ 3] = 0; /* FIXME ?? */
513                 {
514                     DWORD  f = wcda.lpdwTrackPos[wcda.nCurTrack] - wcda.dwCurFrame;
515                     io_stru[ 4] = f / CDFRAMES_PERMIN;
516                     io_stru[ 5] = (f - CDFRAMES_PERMIN * io_stru[4]) / CDFRAMES_PERSEC;
517                     io_stru[ 6] = f - CDFRAMES_PERMIN * io_stru[4] - CDFRAMES_PERSEC * io_stru[5];
518                 }
519                 io_stru[ 7] = 0;
520                 {
521                     DWORD  f = wcda.dwCurFrame;
522                     io_stru[ 8] = f / CDFRAMES_PERMIN;
523                     io_stru[ 9] = (f - CDFRAMES_PERMIN * io_stru[4]) / CDFRAMES_PERSEC;
524                     io_stru[10] = f - CDFRAMES_PERMIN * io_stru[4] - CDFRAMES_PERSEC * io_stru[5];
525                 }
526                 break;
527                 
528             case 15: /* Audio status info */
529                 /* !!!! FIXME FIXME FIXME !! */
530                 *((WORD*)(io_stru+1))  = (wcda.cdaMode == WINE_CDA_PAUSE);
531                 *((DWORD*)(io_stru+3)) = wcda.lpdwTrackPos[0];
532                 *((DWORD*)(io_stru+7)) = wcda.lpdwTrackPos[wcda.nTracks - 1];
533                 break;
534                 
535             default:
536                 FIXME(int," Cdrom-driver: IOCTL INPUT: Unimplemented <%d>!!\n", io_stru[0]); 
537                 Error=0x0c; 
538                 break;  
539             }   
540             break;
541             
542         case 12:
543             io_stru = (dorealmode) ? 
544                 DOSMEM_MapRealToLinear(MAKELONG(*((WORD*)(driver_request + 14)), *((WORD*)(driver_request + 16)))) :
545                     PTR_SEG_OFF_TO_LIN(*((WORD*)(driver_request + 16)), *((WORD*)(driver_request + 18)));
546             
547             TRACE(int," --> IOCTL OUTPUT <%d>\n",io_stru[0]); 
548             switch (io_stru[0]) {
549             case 0: /* eject */ 
550                 CDAUDIO_SetDoor(&wcda, 1);
551                 TRACE(int," ----> EJECT\n"); 
552                 break;
553             case 2: /* reset drive */
554                 CDAUDIO_Reset(&wcda);
555                 TRACE(int," ----> RESET\n"); 
556                 break;
557             case 3: /* Audio Channel Control */
558                 FIXME(int, " ----> AUDIO CHANNEL CONTROL (NIY)\n");
559                 break;
560             case 5: /* close tray */
561                 CDAUDIO_SetDoor(&wcda, 0);
562                 TRACE(int," ----> CLOSE TRAY\n"); 
563                 break;
564             default:
565                 FIXME(int," Cdrom-driver: IOCTL OUPUT: Unimplemented <%d>!!\n",
566                       io_stru[0]); 
567                 Error=0x0c;
568                 break;  
569             }   
570             break;
571             
572         case 132:  /* FIXME - It didn't function for me... */
573             TRACE(int," --> PLAY AUDIO\n");
574             if (driver_request[13] == 0) {
575                 TRACE(int,"Mode :<0x%02X> , [%ld-%ld]\n",
576                       (unsigned char)driver_request[13],
577                       ((DWORD*)driver_request+14)[0],
578                       ((DWORD*)driver_request+18)[0]);
579                 CDAUDIO_Play(&wcda, ((DWORD*)driver_request+14)[0], ((DWORD*)driver_request+14)[0] + ((DWORD*)driver_request+18)[0]);
580             } else {
581                 ERR(int, "CDRom-Driver: Unsupported address mode !!\n");
582                 Error=0x0c;
583             }
584             break;
585             
586         case 133:
587             if (wcda.cdaMode == WINE_CDA_PLAY) {
588                 CDAUDIO_Pause(&wcda, 1);
589                 TRACE(int," --> STOP AUDIO (Paused)\n");
590             } else {
591                 CDAUDIO_Stop(&wcda);
592                 TRACE(int," --> STOP AUDIO (Stopped)\n");
593             }
594             break;
595             
596         case 136:
597             TRACE(int," --> RESUME AUDIO\n");
598             CDAUDIO_Pause(&wcda, 0);
599             break;
600             
601         default:
602             FIXME(int," CDRom-Driver - ioctl uninplemented <%d>\n",driver_request[2]); 
603             Error=0x0c; 
604         }
605         
606         if (Error<255) {
607             driver_request[4] |= 127;
608             driver_request[3] = Error;
609         }
610         driver_request[4] |= 2 * (wcda.cdaMode = WINE_CDA_PLAY);
611         
612         /*  close (fdcd); FIXME !! -- cannot use close when ejecting 
613             the cd-rom - close would close it again */ 
614     }
615     break;
616     default:
617         FIXME(int, "Unimplemented MSCDEX function 0x%02X.\n", AL_reg(context));
618         break;
619     }
620 }