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