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