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