]> icculus.org git repositories - btb/d2x.git/blob - unused/bios/rbaudio.c
shareware stuff
[btb/d2x.git] / unused / bios / rbaudio.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 char rbaudio_rcsid[] = "$Id: rbaudio.c,v 1.1.1.1 2001-01-19 03:30:14 bradleyb Exp $";
15
16
17 #include <dos.h>
18 #include <dpmi.h>
19 #include <string.h>
20
21 #include "mono.h"
22 #include "error.h"
23 #include "rbaudio.h"
24 #include "fix.h"
25 #include "timer.h"
26
27
28 #define HSG_CD_PLAYBACK 0
29 #define MSF_CD_PLAYBACK 1
30
31 #define RBERR_WRITEPROTVIOL     0
32 #define RBERR_UNKNOWNUNIT               1
33 #define RBERR_DRIVENOTREADY     2
34 #define RBERR_UNKNOWNCOMMAND    3
35 #define RBERR_CRCERROR                  4
36 #define RBERR_BADDRIVEREQ               5
37 #define RBERR_SEEKERROR                 6
38 #define RBERR_UNKNOWNMEDIA              7
39 #define RBERR_SECTORNOTFOUND    8
40 #define RBERR_WRITEFAULT                10
41 #define RBERR_READFAULT                 12
42 #define RBERR_GENERALFAILURE    13
43 #define RBERR_BADDISKCHANGE     15
44
45 #define RBSTAT_ERROR                            0x8000
46 #define RBSTAT_BUSY                             0x0200
47 #define RBSTAT_DONE                             0x0100
48
49 #pragma pack(1)
50
51
52 // Request Header       -----------------------------------------------------
53
54 typedef struct _REQHDR {
55         unsigned char length;
56         unsigned char subunit;
57         unsigned char func;
58         unsigned short status;
59         char array[8];
60 } REQHDR;
61
62 typedef struct _IOCTL {
63         char bpb;
64         unsigned short buffer_realoff;
65         unsigned short buffer_realseg;
66         unsigned short xferlen;
67         unsigned short sector;
68         unsigned long id;
69 } IOCTL;
70
71
72 //      CD Information -----------------------------------------------------
73
74 static struct {
75         char code;                       // Control Block Code
76         char track;                      // Track Number
77         long start;                      // Start of Track in MSF (Redbook)
78         unsigned char info;              // Track Control Info
79 } CDTrackInfo = {11,2,0L,0};
80
81 static struct {
82         REQHDR reqheader;
83         IOCTL ioctl;
84 } TrackInfoREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
85   
86 static struct {
87         unsigned char code;
88         unsigned long devstat;
89 } DevStat = {6,0};
90
91 static struct {
92         REQHDR  reqhdr;
93         IOCTL   ioctl;
94 } DevStatREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
95
96 static struct {
97         unsigned char code;
98         char byte;
99 } MedChg        = {9,0};
100
101 static struct {
102         REQHDR  reqhdr;
103         IOCTL   ioctl;
104 } MedChgREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
105
106 static struct {
107         REQHDR  reqhdr;
108 } StopREQ = {{13,0,133,0,""}};
109
110 static struct {
111         REQHDR  reqhdr;
112 } ResumeREQ = {{13,0,136,0,""}};
113
114 static struct {
115         unsigned char code;
116         unsigned char first;
117         unsigned char last;
118         unsigned long lead_out;
119 } CDInfo = {10,0,0,0L};
120
121 static struct {
122         REQHDR  reqhdr;
123         IOCTL   ioctl;
124 } CDInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
125
126 static struct {
127         REQHDR  reqheader;
128         char    mode;                                   // Play in HSG or MSF(RedBook)
129         unsigned long playbeg;
130         unsigned long playlen;                // HSG Format
131         char            empty;                                          // Empty byte used so that all requests have same size
132 } PlayREQ = {{13,0,132,0,""},0,0,0,0};
133
134 static struct {
135         unsigned char code;
136         unsigned char out0in;
137         unsigned char out0vol;
138         unsigned char out1in;
139         unsigned char out1vol;
140         unsigned char out2in;
141         unsigned char out2vol;
142         unsigned char out3in;
143         unsigned char out3vol;
144 } AUDChannelCtl = {3,0,0,0,0,0,0,0,0};
145
146 static struct {
147         REQHDR  reqhdr;
148         IOCTL           ioctl;
149 } AUDChannelCtlREQ = {{13,0,12,0,""},{0,0,0,0,0}};
150
151 static struct {
152         unsigned char code;
153         unsigned char out0in;
154         unsigned char out0vol;
155         unsigned char out1in;
156         unsigned char out1vol;
157         unsigned char out2in;
158         unsigned char out2vol;
159         unsigned char out3in;
160         unsigned char out3vol;
161 } AUDChannelInfo = {4,0,0,0,0,0,0,0,0};
162
163 static struct {
164         REQHDR  reqhdr;
165         IOCTL           ioctl;
166 } AUDChannelInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
167
168 static struct {
169         unsigned char code;
170         unsigned char mode;
171         unsigned long headloc;
172 } HeadInfo = {1,MSF_CD_PLAYBACK,0L};
173
174 static struct {
175         REQHDR  reqhdr;
176         IOCTL           ioctl;
177 } HeadInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
178
179 //@@static struct {
180 //@@    ubyte code;
181 //@@    ubyte adr;
182 //@@    ubyte   tno;
183 //@@    ubyte   point;
184 //@@    ubyte   min;
185 //@@    ubyte   sec;
186 //@@    ubyte   frame;
187 //@@    ubyte   zero;
188 //@@    ubyte   pmin;
189 //@@    ubyte   psec;
190 //@@    ubyte   pframe;
191 //@@} QChannelInfo = {12, MSF_CD_PLAYBACK, 2,0,0,0,0,0,0,0,0    };
192 //@@
193 //@@static struct {
194 //@@    REQHDR  reqhdr;
195 //@@    IOCTL           ioctl;
196 //@@} QChannelInfoREQ = {{13, 0, 3, 0, ""}, {0,0,0,0,0}};
197
198
199 //      Translation from RealMode->Protected mode buffer
200
201 typedef struct _REQ_xlatbuf {                   // Buffer for Real Mode Callback
202         REQHDR reqheader;
203         char ioctl[10];
204 } REQ_xlatbuf;
205
206
207
208 //      Other Data ---------------------------------------------------------
209
210 static unsigned char CDDriveLetter;
211 static unsigned long CDNumTracks,CDTrackStart[101];
212 static int RedBookEnabled = 0;
213 static int RedBookPlaybackType = MSF_CD_PLAYBACK;
214 static fix Playback_Start_Time = 0;
215 static fix Playback_Pause_Time = 0;
216 static fix Playback_Length = 0;
217 static int Playback_first_track,Playback_last_track;
218                                                          
219
220 //      Prototypes ---------------------------------------------------------
221
222 int RBSendRequest(char *request, char *xferptr, int xferlen);   
223 unsigned long msf_to_lsn(unsigned long msf);
224 unsigned long lsn_to_msf(unsigned long lsn);
225
226
227 //      --------------------------------------------------------------------
228
229 void RBAInit(ubyte cd_drive_num)        //drive a == 0, drive b == 1
230 {
231         union REGS regs;
232
233 //      Detect presence of CD-ROM
234         regs.x.eax = 0x1500;
235         regs.x.ebx = 0;
236         int386(0x2f, &regs, &regs);
237         
238         if (regs.x.ebx == 0) 
239                 RBADisable();                                           // Disable RedBook
240         //      Error("RBA Error: MSCDEX.EXE compatible driver couldn't be found.");
241         else {
242                 CDDriveLetter = cd_drive_num;
243 //              CDDriveLetter = (unsigned char)regs.x.ecx;
244
245                 RBAEnable();
246                 RBACheckMediaChange();
247
248                 RBSendRequest((char*)&DevStatREQ, (char*)&DevStat, sizeof(DevStat));
249                 
250         // If Door drive open, or no disc in CD-ROM, then Redbook is Disabled.
251                 if ((DevStat.devstat&2048) || (DevStat.devstat&1) || RBAEnabled() == 0) 
252                         RBADisable();
253
254 //@@            if (DevStat.devstat&4) 
255 //@@                    mprintf((0, "supports cooked and raw reading.\n"));
256 //@@            else 
257 //@@                    mprintf((0, "supports only cooked reading.\n"));
258 //@@
259 //@@            if (DevStat.devstat&256) 
260 //@@                    mprintf((0, "audio channel manipulation.\n"));
261 //@@            else 
262 //@@                    mprintf((0, "no audio channel manipulation.\n"));
263         
264                 if (DevStat.devstat&512) {
265 //                      RedBookPlaybackType = MSF_CD_PLAYBACK;
266                         mprintf((0, "supports HSG and RedBook addressing mode.\n"));
267                 }
268                 else {
269 //                      RedBookPlaybackType = HSG_CD_PLAYBACK;
270                         mprintf((0, "supports HSG addressing only.\n"));
271                 }
272                         
273                 RedBookPlaybackType = MSF_CD_PLAYBACK;
274
275         }
276 }
277
278 //find out how many tracks on the CD, and their starting locations
279 void RBARegisterCD(void)
280 {
281         int i;
282         int error;
283
284         //      Get CD Information First
285         RBACheckMediaChange();
286
287         error = RBSendRequest((char*)&CDInfoREQ, (char*)&CDInfo, sizeof(CDInfo));
288
289         if (error & RBSTAT_ERROR) {             //error!
290                 CDNumTracks = 0;
291                 return;
292         }
293         
294         CDNumTracks = (CDInfo.last-CDInfo.first)+1;
295
296         // Define Track Starts
297         for (i=CDInfo.first; i<=CDNumTracks; i++)
298         {
299                 CDTrackInfo.track = i;
300                 RBSendRequest((char *)&TrackInfoREQ, (char*)&CDTrackInfo, sizeof(CDTrackInfo));
301                 CDTrackStart[i] = CDTrackInfo.start;
302         }
303
304         CDTrackStart[i] = CDInfo.lead_out;
305
306
307
308 long RBAGetDeviceStatus()
309 {
310         RBSendRequest((char*)&DevStatREQ, (char*)&DevStat, sizeof(DevStat));
311         return (long)DevStat.devstat;
312 }
313
314
315 int RBAGetNumberOfTracks(void)
316 {
317         //      Get CD Information
318         if (RBACheckMediaChange())
319                 RBARegisterCD(); 
320
321         return CDNumTracks;
322 }
323
324 //returns the length in seconds of tracks first through last, inclusive
325 int get_track_time(int first,int last)
326 {
327         unsigned long playlen;
328         int min, sec;
329
330         playlen = msf_to_lsn(CDTrackStart[last+1]) - msf_to_lsn(CDTrackStart[first]);
331         playlen = lsn_to_msf(playlen);
332         min   = (int)((playlen >> 16) & 0xFF);
333         sec   = (int)((playlen >>  8) & 0xFF);
334
335         return (min*60+sec);
336 }
337
338 int RBAPlayTrack(int track)
339 {
340         Playback_Start_Time = 0;
341         Playback_Length = 0;
342
343         if (track < CDInfo.first || track > CDInfo.last) {
344                 mprintf((0,"CD not Redbook Compatible. Will not play track.\n"));
345         }
346 //              Error("RBA Error: Track %d doesn't exist on CD!!!", track);
347         
348         if (RBACheckMediaChange()) {
349                 mprintf((0, "Unable to play track due to CD change.\n"));
350                 return 0;
351         }
352
353         //      Play the track now!!!
354         PlayREQ.mode = RedBookPlaybackType;
355         PlayREQ.playbeg = CDTrackStart[track];
356
357         if (RedBookPlaybackType == MSF_CD_PLAYBACK) {
358                 PlayREQ.playlen = 
359                         msf_to_lsn(CDTrackStart[track+1]) - msf_to_lsn(CDTrackStart[track]);
360         }
361         else {
362                 mprintf((1, "RBERR: No! No! No! This shouldn't happen (HSG_CD_PLAYBACK).\n"));
363                 Int3();
364                 PlayREQ.playlen = CDTrackStart[track+1] - CDTrackStart[track];
365         }
366         RBSendRequest((char *)&PlayREQ,NULL,0);
367
368         Playback_Length = i2f(get_track_time(track,track));
369         Playback_Start_Time = timer_get_fixed_seconds();
370
371         Playback_first_track = Playback_last_track = track;
372
373         mprintf( (0,"Playing Track %d (len: %d secs)\n", track, f2i(Playback_Length)) );
374
375         return 1;
376 }
377
378 //plays tracks first through last, inclusive
379 int RBAPlayTracks(int first, int last)
380 {
381         Playback_Start_Time = 0;
382         Playback_Length = 0;
383
384         if (first < CDInfo.first || last > CDInfo.last) {
385                 mprintf((0,"Invalid start or end track.\n"));
386         }
387 //              Error("RBA Error: Track %d doesn't exist on CD!!!", track);
388         
389         if (RBACheckMediaChange()) {
390                 mprintf((0, "Unable to play track due to CD change.\n"));
391                 return 0;
392         }
393
394         //      Play the track now!!!
395         PlayREQ.mode = RedBookPlaybackType;
396         PlayREQ.playbeg = CDTrackStart[first];
397
398         if (RedBookPlaybackType == MSF_CD_PLAYBACK) {
399                 PlayREQ.playlen = 
400                         msf_to_lsn(CDTrackStart[last+1]) - msf_to_lsn(CDTrackStart[first]);
401         }
402         else {
403                 mprintf((1, "RBERR: No! No! No! This shouldn't happen (HSG_CD_PLAYBACK).\n"));
404                 Int3();
405                 PlayREQ.playlen = CDTrackStart[last+1] - CDTrackStart[first];
406         }
407         RBSendRequest((char *)&PlayREQ,NULL,0);
408
409         Playback_Length = i2f(get_track_time(first,last));
410         Playback_Start_Time = timer_get_fixed_seconds();
411
412         Playback_first_track = first;
413         Playback_last_track = last;
414
415         mprintf( (0,"Playing Tracks %d-%d (len: %d secs)\n", first,last,f2i(Playback_Length)) );
416
417         return 1;
418 }
419
420
421 void RBAPause()
422 {
423         RBSendRequest((char *)&StopREQ, NULL, 0);
424         Playback_Pause_Time = timer_get_fixed_seconds();
425 }
426
427
428 int RBAResume()
429 {
430         if (RBACheckMediaChange()) {
431                 RBARegisterCD();
432                 return RBA_MEDIA_CHANGED;
433         }
434         RBSendRequest((char *)&ResumeREQ, NULL, 0);
435
436         Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time;
437
438         return 1;
439 }
440
441
442 void RBAStop()
443 {
444         if (RBACheckMediaChange())
445                 RBARegisterCD();
446
447         RBSendRequest((char *)&StopREQ,NULL,0);      
448 }
449
450
451 void RBASetStereoAudio(RBACHANNELCTL *channels)
452 {
453         AUDChannelCtl.out0in = channels->out0in;
454         AUDChannelCtl.out0vol = channels->out0vol;
455         AUDChannelCtl.out1in = channels->out1in;
456         AUDChannelCtl.out1vol = channels->out1vol;
457         AUDChannelCtl.out2in = AUDChannelCtl.out2vol = 0;
458         AUDChannelCtl.out3in = AUDChannelCtl.out3vol = 0;
459         
460         RBSendRequest((char*)&AUDChannelCtlREQ, (char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
461 }
462
463
464 void RBASetQuadAudio(RBACHANNELCTL *channels)
465 {
466         AUDChannelCtl.out0in = (unsigned char)channels->out0in;
467         AUDChannelCtl.out0vol = (unsigned char)channels->out0vol;
468         AUDChannelCtl.out1in = (unsigned char)channels->out1in;
469         AUDChannelCtl.out1vol = (unsigned char)channels->out1vol;
470         AUDChannelCtl.out2in = (unsigned char)channels->out2in;
471         AUDChannelCtl.out2vol = (unsigned char)channels->out2vol;
472         AUDChannelCtl.out3in = (unsigned char)channels->out3in;
473         AUDChannelCtl.out3vol = (unsigned char)channels->out3vol;
474         
475         RBSendRequest((char*)&AUDChannelCtlREQ, (char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
476 }
477
478
479 void RBAGetAudioInfo(RBACHANNELCTL *channels)
480 {
481         RBSendRequest((char*)&AUDChannelInfoREQ, (char*)&AUDChannelInfo, sizeof(AUDChannelInfo));
482         
483         channels->out0in = (int)AUDChannelInfo.out0in;
484         channels->out0vol = (int)AUDChannelInfo.out0vol;
485         channels->out1in = (int)AUDChannelInfo.out1in;
486         channels->out1vol = (int)AUDChannelInfo.out1vol;
487         channels->out2in = (int)AUDChannelInfo.out2in;
488         channels->out2vol = (int)AUDChannelInfo.out2vol;
489         channels->out3in = (int)AUDChannelInfo.out3in;
490         channels->out3vol = (int)AUDChannelInfo.out3vol;
491
492 }
493
494
495 void RBASetChannelVolume(int channel, int volume) 
496 {
497         //RBACHANNELCTL channels;
498         
499         RBSendRequest((char*)&AUDChannelInfoREQ, (char*)&AUDChannelInfo, sizeof(AUDChannelInfo));
500
501         AUDChannelCtl.out0in = AUDChannelInfo.out0in;
502         AUDChannelCtl.out0vol = AUDChannelInfo.out0vol;
503         AUDChannelCtl.out1in = AUDChannelInfo.out1in;
504         AUDChannelCtl.out1vol = AUDChannelInfo.out1vol;
505         AUDChannelCtl.out2in = AUDChannelInfo.out2in;
506         AUDChannelCtl.out2vol = AUDChannelInfo.out2vol;
507         AUDChannelCtl.out3in = AUDChannelInfo.out3in;
508         AUDChannelCtl.out3vol = AUDChannelInfo.out3vol;
509
510         if (channel == 0) AUDChannelCtl.out0vol = (unsigned char)volume;
511         if (channel == 1) AUDChannelCtl.out1vol = (unsigned char)volume;
512         if (channel == 2) AUDChannelCtl.out2vol = (unsigned char)volume;
513         if (channel == 3) AUDChannelCtl.out3vol = (unsigned char)volume;
514         
515         RBSendRequest((char*)&AUDChannelCtlREQ,(char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
516
517 }
518
519
520 void RBASetVolume(int volume)
521 {
522         RBASetChannelVolume(0,volume);
523         RBASetChannelVolume(1,volume);
524 }
525
526
527 long RBAGetHeadLoc(int *min, int *sec, int *frame)
528 {
529         unsigned long loc;
530
531         if (RBACheckMediaChange())
532                 return RBA_MEDIA_CHANGED;
533
534         HeadInfo.mode = RedBookPlaybackType;
535         RBSendRequest((char*)&HeadInfoREQ, (char*)&HeadInfo, sizeof(HeadInfo));
536         
537         if (RedBookPlaybackType == MSF_CD_PLAYBACK)
538                 loc = HeadInfo.headloc;
539         else 
540                 loc = lsn_to_msf(HeadInfo.headloc);
541
542         *min   = (int)((loc >> 16) & 0xFF);
543         *sec   = (int)((loc >>  8) & 0xFF);
544         *frame = (int)((loc >>  0) & 0xFF);
545
546         return loc;
547 }               
548
549 //return the track number currently playing.  Useful if RBAPlayTracks() 
550 //is called.  Returns 0 if no track playing, else track number
551 int RBAGetTrackNum()
552 {
553         int track;
554         int delta_time; //in seconds
555
556         if (!RBAPeekPlayStatus())
557                 return 0;
558
559         delta_time = f2i(timer_get_fixed_seconds()-Playback_Start_Time+f1_0/2);
560
561         for (track=Playback_first_track;track<=Playback_last_track && delta_time>0;track++) {
562
563                 delta_time -= get_track_time(track,track);
564
565                 if (delta_time < 0)
566                         break;
567         }
568
569         Assert(track <= Playback_last_track);
570
571         return track;
572 }
573
574
575 int RBAPeekPlayStatus()
576 {
577         if (RBACheckMediaChange()) {            //if media changed, then not playing
578                 RBARegisterCD(); 
579                 return 0;
580         }
581
582         if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0;
583         else return 1; 
584 }
585         
586
587 int RBAEnabled()
588 {
589         if (RedBookEnabled < 1) return 0;
590         else return 1;
591 }
592
593
594 void RBAEnable()
595 {
596         RedBookEnabled = 1;
597         mprintf((0, "Enabling Redbook...\n"));
598 }
599
600
601 void RBADisable()
602 {
603         RedBookEnabled = 0;
604         mprintf((0, "Disabling Redbook...\n"));
605 }
606
607
608
609 //      RB functions:  Internal to RBA library  -----------------------------
610
611 //returns 1 if media has changed, else 0
612 int RBACheckMediaChange()
613 {
614         RBSendRequest((char *)&MedChgREQ, (char*)&MedChg, sizeof(MedChg));
615
616         if (MedChg.byte == 255 || MedChg.byte == 0) {
617                 // New media in drive (or maybe no media in drive)
618                 mprintf((0,"CD-ROM Media change detected\n"));
619                 return 1;
620         }
621         return 0;
622 }
623
624
625 //      Send Request
626
627 //returns 16-bit value.  Bit 15 means error.
628 //WE NEED SYMBOLOIC CONSTANTS FOR ALL RETURN CODES & STATUS BITS
629 int RBSendRequest(char *request, char *xferptr, int xferlen)
630 {
631         //union REGS regs;
632         dpmi_real_regs rregs;
633
634         IOCTL *ioctl;
635         REQ_xlatbuf *xlat_req;                          // Translated Buffer Request 
636         char *xlat_xferptr;                                     // Translated Buffer Transfer Buffer Ptr
637         
638         unsigned short status;
639         
640         if (!RedBookEnabled) return 0;  // Don't send request if no RBA
641
642         memset(&rregs,0,sizeof(dpmi_real_regs));
643
644 // Get Temporary Real Mode Buffer for request translation
645         xlat_req = (REQ_xlatbuf *)dpmi_get_temp_low_buffer(128+xferlen);
646         memcpy(xlat_req, request, sizeof(REQHDR)+sizeof(IOCTL));
647         ioctl = (IOCTL *)(((char*)xlat_req)+sizeof(REQHDR));
648
649 //      Set Transfer Buffer in IOCTL reqion of 'request'        
650         if (xferlen && xferptr) {
651                 xlat_xferptr = ((char*)xlat_req) + 128;
652                 memcpy(xlat_xferptr, xferptr, xferlen);
653                 
654                 ioctl->buffer_realoff = DPMI_real_offset(xlat_xferptr);
655                 ioctl->buffer_realseg = DPMI_real_segment(xlat_xferptr);
656                 ioctl->xferlen = xferlen;
657         }
658
659 //      Setup and Send Request Packet
660         rregs.eax = 0x1510;
661         rregs.ecx = (unsigned)(CDDriveLetter);
662         rregs.es  = DPMI_real_segment(xlat_req);
663         rregs.ebx = DPMI_real_offset(xlat_req);
664         dpmi_real_int386x(0x2f, &rregs);
665
666 //      Return Translate Buffer to Protected mode 'xferptr'
667         if (xferlen && xferptr) {
668                 memcpy(xferptr, xlat_xferptr, xferlen);
669         }
670         memcpy(request, xlat_req, sizeof(REQHDR)+sizeof(IOCTL));
671         
672 //      Check for Errors.
673         status = ((REQHDR *)request)->status;
674
675         if (status & RBSTAT_ERROR) {
676                 mprintf((0,"Error in SendRequest: %x.\n", status));
677         }
678
679         return status;
680
681                 
682
683 //      Converts Logical Sector Number to Minutes Seconds Frame Format
684
685 unsigned long lsn_to_msf(unsigned long lsn)
686 {
687    unsigned long min,sec,frame;
688    lsn += 150;
689
690         min   =  lsn / (60*75);
691    lsn   =  lsn % (60*75);
692         sec   =  lsn / 75;
693    lsn   =  lsn % 75;
694         frame =  lsn;
695
696         return((min << 16) + (sec << 8) + (frame << 0));
697 }
698
699 // convert minutes seconds frame format to a logical sector number.
700
701 unsigned long msf_to_lsn(unsigned long msf)
702 {
703         unsigned long min,sec,frame;
704
705         min   = (msf >> 16) & 0xFF;
706    sec   = (msf >>  8) & 0xFF;
707    frame = (msf >>  0) & 0xFF;
708
709         return(  (min * 60*75) + (sec * 75) + frame - 150  );
710 }
711