cdrom: allow resetting with lid open
[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
1136 /* This adds the string "PCSX" in Playstation bios boot screen */
1137 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1138 cdr.Stat = Complete;
1139 break;
1140
1141 case CdlInit:
1142 case CdlInit + CMD_WHILE_NOT_READY:
1143 StopCdda();
1144 StopReading();
1145 SetPlaySeekRead(cdr.StatP, 0);
1146 // yes, it really sets STATUS_SHELLOPEN
1147 cdr.StatP |= STATUS_SHELLOPEN;
1148 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1149 set_event(PSXINT_CDRLID, 20480);
1150 start_rotating = 1;
1151 break;
1152
1153 case CdlGetQ:
1154 case CdlGetQ + CMD_WHILE_NOT_READY:
1155 break;
1156
1157 case CdlReadToc:
1158 case CdlReadToc + CMD_WHILE_NOT_READY:
1159 cdr.LocL[0] = LOCL_INVALID;
1160 second_resp_time = cdReadTime * 180 / 4;
1161 start_rotating = 1;
1162 break;
1163
1164 case CdlReadToc + CMD_PART2:
1165 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1166 cdr.Stat = Complete;
1167 break;
1168
1169 case CdlReadN:
1170 case CdlReadS:
1171 if (cdr.Reading && !cdr.SetlocPending)
1172 break;
1173
1174 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1175
1176 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1177 // Read* acts as play for cdda tracks in cdda mode
1178 goto do_CdlPlay;
1179
1180 StopCdda();
1181 if (cdr.SetlocPending) {
1182 seekTime = cdrSeekTime(cdr.SetSector);
1183 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1184 cdr.SetlocPending = 0;
1185 }
1186 cdr.Reading = 1;
1187 cdr.FirstSector = 1;
1188
1189 // Fighting Force 2 - update subq time immediately
1190 // - fixes new game
1191 UpdateSubq(cdr.SetSectorPlay);
1192 cdr.LocL[0] = LOCL_INVALID;
1193 cdr.SubqForwardSectors = 1;
1194 cdr.sectorsRead = 0;
1195
1196 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1197 cycles += seekTime;
1198 if (Config.hacks.cdr_read_timing)
1199 cycles = cdrAlignTimingHack(cycles);
1200 CDRPLAYREAD_INT(cycles, 1);
1201
1202 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1203 start_rotating = 1;
1204 break;
1205
1206 case CdlSync:
1207 default:
1208 error = ERROR_INVALIDCMD;
1209 // FALLTHROUGH
1210
1211 set_error:
1212 SetResultSize(2);
1213 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1214 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1215 cdr.Stat = DiskError;
1216 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1217 break;
1218 }
1219
1220 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1221 cdr.DriveState = DRIVESTATE_STANDBY;
1222 cdr.StatP |= STATUS_ROTATING;
1223 }
1224
1225 if (second_resp_time) {
1226 cdr.CmdInProgress = Cmd | 0x100;
1227 set_event(PSXINT_CDR, second_resp_time);
1228 }
1229 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1230 cdr.CmdInProgress = cdr.Cmd;
1231 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1232 }
1233
1234 setIrq(Cmd);
1235}
1236
1237#ifdef HAVE_ARMV7
1238 #define ssat32_to_16(v) \
1239 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1240#else
1241 #define ssat32_to_16(v) do { \
1242 if (v < -32768) v = -32768; \
1243 else if (v > 32767) v = 32767; \
1244 } while (0)
1245#endif
1246
1247static void cdrPrepCdda(s16 *buf, int samples)
1248{
1249#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1250 int i;
1251 for (i = 0; i < samples; i++) {
1252 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1253 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1254 }
1255#endif
1256}
1257
1258static void cdrAttenuate(s16 *buf, int samples, int stereo)
1259{
1260 int i, l, r;
1261 int ll = cdr.AttenuatorLeftToLeft;
1262 int lr = cdr.AttenuatorLeftToRight;
1263 int rl = cdr.AttenuatorRightToLeft;
1264 int rr = cdr.AttenuatorRightToRight;
1265
1266 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1267 return;
1268
1269 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1270 return;
1271
1272 if (stereo) {
1273 for (i = 0; i < samples; i++) {
1274 l = buf[i * 2];
1275 r = buf[i * 2 + 1];
1276 l = (l * ll + r * rl) >> 7;
1277 r = (r * rr + l * lr) >> 7;
1278 ssat32_to_16(l);
1279 ssat32_to_16(r);
1280 buf[i * 2] = l;
1281 buf[i * 2 + 1] = r;
1282 }
1283 }
1284 else {
1285 for (i = 0; i < samples; i++) {
1286 l = buf[i];
1287 l = l * (ll + rl) >> 7;
1288 //r = r * (rr + lr) >> 7;
1289 ssat32_to_16(l);
1290 //ssat32_to_16(r);
1291 buf[i] = l;
1292 }
1293 }
1294}
1295
1296static void cdrReadInterruptSetResult(unsigned char result)
1297{
1298 if (cdr.Stat) {
1299 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1300 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1301 cdr.CmdInProgress, cdr.Stat);
1302 cdr.Irq1Pending = result;
1303 return;
1304 }
1305 SetResultSize(1);
1306 cdr.Result[0] = result;
1307 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1308 setIrq(0x1004);
1309}
1310
1311static void cdrUpdateTransferBuf(const u8 *buf)
1312{
1313 if (!buf)
1314 return;
1315 memcpy(cdr.Transfer, buf, DATA_SIZE);
1316 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1317 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1318 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1319 if (cdr.FifoOffset < 2048 + 12)
1320 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1321}
1322
1323static void cdrReadInterrupt(void)
1324{
1325 u8 *buf = NULL, *hdr;
1326 u8 subqPos[3];
1327 int read_ok;
1328
1329 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1330 msfiAdd(subqPos, cdr.SubqForwardSectors);
1331 UpdateSubq(subqPos);
1332 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1333 cdr.SubqForwardSectors++;
1334 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1335 return;
1336 }
1337
1338 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1339 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1340 cdr.sectorsRead++;
1341
1342 read_ok = ReadTrack(cdr.SetSectorPlay);
1343 if (read_ok)
1344 buf = CDR_getBuffer();
1345 if (buf == NULL)
1346 read_ok = 0;
1347
1348 if (!read_ok) {
1349 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1350 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1351 return;
1352 }
1353 memcpy(cdr.LocL, buf, 8);
1354
1355 if (!cdr.Stat && !cdr.Irq1Pending)
1356 cdrUpdateTransferBuf(buf);
1357
1358 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1359 hdr = buf + 4;
1360 // Firemen 2: Multi-XA files - briefings, cutscenes
1361 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1362 cdr.File = hdr[0];
1363 cdr.Channel = hdr[1];
1364 }
1365
1366 /* Gameblabla
1367 * Skips playing on channel 255.
1368 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1369 * TODO : Check if this is the proper behaviour.
1370 * */
1371 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1372 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1373 if (!ret) {
1374 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1375 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1376 cdr.FirstSector = 0;
1377 }
1378 else cdr.FirstSector = -1;
1379 }
1380 }
1381
1382 /*
1383 Croc 2: $40 - only FORM1 (*)
1384 Judge Dredd: $C8 - only FORM1 (*)
1385 Sim Theme Park - no adpcm at all (zero)
1386 */
1387
1388 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1389 cdrReadInterruptSetResult(cdr.StatP);
1390
1391 msfiAdd(cdr.SetSectorPlay, 1);
1392
1393 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1394}
1395
1396/*
1397cdrRead0:
1398 bit 0,1 - mode
1399 bit 2 - unknown
1400 bit 3 - unknown
1401 bit 4 - unknown
1402 bit 5 - 1 result ready
1403 bit 6 - 1 dma ready
1404 bit 7 - 1 command being processed
1405*/
1406
1407unsigned char cdrRead0(void) {
1408 if (cdr.ResultReady)
1409 cdr.Ctrl |= 0x20;
1410 else
1411 cdr.Ctrl &= ~0x20;
1412
1413 cdr.Ctrl |= 0x40; // data fifo not empty
1414
1415 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1416 cdr.Ctrl |= 0x18;
1417
1418 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1419
1420 return psxHu8(0x1800) = cdr.Ctrl;
1421}
1422
1423void cdrWrite0(unsigned char rt) {
1424 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1425
1426 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1427}
1428
1429unsigned char cdrRead1(void) {
1430 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1431 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1432 else
1433 psxHu8(0x1801) = 0;
1434 cdr.ResultP++;
1435 if (cdr.ResultP == cdr.ResultC)
1436 cdr.ResultReady = 0;
1437
1438 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1439
1440 return psxHu8(0x1801);
1441}
1442
1443void cdrWrite1(unsigned char rt) {
1444 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1445 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1446
1447 switch (cdr.Ctrl & 3) {
1448 case 0:
1449 break;
1450 case 3:
1451 cdr.AttenuatorRightToRightT = rt;
1452 return;
1453 default:
1454 return;
1455 }
1456
1457#ifdef CDR_LOG_CMD_IRQ
1458 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1459 if (cdr.ParamC) {
1460 int i;
1461 SysPrintf(" Param[%d] = {", cdr.ParamC);
1462 for (i = 0; i < cdr.ParamC; i++)
1463 SysPrintf(" %x,", cdr.Param[i]);
1464 SysPrintf("}\n");
1465 } else {
1466 SysPrintf("\n");
1467 }
1468#endif
1469
1470 cdr.ResultReady = 0;
1471 cdr.Ctrl |= 0x80;
1472
1473 if (!cdr.CmdInProgress) {
1474 cdr.CmdInProgress = rt;
1475 // should be something like 12k + controller delays
1476 set_event(PSXINT_CDR, 5000);
1477 }
1478 else {
1479 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1480 rt, cdr.Cmd, cdr.CmdInProgress);
1481 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1482 cdr.CmdInProgress = rt;
1483 }
1484
1485 cdr.Cmd = rt;
1486}
1487
1488unsigned char cdrRead2(void) {
1489 unsigned char ret = cdr.Transfer[0x920];
1490
1491 if (cdr.FifoOffset < cdr.FifoSize)
1492 ret = cdr.Transfer[cdr.FifoOffset++];
1493 else
1494 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1495
1496 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1497 return ret;
1498}
1499
1500void cdrWrite2(unsigned char rt) {
1501 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1502 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1503
1504 switch (cdr.Ctrl & 3) {
1505 case 0:
1506 if (cdr.ParamC < 8) // FIXME: size and wrapping
1507 cdr.Param[cdr.ParamC++] = rt;
1508 return;
1509 case 1:
1510 cdr.Reg2 = rt;
1511 setIrq(0x1005);
1512 return;
1513 case 2:
1514 cdr.AttenuatorLeftToLeftT = rt;
1515 return;
1516 case 3:
1517 cdr.AttenuatorRightToLeftT = rt;
1518 return;
1519 }
1520}
1521
1522unsigned char cdrRead3(void) {
1523 if (cdr.Ctrl & 0x1)
1524 psxHu8(0x1803) = cdr.Stat | 0xE0;
1525 else
1526 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1527
1528 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1529 return psxHu8(0x1803);
1530}
1531
1532void cdrWrite3(unsigned char rt) {
1533 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1534 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1535
1536 switch (cdr.Ctrl & 3) {
1537 case 0:
1538 break; // transfer
1539 case 1:
1540 if (cdr.Stat & rt) {
1541 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1542 + psxRegs.intCycle[PSXINT_CDR].cycle;
1543 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1544#ifdef CDR_LOG_CMD_IRQ
1545 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1546 !!pending, cdr.CmdInProgress,
1547 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1548#endif
1549 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1550 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1551 {
1552 s32 c = 2048;
1553 if (cdr.CmdInProgress) {
1554 c = 2048 - (psxRegs.cycle - nextCycle);
1555 c = MAX_VALUE(c, 512);
1556 }
1557 set_event(PSXINT_CDR, c);
1558 }
1559 }
1560 cdr.Stat &= ~rt;
1561
1562 if (rt & 0x40)
1563 cdr.ParamC = 0;
1564 return;
1565 case 2:
1566 cdr.AttenuatorLeftToRightT = rt;
1567 return;
1568 case 3:
1569 if (rt & 0x20) {
1570 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1571 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1572 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1573 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1574 }
1575 return;
1576 }
1577
1578 // test: Viewpoint
1579 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1580 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1581 }
1582 else if (rt & 0x80) {
1583 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1584 case MODE_SIZE_2328:
1585 case 0x00:
1586 cdr.FifoOffset = 12;
1587 cdr.FifoSize = 2048 + 12;
1588 break;
1589
1590 case MODE_SIZE_2340:
1591 default:
1592 cdr.FifoOffset = 0;
1593 cdr.FifoSize = 2340;
1594 break;
1595 }
1596 }
1597 else if (!(rt & 0xc0))
1598 cdr.FifoOffset = DATA_SIZE; // fifo empty
1599}
1600
1601void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1602 u32 cdsize, max_words;
1603 int size;
1604 u8 *ptr;
1605
1606#if 0
1607 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1608 if (cdr.FifoOffset == 0) {
1609 ptr = cdr.Transfer;
1610 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1611 }
1612 SysPrintf("\n");
1613#endif
1614
1615 switch (chcr & 0x71000000) {
1616 case 0x11000000:
1617 ptr = getDmaRam(madr, &max_words);
1618 if (ptr == INVALID_PTR) {
1619 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1620 break;
1621 }
1622
1623 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1624
1625 /*
1626 GS CDX: Enhancement CD crash
1627 - Setloc 0:0:0
1628 - CdlPlay
1629 - Spams DMA3 and gets buffer overrun
1630 */
1631 size = DATA_SIZE - cdr.FifoOffset;
1632 if (size > cdsize)
1633 size = cdsize;
1634 if (size > max_words * 4)
1635 size = max_words * 4;
1636 if (size > 0)
1637 {
1638 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1639 cdr.FifoOffset += size;
1640 }
1641 if (size < cdsize) {
1642 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1643 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1644 }
1645 psxCpu->Clear(madr, cdsize / 4);
1646
1647 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1648
1649 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1650 if (chcr & 0x100) {
1651 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1652 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1653 }
1654 else {
1655 // halted
1656 psxRegs.cycle += (cdsize/4) * 24 - 20;
1657 }
1658 return;
1659
1660 default:
1661 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1662 break;
1663 }
1664
1665 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1666 DMA_INTERRUPT(3);
1667}
1668
1669void cdrDmaInterrupt(void)
1670{
1671 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1672 {
1673 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1674 DMA_INTERRUPT(3);
1675 }
1676}
1677
1678static void getCdInfo(void)
1679{
1680 u8 tmp;
1681
1682 CDR_getTN(cdr.ResultTN);
1683 CDR_getTD(0, cdr.SetSectorEnd);
1684 tmp = cdr.SetSectorEnd[0];
1685 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1686 cdr.SetSectorEnd[2] = tmp;
1687}
1688
1689void cdrReset() {
1690 memset(&cdr, 0, sizeof(cdr));
1691 cdr.CurTrack = 1;
1692 cdr.File = 1;
1693 cdr.Channel = 1;
1694 cdr.Reg2 = 0x1f;
1695 cdr.Stat = NoIntr;
1696 cdr.FifoOffset = DATA_SIZE; // fifo empty
1697
1698 CDR_getStatus(&stat);
1699 if (stat.Status & STATUS_SHELLOPEN) {
1700 cdr.DriveState = DRIVESTATE_LID_OPEN;
1701 cdr.StatP = STATUS_SHELLOPEN;
1702 }
1703 else if (CdromId[0] == '\0') {
1704 cdr.DriveState = DRIVESTATE_STOPPED;
1705 cdr.StatP = 0;
1706 }
1707 else {
1708 cdr.DriveState = DRIVESTATE_STANDBY;
1709 cdr.StatP = STATUS_ROTATING;
1710 }
1711
1712 // BIOS player - default values
1713 cdr.AttenuatorLeftToLeft = 0x80;
1714 cdr.AttenuatorLeftToRight = 0x00;
1715 cdr.AttenuatorRightToLeft = 0x00;
1716 cdr.AttenuatorRightToRight = 0x80;
1717
1718 getCdInfo();
1719}
1720
1721int cdrFreeze(void *f, int Mode) {
1722 u32 tmp;
1723 u8 tmpp[3];
1724
1725 if (Mode == 0 && !Config.Cdda)
1726 CDR_stop();
1727
1728 cdr.freeze_ver = 0x63647202;
1729 gzfreeze(&cdr, sizeof(cdr));
1730
1731 if (Mode == 1) {
1732 cdr.ParamP = cdr.ParamC;
1733 tmp = cdr.FifoOffset;
1734 }
1735
1736 gzfreeze(&tmp, sizeof(tmp));
1737
1738 if (Mode == 0) {
1739 getCdInfo();
1740
1741 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1742 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1743 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1744 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1745
1746 // read right sub data
1747 tmpp[0] = btoi(cdr.Prev[0]);
1748 tmpp[1] = btoi(cdr.Prev[1]);
1749 tmpp[2] = btoi(cdr.Prev[2]);
1750 cdr.Prev[0]++;
1751 ReadTrack(tmpp);
1752
1753 if (cdr.Play) {
1754 if (cdr.freeze_ver < 0x63647202)
1755 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1756
1757 Find_CurTrack(cdr.SetSectorPlay);
1758 if (!Config.Cdda)
1759 CDR_play(cdr.SetSectorPlay);
1760 }
1761
1762 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1763 // old versions did not latch Reg2, have to fixup..
1764 if (cdr.Reg2 == 0) {
1765 SysPrintf("cdrom: fixing up old savestate\n");
1766 cdr.Reg2 = 7;
1767 }
1768 // also did not save Attenuator..
1769 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1770 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1771 {
1772 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1773 }
1774 }
1775 }
1776
1777 return 0;
1778}
1779
1780void LidInterrupt(void) {
1781 getCdInfo();
1782 cdrLidSeekInterrupt();
1783}