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