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