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