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