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