cdrom: handle fifo overreads
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
... / ...
CommitLineData
1/***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18 ***************************************************************************/
19
20/*
21* Handles all CD-ROM registers and functions.
22*/
23
24#include "cdrom.h"
25#include "ppf.h"
26#include "psxdma.h"
27#include "arm_features.h"
28
29/* logging */
30#if 0
31#define CDR_LOG SysPrintf
32#else
33#define CDR_LOG(...)
34#endif
35#if 0
36#define CDR_LOG_I SysPrintf
37#else
38#define CDR_LOG_I log_unhandled
39#endif
40#if 0
41#define CDR_LOG_IO SysPrintf
42#else
43#define CDR_LOG_IO(...)
44#endif
45//#define CDR_LOG_CMD_IRQ
46
47static struct {
48 // unused members maintain savesate compatibility
49 unsigned char unused0;
50 unsigned char unused1;
51 unsigned char Reg2;
52 unsigned char unused2;
53 unsigned char Ctrl;
54 unsigned char Stat;
55
56 unsigned char StatP;
57
58 unsigned char Transfer[DATA_SIZE];
59 struct {
60 unsigned char Track;
61 unsigned char Index;
62 unsigned char Relative[3];
63 unsigned char Absolute[3];
64 } subq;
65 unsigned char TrackChanged;
66 unsigned char unused3[3];
67 unsigned int freeze_ver;
68
69 unsigned char Prev[4];
70 unsigned char Param[8];
71 unsigned char Result[16];
72
73 unsigned char ParamC;
74 unsigned char ParamP;
75 unsigned char ResultC;
76 unsigned char ResultP;
77 unsigned char ResultReady;
78 unsigned char Cmd;
79 unsigned char unused4;
80 unsigned char SetlocPending;
81 u32 Reading;
82
83 unsigned char ResultTN[6];
84 unsigned char ResultTD[4];
85 unsigned char SetSectorPlay[4];
86 unsigned char SetSectorEnd[4];
87 unsigned char SetSector[4];
88 unsigned char Track;
89 boolean Play, Muted;
90 int CurTrack;
91 int Mode, File, Channel;
92 unsigned char LocL[8];
93 int FirstSector;
94
95 xa_decode_t Xa;
96
97 u16 FifoOffset;
98 u16 FifoSize;
99
100 u16 CmdInProgress;
101 u8 Irq1Pending;
102 u8 unused5;
103 u32 unused6;
104
105 u8 unused7;
106
107 u8 DriveState;
108 u8 FastForward;
109 u8 FastBackward;
110 u8 unused8;
111
112 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
113 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
114 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
115 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
116} cdr;
117static s16 read_buf[CD_FRAMESIZE_RAW/2];
118
119/* CD-ROM magic numbers */
120#define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
121#define CdlNop 1
122#define CdlSetloc 2
123#define CdlPlay 3
124#define CdlForward 4
125#define CdlBackward 5
126#define CdlReadN 6
127#define CdlStandby 7
128#define CdlStop 8
129#define CdlPause 9
130#define CdlReset 10
131#define CdlMute 11
132#define CdlDemute 12
133#define CdlSetfilter 13
134#define CdlSetmode 14
135#define CdlGetparam 15
136#define CdlGetlocL 16
137#define CdlGetlocP 17
138#define CdlReadT 18
139#define CdlGetTN 19
140#define CdlGetTD 20
141#define CdlSeekL 21
142#define CdlSeekP 22
143#define CdlSetclock 23
144#define CdlGetclock 24
145#define CdlTest 25
146#define CdlID 26
147#define CdlReadS 27
148#define CdlInit 28
149#define CdlGetQ 29
150#define CdlReadToc 30
151
152#ifdef CDR_LOG_CMD_IRQ
153static const char * const CmdName[0x100] = {
154 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
155 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
156 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
157 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
158 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
159 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
160 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
161 "CdlInit", NULL, "CDlReadToc", NULL
162};
163#endif
164
165unsigned char Test04[] = { 0 };
166unsigned char Test05[] = { 0 };
167unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
168unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
169unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
170
171// cdr.Stat:
172#define NoIntr 0
173#define DataReady 1
174#define Complete 2
175#define Acknowledge 3
176#define DataEnd 4
177#define DiskError 5
178
179/* Modes flags */
180#define MODE_SPEED (1<<7) // 0x80
181#define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
182#define MODE_SIZE_2340 (1<<5) // 0x20
183#define MODE_SIZE_2328 (1<<4) // 0x10
184#define MODE_SIZE_2048 (0<<4) // 0x00
185#define MODE_SF (1<<3) // 0x08 channel on/off
186#define MODE_REPORT (1<<2) // 0x04
187#define MODE_AUTOPAUSE (1<<1) // 0x02
188#define MODE_CDDA (1<<0) // 0x01
189
190/* Status flags */
191#define STATUS_PLAY (1<<7) // 0x80
192#define STATUS_SEEK (1<<6) // 0x40
193#define STATUS_READ (1<<5) // 0x20
194#define STATUS_SHELLOPEN (1<<4) // 0x10
195#define STATUS_UNKNOWN3 (1<<3) // 0x08
196#define STATUS_UNKNOWN2 (1<<2) // 0x04
197#define STATUS_ROTATING (1<<1) // 0x02
198#define STATUS_ERROR (1<<0) // 0x01
199
200/* Errors */
201#define ERROR_NOTREADY (1<<7) // 0x80
202#define ERROR_INVALIDCMD (1<<6) // 0x40
203#define ERROR_INVALIDARG (1<<5) // 0x20
204
205// 1x = 75 sectors per second
206// PSXCLK = 1 sec in the ps
207// so (PSXCLK / 75) = cdr read time (linuzappz)
208#define cdReadTime (PSXCLK / 75)
209
210#define LOCL_INVALID 0xff
211
212enum drive_state {
213 DRIVESTATE_STANDBY = 0, // pause, play, read
214 DRIVESTATE_LID_OPEN,
215 DRIVESTATE_RESCAN_CD,
216 DRIVESTATE_PREPARE_CD,
217 DRIVESTATE_STOPPED,
218};
219
220static struct CdrStat stat;
221
222static unsigned int msf2sec(const u8 *msf) {
223 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
224}
225
226// for that weird psemu API..
227static unsigned int fsm2sec(const u8 *msf) {
228 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
229}
230
231static void sec2msf(unsigned int s, u8 *msf) {
232 msf[0] = s / 75 / 60;
233 s = s - msf[0] * 75 * 60;
234 msf[1] = s / 75;
235 s = s - msf[1] * 75;
236 msf[2] = s;
237}
238
239// cdrInterrupt
240#define CDR_INT(eCycle) { \
241 psxRegs.interrupt |= (1 << PSXINT_CDR); \
242 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
243 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
244 new_dyna_set_event(PSXINT_CDR, eCycle); \
245}
246
247// cdrPlayReadInterrupt
248#define CDRPLAYREAD_INT(eCycle, isFirst) { \
249 u32 e_ = eCycle; \
250 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
251 if (isFirst) \
252 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
253 else \
254 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
255 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
256 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
257}
258
259// cdrLidSeekInterrupt
260#define CDRLID_INT(eCycle) { \
261 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
262 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
263 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
264 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
265}
266
267#define StopReading() { \
268 cdr.Reading = 0; \
269 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
270}
271
272#define StopCdda() { \
273 if (cdr.Play && !Config.Cdda) CDR_stop(); \
274 cdr.Play = FALSE; \
275 cdr.FastForward = 0; \
276 cdr.FastBackward = 0; \
277}
278
279#define SetPlaySeekRead(x, f) { \
280 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
281 x |= f; \
282}
283
284#define SetResultSize(size) { \
285 cdr.ResultP = 0; \
286 cdr.ResultC = size; \
287 cdr.ResultReady = 1; \
288}
289
290static void setIrq(int log_cmd)
291{
292 if (cdr.Stat & cdr.Reg2)
293 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
294
295#ifdef CDR_LOG_CMD_IRQ
296 if (cdr.Stat)
297 {
298 int i;
299 SysPrintf("%u cdrom: CDR IRQ=%d cmd %02x stat %02x: ",
300 psxRegs.cycle, !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
301 for (i = 0; i < cdr.ResultC; i++)
302 SysPrintf("%02x ", cdr.Result[i]);
303 SysPrintf("\n");
304 }
305#endif
306}
307
308// timing used in this function was taken from tests on real hardware
309// (yes it's slow, but you probably don't want to modify it)
310void cdrLidSeekInterrupt(void)
311{
312 CDR_LOG_I("%u %s cdr.DriveState=%d\n", psxRegs.cycle, __func__, cdr.DriveState);
313
314 switch (cdr.DriveState) {
315 default:
316 case DRIVESTATE_STANDBY:
317 StopCdda();
318 StopReading();
319 SetPlaySeekRead(cdr.StatP, 0);
320
321 if (CDR_getStatus(&stat) == -1)
322 return;
323
324 if (stat.Status & STATUS_SHELLOPEN)
325 {
326 cdr.DriveState = DRIVESTATE_LID_OPEN;
327 CDRLID_INT(0x800);
328 }
329 break;
330
331 case DRIVESTATE_LID_OPEN:
332 if (CDR_getStatus(&stat) == -1)
333 stat.Status &= ~STATUS_SHELLOPEN;
334
335 // 02, 12, 10
336 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
337 cdr.StatP |= STATUS_SHELLOPEN;
338
339 // could generate error irq here, but real hardware
340 // only sometimes does that
341 // (not done when lots of commands are sent?)
342
343 CDRLID_INT(cdReadTime * 30);
344 break;
345 }
346 else if (cdr.StatP & STATUS_ROTATING) {
347 cdr.StatP &= ~STATUS_ROTATING;
348 }
349 else if (!(stat.Status & STATUS_SHELLOPEN)) {
350 // closed now
351 CheckCdrom();
352
353 // cdr.StatP STATUS_SHELLOPEN is "sticky"
354 // and is only cleared by CdlNop
355
356 cdr.DriveState = DRIVESTATE_RESCAN_CD;
357 CDRLID_INT(cdReadTime * 105);
358 break;
359 }
360
361 // recheck for close
362 CDRLID_INT(cdReadTime * 3);
363 break;
364
365 case DRIVESTATE_RESCAN_CD:
366 cdr.StatP |= STATUS_ROTATING;
367 cdr.DriveState = DRIVESTATE_PREPARE_CD;
368
369 // this is very long on real hardware, over 6 seconds
370 // make it a bit faster here...
371 CDRLID_INT(cdReadTime * 150);
372 break;
373
374 case DRIVESTATE_PREPARE_CD:
375 if (cdr.StatP & STATUS_SEEK) {
376 SetPlaySeekRead(cdr.StatP, 0);
377 cdr.DriveState = DRIVESTATE_STANDBY;
378 }
379 else {
380 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
381 CDRLID_INT(cdReadTime * 26);
382 }
383 break;
384 }
385}
386
387static void Find_CurTrack(const u8 *time)
388{
389 int current, sect;
390
391 current = msf2sec(time);
392
393 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
394 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
395 sect = fsm2sec(cdr.ResultTD);
396 if (sect - current >= 150)
397 break;
398 }
399}
400
401static void generate_subq(const u8 *time)
402{
403 unsigned char start[3], next[3];
404 unsigned int this_s, start_s, next_s, pregap;
405 int relative_s;
406
407 CDR_getTD(cdr.CurTrack, start);
408 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
409 pregap = 150;
410 CDR_getTD(cdr.CurTrack + 1, next);
411 }
412 else {
413 // last track - cd size
414 pregap = 0;
415 next[0] = cdr.SetSectorEnd[2];
416 next[1] = cdr.SetSectorEnd[1];
417 next[2] = cdr.SetSectorEnd[0];
418 }
419
420 this_s = msf2sec(time);
421 start_s = fsm2sec(start);
422 next_s = fsm2sec(next);
423
424 cdr.TrackChanged = FALSE;
425
426 if (next_s - this_s < pregap) {
427 cdr.TrackChanged = TRUE;
428 cdr.CurTrack++;
429 start_s = next_s;
430 }
431
432 cdr.subq.Index = 1;
433
434 relative_s = this_s - start_s;
435 if (relative_s < 0) {
436 cdr.subq.Index = 0;
437 relative_s = -relative_s;
438 }
439 sec2msf(relative_s, cdr.subq.Relative);
440
441 cdr.subq.Track = itob(cdr.CurTrack);
442 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
443 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
444 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
445 cdr.subq.Absolute[0] = itob(time[0]);
446 cdr.subq.Absolute[1] = itob(time[1]);
447 cdr.subq.Absolute[2] = itob(time[2]);
448}
449
450static int ReadTrack(const u8 *time) {
451 unsigned char tmp[3];
452 struct SubQ *subq;
453 int read_ok;
454 u16 crc;
455
456 tmp[0] = itob(time[0]);
457 tmp[1] = itob(time[1]);
458 tmp[2] = itob(time[2]);
459
460 if (memcmp(cdr.Prev, tmp, 3) == 0)
461 return 1;
462
463 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
464
465 read_ok = CDR_readTrack(tmp);
466 if (read_ok)
467 memcpy(cdr.Prev, tmp, 3);
468
469 if (CheckSBI(time))
470 return read_ok;
471
472 subq = (struct SubQ *)CDR_getBufferSub();
473 if (subq != NULL && cdr.CurTrack == 1) {
474 crc = calcCrc((u8 *)subq + 12, 10);
475 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
476 cdr.subq.Track = subq->TrackNumber;
477 cdr.subq.Index = subq->IndexNumber;
478 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
479 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
480 }
481 else {
482 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
483 tmp[0], tmp[1], tmp[2]);
484 }
485 }
486 else {
487 generate_subq(time);
488 }
489
490 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
491 cdr.subq.Track, cdr.subq.Index,
492 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
493 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
494
495 return read_ok;
496}
497
498static void cdrPlayInterrupt_Autopause()
499{
500 u32 abs_lev_max = 0;
501 boolean abs_lev_chselect;
502 u32 i;
503
504 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
505 CDR_LOG( "CDDA STOP\n" );
506
507 // Magic the Gathering
508 // - looping territory cdda
509
510 // ...?
511 //cdr.ResultReady = 1;
512 //cdr.Stat = DataReady;
513 cdr.Stat = DataEnd;
514 setIrq(0x1000); // 0x1000 just for logging purposes
515
516 StopCdda();
517 SetPlaySeekRead(cdr.StatP, 0);
518 }
519 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
520 cdr.Result[0] = cdr.StatP;
521 cdr.Result[1] = cdr.subq.Track;
522 cdr.Result[2] = cdr.subq.Index;
523
524 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
525
526 /* 8 is a hack. For accuracy, it should be 588. */
527 for (i = 0; i < 8; i++)
528 {
529 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
530 }
531 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
532 abs_lev_max |= abs_lev_chselect << 15;
533
534 if (cdr.subq.Absolute[2] & 0x10) {
535 cdr.Result[3] = cdr.subq.Relative[0];
536 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
537 cdr.Result[5] = cdr.subq.Relative[2];
538 }
539 else {
540 cdr.Result[3] = cdr.subq.Absolute[0];
541 cdr.Result[4] = cdr.subq.Absolute[1];
542 cdr.Result[5] = cdr.subq.Absolute[2];
543 }
544
545 cdr.Result[6] = abs_lev_max >> 0;
546 cdr.Result[7] = abs_lev_max >> 8;
547
548 // Rayman: Logo freeze (resultready + dataready)
549 cdr.ResultReady = 1;
550 cdr.Stat = DataReady;
551
552 SetResultSize(8);
553 setIrq(0x1001);
554 }
555}
556
557static int cdrSeekTime(unsigned char *target)
558{
559 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
560 int seekTime = abs(diff) * (cdReadTime / 200);
561 /*
562 * Gameblabla :
563 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
564 * and was unreliable for that game.
565 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
566 *
567 * Obviously, this isn't perfect but right now, it should be a bit better.
568 * Games to test this against if you change that setting :
569 * - Driver (titlescreen music delay and retry mission)
570 * - Worms Pinball (Will either not boot or crash in the memory card screen)
571 * - Viewpoint (short pauses if the delay in the ingame music is too long)
572 *
573 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
574 * However, 1000000 is not enough for Worms Pinball to reliably boot.
575 */
576 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
577 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
578 return seekTime;
579}
580
581static u32 cdrAlignTimingHack(u32 cycles)
582{
583 /*
584 * timing hack for T'ai Fu - Wrath of the Tiger:
585 * The game has a bug where it issues some cdc commands from a low priority
586 * vint handler, however there is a higher priority default bios handler
587 * that acks the vint irq and returns, so game's handler is not reached
588 * (see bios irq handler chains at e004 and the game's irq handling func
589 * at 80036810). For the game to work, vint has to arrive after the bios
590 * vint handler rejects some other irq (of which only cd and rcnt2 are
591 * active), but before the game's handler loop reads I_STAT. The time
592 * window for this is quite small (~1k cycles of so). Apparently this
593 * somehow happens naturally on the real hardware.
594 */
595 u32 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
596 vint_rel += PSXCLK / 60;
597 while ((s32)(vint_rel - cycles) < 0)
598 vint_rel += PSXCLK / 60;
599 return vint_rel;
600}
601
602static void cdrUpdateTransferBuf(const u8 *buf);
603static void cdrReadInterrupt(void);
604static void cdrPrepCdda(s16 *buf, int samples);
605static void cdrAttenuate(s16 *buf, int samples, int stereo);
606
607void cdrPlayReadInterrupt(void)
608{
609 if (cdr.Reading) {
610 cdrReadInterrupt();
611 return;
612 }
613
614 if (!cdr.Play) return;
615
616 CDR_LOG( "CDDA - %d:%d:%d\n",
617 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
618
619 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
620 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
621 StopCdda();
622 SetPlaySeekRead(cdr.StatP, 0);
623 cdr.TrackChanged = TRUE;
624 }
625 else {
626 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
627 }
628
629 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
630 cdrPlayInterrupt_Autopause();
631
632 if (!cdr.Muted && !Config.Cdda) {
633 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
634 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
635 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
636 cdr.FirstSector = 0;
637 }
638
639 cdr.SetSectorPlay[2]++;
640 if (cdr.SetSectorPlay[2] == 75) {
641 cdr.SetSectorPlay[2] = 0;
642 cdr.SetSectorPlay[1]++;
643 if (cdr.SetSectorPlay[1] == 60) {
644 cdr.SetSectorPlay[1] = 0;
645 cdr.SetSectorPlay[0]++;
646 }
647 }
648
649 // update for CdlGetlocP/autopause
650 generate_subq(cdr.SetSectorPlay);
651
652 CDRPLAYREAD_INT(cdReadTime, 0);
653}
654
655#define CMD_PART2 0x100
656#define CMD_WHILE_NOT_READY 0x200
657
658void cdrInterrupt(void) {
659 int start_rotating = 0;
660 int error = 0;
661 u32 cycles, seekTime = 0;
662 u32 second_resp_time = 0;
663 const void *buf;
664 u8 ParamC;
665 u8 set_loc[3];
666 int read_ok;
667 u16 not_ready = 0;
668 u16 Cmd;
669 int i;
670
671 if (cdr.Stat) {
672 CDR_LOG_I("%u cdrom: cmd %02x with irqstat %x\n",
673 psxRegs.cycle, cdr.CmdInProgress, cdr.Stat);
674 return;
675 }
676 if (cdr.Irq1Pending) {
677 // hand out the "newest" sector, according to nocash
678 cdrUpdateTransferBuf(CDR_getBuffer());
679 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
680 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
681 SetResultSize(1);
682 cdr.Result[0] = cdr.Irq1Pending;
683 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
684 cdr.Irq1Pending = 0;
685 setIrq(0x1003);
686 return;
687 }
688
689 // default response
690 SetResultSize(1);
691 cdr.Result[0] = cdr.StatP;
692 cdr.Stat = Acknowledge;
693
694 Cmd = cdr.CmdInProgress;
695 cdr.CmdInProgress = 0;
696 ParamC = cdr.ParamC;
697
698 if (Cmd < 0x100) {
699 cdr.Ctrl &= ~0x80;
700 cdr.ParamC = 0;
701 cdr.Cmd = 0;
702 }
703
704 switch (cdr.DriveState) {
705 case DRIVESTATE_PREPARE_CD:
706 if (Cmd > 2) {
707 // Syphon filter 2 expects commands to work shortly after it sees
708 // STATUS_ROTATING, so give up trying to emulate the startup seq
709 cdr.DriveState = DRIVESTATE_STANDBY;
710 cdr.StatP &= ~STATUS_SEEK;
711 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
712 break;
713 }
714 // fallthrough
715 case DRIVESTATE_LID_OPEN:
716 case DRIVESTATE_RESCAN_CD:
717 // no disk or busy with the initial scan, allowed cmds are limited
718 not_ready = CMD_WHILE_NOT_READY;
719 break;
720 }
721
722 switch (Cmd | not_ready) {
723 case CdlNop:
724 case CdlNop + CMD_WHILE_NOT_READY:
725 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
726 cdr.StatP &= ~STATUS_SHELLOPEN;
727 break;
728
729 case CdlSetloc:
730 case CdlSetloc + CMD_WHILE_NOT_READY:
731 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
732
733 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
734 if (((cdr.Param[0] & 0x0F) > 0x09) || (cdr.Param[0] > 0x99) || ((cdr.Param[1] & 0x0F) > 0x09) || (cdr.Param[1] >= 0x60) || ((cdr.Param[2] & 0x0F) > 0x09) || (cdr.Param[2] >= 0x75))
735 {
736 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
737 error = ERROR_INVALIDARG;
738 goto set_error;
739 }
740 else
741 {
742 for (i = 0; i < 3; i++)
743 set_loc[i] = btoi(cdr.Param[i]);
744 memcpy(cdr.SetSector, set_loc, 3);
745 cdr.SetSector[3] = 0;
746 cdr.SetlocPending = 1;
747 }
748 break;
749
750 do_CdlPlay:
751 case CdlPlay:
752 StopCdda();
753 StopReading();
754
755 cdr.FastBackward = 0;
756 cdr.FastForward = 0;
757
758 // BIOS CD Player
759 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
760
761 if (ParamC != 0 && cdr.Param[0] != 0) {
762 int track = btoi( cdr.Param[0] );
763
764 if (track <= cdr.ResultTN[1])
765 cdr.CurTrack = track;
766
767 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
768
769 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
770 for (i = 0; i < 3; i++)
771 set_loc[i] = cdr.ResultTD[2 - i];
772 seekTime = cdrSeekTime(set_loc);
773 memcpy(cdr.SetSectorPlay, set_loc, 3);
774 }
775 }
776 else if (cdr.SetlocPending) {
777 seekTime = cdrSeekTime(cdr.SetSector);
778 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
779 }
780 else {
781 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
782 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
783 }
784 cdr.SetlocPending = 0;
785
786 /*
787 Rayman: detect track changes
788 - fixes logo freeze
789
790 Twisted Metal 2: skip PREGAP + starting accurate SubQ
791 - plays tracks without retry play
792
793 Wild 9: skip PREGAP + starting accurate SubQ
794 - plays tracks without retry play
795 */
796 Find_CurTrack(cdr.SetSectorPlay);
797 ReadTrack(cdr.SetSectorPlay);
798 cdr.LocL[0] = LOCL_INVALID;
799 cdr.TrackChanged = FALSE;
800 cdr.FirstSector = 1;
801
802 if (!Config.Cdda)
803 CDR_play(cdr.SetSectorPlay);
804
805 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
806
807 // BIOS player - set flag again
808 cdr.Play = TRUE;
809
810 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
811 start_rotating = 1;
812 break;
813
814 case CdlForward:
815 // TODO: error 80 if stopped
816 cdr.Stat = Complete;
817
818 // GameShark CD Player: Calls 2x + Play 2x
819 cdr.FastForward = 1;
820 cdr.FastBackward = 0;
821 break;
822
823 case CdlBackward:
824 cdr.Stat = Complete;
825
826 // GameShark CD Player: Calls 2x + Play 2x
827 cdr.FastBackward = 1;
828 cdr.FastForward = 0;
829 break;
830
831 case CdlStandby:
832 if (cdr.DriveState != DRIVESTATE_STOPPED) {
833 error = ERROR_INVALIDARG;
834 goto set_error;
835 }
836 second_resp_time = cdReadTime * 125 / 2;
837 start_rotating = 1;
838 break;
839
840 case CdlStandby + CMD_PART2:
841 cdr.Stat = Complete;
842 break;
843
844 case CdlStop:
845 if (cdr.Play) {
846 // grab time for current track
847 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
848
849 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
850 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
851 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
852 }
853
854 StopCdda();
855 StopReading();
856 SetPlaySeekRead(cdr.StatP, 0);
857 cdr.StatP &= ~STATUS_ROTATING;
858 cdr.LocL[0] = LOCL_INVALID;
859
860 second_resp_time = 0x800;
861 if (cdr.DriveState == DRIVESTATE_STANDBY)
862 second_resp_time = cdReadTime * 30 / 2;
863
864 cdr.DriveState = DRIVESTATE_STOPPED;
865 break;
866
867 case CdlStop + CMD_PART2:
868 cdr.Stat = Complete;
869 break;
870
871 case CdlPause:
872 StopCdda();
873 StopReading();
874 /*
875 Gundam Battle Assault 2: much slower (*)
876 - Fixes boot, gameplay
877
878 Hokuto no Ken 2: slower
879 - Fixes intro + subtitles
880
881 InuYasha - Feudal Fairy Tale: slower
882 - Fixes battles
883 */
884 /* Gameblabla - Tightening the timings (as taken from Duckstation).
885 * The timings from Duckstation are based upon hardware tests.
886 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
887 * seems to be timing sensitive as it can depend on the CPU's clock speed.
888 * */
889 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
890 {
891 second_resp_time = 7000;
892 }
893 else
894 {
895 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
896 }
897 SetPlaySeekRead(cdr.StatP, 0);
898 break;
899
900 case CdlPause + CMD_PART2:
901 cdr.Stat = Complete;
902 break;
903
904 case CdlReset:
905 case CdlReset + CMD_WHILE_NOT_READY:
906 StopCdda();
907 StopReading();
908 SetPlaySeekRead(cdr.StatP, 0);
909 cdr.LocL[0] = LOCL_INVALID;
910 cdr.Muted = FALSE;
911 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
912 second_resp_time = not_ready ? 70000 : 4100000;
913 start_rotating = 1;
914 break;
915
916 case CdlReset + CMD_PART2:
917 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
918 cdr.Stat = Complete;
919 break;
920
921 case CdlMute:
922 cdr.Muted = TRUE;
923 break;
924
925 case CdlDemute:
926 cdr.Muted = FALSE;
927 break;
928
929 case CdlSetfilter:
930 cdr.File = cdr.Param[0];
931 cdr.Channel = cdr.Param[1];
932 break;
933
934 case CdlSetmode:
935 case CdlSetmode + CMD_WHILE_NOT_READY:
936 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
937 cdr.Mode = cdr.Param[0];
938 break;
939
940 case CdlGetparam:
941 case CdlGetparam + CMD_WHILE_NOT_READY:
942 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
943 SetResultSize(5);
944 cdr.Result[1] = cdr.Mode;
945 cdr.Result[2] = 0;
946 cdr.Result[3] = cdr.File;
947 cdr.Result[4] = cdr.Channel;
948 break;
949
950 case CdlGetlocL:
951 if (cdr.LocL[0] == LOCL_INVALID) {
952 error = 0x80;
953 goto set_error;
954 }
955 SetResultSize(8);
956 memcpy(cdr.Result, cdr.LocL, 8);
957 break;
958
959 case CdlGetlocP:
960 SetResultSize(8);
961 memcpy(&cdr.Result, &cdr.subq, 8);
962 break;
963
964 case CdlReadT: // SetSession?
965 // really long
966 second_resp_time = cdReadTime * 290 / 4;
967 start_rotating = 1;
968 break;
969
970 case CdlReadT + CMD_PART2:
971 cdr.Stat = Complete;
972 break;
973
974 case CdlGetTN:
975 SetResultSize(3);
976 if (CDR_getTN(cdr.ResultTN) == -1) {
977 cdr.Stat = DiskError;
978 cdr.Result[0] |= STATUS_ERROR;
979 } else {
980 cdr.Stat = Acknowledge;
981 cdr.Result[1] = itob(cdr.ResultTN[0]);
982 cdr.Result[2] = itob(cdr.ResultTN[1]);
983 }
984 break;
985
986 case CdlGetTD:
987 cdr.Track = btoi(cdr.Param[0]);
988 SetResultSize(4);
989 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
990 cdr.Stat = DiskError;
991 cdr.Result[0] |= STATUS_ERROR;
992 } else {
993 cdr.Stat = Acknowledge;
994 cdr.Result[0] = cdr.StatP;
995 cdr.Result[1] = itob(cdr.ResultTD[2]);
996 cdr.Result[2] = itob(cdr.ResultTD[1]);
997 /* According to Nocash's documentation, the function doesn't care about ff.
998 * This can be seen also in Mednafen's implementation. */
999 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1000 }
1001 break;
1002
1003 case CdlSeekL:
1004 case CdlSeekP:
1005 StopCdda();
1006 StopReading();
1007 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1008
1009 seekTime = cdrSeekTime(cdr.SetSector);
1010 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1011 /*
1012 Crusaders of Might and Magic = 0.5x-4x
1013 - fix cutscene speech start
1014
1015 Eggs of Steel = 2x-?
1016 - fix new game
1017
1018 Medievil = ?-4x
1019 - fix cutscene speech
1020
1021 Rockman X5 = 0.5-4x
1022 - fix capcom logo
1023 */
1024 second_resp_time = cdReadTime + seekTime;
1025 start_rotating = 1;
1026 break;
1027
1028 case CdlSeekL + CMD_PART2:
1029 case CdlSeekP + CMD_PART2:
1030 SetPlaySeekRead(cdr.StatP, 0);
1031 cdr.Result[0] = cdr.StatP;
1032 cdr.Stat = Complete;
1033
1034 Find_CurTrack(cdr.SetSectorPlay);
1035 read_ok = ReadTrack(cdr.SetSectorPlay);
1036 if (read_ok && (buf = CDR_getBuffer()))
1037 memcpy(cdr.LocL, buf, 8);
1038 cdr.TrackChanged = FALSE;
1039 break;
1040
1041 case CdlTest:
1042 case CdlTest + CMD_WHILE_NOT_READY:
1043 switch (cdr.Param[0]) {
1044 case 0x20: // System Controller ROM Version
1045 SetResultSize(4);
1046 memcpy(cdr.Result, Test20, 4);
1047 break;
1048 case 0x22:
1049 SetResultSize(8);
1050 memcpy(cdr.Result, Test22, 4);
1051 break;
1052 case 0x23: case 0x24:
1053 SetResultSize(8);
1054 memcpy(cdr.Result, Test23, 4);
1055 break;
1056 }
1057 break;
1058
1059 case CdlID:
1060 second_resp_time = 20480;
1061 break;
1062
1063 case CdlID + CMD_PART2:
1064 SetResultSize(8);
1065 cdr.Result[0] = cdr.StatP;
1066 cdr.Result[1] = 0;
1067 cdr.Result[2] = 0;
1068 cdr.Result[3] = 0;
1069
1070 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1071 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1072 cdr.Result[1] = 0xc0;
1073 }
1074 else {
1075 if (stat.Type == 2)
1076 cdr.Result[1] |= 0x10;
1077 if (CdromId[0] == '\0')
1078 cdr.Result[1] |= 0x80;
1079 }
1080 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1081
1082 /* This adds the string "PCSX" in Playstation bios boot screen */
1083 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1084 cdr.Stat = Complete;
1085 break;
1086
1087 case CdlInit:
1088 case CdlInit + CMD_WHILE_NOT_READY:
1089 StopCdda();
1090 StopReading();
1091 SetPlaySeekRead(cdr.StatP, 0);
1092 // yes, it really sets STATUS_SHELLOPEN
1093 cdr.StatP |= STATUS_SHELLOPEN;
1094 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1095 CDRLID_INT(20480);
1096 start_rotating = 1;
1097 break;
1098
1099 case CdlGetQ:
1100 case CdlGetQ + CMD_WHILE_NOT_READY:
1101 break;
1102
1103 case CdlReadToc:
1104 case CdlReadToc + CMD_WHILE_NOT_READY:
1105 cdr.LocL[0] = LOCL_INVALID;
1106 second_resp_time = cdReadTime * 180 / 4;
1107 start_rotating = 1;
1108 break;
1109
1110 case CdlReadToc + CMD_PART2:
1111 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1112 cdr.Stat = Complete;
1113 break;
1114
1115 case CdlReadN:
1116 case CdlReadS:
1117 if (cdr.Reading && !cdr.SetlocPending)
1118 break;
1119
1120 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1121
1122 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1123 // Read* acts as play for cdda tracks in cdda mode
1124 goto do_CdlPlay;
1125
1126 StopCdda();
1127 if (cdr.SetlocPending) {
1128 seekTime = cdrSeekTime(cdr.SetSector);
1129 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1130 cdr.SetlocPending = 0;
1131 }
1132 cdr.Reading = 1;
1133 cdr.FirstSector = 1;
1134
1135 // Fighting Force 2 - update subq time immediately
1136 // - fixes new game
1137 ReadTrack(cdr.SetSectorPlay);
1138 cdr.LocL[0] = LOCL_INVALID;
1139
1140 cycles = (cdr.Mode & 0x80) ? cdReadTime : cdReadTime * 2;
1141 cycles += seekTime;
1142 cycles = cdrAlignTimingHack(cycles);
1143 CDRPLAYREAD_INT(cycles, 1);
1144
1145 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1146 start_rotating = 1;
1147 break;
1148
1149 case CdlSync:
1150 default:
1151 error = ERROR_INVALIDCMD;
1152 // FALLTHROUGH
1153
1154 set_error:
1155 CDR_LOG_I("cdrom: cmd %02x error %02x\n", Cmd, error);
1156 SetResultSize(2);
1157 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1158 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1159 cdr.Stat = DiskError;
1160 break;
1161 }
1162
1163 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1164 cdr.DriveState = DRIVESTATE_STANDBY;
1165 cdr.StatP |= STATUS_ROTATING;
1166 }
1167
1168 if (second_resp_time) {
1169 cdr.CmdInProgress = Cmd | 0x100;
1170 CDR_INT(second_resp_time);
1171 }
1172 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1173 cdr.CmdInProgress = cdr.Cmd;
1174 CDR_LOG_I("%u cdrom: cmd %02x came before %02x finished\n",
1175 psxRegs.cycle, cdr.Cmd, Cmd);
1176 }
1177
1178 setIrq(Cmd);
1179}
1180
1181#ifdef HAVE_ARMV7
1182 #define ssat32_to_16(v) \
1183 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1184#else
1185 #define ssat32_to_16(v) do { \
1186 if (v < -32768) v = -32768; \
1187 else if (v > 32767) v = 32767; \
1188 } while (0)
1189#endif
1190
1191static void cdrPrepCdda(s16 *buf, int samples)
1192{
1193#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1194 int i;
1195 for (i = 0; i < samples; i++) {
1196 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1197 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1198 }
1199#endif
1200}
1201
1202static void cdrAttenuate(s16 *buf, int samples, int stereo)
1203{
1204 int i, l, r;
1205 int ll = cdr.AttenuatorLeftToLeft;
1206 int lr = cdr.AttenuatorLeftToRight;
1207 int rl = cdr.AttenuatorRightToLeft;
1208 int rr = cdr.AttenuatorRightToRight;
1209
1210 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1211 return;
1212
1213 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1214 return;
1215
1216 if (stereo) {
1217 for (i = 0; i < samples; i++) {
1218 l = buf[i * 2];
1219 r = buf[i * 2 + 1];
1220 l = (l * ll + r * rl) >> 7;
1221 r = (r * rr + l * lr) >> 7;
1222 ssat32_to_16(l);
1223 ssat32_to_16(r);
1224 buf[i * 2] = l;
1225 buf[i * 2 + 1] = r;
1226 }
1227 }
1228 else {
1229 for (i = 0; i < samples; i++) {
1230 l = buf[i];
1231 l = l * (ll + rl) >> 7;
1232 //r = r * (rr + lr) >> 7;
1233 ssat32_to_16(l);
1234 //ssat32_to_16(r);
1235 buf[i] = l;
1236 }
1237 }
1238}
1239
1240static void cdrReadInterruptSetResult(unsigned char result)
1241{
1242 if (cdr.Stat) {
1243 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1244 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1245 cdr.CmdInProgress, cdr.Stat);
1246 cdr.Irq1Pending = result;
1247 return;
1248 }
1249 SetResultSize(1);
1250 cdr.Result[0] = result;
1251 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1252 setIrq(0x1004);
1253}
1254
1255static void cdrUpdateTransferBuf(const u8 *buf)
1256{
1257 if (!buf)
1258 return;
1259 memcpy(cdr.Transfer, buf, DATA_SIZE);
1260 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1261 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1262 if (cdr.FifoOffset < 2048 + 12)
1263 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1264}
1265
1266static void cdrReadInterrupt(void)
1267{
1268 u8 *buf = NULL, *hdr;
1269 int read_ok;
1270
1271 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1272
1273 read_ok = ReadTrack(cdr.SetSectorPlay);
1274 if (read_ok)
1275 buf = CDR_getBuffer();
1276 if (buf == NULL)
1277 read_ok = 0;
1278
1279 if (!read_ok) {
1280 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1281 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1282 return;
1283 }
1284 memcpy(cdr.LocL, buf, 8);
1285
1286 if (!cdr.Irq1Pending)
1287 cdrUpdateTransferBuf(buf);
1288
1289 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1290 hdr = buf + 4;
1291 // Firemen 2: Multi-XA files - briefings, cutscenes
1292 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1293 cdr.File = hdr[0];
1294 cdr.Channel = hdr[1];
1295 }
1296
1297 /* Gameblabla
1298 * Skips playing on channel 255.
1299 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1300 * TODO : Check if this is the proper behaviour.
1301 * */
1302 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1303 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1304 if (!ret) {
1305 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1306 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1307 cdr.FirstSector = 0;
1308 }
1309 else cdr.FirstSector = -1;
1310 }
1311 }
1312
1313 /*
1314 Croc 2: $40 - only FORM1 (*)
1315 Judge Dredd: $C8 - only FORM1 (*)
1316 Sim Theme Park - no adpcm at all (zero)
1317 */
1318
1319 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1320 cdrReadInterruptSetResult(cdr.StatP);
1321
1322 cdr.SetSectorPlay[2]++;
1323 if (cdr.SetSectorPlay[2] == 75) {
1324 cdr.SetSectorPlay[2] = 0;
1325 cdr.SetSectorPlay[1]++;
1326 if (cdr.SetSectorPlay[1] == 60) {
1327 cdr.SetSectorPlay[1] = 0;
1328 cdr.SetSectorPlay[0]++;
1329 }
1330 }
1331
1332 if (!cdr.Irq1Pending) {
1333 // update for CdlGetlocP
1334 ReadTrack(cdr.SetSectorPlay);
1335 }
1336
1337 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1338}
1339
1340/*
1341cdrRead0:
1342 bit 0,1 - mode
1343 bit 2 - unknown
1344 bit 3 - unknown
1345 bit 4 - unknown
1346 bit 5 - 1 result ready
1347 bit 6 - 1 dma ready
1348 bit 7 - 1 command being processed
1349*/
1350
1351unsigned char cdrRead0(void) {
1352 if (cdr.ResultReady)
1353 cdr.Ctrl |= 0x20;
1354 else
1355 cdr.Ctrl &= ~0x20;
1356
1357 cdr.Ctrl |= 0x40; // data fifo not empty
1358
1359 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1360 cdr.Ctrl |= 0x18;
1361
1362 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1363
1364 return psxHu8(0x1800) = cdr.Ctrl;
1365}
1366
1367void cdrWrite0(unsigned char rt) {
1368 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1369
1370 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1371}
1372
1373unsigned char cdrRead1(void) {
1374 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1375 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1376 else
1377 psxHu8(0x1801) = 0;
1378 cdr.ResultP++;
1379 if (cdr.ResultP == cdr.ResultC)
1380 cdr.ResultReady = 0;
1381
1382 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1383
1384 return psxHu8(0x1801);
1385}
1386
1387void cdrWrite1(unsigned char rt) {
1388 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1389 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1390
1391 switch (cdr.Ctrl & 3) {
1392 case 0:
1393 break;
1394 case 3:
1395 cdr.AttenuatorRightToRightT = rt;
1396 return;
1397 default:
1398 return;
1399 }
1400
1401#ifdef CDR_LOG_CMD_IRQ
1402 SysPrintf("%u cdrom: CD1 write: %x (%s)", psxRegs.cycle, rt, CmdName[rt]);
1403 if (cdr.ParamC) {
1404 int i;
1405 SysPrintf(" Param[%d] = {", cdr.ParamC);
1406 for (i = 0; i < cdr.ParamC; i++)
1407 SysPrintf(" %x,", cdr.Param[i]);
1408 SysPrintf("}\n");
1409 } else {
1410 SysPrintf("\n");
1411 }
1412#endif
1413
1414 cdr.ResultReady = 0;
1415 cdr.Ctrl |= 0x80;
1416
1417 if (!cdr.CmdInProgress) {
1418 cdr.CmdInProgress = rt;
1419 // should be something like 12k + controller delays
1420 CDR_INT(5000);
1421 }
1422 else {
1423 CDR_LOG_I("%u cdrom: cmd while busy: %02x, prev %02x, busy %02x\n",
1424 psxRegs.cycle, rt, cdr.Cmd, cdr.CmdInProgress);
1425 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1426 cdr.CmdInProgress = rt;
1427 }
1428
1429 cdr.Cmd = rt;
1430}
1431
1432unsigned char cdrRead2(void) {
1433 unsigned char ret = cdr.Transfer[0x920];
1434
1435 if (cdr.FifoOffset < cdr.FifoSize)
1436 ret = cdr.Transfer[cdr.FifoOffset++];
1437 else
1438 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1439
1440 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1441 return ret;
1442}
1443
1444void cdrWrite2(unsigned char rt) {
1445 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1446 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1447
1448 switch (cdr.Ctrl & 3) {
1449 case 0:
1450 if (cdr.ParamC < 8) // FIXME: size and wrapping
1451 cdr.Param[cdr.ParamC++] = rt;
1452 return;
1453 case 1:
1454 cdr.Reg2 = rt;
1455 setIrq(0x1005);
1456 return;
1457 case 2:
1458 cdr.AttenuatorLeftToLeftT = rt;
1459 return;
1460 case 3:
1461 cdr.AttenuatorRightToLeftT = rt;
1462 return;
1463 }
1464}
1465
1466unsigned char cdrRead3(void) {
1467 if (cdr.Ctrl & 0x1)
1468 psxHu8(0x1803) = cdr.Stat | 0xE0;
1469 else
1470 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1471
1472 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1473 return psxHu8(0x1803);
1474}
1475
1476void cdrWrite3(unsigned char rt) {
1477 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1478 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1479
1480 switch (cdr.Ctrl & 3) {
1481 case 0:
1482 break; // transfer
1483 case 1:
1484 if (cdr.Stat & rt) {
1485#ifdef CDR_LOG_CMD_IRQ
1486 SysPrintf("%u cdrom: ack %02x (w %02x)\n",
1487 psxRegs.cycle, cdr.Stat & rt, rt);
1488#endif
1489 // note: Croc vs Discworld Noir
1490 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1491 (cdr.CmdInProgress || cdr.Irq1Pending))
1492 CDR_INT(850); // 711-993
1493 }
1494 cdr.Stat &= ~rt;
1495
1496 if (rt & 0x40)
1497 cdr.ParamC = 0;
1498 return;
1499 case 2:
1500 cdr.AttenuatorLeftToRightT = rt;
1501 return;
1502 case 3:
1503 if (rt & 0x20) {
1504 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1505 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1506 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1507 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1508 }
1509 return;
1510 }
1511
1512 // test: Viewpoint
1513 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1514 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1515 }
1516 else if (rt & 0x80) {
1517 switch (cdr.Mode & 0x30) {
1518 case MODE_SIZE_2328:
1519 case 0x00:
1520 cdr.FifoOffset = 12;
1521 cdr.FifoSize = 2048 + 12;
1522 break;
1523
1524 case MODE_SIZE_2340:
1525 default:
1526 cdr.FifoOffset = 0;
1527 cdr.FifoSize = 2340;
1528 break;
1529 }
1530 }
1531 else if (!(rt & 0xc0))
1532 cdr.FifoOffset = DATA_SIZE; // fifo empty
1533}
1534
1535void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1536 u32 cdsize;
1537 int size;
1538 u8 *ptr;
1539
1540 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1541
1542 switch (chcr & 0x71000000) {
1543 case 0x11000000:
1544 ptr = (u8 *)PSXM(madr);
1545 if (ptr == INVALID_PTR) {
1546 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1547 break;
1548 }
1549
1550 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1551
1552 /*
1553 GS CDX: Enhancement CD crash
1554 - Setloc 0:0:0
1555 - CdlPlay
1556 - Spams DMA3 and gets buffer overrun
1557 */
1558 size = DATA_SIZE - cdr.FifoOffset;
1559 if (size > cdsize)
1560 size = cdsize;
1561 if (size > 0)
1562 {
1563 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1564 cdr.FifoOffset += size;
1565 }
1566 if (size < cdsize) {
1567 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1568 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1569 }
1570 psxCpu->Clear(madr, cdsize / 4);
1571
1572 CDRDMA_INT((cdsize/4) * 24);
1573
1574 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1575 if (chcr & 0x100) {
1576 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1577 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1578 }
1579 else {
1580 // halted
1581 psxRegs.cycle += (cdsize/4) * 24 - 20;
1582 }
1583 return;
1584
1585 default:
1586 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1587 break;
1588 }
1589
1590 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1591 DMA_INTERRUPT(3);
1592}
1593
1594void cdrDmaInterrupt(void)
1595{
1596 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1597 {
1598 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1599 DMA_INTERRUPT(3);
1600 }
1601}
1602
1603static void getCdInfo(void)
1604{
1605 u8 tmp;
1606
1607 CDR_getTN(cdr.ResultTN);
1608 CDR_getTD(0, cdr.SetSectorEnd);
1609 tmp = cdr.SetSectorEnd[0];
1610 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1611 cdr.SetSectorEnd[2] = tmp;
1612}
1613
1614void cdrReset() {
1615 memset(&cdr, 0, sizeof(cdr));
1616 cdr.CurTrack = 1;
1617 cdr.File = 1;
1618 cdr.Channel = 1;
1619 cdr.Reg2 = 0x1f;
1620 cdr.Stat = NoIntr;
1621 cdr.FifoOffset = DATA_SIZE; // fifo empty
1622 if (CdromId[0] == '\0') {
1623 cdr.DriveState = DRIVESTATE_STOPPED;
1624 cdr.StatP = 0;
1625 }
1626 else {
1627 cdr.DriveState = DRIVESTATE_STANDBY;
1628 cdr.StatP = STATUS_ROTATING;
1629 }
1630
1631 // BIOS player - default values
1632 cdr.AttenuatorLeftToLeft = 0x80;
1633 cdr.AttenuatorLeftToRight = 0x00;
1634 cdr.AttenuatorRightToLeft = 0x00;
1635 cdr.AttenuatorRightToRight = 0x80;
1636
1637 getCdInfo();
1638}
1639
1640int cdrFreeze(void *f, int Mode) {
1641 u32 tmp;
1642 u8 tmpp[3];
1643
1644 if (Mode == 0 && !Config.Cdda)
1645 CDR_stop();
1646
1647 cdr.freeze_ver = 0x63647202;
1648 gzfreeze(&cdr, sizeof(cdr));
1649
1650 if (Mode == 1) {
1651 cdr.ParamP = cdr.ParamC;
1652 tmp = cdr.FifoOffset;
1653 }
1654
1655 gzfreeze(&tmp, sizeof(tmp));
1656
1657 if (Mode == 0) {
1658 getCdInfo();
1659
1660 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1661 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1662
1663 // read right sub data
1664 tmpp[0] = btoi(cdr.Prev[0]);
1665 tmpp[1] = btoi(cdr.Prev[1]);
1666 tmpp[2] = btoi(cdr.Prev[2]);
1667 cdr.Prev[0]++;
1668 ReadTrack(tmpp);
1669
1670 if (cdr.Play) {
1671 if (cdr.freeze_ver < 0x63647202)
1672 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1673
1674 Find_CurTrack(cdr.SetSectorPlay);
1675 if (!Config.Cdda)
1676 CDR_play(cdr.SetSectorPlay);
1677 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1678 CDRPLAYREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1679 }
1680
1681 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1682 // old versions did not latch Reg2, have to fixup..
1683 if (cdr.Reg2 == 0) {
1684 SysPrintf("cdrom: fixing up old savestate\n");
1685 cdr.Reg2 = 7;
1686 }
1687 // also did not save Attenuator..
1688 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1689 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1690 {
1691 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1692 }
1693 }
1694 }
1695
1696 return 0;
1697}
1698
1699void LidInterrupt(void) {
1700 getCdInfo();
1701 cdrLidSeekInterrupt();
1702}