1 /* Mednafen - Multi-system Emulator
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "../mednafen.h"
19 #include "../mednafen-endian.h"
20 #include <math.h>
21 #include "pcecd_drive.h"
22 #include "../cdrom/cdromif.h"
23 #include "../cdrom/SimpleFIFO.h"
24 #include "../state_helpers.h"
25 
26 static uint32 CD_DATA_TRANSFER_RATE;
27 static uint32 System_Clock;
28 static void (*CDIRQCallback)(int);
29 static void (*CDStuffSubchannels)(uint8, int);
30 static Blip_Buffer *sbuf[2];
31 static CDIF *Cur_CDIF;
32 
33 // Internal operation to the SCSI CD unit.  Only pass 1 or 0 to these macros!
34 #define SetIOP(mask, set)	{ cd_bus.signals &= ~mask; if(set) cd_bus.signals |= mask; }
35 
36 #define SetBSY(set)		SetIOP(PCECD_Drive_BSY_mask, set)
37 #define SetIO(set)              SetIOP(PCECD_Drive_IO_mask, set)
38 #define SetCD(set)              SetIOP(PCECD_Drive_CD_mask, set)
39 #define SetMSG(set)             SetIOP(PCECD_Drive_MSG_mask, set)
40 
SetREQ(bool set)41 static INLINE void SetREQ(bool set)
42 {
43  if(set && !REQ_signal)
44   CDIRQCallback(PCECD_Drive_IRQ_MAGICAL_REQ);
45 
46  SetIOP(PCECD_Drive_REQ_mask, set);
47 }
48 
49 #define SetkingACK(set)		SetIOP(PCECD_Drive_kingACK_mask, set)
50 #define SetkingRST(set)         SetIOP(PCECD_Drive_kingRST_mask, set)
51 #define SetkingSEL(set)         SetIOP(PCECD_Drive_kingSEL_mask, set)
52 
53 
54 enum
55 {
56  QMode_Zero = 0,
57  QMode_Time = 1,
58  QMode_MCN = 2, // Media Catalog Number
59  QMode_ISRC = 3 // International Standard Recording Code
60 };
61 
62 typedef struct
63 {
64  bool last_RST_signal;
65 
66  // The pending message to send(in the message phase)
67  uint8 message_pending;
68 
69  bool status_sent, message_sent;
70 
71  // Pending error codes
72  uint8 key_pending, asc_pending, ascq_pending, fru_pending;
73 
74  uint8 command_buffer[256];
75  uint8 command_buffer_pos;
76  uint8 command_size_left;
77 
78  // FALSE if not all pending data is in the FIFO, TRUE if it is.
79  // Used for multiple sector CD reads.
80  bool data_transfer_done;
81 
82  bool TrayOpen;
83  bool DiscChanged;
84 
85  uint8 SubQBuf[4][0xC];		// One for each of the 4 most recent q-Modes.
86  uint8 SubQBuf_Last[0xC];	// The most recent q subchannel data, regardless of q-mode.
87 
88  uint8 SubPWBuf[96];
89 
90 } pcecd_drive_t;
91 
92 typedef Blip_Synth CDSynth;
93 
94 enum
95 {
96  CDDASTATUS_PAUSED = -1,
97  CDDASTATUS_STOPPED = 0,
98  CDDASTATUS_PLAYING = 1,
99 };
100 
101 enum
102 {
103  PLAYMODE_SILENT = 0x00,
104  PLAYMODE_NORMAL,
105  PLAYMODE_INTERRUPT,
106  PLAYMODE_LOOP,
107 };
108 
109 typedef struct
110 {
111  int32 CDDADivAcc;
112  uint32 scan_sec_end;
113 
114  uint8 PlayMode;
115  CDSynth CDDASynth;
116  int32 CDDAVolume;
117  int16 last_sample[2];
118  int16 CDDASectorBuffer[1176];
119  uint32 CDDAReadPos;
120 
121  int8 CDDAStatus;
122  uint8 ScanMode;
123  int32 CDDADiv;
124  int CDDATimeDiv;
125 } cdda_t;
126 
MakeSense(uint8 target[18],uint8 key,uint8 asc,uint8 ascq,uint8 fru)127 static INLINE void MakeSense(uint8 target[18], uint8 key, uint8 asc, uint8 ascq, uint8 fru)
128 {
129  memset(target, 0, 18);
130 
131  target[0] = 0x70;		// Current errors and sense data is not SCSI compliant
132  target[2] = key;
133  target[7] = 0x0A;
134  target[12] = asc;		// Additional Sense Code
135  target[13] = ascq;		// Additional Sense Code Qualifier
136  target[14] = fru;		// Field Replaceable Unit code
137 }
138 
139 static pcecd_drive_timestamp_t lastts;
140 static int64 monotonic_timestamp;
141 static int64 pce_lastsapsp_timestamp;
142 
143 static pcecd_drive_t cd;
144 pcecd_drive_bus_t cd_bus;
145 static cdda_t cdda;
146 
147 static SimpleFIFO din(2048);
148 
149 static TOC toc;
150 
151 static uint32 read_sec_start;
152 static uint32 read_sec;
153 static uint32 read_sec_end;
154 
155 static int32 CDReadTimer;
156 static uint32 SectorAddr;
157 static uint32 SectorCount;
158 
159 
160 enum
161 {
162  PHASE_BUS_FREE = 0,
163  PHASE_COMMAND,
164  PHASE_DATA_IN,
165  PHASE_STATUS,
166  PHASE_MESSAGE_IN,
167 };
168 
169 static unsigned int CurrentPhase;
170 static void ChangePhase(const unsigned int new_phase);
171 
VirtualReset(void)172 static void VirtualReset(void)
173 {
174  din.Flush();
175 
176  cdda.CDDADivAcc = (int64)System_Clock * 65536 / 44100;
177  CDReadTimer = 0;
178 
179  pce_lastsapsp_timestamp = monotonic_timestamp;
180 
181  SectorAddr = SectorCount = 0;
182  read_sec_start = read_sec = 0;
183  read_sec_end = ~0;
184 
185  cdda.PlayMode = PLAYMODE_SILENT;
186  cdda.CDDAReadPos = 0;
187  cdda.CDDAStatus = CDDASTATUS_STOPPED;
188  cdda.CDDADiv = 0;
189 
190  cdda.ScanMode = 0;
191  cdda.scan_sec_end = 0;
192 
193  ChangePhase(PHASE_BUS_FREE);
194 }
195 
PCECD_Drive_Power(pcecd_drive_timestamp_t system_timestamp)196 void PCECD_Drive_Power(pcecd_drive_timestamp_t system_timestamp)
197 {
198  memset(&cd, 0, sizeof(pcecd_drive_t));
199  memset(&cd_bus, 0, sizeof(pcecd_drive_bus_t));
200 
201  monotonic_timestamp = system_timestamp;
202 
203  cd.DiscChanged = false;
204 
205  if(Cur_CDIF && !cd.TrayOpen)
206   Cur_CDIF->ReadTOC(&toc);
207 
208  CurrentPhase = PHASE_BUS_FREE;
209 
210  VirtualReset();
211 }
212 
213 
PCECD_Drive_SetDB(uint8 data)214 void PCECD_Drive_SetDB(uint8 data)
215 {
216    cd_bus.DB = data;
217    //printf("Set DB: %02x\n", data);
218 }
219 
PCECD_Drive_SetACK(bool set)220 void PCECD_Drive_SetACK(bool set)
221 {
222    SetkingACK(set);
223    //printf("Set ACK: %d\n", set);
224 }
225 
PCECD_Drive_SetSEL(bool set)226 void PCECD_Drive_SetSEL(bool set)
227 {
228    SetkingSEL(set);
229    //printf("Set SEL: %d\n", set);
230 }
231 
PCECD_Drive_SetRST(bool set)232 void PCECD_Drive_SetRST(bool set)
233 {
234    SetkingRST(set);
235    //printf("Set RST: %d\n", set);
236 }
237 
GenSubQFromSubPW(void)238 static void GenSubQFromSubPW(void)
239 {
240    uint8 SubQBuf[0xC];
241 
242    subq_deinterleave(cd.SubPWBuf, SubQBuf);
243 
244    //printf("Real %d/ SubQ %d - ", read_sec, BCD_to_U8(SubQBuf[7]) * 75 * 60 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150);
245    // Debug code, remove me.
246    //for(int i = 0; i < 0xC; i++)
247    // printf("%02x ", SubQBuf[i]);
248    //printf("\n");
249 
250    if(subq_check_checksum(SubQBuf))
251    {
252       memcpy(cd.SubQBuf_Last, SubQBuf, 0xC);
253 
254       uint8 adr = SubQBuf[0] & 0xF;
255 
256       if(adr <= 0x3)
257          memcpy(cd.SubQBuf[adr], SubQBuf, 0xC);
258 
259       //if(adr == 0x02)
260       //for(int i = 0; i < 12; i++)
261       // printf("%02x\n", cd.SubQBuf[0x2][i]);
262    }
263 }
264 
265 
266 #define STATUS_GOOD		0
267 #define STATUS_CHECK_CONDITION	1
268 
269 #define SENSEKEY_NO_SENSE		0x0
270 #define SENSEKEY_NOT_READY		0x2
271 #define SENSEKEY_MEDIUM_ERROR		0x3
272 #define SENSEKEY_HARDWARE_ERROR		0x4
273 #define SENSEKEY_ILLEGAL_REQUEST	0x5
274 #define SENSEKEY_UNIT_ATTENTION		0x6
275 #define SENSEKEY_ABORTED_COMMAND	0xB
276 
277 #define ASC_MEDIUM_NOT_PRESENT		0x3A
278 
279 
280 // NEC sub-errors(ASC), no ASCQ.
281 #define NSE_NO_DISC			0x0B		// Used with SENSEKEY_NOT_READY	- This condition occurs when tray is closed with no disc present.
282 #define NSE_TRAY_OPEN			0x0D		// Used with SENSEKEY_NOT_READY
283 #define NSE_SEEK_ERROR			0x15
284 #define NSE_HEADER_READ_ERROR		0x16		// Used with SENSEKEY_MEDIUM_ERROR
285 #define NSE_NOT_AUDIO_TRACK		0x1C		// Used with SENSEKEY_MEDIUM_ERROR
286 #define NSE_NOT_DATA_TRACK		0x1D		// Used with SENSEKEY_MEDIUM_ERROR
287 #define NSE_INVALID_COMMAND		0x20
288 #define NSE_INVALID_ADDRESS		0x21
289 #define NSE_INVALID_PARAMETER		0x22
290 #define NSE_END_OF_VOLUME		0x25
291 #define NSE_INVALID_REQUEST_IN_CDB	0x27
292 #define NSE_DISC_CHANGED		0x28		// Used with SENSEKEY_UNIT_ATTENTION
293 #define NSE_AUDIO_NOT_PLAYING		0x2C
294 
295 // ASC, ASCQ pair
296 #define AP_UNRECOVERED_READ_ERROR	0x11, 0x00
297 #define AP_LEC_UNCORRECTABLE_ERROR	0x11, 0x05
298 #define AP_CIRC_UNRECOVERED_ERROR	0x11, 0x06
299 
300 #define AP_UNKNOWN_MEDIUM_FORMAT	0x30, 0x01
301 #define AP_INCOMPAT_MEDIUM_FORMAT	0x30, 0x02
302 
ChangePhase(const unsigned int new_phase)303 static void ChangePhase(const unsigned int new_phase)
304 {
305    //printf("New phase: %d %lld\n", new_phase, monotonic_timestamp);
306    switch(new_phase)
307    {
308       case PHASE_BUS_FREE:
309          SetBSY(false);
310          SetMSG(false);
311          SetCD(false);
312          SetIO(false);
313          SetREQ(false);
314 
315          CDIRQCallback(0x8000 | PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
316          break;
317 
318       case PHASE_DATA_IN:		// Us to them
319          SetBSY(true);
320          SetMSG(false);
321          SetCD(false);
322          SetIO(true);
323          //SetREQ(true);
324          SetREQ(false);
325          break;
326 
327       case PHASE_STATUS:		// Us to them
328          SetBSY(true);
329          SetMSG(false);
330          SetCD(true);
331          SetIO(true);
332          SetREQ(true);
333          break;
334 
335       case PHASE_MESSAGE_IN:	// Us to them
336          SetBSY(true);
337          SetMSG(true);
338          SetCD(true);
339          SetIO(true);
340          SetREQ(true);
341          break;
342 
343       case PHASE_COMMAND:		// Them to us
344          SetBSY(true);
345          SetMSG(false);
346          SetCD(true);
347          SetIO(false);
348          SetREQ(true);
349          break;
350    }
351    CurrentPhase = new_phase;
352 }
353 
SendStatusAndMessage(uint8 status,uint8 message)354 static void SendStatusAndMessage(uint8 status, uint8 message)
355 {
356    // This should never ever happen, but that doesn't mean it won't. ;)
357    if(din.in_count)
358    {
359       /* BUG: x bytes still in SCSI CD FIFO */
360       din.Flush();
361    }
362 
363    cd.message_pending = message;
364 
365    cd.status_sent = FALSE;
366    cd.message_sent = FALSE;
367 
368 
369    if(status == STATUS_GOOD)
370       cd_bus.DB = 0x00;
371    else
372       cd_bus.DB = 0x01;
373 
374 
375    ChangePhase(PHASE_STATUS);
376 }
377 
DoSimpleDataIn(const uint8 * data_in,uint32 len)378 static void DoSimpleDataIn(const uint8 *data_in, uint32 len)
379 {
380  din.Write(data_in, len);
381 
382  cd.data_transfer_done = true;
383 
384  ChangePhase(PHASE_DATA_IN);
385 }
386 
PCECD_Drive_SetDisc(bool tray_open,CDIF * cdif,bool no_emu_side_effects)387 void PCECD_Drive_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects)
388 {
389  Cur_CDIF = cdif;
390 
391  // Closing the tray.
392  if(cd.TrayOpen && !tray_open)
393  {
394   cd.TrayOpen = false;
395 
396   if(cdif)
397   {
398    cdif->ReadTOC(&toc);
399 
400    if(!no_emu_side_effects)
401    {
402     memset(cd.SubQBuf, 0, sizeof(cd.SubQBuf));
403     memset(cd.SubQBuf_Last, 0, sizeof(cd.SubQBuf_Last));
404     cd.DiscChanged = true;
405    }
406   }
407  }
408  else if(!cd.TrayOpen && tray_open)	// Opening the tray
409  {
410   cd.TrayOpen = true;
411  }
412 }
413 
CommandCCError(int key,int asc=0,int ascq=0)414 static void CommandCCError(int key, int asc = 0, int ascq = 0)
415 {
416    /* CC error */
417 
418    cd.key_pending = key;
419    cd.asc_pending = asc;
420    cd.ascq_pending = ascq;
421    cd.fru_pending = 0x00;
422 
423    SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
424 }
425 
ValidateRawDataSector(uint8 * data,const uint32 lba)426 static bool ValidateRawDataSector(uint8 *data, const uint32 lba)
427 {
428  if(!edc_lec_check_and_correct(data, false))
429  {
430     MDFN_DispMessage("Uncorrectable data at sector %u", lba);
431 
432     din.Flush();
433     cd.data_transfer_done = false;
434 
435     CommandCCError(SENSEKEY_MEDIUM_ERROR, AP_LEC_UNCORRECTABLE_ERROR);
436     return(false);
437  }
438 
439  return(true);
440 }
441 
DoREADBase(uint32 sa,uint32 sc)442 static void DoREADBase(uint32 sa, uint32 sc)
443 {
444  if(sa > toc.tracks[100].lba) // Another one of those off-by-one PC-FX CD bugs.
445  {
446   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
447   return;
448  }
449 
450  // Case for READ(10) and READ(12) where sc == 0, and sa == toc.tracks[100].lba
451  if(!sc && sa == toc.tracks[100].lba)
452  {
453   CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_HEADER_READ_ERROR);
454   return;
455  }
456 
457  SectorAddr = sa;
458  SectorCount = sc;
459  if(SectorCount)
460  {
461   Cur_CDIF->HintReadSector(sa);	//, sa + sc);
462 
463   CDReadTimer = (uint64)3 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
464  }
465  else
466  {
467   CDReadTimer = 0;
468   SendStatusAndMessage(STATUS_GOOD, 0x00);
469  }
470  cdda.CDDAStatus = CDDASTATUS_STOPPED;
471 }
472 
473 //
474 //
475 //
DoTESTUNITREADY(const uint8 * cdb)476 static void DoTESTUNITREADY(const uint8 *cdb)
477 {
478  SendStatusAndMessage(STATUS_GOOD, 0x00);
479 }
480 
DoREQUESTSENSE(const uint8 * cdb)481 static void DoREQUESTSENSE(const uint8 *cdb)
482 {
483  uint8 data_in[18];
484 
485  MakeSense(data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending);
486 
487  DoSimpleDataIn(data_in, 18);
488 
489  cd.key_pending = 0;
490  cd.asc_pending = 0;
491  cd.ascq_pending = 0;
492  cd.fru_pending = 0;
493 }
494 
495 /********************************************************
496 *							*
497 *	SCSI      Command 0x08 - READ(6)		*
498 *							*
499 ********************************************************/
DoREAD6(const uint8 * cdb)500 static void DoREAD6(const uint8 *cdb)
501 {
502    uint32 sa = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | (cdb[3] << 0);
503    uint32 sc = cdb[4];
504 
505    // TODO: confirm real PCE does this(PC-FX does at least).
506    if(!sc)
507    {
508       /* READ(6) with count == 0 */
509       sc = 256;
510    }
511 
512    DoREADBase(sa, sc);
513 }
514 
515 /********************************************************
516 *							*
517 *	PC Engine CD Command 0xD8 - SAPSP		*
518 *							*
519 ********************************************************/
DoNEC_PCE_SAPSP(const uint8 * cdb)520 static void DoNEC_PCE_SAPSP(const uint8 *cdb)
521 {
522  uint32 new_read_sec_start;
523 
524  //printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
525  switch (cdb[9] & 0xc0)
526  {
527   default:
528      /* Unknown SAPSP 9 */
529   case 0x00:
530    new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
531    break;
532 
533   case 0x40:
534    new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
535    break;
536 
537   case 0x80:
538    {
539     int track = BCD_to_U8(cdb[2]);
540 
541     if(!track)
542      track = 1;
543     else if(track >= toc.last_track + 1)
544      track = 100;
545     new_read_sec_start = toc.tracks[track].lba;
546    }
547    break;
548  }
549 
550  //printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock);
551  if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190)
552  {
553   pce_lastsapsp_timestamp = monotonic_timestamp;
554 
555   SendStatusAndMessage(STATUS_GOOD, 0x00);
556   CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
557   return;
558  }
559 
560  pce_lastsapsp_timestamp = monotonic_timestamp;
561 
562  read_sec = read_sec_start = new_read_sec_start;
563  read_sec_end = toc.tracks[100].lba;
564 
565 
566  cdda.CDDAReadPos = 588;
567 
568  cdda.CDDAStatus = CDDASTATUS_PAUSED;
569  cdda.PlayMode = PLAYMODE_SILENT;
570 
571  if(cdb[1])
572  {
573   cdda.PlayMode = PLAYMODE_NORMAL;
574   cdda.CDDAStatus = CDDASTATUS_PLAYING;
575  }
576 
577  if(read_sec < toc.tracks[100].lba)
578   Cur_CDIF->HintReadSector(read_sec);
579 
580  SendStatusAndMessage(STATUS_GOOD, 0x00);
581  CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
582 }
583 
584 
585 
586 /********************************************************
587 *							*
588 *	PC Engine CD Command 0xD9 - SAPEP		*
589 *							*
590 ********************************************************/
DoNEC_PCE_SAPEP(const uint8 * cdb)591 static void DoNEC_PCE_SAPEP(const uint8 *cdb)
592 {
593    uint32 new_read_sec_end;
594 
595    //printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
596 
597    switch (cdb[9] & 0xc0)
598    {
599       default:
600          /* Unknown SAPEP 9 */
601       case 0x00:
602          new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
603          break;
604 
605       case 0x40:
606          new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2]));
607          new_read_sec_end -= 150;
608          break;
609 
610       case 0x80:
611          {
612             int track = BCD_to_U8(cdb[2]);
613 
614             if(!track)
615                track = 1;
616             else if(track >= toc.last_track + 1)
617                track = 100;
618             new_read_sec_end = toc.tracks[track].lba;
619          }
620          break;
621    }
622 
623    read_sec_end = new_read_sec_end;
624 
625    switch(cdb[1])	// PCE CD(TODO: Confirm these, and check the mode mask):
626    {
627       default:
628       case 0x03: cdda.PlayMode = PLAYMODE_NORMAL;
629                  cdda.CDDAStatus = CDDASTATUS_PLAYING;
630                  break;
631 
632       case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT;
633                  cdda.CDDAStatus = CDDASTATUS_PLAYING;
634                  break;
635 
636       case 0x01: cdda.PlayMode = PLAYMODE_LOOP;
637                  cdda.CDDAStatus = CDDASTATUS_PLAYING;
638                  break;
639 
640       case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
641                  cdda.CDDAStatus = CDDASTATUS_STOPPED;
642                  break;
643    }
644 
645    SendStatusAndMessage(STATUS_GOOD, 0x00);
646 }
647 
648 
649 
650 /********************************************************
651 *							*
652 *	PC Engine CD Command 0xDA - Pause		*
653 *							*
654 ********************************************************/
DoNEC_PCE_PAUSE(const uint8 * cdb)655 static void DoNEC_PCE_PAUSE(const uint8 *cdb)
656 {
657  if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
658  {
659   cdda.CDDAStatus = CDDASTATUS_PAUSED;
660   SendStatusAndMessage(STATUS_GOOD, 0x00);
661  }
662  else // Definitely give an error if it tries to pause when no track is playing!
663  {
664   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
665  }
666 }
667 
668 
669 
670 /********************************************************
671 *							*
672 *	PC Engine CD Command 0xDD - Read Subchannel Q	*
673 *							*
674 ********************************************************/
DoNEC_PCE_READSUBQ(const uint8 * cdb)675 static void DoNEC_PCE_READSUBQ(const uint8 *cdb)
676 {
677  uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
678  uint8 data_in[8192];
679 
680  memset(data_in, 0x00, 10);
681 
682  data_in[2] = SubQBuf[1];     // Track
683  data_in[3] = SubQBuf[2];     // Index
684  data_in[4] = SubQBuf[3];     // M(rel)
685  data_in[5] = SubQBuf[4];     // S(rel)
686  data_in[6] = SubQBuf[5];     // F(rel)
687  data_in[7] = SubQBuf[7];     // M(abs)
688  data_in[8] = SubQBuf[8];     // S(abs)
689  data_in[9] = SubQBuf[9];     // F(abs)
690 
691  if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
692   data_in[0] = 2;		// Pause
693  else if(cdda.CDDAStatus == CDDASTATUS_PLAYING)
694   data_in[0] = 0;		// Playing
695  else
696   data_in[0] = 3;		// Stopped
697 
698  DoSimpleDataIn(data_in, 10);
699 }
700 
701 
702 
703 /********************************************************
704 *							*
705 *	PC Engine CD Command 0xDE - Get Directory Info	*
706 *							*
707 ********************************************************/
DoNEC_PCE_GETDIRINFO(const uint8 * cdb)708 static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb)
709 {
710    // Problems:
711    //	Returned data lengths on real PCE are not confirmed.
712    //	Mode 0x03 behavior not tested on real PCE
713 
714    uint8 data_in[2048];
715    uint32 data_in_size = 0;
716 
717    memset(data_in, 0, sizeof(data_in));
718 
719    switch(cdb[1])
720    {
721       default:
722          MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]);
723       case 0x0:
724          data_in[0] = U8_to_BCD(toc.first_track);
725          data_in[1] = U8_to_BCD(toc.last_track);
726 
727          data_in_size = 2;
728          break;
729 
730       case 0x1:
731          {
732             uint8 m, s, f;
733 
734             LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
735 
736             data_in[0] = U8_to_BCD(m);
737             data_in[1] = U8_to_BCD(s);
738             data_in[2] = U8_to_BCD(f);
739 
740             data_in_size = 3;
741          }
742          break;
743 
744       case 0x2:
745          {
746             uint8 m, s, f;
747             int track = BCD_to_U8(cdb[2]);
748 
749             if(!track)
750                track = 1;
751             else if(cdb[2] == 0xAA)
752             {
753                track = 100;
754             }
755             else if(track > 99)
756             {
757                CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
758                return;
759             }
760 
761             LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
762 
763             data_in[0] = U8_to_BCD(m);
764             data_in[1] = U8_to_BCD(s);
765             data_in[2] = U8_to_BCD(f);
766             data_in[3] = toc.tracks[track].control;
767             data_in_size = 4;
768          }
769          break;
770    }
771 
772    DoSimpleDataIn(data_in, data_in_size);
773 }
774 
775 #define SCF_REQUIRES_MEDIUM	0x01
776 
777 typedef struct
778 {
779  uint8 cmd;
780  uint8 flags;
781  void (*func)(const uint8 *cdb);
782  const char *pretty_name;
783  const char *format_string;
784 } SCSICH;
785 
786 static const uint8 RequiredCDBLen[16] =
787 {
788  6, // 0x0n
789  6, // 0x1n
790  10, // 0x2n
791  10, // 0x3n
792  10, // 0x4n
793  10, // 0x5n
794  10, // 0x6n
795  10, // 0x7n
796  10, // 0x8n
797  10, // 0x9n
798  12, // 0xAn
799  12, // 0xBn
800  10, // 0xCn
801  10, // 0xDn
802  10, // 0xEn
803  10, // 0xFn
804 };
805 
806 static SCSICH PCECommandDefs[] =
807 {
808  { 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" },
809  { 0x03, 0, DoREQUESTSENSE, "Request Sense" },
810  { 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" },
811 
812  { 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPSP, "Set Audio Playback Start Position" },
813  { 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPEP, "Set Audio Playback End Position" },
814  { 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PCE_PAUSE, "Pause" },
815  { 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_PCE_READSUBQ, "Read Subchannel Q" },
816  { 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_PCE_GETDIRINFO, "Get Dir Info" },
817 
818  { 0xFF, 0, 0, NULL, NULL },
819 };
820 
PCECD_Drive_ResetTS(void)821 void PCECD_Drive_ResetTS(void)
822 {
823  lastts = 0;
824 }
825 
PCECD_Drive_GetCDDAValues(int16 * left,int16 * right)826 void PCECD_Drive_GetCDDAValues(int16 *left, int16 *right)
827 {
828  if(cdda.CDDAStatus)
829  {
830   *left = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2];
831   *right = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 1];
832  }
833  else
834   *left = *right = 0;
835 }
836 
RunCDDA(uint32 system_timestamp,int32 run_time)837 static INLINE void RunCDDA(uint32 system_timestamp, int32 run_time)
838 {
839  if(cdda.CDDAStatus == CDDASTATUS_PLAYING)
840  {
841   int32 sample[2];
842 
843   cdda.CDDADiv -= run_time << 16;
844 
845   while(cdda.CDDADiv <= 0)
846   {
847    const uint32 synthtime = ((system_timestamp + (cdda.CDDADiv >> 16))) / cdda.CDDATimeDiv;
848 
849    cdda.CDDADiv += cdda.CDDADivAcc;
850 
851    //MDFN_DispMessage("%d %d %d\n", read_sec_start, read_sec, read_sec_end);
852 
853    if(cdda.CDDAReadPos == 588)
854    {
855     if(read_sec >= read_sec_end)
856     {
857      switch(cdda.PlayMode)
858      {
859       case PLAYMODE_SILENT:
860       case PLAYMODE_NORMAL:
861        cdda.CDDAStatus = CDDASTATUS_STOPPED;
862        break;
863 
864       case PLAYMODE_INTERRUPT:
865        cdda.CDDAStatus = CDDASTATUS_STOPPED;
866        CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
867        break;
868 
869       case PLAYMODE_LOOP:
870        read_sec = read_sec_start;
871        break;
872      }
873 
874      // If CDDA playback is stopped, break out of our while(CDDADiv ...) loop and don't play any more sound!
875      if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
876       break;
877     }
878 
879     // Don't play past the user area of the disc.
880     if(read_sec >= toc.tracks[100].lba)
881     {
882      cdda.CDDAStatus = CDDASTATUS_STOPPED;
883      break;
884     }
885 
886     if(cd.TrayOpen)
887     {
888      cdda.CDDAStatus = CDDASTATUS_STOPPED;
889 
890      #if 0
891      cd.data_transfer_done = FALSE;
892      cd.key_pending = SENSEKEY_NOT_READY;
893      cd.asc_pending = ASC_MEDIUM_NOT_PRESENT;
894      cd.ascq_pending = 0x00;
895      cd.fru_pending = 0x00;
896      SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
897      #endif
898 
899      break;
900     }
901 
902 
903     cdda.CDDAReadPos = 0;
904 
905     {
906      uint8 tmpbuf[2352 + 96];
907 
908      Cur_CDIF->ReadRawSector(tmpbuf, read_sec);	//, read_sec_end, read_sec_start);
909 
910      for(int i = 0; i < 588 * 2; i++)
911       cdda.CDDASectorBuffer[i] = MDFN_de16lsb(&tmpbuf[i * 2]);
912 
913      memcpy(cd.SubPWBuf, tmpbuf + 2352, 96);
914     }
915     GenSubQFromSubPW();
916     read_sec++;
917    } // End    if(CDDAReadPos == 588)
918 
919    // If the last valid sub-Q data decoded indicate that the corresponding sector is a data sector, don't output the
920    // current sector as audio.
921    sample[0] = sample[1] = 0;
922 
923    if(!(cd.SubQBuf_Last[0] & 0x40) && cdda.PlayMode != PLAYMODE_SILENT)
924    {
925     sample[0] = (cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 0] * cdda.CDDAVolume) >> 16;
926     sample[1] = (cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 1] * cdda.CDDAVolume) >> 16;
927    }
928 
929    if(!(cdda.CDDAReadPos % 6))
930    {
931     int subindex = cdda.CDDAReadPos / 6 - 2;
932 
933     if(subindex >= 0)
934      CDStuffSubchannels(cd.SubPWBuf[subindex], subindex);
935     else // The system-specific emulation code should handle what value the sync bytes are.
936      CDStuffSubchannels(0x00, subindex);
937    }
938 
939    if(sbuf[0] && sbuf[1])
940    {
941     Blip_Synth_offset(&cdda.CDDASynth, synthtime, sample[0] - cdda.last_sample[0], sbuf[0]);
942     Blip_Synth_offset(&cdda.CDDASynth, synthtime, sample[1] - cdda.last_sample[1], sbuf[1]);
943    }
944 
945    cdda.last_sample[0] = sample[0];
946    cdda.last_sample[1] = sample[1];
947 
948    cdda.CDDAReadPos++;
949   }
950  }
951 }
952 
RunCDRead(uint32 system_timestamp,int32 run_time)953 static INLINE void RunCDRead(uint32 system_timestamp, int32 run_time)
954 {
955    if(CDReadTimer > 0)
956    {
957       CDReadTimer -= run_time;
958 
959       if(CDReadTimer <= 0)
960       {
961          if(din.CanWrite() < 2048)
962          {
963             //printf("Carp: %d %d %d\n", din.CanWrite(), SectorCount, CDReadTimer);
964             //CDReadTimer = (cd.data_in_size - cd.data_in_pos) * 10;
965 
966             CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
967 
968             //CDReadTimer += (uint64) 1 * 128 * System_Clock / CD_DATA_TRANSFER_RATE;
969          }
970          else
971          {
972             uint8 tmp_read_buf[2352 + 96];
973 
974             if(cd.TrayOpen)
975             {
976                din.Flush();
977                cd.data_transfer_done = FALSE;
978 
979                CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
980             }
981             else if(SectorAddr >= toc.tracks[100].lba)
982             {
983                CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
984             }
985             else if(!Cur_CDIF->ReadRawSector(tmp_read_buf, SectorAddr))	//, SectorAddr + SectorCount))
986             {
987                cd.data_transfer_done = FALSE;
988 
989                CommandCCError(SENSEKEY_ILLEGAL_REQUEST);
990             }
991             else if(ValidateRawDataSector(tmp_read_buf, SectorAddr))
992             {
993                memcpy(cd.SubPWBuf, tmp_read_buf + 2352, 96);
994 
995                if(tmp_read_buf[12 + 3] == 0x2)
996                   din.Write(tmp_read_buf + 24, 2048);
997                else
998                   din.Write(tmp_read_buf + 16, 2048);
999 
1000                GenSubQFromSubPW();
1001 
1002                CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_READY);
1003 
1004                SectorAddr++;
1005                SectorCount--;
1006 
1007                if(CurrentPhase != PHASE_DATA_IN)
1008                   ChangePhase(PHASE_DATA_IN);
1009 
1010                if(SectorCount)
1011                {
1012                   cd.data_transfer_done = FALSE;
1013                   CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
1014                }
1015                else
1016                {
1017                   cd.data_transfer_done = TRUE;
1018                }
1019             }
1020          }				// end else to if(!Cur_CDIF->ReadSector
1021 
1022       }
1023    }
1024 }
1025 
PCECD_Drive_Run(pcecd_drive_timestamp_t system_timestamp)1026 uint32 PCECD_Drive_Run(pcecd_drive_timestamp_t system_timestamp)
1027 {
1028    int32 run_time = system_timestamp - lastts;
1029 
1030    monotonic_timestamp += run_time;
1031 
1032    lastts = system_timestamp;
1033 
1034    RunCDRead(system_timestamp, run_time);
1035    RunCDDA(system_timestamp, run_time);
1036 
1037    bool ResetNeeded = false;
1038 
1039    if(RST_signal && !cd.last_RST_signal)
1040       ResetNeeded = true;
1041 
1042    cd.last_RST_signal = RST_signal;
1043 
1044    if(ResetNeeded)
1045    {
1046       //puts("RST");
1047       VirtualReset();
1048    }
1049    else switch(CurrentPhase)
1050    {
1051       case PHASE_BUS_FREE:
1052          if(SEL_signal)
1053          {
1054             ChangePhase(PHASE_COMMAND);
1055          }
1056          break;
1057 
1058       case PHASE_COMMAND:
1059          if(REQ_signal && ACK_signal)	// Data bus is valid nowww
1060          {
1061             //printf("Command Phase Byte I->T: %02x, %d\n", cd_bus.DB, cd.command_buffer_pos);
1062             cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB;
1063             SetREQ(FALSE);
1064          }
1065 
1066          if(!REQ_signal && !ACK_signal && cd.command_buffer_pos)	// Received at least one byte, what should we do?
1067          {
1068             if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
1069             {
1070                const SCSICH* cmd_info_ptr = PCECommandDefs;
1071 
1072                while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0])
1073                   cmd_info_ptr++;
1074 
1075                if(cmd_info_ptr->pretty_name == NULL)	// Command not found!
1076                {
1077                   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND);
1078 
1079                   /* Bad Command */
1080 
1081                   cd.command_buffer_pos = 0;
1082                }
1083                else
1084                {
1085                   if(cd.TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
1086                   {
1087                      CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
1088                   }
1089                   else if(!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
1090                   {
1091                      CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
1092                   }
1093                   else if(cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
1094                   {
1095                      CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED);
1096                      cd.DiscChanged = false;
1097                   }
1098                   else
1099                   {
1100                      cmd_info_ptr->func(cd.command_buffer);
1101                   }
1102 
1103                   cd.command_buffer_pos = 0;
1104                }
1105             } // end if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
1106             else			// Otherwise, get more data for the command!
1107                SetREQ(TRUE);
1108          }
1109          break;
1110 
1111       case PHASE_STATUS:
1112          if(REQ_signal && ACK_signal)
1113          {
1114             SetREQ(FALSE);
1115             cd.status_sent = TRUE;
1116          }
1117 
1118          if(!REQ_signal && !ACK_signal && cd.status_sent)
1119          {
1120             // Status sent, so get ready to send the message!
1121             cd.status_sent = FALSE;
1122             cd_bus.DB = cd.message_pending;
1123 
1124             ChangePhase(PHASE_MESSAGE_IN);
1125          }
1126          break;
1127 
1128       case PHASE_DATA_IN:
1129          if(!REQ_signal && !ACK_signal)
1130          {
1131             //puts("REQ and ACK false");
1132             if(din.in_count == 0)	// aaand we're done!
1133             {
1134                CDIRQCallback(0x8000 | PCECD_Drive_IRQ_DATA_TRANSFER_READY);
1135 
1136                if(cd.data_transfer_done)
1137                {
1138                   SendStatusAndMessage(STATUS_GOOD, 0x00);
1139                   cd.data_transfer_done = FALSE;
1140                   CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
1141                }
1142             }
1143             else
1144             {
1145                cd_bus.DB = din.ReadUnit();
1146                SetREQ(TRUE);
1147             }
1148          }
1149          if(REQ_signal && ACK_signal)
1150          {
1151             //puts("REQ and ACK true");
1152             SetREQ(FALSE);
1153          }
1154          break;
1155 
1156       case PHASE_MESSAGE_IN:
1157          if(REQ_signal && ACK_signal)
1158          {
1159             SetREQ(FALSE);
1160             cd.message_sent = TRUE;
1161          }
1162 
1163          if(!REQ_signal && !ACK_signal && cd.message_sent)
1164          {
1165             cd.message_sent = FALSE;
1166             ChangePhase(PHASE_BUS_FREE);
1167          }
1168          break;
1169    }
1170 
1171    int32 next_time = 0x7fffffff;
1172 
1173    if(CDReadTimer > 0 && CDReadTimer < next_time)
1174       next_time = CDReadTimer;
1175 
1176    if(cdda.CDDAStatus == CDDASTATUS_PLAYING)
1177    {
1178       int32 cdda_div_sexytime = (cdda.CDDADiv + 0xFFFF) >> 16;
1179       if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time)
1180          next_time = cdda_div_sexytime;
1181    }
1182 
1183    assert(next_time >= 0);
1184 
1185    return(next_time);
1186 }
1187 
PCECD_Drive_SetLog(void (* logfunc)(const char *,const char *,...))1188 void PCECD_Drive_SetLog(void (*logfunc)(const char *, const char *, ...))
1189 {
1190 }
1191 
PCECD_Drive_SetTransferRate(uint32 TransferRate)1192 void PCECD_Drive_SetTransferRate(uint32 TransferRate)
1193 {
1194  CD_DATA_TRANSFER_RATE = TransferRate;
1195 }
1196 
PCECD_Drive_Close(void)1197 void PCECD_Drive_Close(void)
1198 {
1199 
1200 }
1201 
PCECD_Drive_Init(int cdda_time_div,Blip_Buffer * leftbuf,Blip_Buffer * rightbuf,uint32 TransferRate,uint32 SystemClock,void (* IRQFunc)(int),void (* SSCFunc)(uint8,int))1202 void PCECD_Drive_Init(int cdda_time_div, Blip_Buffer *leftbuf, Blip_Buffer *rightbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int))
1203 {
1204  Cur_CDIF = NULL;
1205  cd.TrayOpen = false;
1206 
1207  monotonic_timestamp = 0;
1208  lastts = 0;
1209 
1210  //din = new SimpleFIFO(2048);
1211 
1212  cdda.CDDATimeDiv = cdda_time_div;
1213 
1214  cdda.CDDAVolume = 65536;
1215  Blip_Synth_set_volume(&cdda.CDDASynth, 1.0f / 65536, 1);
1216  sbuf[0] = leftbuf;
1217  sbuf[1] = rightbuf;
1218 
1219  CD_DATA_TRANSFER_RATE = TransferRate;
1220  System_Clock = SystemClock;
1221  CDIRQCallback = IRQFunc;
1222  CDStuffSubchannels = SSCFunc;
1223 }
1224 
PCECD_Drive_SetCDDAVolume(unsigned vol)1225 void PCECD_Drive_SetCDDAVolume(unsigned vol)
1226 {
1227  cdda.CDDAVolume = vol;
1228 }
1229 
PCECD_Drive_StateAction(StateMem * sm,int load,int data_only,const char * sname)1230 int PCECD_Drive_StateAction(StateMem * sm, int load, int data_only, const char *sname)
1231 {
1232  SFORMAT StateRegs[] =
1233  {
1234   SFVARN(cd_bus.DB, "DB"),
1235   SFVARN(cd_bus.signals, "Signals"),
1236   SFVARN(CurrentPhase, "CurrentPhase"),
1237 
1238   SFVAR_BOOL(cd.last_RST_signal),
1239   SFVARN(cd.message_pending, "message_pending"),
1240   SFVAR_BOOL(cd.status_sent),
1241   SFVAR_BOOL(cd.message_sent),
1242   SFVARN(cd.key_pending, "key_pending"),
1243   SFVARN(cd.asc_pending, "asc_pending"),
1244   SFVARN(cd.ascq_pending, "ascq_pending"),
1245   SFVARN(cd.fru_pending, "fru_pending"),
1246 
1247   SFARRAYN(cd.command_buffer, 256, "command_buffer"),
1248   SFVARN(cd.command_buffer_pos, "command_buffer_pos"),
1249   SFVARN(cd.command_size_left, "command_size_left"),
1250 
1251   // Don't save the FIFO's write position, it will be reconstructed from read_pos and in_count
1252   SFARRAYN(&din.data[0], din.size, "din_fifo"),
1253   SFVARN(din.read_pos, "din_read_pos"),
1254   SFVARN(din.in_count, "din_in_count"),
1255   SFVAR_BOOL(cd.data_transfer_done),
1256 
1257   SFVAR_BOOL(cd.TrayOpen),
1258   SFVAR_BOOL(cd.DiscChanged),
1259 
1260   SFVAR(cdda.PlayMode),
1261   SFARRAY16(cdda.CDDASectorBuffer, 1176),
1262   SFVAR(cdda.CDDAReadPos),
1263   SFVAR(cdda.CDDAStatus),
1264   SFVAR(cdda.CDDADiv),
1265   SFVAR(read_sec_start),
1266   SFVAR(read_sec),
1267   SFVAR(read_sec_end),
1268 
1269   SFVAR(CDReadTimer),
1270   SFVAR(SectorAddr),
1271   SFVAR(SectorCount),
1272 
1273   SFVAR(cdda.ScanMode),
1274   SFVAR(cdda.scan_sec_end),
1275 
1276   SFARRAYN(&cd.SubQBuf[0][0], sizeof(cd.SubQBuf), "SubQBufs"),
1277   SFARRAYN(cd.SubQBuf_Last, sizeof(cd.SubQBuf_Last), "SubQBufLast"),
1278   SFARRAYN(cd.SubPWBuf, sizeof(cd.SubPWBuf), "SubPWBuf"),
1279 
1280   SFVARN(monotonic_timestamp, "monotonic_timestamp"),
1281   SFVARN(pce_lastsapsp_timestamp, "pce_lastsapsp_timestamp"),
1282 
1283   SFEND
1284  };
1285 
1286  int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, sname, false);
1287 
1288  if(load)
1289  {
1290   din.in_count &= din.size - 1;
1291   din.read_pos &= din.size - 1;
1292   din.write_pos = (din.read_pos + din.in_count) & (din.size - 1);
1293 
1294   if(cdda.CDDADiv <= 0)
1295    cdda.CDDADiv = 1;
1296   //printf("%d %d %d\n", din.in_count, din.read_pos, din.write_pos);
1297  }
1298 
1299  return (ret);
1300 }
1301