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