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