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