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