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