input changes part3
[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 LastReadCycles;
106
107 u8 unused7;
108
109 u8 DriveState;
110 u8 FastForward;
111 u8 FastBackward;
112 u8 errorRetryhack;
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
565// LastReadCycles
566static int cdrSeekTime(unsigned char *target)
567{
568 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
569 int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
570 seekTime = MAX_VALUE(seekTime, 20000);
571
572 // need this stupidly long penalty or else Spyro2 intro desyncs
573 pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 4 ? cdReadTime * 25 : 0;
574 seekTime += pausePenalty;
575
576 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
577 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
578 return seekTime;
579}
580
581static u32 cdrAlignTimingHack(u32 cycles)
582{
583 /*
584 * timing hack for T'ai Fu - Wrath of the Tiger:
585 * The game has a bug where it issues some cdc commands from a low priority
586 * vint handler, however there is a higher priority default bios handler
587 * that acks the vint irq and returns, so game's handler is not reached
588 * (see bios irq handler chains at e004 and the game's irq handling func
589 * at 80036810). For the game to work, vint has to arrive after the bios
590 * vint handler rejects some other irq (of which only cd and rcnt2 are
591 * active), but before the game's handler loop reads I_STAT. The time
592 * window for this is quite small (~1k cycles of so). Apparently this
593 * somehow happens naturally on the real hardware.
594 *
595 * Note: always enforcing this breaks other games like Crash PAL version
596 * (inputs get dropped because bios handler doesn't see interrupts).
597 */
598 u32 vint_rel;
599 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
600 return cycles;
601 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
602 vint_rel += PSXCLK / 60;
603 while ((s32)(vint_rel - cycles) < 0)
604 vint_rel += PSXCLK / 60;
605 return vint_rel;
606}
607
608static void cdrUpdateTransferBuf(const u8 *buf);
609static void cdrReadInterrupt(void);
610static void cdrPrepCdda(s16 *buf, int samples);
611static void cdrAttenuate(s16 *buf, int samples, int stereo);
612
613static void msfiAdd(u8 *msfi, u32 count)
614{
615 assert(count < 75);
616 msfi[2] += count;
617 if (msfi[2] >= 75) {
618 msfi[2] -= 75;
619 msfi[1]++;
620 if (msfi[1] == 60) {
621 msfi[1] = 0;
622 msfi[0]++;
623 }
624 }
625}
626
627void cdrPlayReadInterrupt(void)
628{
629 cdr.LastReadCycles = psxRegs.cycle;
630
631 if (cdr.Reading) {
632 cdrReadInterrupt();
633 return;
634 }
635
636 if (!cdr.Play) return;
637
638 CDR_LOG( "CDDA - %d:%d:%d\n",
639 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
640
641 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
642 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
643 StopCdda();
644 SetPlaySeekRead(cdr.StatP, 0);
645 cdr.TrackChanged = TRUE;
646 }
647 else {
648 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
649 }
650
651 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
652 cdrPlayInterrupt_Autopause();
653
654 if (!cdr.Muted && !Config.Cdda) {
655 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
656 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
657 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
658 cdr.FirstSector = 0;
659 }
660
661 msfiAdd(cdr.SetSectorPlay, 1);
662
663 // update for CdlGetlocP/autopause
664 generate_subq(cdr.SetSectorPlay);
665
666 CDRPLAYREAD_INT(cdReadTime, 0);
667}
668
669#define CMD_PART2 0x100
670#define CMD_WHILE_NOT_READY 0x200
671
672void cdrInterrupt(void) {
673 int start_rotating = 0;
674 int error = 0;
675 u32 cycles, seekTime = 0;
676 u32 second_resp_time = 0;
677 const void *buf;
678 u8 ParamC;
679 u8 set_loc[3];
680 int read_ok;
681 u16 not_ready = 0;
682 u16 Cmd;
683 int i;
684
685 if (cdr.Stat) {
686 CDR_LOG_I("cmd %02x with irqstat %x\n",
687 cdr.CmdInProgress, cdr.Stat);
688 return;
689 }
690 if (cdr.Irq1Pending) {
691 // hand out the "newest" sector, according to nocash
692 cdrUpdateTransferBuf(CDR_getBuffer());
693 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
694 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
695 cdr.CmdInProgress, cdr.Irq1Pending);
696 SetResultSize(1);
697 cdr.Result[0] = cdr.Irq1Pending;
698 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
699 cdr.Irq1Pending = 0;
700 setIrq(0x1003);
701 return;
702 }
703
704 // default response
705 SetResultSize(1);
706 cdr.Result[0] = cdr.StatP;
707 cdr.Stat = Acknowledge;
708
709 Cmd = cdr.CmdInProgress;
710 cdr.CmdInProgress = 0;
711 ParamC = cdr.ParamC;
712
713 if (Cmd < 0x100) {
714 cdr.Ctrl &= ~0x80;
715 cdr.ParamC = 0;
716 cdr.Cmd = 0;
717 }
718
719 switch (cdr.DriveState) {
720 case DRIVESTATE_PREPARE_CD:
721 if (Cmd > 2) {
722 // Syphon filter 2 expects commands to work shortly after it sees
723 // STATUS_ROTATING, so give up trying to emulate the startup seq
724 cdr.DriveState = DRIVESTATE_STANDBY;
725 cdr.StatP &= ~STATUS_SEEK;
726 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
727 break;
728 }
729 // fallthrough
730 case DRIVESTATE_LID_OPEN:
731 case DRIVESTATE_RESCAN_CD:
732 // no disk or busy with the initial scan, allowed cmds are limited
733 not_ready = CMD_WHILE_NOT_READY;
734 break;
735 }
736
737 switch (Cmd | not_ready) {
738 case CdlNop:
739 case CdlNop + CMD_WHILE_NOT_READY:
740 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
741 cdr.StatP &= ~STATUS_SHELLOPEN;
742 break;
743
744 case CdlSetloc:
745 case CdlSetloc + CMD_WHILE_NOT_READY:
746 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
747
748 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
749 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))
750 {
751 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
752 if (++cdr.errorRetryhack > 100)
753 break;
754 error = ERROR_INVALIDARG;
755 goto set_error;
756 }
757 else
758 {
759 for (i = 0; i < 3; i++)
760 set_loc[i] = btoi(cdr.Param[i]);
761 memcpy(cdr.SetSector, set_loc, 3);
762 cdr.SetSector[3] = 0;
763 cdr.SetlocPending = 1;
764 cdr.errorRetryhack = 0;
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) ? 1 : 2) * 1097107);
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 = MODE_SIZE_2340; /* 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 & MODE_SPEED) ? 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("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("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1197 }
1198
1199 setIrq(Cmd);
1200}
1201
1202#ifdef HAVE_ARMV7
1203 #define ssat32_to_16(v) \
1204 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1205#else
1206 #define ssat32_to_16(v) do { \
1207 if (v < -32768) v = -32768; \
1208 else if (v > 32767) v = 32767; \
1209 } while (0)
1210#endif
1211
1212static void cdrPrepCdda(s16 *buf, int samples)
1213{
1214#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1215 int i;
1216 for (i = 0; i < samples; i++) {
1217 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1218 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1219 }
1220#endif
1221}
1222
1223static void cdrAttenuate(s16 *buf, int samples, int stereo)
1224{
1225 int i, l, r;
1226 int ll = cdr.AttenuatorLeftToLeft;
1227 int lr = cdr.AttenuatorLeftToRight;
1228 int rl = cdr.AttenuatorRightToLeft;
1229 int rr = cdr.AttenuatorRightToRight;
1230
1231 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1232 return;
1233
1234 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1235 return;
1236
1237 if (stereo) {
1238 for (i = 0; i < samples; i++) {
1239 l = buf[i * 2];
1240 r = buf[i * 2 + 1];
1241 l = (l * ll + r * rl) >> 7;
1242 r = (r * rr + l * lr) >> 7;
1243 ssat32_to_16(l);
1244 ssat32_to_16(r);
1245 buf[i * 2] = l;
1246 buf[i * 2 + 1] = r;
1247 }
1248 }
1249 else {
1250 for (i = 0; i < samples; i++) {
1251 l = buf[i];
1252 l = l * (ll + rl) >> 7;
1253 //r = r * (rr + lr) >> 7;
1254 ssat32_to_16(l);
1255 //ssat32_to_16(r);
1256 buf[i] = l;
1257 }
1258 }
1259}
1260
1261static void cdrReadInterruptSetResult(unsigned char result)
1262{
1263 if (cdr.Stat) {
1264 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1265 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1266 cdr.CmdInProgress, cdr.Stat);
1267 cdr.Irq1Pending = result;
1268 return;
1269 }
1270 SetResultSize(1);
1271 cdr.Result[0] = result;
1272 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1273 setIrq(0x1004);
1274}
1275
1276static void cdrUpdateTransferBuf(const u8 *buf)
1277{
1278 if (!buf)
1279 return;
1280 memcpy(cdr.Transfer, buf, DATA_SIZE);
1281 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1282 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1283 if (cdr.FifoOffset < 2048 + 12)
1284 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1285}
1286
1287static void cdrReadInterrupt(void)
1288{
1289 u8 *buf = NULL, *hdr;
1290 u8 subqPos[3];
1291 int read_ok;
1292
1293 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1294 msfiAdd(subqPos, cdr.SubqForwardSectors);
1295 UpdateSubq(subqPos);
1296 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1297 cdr.SubqForwardSectors++;
1298 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1299 return;
1300 }
1301
1302 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1303 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1304
1305 read_ok = ReadTrack(cdr.SetSectorPlay);
1306 if (read_ok)
1307 buf = CDR_getBuffer();
1308 if (buf == NULL)
1309 read_ok = 0;
1310
1311 if (!read_ok) {
1312 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1313 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1314 return;
1315 }
1316 memcpy(cdr.LocL, buf, 8);
1317
1318 if (!cdr.Stat && !cdr.Irq1Pending)
1319 cdrUpdateTransferBuf(buf);
1320
1321 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1322 hdr = buf + 4;
1323 // Firemen 2: Multi-XA files - briefings, cutscenes
1324 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1325 cdr.File = hdr[0];
1326 cdr.Channel = hdr[1];
1327 }
1328
1329 /* Gameblabla
1330 * Skips playing on channel 255.
1331 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1332 * TODO : Check if this is the proper behaviour.
1333 * */
1334 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1335 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1336 if (!ret) {
1337 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1338 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1339 cdr.FirstSector = 0;
1340 }
1341 else cdr.FirstSector = -1;
1342 }
1343 }
1344
1345 /*
1346 Croc 2: $40 - only FORM1 (*)
1347 Judge Dredd: $C8 - only FORM1 (*)
1348 Sim Theme Park - no adpcm at all (zero)
1349 */
1350
1351 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1352 cdrReadInterruptSetResult(cdr.StatP);
1353
1354 msfiAdd(cdr.SetSectorPlay, 1);
1355
1356 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1357}
1358
1359/*
1360cdrRead0:
1361 bit 0,1 - mode
1362 bit 2 - unknown
1363 bit 3 - unknown
1364 bit 4 - unknown
1365 bit 5 - 1 result ready
1366 bit 6 - 1 dma ready
1367 bit 7 - 1 command being processed
1368*/
1369
1370unsigned char cdrRead0(void) {
1371 if (cdr.ResultReady)
1372 cdr.Ctrl |= 0x20;
1373 else
1374 cdr.Ctrl &= ~0x20;
1375
1376 cdr.Ctrl |= 0x40; // data fifo not empty
1377
1378 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1379 cdr.Ctrl |= 0x18;
1380
1381 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1382
1383 return psxHu8(0x1800) = cdr.Ctrl;
1384}
1385
1386void cdrWrite0(unsigned char rt) {
1387 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1388
1389 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1390}
1391
1392unsigned char cdrRead1(void) {
1393 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1394 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1395 else
1396 psxHu8(0x1801) = 0;
1397 cdr.ResultP++;
1398 if (cdr.ResultP == cdr.ResultC)
1399 cdr.ResultReady = 0;
1400
1401 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1402
1403 return psxHu8(0x1801);
1404}
1405
1406void cdrWrite1(unsigned char rt) {
1407 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1408 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1409
1410 switch (cdr.Ctrl & 3) {
1411 case 0:
1412 break;
1413 case 3:
1414 cdr.AttenuatorRightToRightT = rt;
1415 return;
1416 default:
1417 return;
1418 }
1419
1420#ifdef CDR_LOG_CMD_IRQ
1421 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1422 if (cdr.ParamC) {
1423 int i;
1424 SysPrintf(" Param[%d] = {", cdr.ParamC);
1425 for (i = 0; i < cdr.ParamC; i++)
1426 SysPrintf(" %x,", cdr.Param[i]);
1427 SysPrintf("}\n");
1428 } else {
1429 SysPrintf("\n");
1430 }
1431#endif
1432
1433 cdr.ResultReady = 0;
1434 cdr.Ctrl |= 0x80;
1435
1436 if (!cdr.CmdInProgress) {
1437 cdr.CmdInProgress = rt;
1438 // should be something like 12k + controller delays
1439 CDR_INT(5000);
1440 }
1441 else {
1442 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1443 rt, cdr.Cmd, cdr.CmdInProgress);
1444 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1445 cdr.CmdInProgress = rt;
1446 }
1447
1448 cdr.Cmd = rt;
1449}
1450
1451unsigned char cdrRead2(void) {
1452 unsigned char ret = cdr.Transfer[0x920];
1453
1454 if (cdr.FifoOffset < cdr.FifoSize)
1455 ret = cdr.Transfer[cdr.FifoOffset++];
1456 else
1457 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1458
1459 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1460 return ret;
1461}
1462
1463void cdrWrite2(unsigned char rt) {
1464 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1465 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1466
1467 switch (cdr.Ctrl & 3) {
1468 case 0:
1469 if (cdr.ParamC < 8) // FIXME: size and wrapping
1470 cdr.Param[cdr.ParamC++] = rt;
1471 return;
1472 case 1:
1473 cdr.Reg2 = rt;
1474 setIrq(0x1005);
1475 return;
1476 case 2:
1477 cdr.AttenuatorLeftToLeftT = rt;
1478 return;
1479 case 3:
1480 cdr.AttenuatorRightToLeftT = rt;
1481 return;
1482 }
1483}
1484
1485unsigned char cdrRead3(void) {
1486 if (cdr.Ctrl & 0x1)
1487 psxHu8(0x1803) = cdr.Stat | 0xE0;
1488 else
1489 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1490
1491 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1492 return psxHu8(0x1803);
1493}
1494
1495void cdrWrite3(unsigned char rt) {
1496 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1497 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1498
1499 switch (cdr.Ctrl & 3) {
1500 case 0:
1501 break; // transfer
1502 case 1:
1503 if (cdr.Stat & rt) {
1504 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1505 + psxRegs.intCycle[PSXINT_CDR].cycle;
1506 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1507#ifdef CDR_LOG_CMD_IRQ
1508 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1509 !!pending, cdr.CmdInProgress,
1510 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1511#endif
1512 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1513 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1514 {
1515 s32 c = 2048;
1516 if (cdr.CmdInProgress) {
1517 c = 2048 - (psxRegs.cycle - nextCycle);
1518 c = MAX_VALUE(c, 512);
1519 }
1520 CDR_INT(c);
1521 }
1522 }
1523 cdr.Stat &= ~rt;
1524
1525 if (rt & 0x40)
1526 cdr.ParamC = 0;
1527 return;
1528 case 2:
1529 cdr.AttenuatorLeftToRightT = rt;
1530 return;
1531 case 3:
1532 if (rt & 0x20) {
1533 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1534 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1535 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1536 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1537 }
1538 return;
1539 }
1540
1541 // test: Viewpoint
1542 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1543 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1544 }
1545 else if (rt & 0x80) {
1546 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1547 case MODE_SIZE_2328:
1548 case 0x00:
1549 cdr.FifoOffset = 12;
1550 cdr.FifoSize = 2048 + 12;
1551 break;
1552
1553 case MODE_SIZE_2340:
1554 default:
1555 cdr.FifoOffset = 0;
1556 cdr.FifoSize = 2340;
1557 break;
1558 }
1559 }
1560 else if (!(rt & 0xc0))
1561 cdr.FifoOffset = DATA_SIZE; // fifo empty
1562}
1563
1564void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1565 u32 cdsize, max_words;
1566 int size;
1567 u8 *ptr;
1568
1569#if 0
1570 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1571 if (cdr.FifoOffset == 0) {
1572 ptr = cdr.Transfer;
1573 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1574 }
1575 SysPrintf("\n");
1576#endif
1577
1578 switch (chcr & 0x71000000) {
1579 case 0x11000000:
1580 ptr = getDmaRam(madr, &max_words);
1581 if (ptr == INVALID_PTR) {
1582 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1583 break;
1584 }
1585
1586 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1587
1588 /*
1589 GS CDX: Enhancement CD crash
1590 - Setloc 0:0:0
1591 - CdlPlay
1592 - Spams DMA3 and gets buffer overrun
1593 */
1594 size = DATA_SIZE - cdr.FifoOffset;
1595 if (size > cdsize)
1596 size = cdsize;
1597 if (size > max_words * 4)
1598 size = max_words * 4;
1599 if (size > 0)
1600 {
1601 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1602 cdr.FifoOffset += size;
1603 }
1604 if (size < cdsize) {
1605 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1606 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1607 }
1608 psxCpu->Clear(madr, cdsize / 4);
1609
1610 CDRDMA_INT((cdsize/4) * 24);
1611
1612 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1613 if (chcr & 0x100) {
1614 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1615 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1616 }
1617 else {
1618 // halted
1619 psxRegs.cycle += (cdsize/4) * 24 - 20;
1620 }
1621 return;
1622
1623 default:
1624 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1625 break;
1626 }
1627
1628 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1629 DMA_INTERRUPT(3);
1630}
1631
1632void cdrDmaInterrupt(void)
1633{
1634 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1635 {
1636 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1637 DMA_INTERRUPT(3);
1638 }
1639}
1640
1641static void getCdInfo(void)
1642{
1643 u8 tmp;
1644
1645 CDR_getTN(cdr.ResultTN);
1646 CDR_getTD(0, cdr.SetSectorEnd);
1647 tmp = cdr.SetSectorEnd[0];
1648 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1649 cdr.SetSectorEnd[2] = tmp;
1650}
1651
1652void cdrReset() {
1653 memset(&cdr, 0, sizeof(cdr));
1654 cdr.CurTrack = 1;
1655 cdr.File = 1;
1656 cdr.Channel = 1;
1657 cdr.Reg2 = 0x1f;
1658 cdr.Stat = NoIntr;
1659 cdr.FifoOffset = DATA_SIZE; // fifo empty
1660 if (CdromId[0] == '\0') {
1661 cdr.DriveState = DRIVESTATE_STOPPED;
1662 cdr.StatP = 0;
1663 }
1664 else {
1665 cdr.DriveState = DRIVESTATE_STANDBY;
1666 cdr.StatP = STATUS_ROTATING;
1667 }
1668
1669 // BIOS player - default values
1670 cdr.AttenuatorLeftToLeft = 0x80;
1671 cdr.AttenuatorLeftToRight = 0x00;
1672 cdr.AttenuatorRightToLeft = 0x00;
1673 cdr.AttenuatorRightToRight = 0x80;
1674
1675 getCdInfo();
1676}
1677
1678int cdrFreeze(void *f, int Mode) {
1679 u32 tmp;
1680 u8 tmpp[3];
1681
1682 if (Mode == 0 && !Config.Cdda)
1683 CDR_stop();
1684
1685 cdr.freeze_ver = 0x63647202;
1686 gzfreeze(&cdr, sizeof(cdr));
1687
1688 if (Mode == 1) {
1689 cdr.ParamP = cdr.ParamC;
1690 tmp = cdr.FifoOffset;
1691 }
1692
1693 gzfreeze(&tmp, sizeof(tmp));
1694
1695 if (Mode == 0) {
1696 getCdInfo();
1697
1698 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1699 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1700 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1701 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1702
1703 // read right sub data
1704 tmpp[0] = btoi(cdr.Prev[0]);
1705 tmpp[1] = btoi(cdr.Prev[1]);
1706 tmpp[2] = btoi(cdr.Prev[2]);
1707 cdr.Prev[0]++;
1708 ReadTrack(tmpp);
1709
1710 if (cdr.Play) {
1711 if (cdr.freeze_ver < 0x63647202)
1712 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1713
1714 Find_CurTrack(cdr.SetSectorPlay);
1715 if (!Config.Cdda)
1716 CDR_play(cdr.SetSectorPlay);
1717 }
1718
1719 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1720 // old versions did not latch Reg2, have to fixup..
1721 if (cdr.Reg2 == 0) {
1722 SysPrintf("cdrom: fixing up old savestate\n");
1723 cdr.Reg2 = 7;
1724 }
1725 // also did not save Attenuator..
1726 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1727 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1728 {
1729 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1730 }
1731 }
1732 }
1733
1734 return 0;
1735}
1736
1737void LidInterrupt(void) {
1738 getCdInfo();
1739 cdrLidSeekInterrupt();
1740}