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