Added a first-cut version of MapVirtualKeyExW() that has the same
[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 "config.h"
11
12 #include "wine/winbase16.h"
13 #include "dosexe.h"
14 #include "drive.h"
15 #include "miscemu.h"
16 #include "module.h"
17 #include "task.h"
18 /* #define DEBUG_INT */
19 #include "debugtools.h"
20 #include "cdrom.h"
21
22 DEFAULT_DEBUG_CHANNEL(int);
23
24 /* base WPROCS.DLL ordinal number for VxDs */
25 #define VXD_BASE 400
26
27 static void do_int2f_16( CONTEXT86 *context );
28 static void MSCDEX_Handler( CONTEXT86 *context );
29
30 /**********************************************************************
31  *          INT_Int2fHandler
32  *
33  * Handler for int 2fh (multiplex).
34  */
35 void WINAPI INT_Int2fHandler( CONTEXT86 *context )
36 {
37     TRACE("Subfunction 0x%X\n", AX_reg(context));
38
39     switch(AH_reg(context))
40     {
41     case 0x10:
42         AL_reg(context) = 0xff; /* share is installed */
43         break;
44
45     case 0x11:  /* Network Redirector / IFSFUNC */
46         switch (LOBYTE(context->Eax))
47         {
48         case 0x00:  /* Install check */
49             /* not installed */
50             break;
51         case 0x80:  /* Enhanced services - Install check */
52             /* not installed */
53             break;
54         default:
55             INT_BARF( context, 0x2f );
56             break;
57         }
58         break;
59
60     case 0x12:
61         switch (LOBYTE(context->Eax))
62         {
63         case 0x2e: /* get or set DOS error table address */
64             switch (DL_reg(context))
65             {
66             /* Four tables: even commands are 'get', odd are 'set' */
67             /* DOS 5.0+ ignores "set" commands */
68             case 0x01:
69             case 0x03:
70             case 0x05:
71             case 0x07:
72             case 0x09:
73                 break; 
74             /* Instead of having a message table in DOS-space, */
75             /* we can use a special case for MS-DOS to force   */
76             /* the secondary interface.                        */
77             case 0x00:
78             case 0x02:
79             case 0x04:
80             case 0x06: 
81                 context->SegEs = 0x0001;
82                 DI_reg(context) = 0x0000;
83                 break;
84             case 0x08:
85                 FIXME("No real-mode handler for errors yet! (bye!)\n");
86                 break;
87             default:
88                 INT_BARF(context, 0x2f);
89             }
90             break;
91         default:
92            INT_BARF(context, 0x2f);
93         }  
94         break;
95    
96     case 0x15: /* mscdex */
97         MSCDEX_Handler(context);
98         break;
99
100     case 0x16:
101         do_int2f_16( context );
102         break;
103
104     case 0x1a: /* ANSI.SYS / AVATAR.SYS Install Check */
105         /* Not supported yet, do nothing */
106         break;
107
108     case 0x43:
109 #if 1
110         switch (LOBYTE(context->Eax))
111         {
112         case 0x00:   /* XMS v2+ installation check */
113             WARN("XMS is not fully implemented\n");
114             AL_reg(context) = 0x80;
115             break;
116         case 0x10:   /* XMS v2+ get driver address */
117         {
118             context->SegEs = DOSMEM_xms_seg;
119             BX_reg(context) = 0;
120             break;
121         }
122         default:
123             INT_BARF( context, 0x2f );
124         }
125 #else
126         FIXME("check for XMS (not supported)\n");
127         AL_reg(context) = 0x42; /* != 0x80 */
128 #endif
129         break;
130
131     case 0x45:
132        switch (LOBYTE(context->Eax)) 
133        {
134        case 0x00:
135        case 0x01:
136        case 0x02:
137        case 0x03:
138        case 0x04:
139        case 0x05:
140        case 0x06:
141        case 0x07:
142        case 0x08:
143            /* Microsoft Profiler - not installed */
144            break;
145        default:
146             INT_BARF( context, 0x2f );
147        }
148        break;
149
150     case 0x4a:
151         switch(LOBYTE(context->Eax))
152         {
153         case 0x10:  /* smartdrv */
154             break;  /* not installed */
155         case 0x11:  /* dblspace */
156             break;  /* not installed */
157         case 0x12:  /* realtime compression interface */
158             break;  /* not installed */
159         case 0x32:  /* patch IO.SYS (???) */
160             break;  /* we have no IO.SYS, so we can't patch it :-/ */
161         default:
162             INT_BARF( context, 0x2f );
163         }
164         break;
165     case 0x4b:
166         switch(LOBYTE(context->Eax))
167         {
168         case 0x01:
169         case 0x02:
170         case 0x03:
171         case 0x04:
172         case 0x05:
173             FIXME("Task Switcher - not implemented\n");
174             break;
175         default:
176             INT_BARF( context, 0x2f );
177         }
178         break;
179     case 0x56:  /* INTERLNK */
180         switch(LOBYTE(context->Eax))
181         {
182         case 0x01:  /* check if redirected drive */
183             AL_reg(context) = 0; /* not redirected */
184             break;
185         default:
186             INT_BARF( context, 0x2f );
187         }
188         break;
189     case 0x7a:  /* NOVELL NetWare */
190         switch (LOBYTE(context->Eax))
191         {
192         case 0x0:  /* Low-level Netware installation check AL=0 not installed.*/
193             AL_reg(context) = 0;    
194             break;
195         case 0x20:  /* Get VLM Call Address */
196             /* return nothing -> NetWare not installed */
197             break;
198         default:
199             INT_BARF( context, 0x2f );
200             break;
201         }
202         break;
203     case 0xb7:  /* append */
204         LOBYTE(context->Eax) = 0; /* not installed */
205         break;
206     case 0xb8:  /* network */
207         switch (LOBYTE(context->Eax))
208         {
209         case 0x00:  /* Install check */
210             /* not installed */
211             break;
212         default:
213             INT_BARF( context, 0x2f );
214             break;
215         }
216         break;
217     case 0xbd:  /* some Novell network install check ??? */
218         AX_reg(context) = 0xa5a5; /* pretend to have Novell IPX installed */
219         break;
220     case 0xbf:  /* REDIRIFS.EXE */
221         switch (LOBYTE(context->Eax))
222         {
223         case 0x00:  /* Install check */
224             /* not installed */
225             break;
226         default:
227             INT_BARF( context, 0x2f );
228             break;
229         }
230         break;
231     case 0xd2:
232         switch(LOBYTE(context->Eax))
233         {
234         case 0x01: /* Quarterdeck RPCI - QEMM/QRAM - PCL-838.EXE functions */
235             if(BX_reg(context) == 0x5145 && CX_reg(context) == 0x4D4D 
236               && DX_reg(context) == 0x3432)
237                 TRACE("Check for QEMM v5.0+ (not installed)\n");
238                 break;
239         default:
240             INT_BARF( context, 0x2f );
241             break;
242         }
243         break;
244     case 0xd7:  /* Banyan Vines */
245         switch (LOBYTE(context->Eax))
246         {
247         case 0x01:  /* Install check - Get Int Number */
248             /* not installed */
249             break;
250         default:
251             INT_BARF( context, 0x2f );
252             break;
253         }
254         break;
255     case 0xde:
256         switch(LOBYTE(context->Eax))
257         {
258         case 0x01:   /* Quarterdeck QDPMI.SYS - DESQview */
259             if(BX_reg(context) == 0x4450 && CX_reg(context) == 0x4d49 
260               && DX_reg(context) == 0x8f4f)
261                 TRACE("Check for QDPMI.SYS (not installed)\n");
262                 break;
263         default:
264             INT_BARF( context, 0x2f );
265             break;
266         }
267         break;
268     case 0xfa:  /* Watcom debugger check, returns 0x666 if installed */
269         break;
270     default:
271         INT_BARF( context, 0x2f );
272         break;
273     }
274 }
275
276
277 /**********************************************************************
278  *          do_int2f_16
279  */
280 static void do_int2f_16( CONTEXT86 *context )
281 {
282     DWORD addr;
283
284     switch(LOBYTE(context->Eax))
285     {
286     case 0x00:  /* Windows enhanced mode installation check */
287         AX_reg(context) = (GetWinFlags16() & WF_ENHANCED) ?
288                                                   LOWORD(GetVersion16()) : 0;
289         break;
290         
291     case 0x0a:  /* Get Windows version and type */
292         AX_reg(context) = 0;
293         BX_reg(context) = (LOWORD(GetVersion16()) << 8) |
294                           (LOWORD(GetVersion16()) >> 8);
295         CX_reg(context) = (GetWinFlags16() & WF_ENHANCED) ? 3 : 2;
296         break;
297
298     case 0x0b:  /* Identify Windows-aware TSRs */
299         /* we don't have any pre-Windows TSRs */
300         break;
301
302     case 0x11:  /* Get Shell Parameters - (SHELL= in CONFIG.SYS) */
303         /* We can mock this up. But not today... */ 
304         FIXME("Get Shell Parameters\n");       
305         break;
306
307     case 0x80:  /* Release time-slice */
308         AL_reg(context) = 0;
309         break;
310
311     case 0x81: /* Begin critical section.  */
312         /* FIXME? */
313         break;
314
315     case 0x82: /* End critical section.  */
316         /* FIXME? */
317         break;
318
319     case 0x83:  /* Return Current Virtual Machine ID */
320         /* Virtual Machines are usually created/destroyed when Windows runs
321          * DOS programs. Since we never do, we are always in the System VM.
322          * According to Ralf Brown's Interrupt List, never return 0. But it
323          * seems to work okay (returning 0), just to be sure we return 1.
324          */
325         BX_reg(context) = 1; /* VM 1 is probably the System VM */
326         break;
327
328     case 0x84:  /* Get device API entry point */
329         {
330             HMODULE16 mod = GetModuleHandle16("wprocs");
331             if (mod < 32) mod = LoadLibrary16( "wprocs" );
332             addr = (DWORD)GetProcAddress16( mod, (LPCSTR)(VXD_BASE + BX_reg(context)) );
333             if (!addr)  /* not supported */
334                 ERR("Accessing unknown VxD %04x - Expect a failure now.\n", BX_reg(context) );
335             context->SegEs = SELECTOROF(addr);
336             DI_reg(context) = OFFSETOF(addr);
337         }
338         break;
339
340     case 0x86:  /* DPMI detect mode */
341         AX_reg(context) = 0;  /* Running under DPMI */
342         break;
343
344     case 0x87: /* DPMI installation check */
345 #if 1   /* DPMI still breaks pkunzip */
346         if (ISV86(context)) break; /* so bail out for now if in v86 mode */
347 #endif
348         {
349             SYSTEM_INFO si;
350             GetSystemInfo(&si);
351             AX_reg(context) = 0x0000; /* DPMI Installed */
352             BX_reg(context) = 0x0001; /* 32bits available */
353             CL_reg(context) = si.wProcessorLevel;
354             DX_reg(context) = 0x005a; /* DPMI major/minor 0.90 */
355             SI_reg(context) = 0;      /* # of para. of DOS extended private data */
356             context->SegEs = DOSMEM_dpmi_seg;
357             DI_reg(context) = 0;      /* ES:DI is DPMI switch entry point */
358             break;
359         }
360     case 0x8a:  /* DPMI get vendor-specific API entry point. */
361         /* The 1.0 specs say this should work with all 0.9 hosts.  */
362         break;
363
364     default:
365         INT_BARF( context, 0x2f );
366     }
367 }
368
369 /* FIXME: this macro may have to be changed on architectures where <size> reads/writes 
370  * must be <size> aligned
371  * <size> could be WORD, DWORD...
372  * in this case, we would need two macros, one for read, the other one for write
373  * Note: PTR_AT can be used as an l-value
374  */
375 #define PTR_AT(_ptr, _ofs, _typ)        (*((_typ*)(((char*)_ptr)+(_ofs))))
376
377 /* Use #if 1 if you want full int 2f debug... normal users can leave it at 0 */
378 #if 0
379 /**********************************************************************
380  *          MSCDEX_Dump                                 [internal]
381  *
382  * Dumps mscdex requests to int debug channel.
383  */
384 static  void    MSCDEX_Dump(char* pfx, BYTE* req, int dorealmode)
385 {
386     int         i;
387     BYTE        buf[2048];
388     BYTE*       ptr;
389     BYTE*       ios;
390     
391     ptr = buf;
392     ptr += sprintf(ptr, "%s\tCommand => ", pfx);
393     for (i = 0; i < req[0]; i++) {
394         ptr += sprintf(ptr, "%02x ", req[i]);
395     }
396
397     switch (req[2]) {
398     case 3:
399     case 12:
400         ptr += sprintf(ptr, "\n\t\t\t\tIO_struct => ");
401         ios = (dorealmode) ? PTR_REAL_TO_LIN( PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD)) :
402                              PTR_SEG_OFF_TO_LIN(PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD));
403
404         for (i = 0; i < PTR_AT(req, 18, WORD); i++) {
405             ptr += sprintf(ptr, "%02x ", ios[i]);
406             if ((i & 0x1F) == 0x1F) {
407                 *ptr++ = '\n';
408                 *ptr = 0;
409             }
410         }
411         break;
412     }
413     TRACE("%s\n", buf);
414 }
415 #else
416 #define MSCDEX_Dump(pfx, req, drm)
417 #endif
418
419 static  void    MSCDEX_StoreMSF(DWORD frame, BYTE* val)
420 {
421     val[3] = 0; /* zero */
422     val[2] = frame / CDFRAMES_PERMIN; /* minutes */
423     val[1] = (frame - CDFRAMES_PERMIN * val[2]) / CDFRAMES_PERSEC; /* seconds */
424     val[0] = frame - CDFRAMES_PERMIN * val[2] - CDFRAMES_PERSEC * val[1]; /* frames */
425 }
426
427 static void MSCDEX_Handler(CONTEXT86* context)
428 {
429     int         drive, count;
430     char*       p;
431
432     switch(LOBYTE(context->Eax)) {
433     case 0x00: /* Installation check */
434         /* Count the number of contiguous CDROM drives
435          */
436         for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) {
437             if (DRIVE_GetType(drive) == TYPE_CDROM) {
438                 while (DRIVE_GetType(drive + count) == TYPE_CDROM) count++;
439                 break;
440             }
441         }
442         TRACE("Installation check: %d cdroms, starting at %d\n", count, drive);
443         BX_reg(context) = count;
444         CX_reg(context) = (drive < MAX_DOS_DRIVES) ? drive : 0;
445         break;
446         
447     case 0x0B: /* drive check */
448         AX_reg(context) = (DRIVE_GetType(CX_reg(context)) == TYPE_CDROM);
449         BX_reg(context) = 0xADAD;
450         break;
451         
452     case 0x0C: /* get version */
453         BX_reg(context) = 0x020a;
454         TRACE("Version number => %04x\n", BX_reg(context));
455         break;
456         
457     case 0x0D: /* get drive letters */
458         p = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
459         memset(p, 0, MAX_DOS_DRIVES);
460         for (drive = 0; drive < MAX_DOS_DRIVES; drive++) {
461             if (DRIVE_GetType(drive) == TYPE_CDROM) *p++ = drive;
462         }
463         TRACE("Get drive letters\n");
464         break;
465         
466     case 0x10: /* direct driver access */
467         {
468             static      WINE_CDAUDIO    wcda;
469             int         dev = -1;
470             BYTE*       driver_request;
471             BYTE*       io_stru;        
472             BYTE        Error = 255; /* No Error */ 
473             int         dorealmode = ISV86(context);
474             
475             driver_request = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
476             
477             if (!driver_request) {
478                 /* FIXME - to be deleted ?? */
479                 ERR("ES:BX==0 ! SEGFAULT ?\n");
480                 ERR("-->BX=0x%04x, ES=0x%04lx, DS=0x%04lx, CX=0x%04x\n",
481                     BX_reg(context), context->SegEs, context->SegDs, CX_reg(context));
482                 driver_request[4] |= 0x80;
483                 driver_request[3] = 5;  /* bad request length */
484                 return;
485             }
486             /* FIXME
487              *       - the current implementation only supports a single CD ROM
488              */
489             CDROM_Open(&wcda, -1);
490             dev = CDROM_OpenDev(&wcda);
491             TRACE("CDROM device driver -> command <%d>\n", (unsigned char)driver_request[2]);
492             
493             for (drive = 0; 
494                  drive < MAX_DOS_DRIVES && DRIVE_GetType(drive) != TYPE_CDROM; 
495                  drive++);
496             /* drive contains the first CD ROM */
497             if (CX_reg(context) != drive) {
498                 WARN("Request made doesn't match a CD ROM drive (%d/%d)\n", CX_reg(context), drive);
499                 driver_request[4] |= 0x80;
500                 driver_request[3] = 1;  /* unknown unit */
501                 return;
502             }
503
504             MSCDEX_Dump("Beg", driver_request, dorealmode);
505             
506             /* set status to 0 */
507             PTR_AT(driver_request, 3, WORD) = 0;
508             CDROM_Audio_GetCDStatus(&wcda, dev);
509             
510             switch (driver_request[2]) {
511             case 3:
512                 io_stru = (dorealmode) ? 
513                     PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD) ) :
514                         PTR_SEG_OFF_TO_LIN(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD));
515                 
516                 TRACE(" --> IOCTL INPUT <%d>\n", io_stru[0]); 
517                 switch (io_stru[0]) {
518 #if 0
519                 case 0: /* Get device Header */
520                     {
521                         static  LPSTR ptr = 0;
522                         if (ptr == 0)   {
523                             ptr = SEGPTR_ALLOC(22);
524                             PTR_AT(ptr,  0, DWORD) = ~1;        /* Next Device Driver */
525                             PTR_AT(ptr,  4,  WORD) = 0xC800;    /* Device attributes  */
526                             PTR_AT(ptr,  6,  WORD) = 0x1234;    /* Pointer to device strategy routine: FIXME */
527                             PTR_AT(ptr,  8,  WORD) = 0x3142;    /* Pointer to device interrupt routine: FIXME */
528                             PTR_AT(ptr, 10,  char) = 'W';       /* 8-byte character device name field */
529                             PTR_AT(ptr, 11,  char) = 'I';
530                             PTR_AT(ptr, 12,  char) = 'N';
531                             PTR_AT(ptr, 13,  char) = 'E';
532                             PTR_AT(ptr, 14,  char) = '_';
533                             PTR_AT(ptr, 15,  char) = 'C';
534                             PTR_AT(ptr, 16,  char) = 'D';
535                             PTR_AT(ptr, 17,  char) = '_';
536                             PTR_AT(ptr, 18,  WORD) = 0;         /* Reserved (must be zero) */
537                             PTR_AT(ptr, 20,  BYTE) = 0;         /* Drive letter (must be zero) */
538                             PTR_AT(ptr, 21,  BYTE) = 1;         /* Number of units supported (one or more) FIXME*/
539                         }
540                         PTR_AT(io_stru, DWORD,  0) = SEGPTR_GET(ptr);
541                     }
542                     break;
543 #endif
544                     
545                 case 1: /* location of head */
546                     switch (io_stru[1]) {
547                     case 0:
548                         PTR_AT(io_stru, 2, DWORD) = wcda.dwCurFrame;
549                         break;
550                     case 1:
551                         MSCDEX_StoreMSF(wcda.dwCurFrame, io_stru + 2);
552                         break;
553                     default:
554                         ERR("CD-ROM driver: unsupported addressing mode !!\n");
555                         Error = 0x0c;
556                     }   
557                     TRACE(" ----> HEAD LOCATION <%ld>\n", PTR_AT(io_stru, 2, DWORD)); 
558                     break;
559                     
560                 case 4: /* Audio channel info */
561                     io_stru[1] = 0;
562                     io_stru[2] = 0xff;
563                     io_stru[3] = 1;
564                     io_stru[4] = 0xff;
565                     io_stru[5] = 2;
566                     io_stru[6] = 0;
567                     io_stru[7] = 3;
568                     io_stru[8] = 0;
569                     TRACE(" ----> AUDIO CHANNEL INFO\n"); 
570                     break;
571                     
572                 case 6: /* device status */
573                     PTR_AT(io_stru, 1, DWORD) = 0x00000290;
574                     /* 290 => 
575                      * 1        Supports HSG and Red Book addressing modes      
576                      * 0        Supports audio channel manipulation     
577                      *
578                      * 1        Supports prefetching requests   
579                      * 0        Reserved
580                      * 0        No interleaving
581                      * 1        Data read and plays audio/video tracks
582                      *
583                      * 0        Read only
584                      * 0        Supports only cooked reading
585                      * 0        Door locked
586                      * 0        see below (Door closed/opened)
587                      */
588                     if (wcda.cdaMode == WINE_CDA_OPEN)
589                         io_stru[1] |= 1;
590                     TRACE(" ----> DEVICE STATUS <0x%08lx>\n", PTR_AT(io_stru, 1, DWORD));
591                     break;
592                     
593                 case 8: /* Volume size */
594                     PTR_AT(io_stru, 1, DWORD) = wcda.dwLastFrame;
595                     TRACE(" ----> VOLUME SIZE <%ld>\n", PTR_AT(io_stru, 1, DWORD));
596                     break;
597                     
598                 case 9: /* media changed ? */
599                     /* answers don't know... -1/1 for yes/no would be better */
600                     io_stru[1] = 0; /* FIXME? 1? */
601                     TRACE(" ----> MEDIA CHANGED <%d>\n", io_stru[1]); 
602                     break;
603                     
604                 case 10: /* audio disk info */
605                     io_stru[1] = wcda.nFirstTrack; /* starting track of the disc */
606                     io_stru[2] = wcda.nLastTrack;  /* ending track */
607                     MSCDEX_StoreMSF(wcda.dwLastFrame, io_stru + 3);
608                     
609                     TRACE(" ----> AUDIO DISK INFO <%d-%d/%08lx>\n",
610                           io_stru[1], io_stru[2], PTR_AT(io_stru, 3, DWORD));
611                     break;
612                     
613                 case 11: /* audio track info */
614                     if (io_stru[1] >= wcda.nFirstTrack && io_stru[1] <= wcda.nLastTrack) {
615                         int      nt = io_stru[1] - wcda.nFirstTrack;
616                         MSCDEX_StoreMSF(wcda.lpdwTrackPos[nt], io_stru + 2);
617                         /* starting point if the track */
618                         io_stru[6] = (wcda.lpbTrackFlags[nt] & 0xF0) >> 4;
619                     } else {
620                         PTR_AT(io_stru, 2, DWORD) = 0;
621                         io_stru[6] = 0;
622                     }
623                     TRACE(" ----> AUDIO TRACK INFO[%d] = [%08lx:%d]\n",
624                           io_stru[1], PTR_AT(io_stru, 2, DWORD), io_stru[6]); 
625                     break;
626                     
627                 case 12: /* get Q-Channel info */
628                     io_stru[1] = wcda.lpbTrackFlags[wcda.nCurTrack - 1];
629                     io_stru[2] = wcda.nCurTrack;
630                     io_stru[3] = 0; /* FIXME ?? */ 
631
632                     /* why the heck did MS use another format for 0MSF information... sigh */
633                     {
634                         BYTE    bTmp[4];
635
636                         MSCDEX_StoreMSF(wcda.dwCurFrame - wcda.lpdwTrackPos[wcda.nCurTrack - 1], bTmp);
637                         io_stru[ 4] = bTmp[2];
638                         io_stru[ 5] = bTmp[1];
639                         io_stru[ 6] = bTmp[0];
640                         io_stru[ 7] = 0;
641
642                         MSCDEX_StoreMSF(wcda.dwCurFrame, bTmp);
643                         io_stru[ 8] = bTmp[2];
644                         io_stru[ 9] = bTmp[1];
645                         io_stru[10] = bTmp[0];
646                         io_stru[11] = 0;
647                     }               
648                     TRACE("Q-Channel info: Ctrl/adr=%02x TNO=%02x X=%02x rtt=%02x:%02x:%02x rtd=%02x:%02x:%02x (cf=%08lx, tp=%08lx)\n",
649                           io_stru[ 1], io_stru[ 2], io_stru[ 3], 
650                           io_stru[ 4], io_stru[ 5], io_stru[ 6], 
651                           io_stru[ 8], io_stru[ 9], io_stru[10],
652                           wcda.dwCurFrame, wcda.lpdwTrackPos[wcda.nCurTrack - 1]);
653                     break;
654                     
655                 case 15: /* Audio status info */
656                     /* !!!! FIXME FIXME FIXME !! */
657                     PTR_AT(io_stru, 1,  WORD) = 2 | ((wcda.cdaMode == WINE_CDA_PAUSE) ? 1 : 0);
658                     if (wcda.cdaMode == WINE_CDA_OPEN) {
659                         PTR_AT(io_stru, 3, DWORD) = 0;
660                         PTR_AT(io_stru, 7, DWORD) = 0;
661                     } else {
662                         PTR_AT(io_stru, 3, DWORD) = wcda.lpdwTrackPos[0];
663                         PTR_AT(io_stru, 7, DWORD) = wcda.lpdwTrackPos[wcda.nTracks - 1];
664                     }
665                     TRACE("Audio status info: status=%04x startLoc=%ld endLoc=%ld\n",
666                           PTR_AT(io_stru, 1, WORD), PTR_AT(io_stru, 3, DWORD), PTR_AT(io_stru, 7, DWORD));
667                     break;
668                     
669                 default:
670                     FIXME("IOCTL INPUT: Unimplemented <%d>!!\n", io_stru[0]); 
671                     Error = 0x0c; 
672                     break;      
673                 }       
674                 break;
675                 
676             case 12:
677                 io_stru = (dorealmode) ? 
678                     PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)) :
679                         PTR_SEG_OFF_TO_LIN(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD));
680                 
681                 TRACE(" --> IOCTL OUTPUT <%d>\n", io_stru[0]); 
682                 switch (io_stru[0]) {
683                 case 0: /* eject */ 
684                     CDROM_SetDoor(&wcda, 1, dev);
685                     TRACE(" ----> EJECT\n"); 
686                     break;
687                 case 2: /* reset drive */
688                     CDROM_Reset(&wcda, dev);
689                     TRACE(" ----> RESET\n"); 
690                     break;
691                 case 3: /* Audio Channel Control */
692                     FIXME(" ----> AUDIO CHANNEL CONTROL (NIY)\n");
693                     break;
694                 case 5: /* close tray */
695                     CDROM_SetDoor(&wcda, 0, dev);
696                     TRACE(" ----> CLOSE TRAY\n"); 
697                     break;
698                 default:
699                     FIXME(" IOCTL OUTPUT: Unimplemented <%d>!!\n", io_stru[0]); 
700                     Error = 0x0c;
701                     break;      
702                 }       
703                 break;
704                 
705             case 131: /* seek */
706                 {
707                     DWORD       at;
708                     
709                     at = PTR_AT(driver_request, 20, DWORD);
710                     
711                     TRACE(" --> SEEK AUDIO mode :<0x%02X>, [%ld]\n", 
712                           (BYTE)driver_request[13], at);
713                     
714                     switch (driver_request[13]) {
715                     case 1: /* Red book addressing mode = 0:m:s:f */
716                         /* FIXME : frame <=> msf conversion routines could be shared
717                          * between mscdex and mcicda
718                          */
719                         at = LOBYTE(HIWORD(at)) * CDFRAMES_PERMIN +
720                             HIBYTE(LOWORD(at)) * CDFRAMES_PERSEC +
721                             LOBYTE(LOWORD(at));
722                         /* fall thru */
723                     case 0: /* HSG addressing mode */
724                         CDROM_Audio_Seek(&wcda, at, dev);
725                         break;
726                     default:
727                         ERR("Unsupported address mode !!\n");
728                         Error = 0x0c;
729                         break;
730                     }
731                 }
732                 break;
733
734             case 132: /* play */
735                 {
736                     DWORD       beg, end;
737                     
738                     beg = end = PTR_AT(driver_request, 14, DWORD);
739                     end += PTR_AT(driver_request, 18, DWORD);
740                     
741                     TRACE(" --> PLAY AUDIO mode :<0x%02X>, [%ld-%ld]\n", 
742                           (BYTE)driver_request[13], beg, end);
743                     
744                     switch (driver_request[13]) {
745                     case 1: /* Red book addressing mode = 0:m:s:f */
746                         /* FIXME : frame <=> msf conversion routines could be shared
747                          * between mscdex and mcicda
748                          */
749                         beg = LOBYTE(HIWORD(beg)) * CDFRAMES_PERMIN +
750                             HIBYTE(LOWORD(beg)) * CDFRAMES_PERSEC +
751                             LOBYTE(LOWORD(beg));
752                         end = LOBYTE(HIWORD(end)) * CDFRAMES_PERMIN +
753                             HIBYTE(LOWORD(end)) * CDFRAMES_PERSEC +
754                             LOBYTE(LOWORD(end));
755                         /* fall thru */
756                     case 0: /* HSG addressing mode */
757                         CDROM_Audio_Play(&wcda, beg, end, dev);
758                         break;
759                     default:
760                         ERR("Unsupported address mode !!\n");
761                         Error = 0x0c;
762                         break;
763                     }
764                 }
765                 break;
766                 
767             case 133:
768                 if (wcda.cdaMode == WINE_CDA_PLAY) {
769                     CDROM_Audio_Pause(&wcda, 1, dev);
770                     TRACE(" --> STOP AUDIO (Paused)\n");
771                 } else {
772                     CDROM_Audio_Stop(&wcda, dev);
773                     TRACE(" --> STOP AUDIO (Stopped)\n");
774                 }
775                 break;
776                 
777             case 136:
778                 TRACE(" --> RESUME AUDIO\n");
779                 CDROM_Audio_Pause(&wcda, 0, dev);
780                 break;
781                 
782             default:
783                 FIXME(" ioctl unimplemented <%d>\n", driver_request[2]); 
784                 Error = 0x0c;   
785             }
786             
787             /* setting error codes if any */
788             if (Error < 255) {
789                 driver_request[4] |= 0x80;
790                 driver_request[3] = Error;
791             }
792             
793             CDROM_CloseDev(dev);
794             CDROM_Close(&wcda);
795             /* setting status bits
796              * 3 == playing && done
797              * 1 == done 
798              */
799             driver_request[4] |= (wcda.cdaMode == WINE_CDA_PLAY) ? 3 : 1;
800             
801             MSCDEX_Dump("End", driver_request, dorealmode);
802         }
803         break;
804     default:
805         FIXME("Unimplemented MSCDEX function 0x%02X.\n", LOBYTE(context->Eax));
806         break;
807     }
808 }