cdrom: maybe more accurate lid behavior
[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( "CDDA STOP\n" );
521
522 // Magic the Gathering
523 // - looping territory cdda
524
525 // ...?
526 //cdr.ResultReady = 1;
527 //cdr.Stat = DataReady;
528 cdr.Stat = DataEnd;
529 setIrq(0x1000); // 0x1000 just for logging purposes
530
531 StopCdda();
532 SetPlaySeekRead(cdr.StatP, 0);
533 }
534 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
535 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
536 {
537 cdr.Result[0] = cdr.StatP;
538 cdr.Result[1] = cdr.subq.Track;
539 cdr.Result[2] = cdr.subq.Index;
540
541 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
542
543 /* 8 is a hack. For accuracy, it should be 588. */
544 for (i = 0; i < 8; i++)
545 {
546 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
547 }
548 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
549 abs_lev_max |= abs_lev_chselect << 15;
550
551 if (cdr.subq.Absolute[2] & 0x10) {
552 cdr.Result[3] = cdr.subq.Relative[0];
553 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
554 cdr.Result[5] = cdr.subq.Relative[2];
555 }
556 else {
557 cdr.Result[3] = cdr.subq.Absolute[0];
558 cdr.Result[4] = cdr.subq.Absolute[1];
559 cdr.Result[5] = cdr.subq.Absolute[2];
560 }
561
562 cdr.Result[6] = abs_lev_max >> 0;
563 cdr.Result[7] = abs_lev_max >> 8;
564
565 // Rayman: Logo freeze (resultready + dataready)
566 cdr.ResultReady = 1;
567 cdr.Stat = DataReady;
568
569 SetResultSize(8);
570 setIrq(0x1001);
571 }
572
573 if (cdr.ReportDelay)
574 cdr.ReportDelay--;
575}
576
577static int cdrSeekTime(unsigned char *target)
578{
579 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
580 int seekTime = abs(diff) * (cdReadTime / 2000);
581 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
582 seekTime = MAX_VALUE(seekTime, 20000);
583
584 // need this stupidly long penalty or else Spyro2 intro desyncs
585 // note: if misapplied this breaks MGS cutscenes among other things
586 if (cyclesSinceRS > cdReadTime * 50)
587 seekTime += cdReadTime * 25;
588 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
589 // and then wants some slack time
590 else if (cyclesSinceRS < cdReadTime *3/2)
591 seekTime += cdReadTime;
592
593 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
594 CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
595 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
596 return seekTime;
597}
598
599static u32 cdrAlignTimingHack(u32 cycles)
600{
601 /*
602 * timing hack for T'ai Fu - Wrath of the Tiger:
603 * The game has a bug where it issues some cdc commands from a low priority
604 * vint handler, however there is a higher priority default bios handler
605 * that acks the vint irq and returns, so game's handler is not reached
606 * (see bios irq handler chains at e004 and the game's irq handling func
607 * at 80036810). For the game to work, vint has to arrive after the bios
608 * vint handler rejects some other irq (of which only cd and rcnt2 are
609 * active), but before the game's handler loop reads I_STAT. The time
610 * window for this is quite small (~1k cycles of so). Apparently this
611 * somehow happens naturally on the real hardware.
612 *
613 * Note: always enforcing this breaks other games like Crash PAL version
614 * (inputs get dropped because bios handler doesn't see interrupts).
615 */
616 u32 vint_rel;
617 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
618 return cycles;
619 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
620 vint_rel += PSXCLK / 60;
621 while ((s32)(vint_rel - cycles) < 0)
622 vint_rel += PSXCLK / 60;
623 return vint_rel;
624}
625
626static void cdrUpdateTransferBuf(const u8 *buf);
627static void cdrReadInterrupt(void);
628static void cdrPrepCdda(s16 *buf, int samples);
629static void cdrAttenuate(s16 *buf, int samples, int stereo);
630
631static void msfiAdd(u8 *msfi, u32 count)
632{
633 assert(count < 75);
634 msfi[2] += count;
635 if (msfi[2] >= 75) {
636 msfi[2] -= 75;
637 msfi[1]++;
638 if (msfi[1] == 60) {
639 msfi[1] = 0;
640 msfi[0]++;
641 }
642 }
643}
644
645static void msfiSub(u8 *msfi, u32 count)
646{
647 assert(count < 75);
648 msfi[2] -= count;
649 if ((s8)msfi[2] < 0) {
650 msfi[2] += 75;
651 msfi[1]--;
652 if ((s8)msfi[1] < 0) {
653 msfi[1] = 60;
654 msfi[0]--;
655 }
656 }
657}
658
659void cdrPlayReadInterrupt(void)
660{
661 cdr.LastReadSeekCycles = psxRegs.cycle;
662
663 if (cdr.Reading) {
664 cdrReadInterrupt();
665 return;
666 }
667
668 if (!cdr.Play) return;
669
670 CDR_LOG( "CDDA - %d:%d:%d\n",
671 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
672
673 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
674 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
675 StopCdda();
676 SetPlaySeekRead(cdr.StatP, 0);
677 cdr.TrackChanged = TRUE;
678 }
679 else {
680 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
681 }
682
683 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
684 cdrPlayInterrupt_Autopause();
685
686 if (!cdr.Muted && cdr.Play && !Config.Cdda) {
687 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
688 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
689 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
690 cdr.FirstSector = 0;
691 }
692
693 msfiAdd(cdr.SetSectorPlay, 1);
694
695 // update for CdlGetlocP/autopause
696 generate_subq(cdr.SetSectorPlay);
697
698 CDRPLAYREAD_INT(cdReadTime, 0);
699}
700
701#define CMD_PART2 0x100
702#define CMD_WHILE_NOT_READY 0x200
703
704void cdrInterrupt(void) {
705 int start_rotating = 0;
706 int error = 0;
707 u32 cycles, seekTime = 0;
708 u32 second_resp_time = 0;
709 const void *buf;
710 u8 ParamC;
711 u8 set_loc[3];
712 int read_ok;
713 u16 not_ready = 0;
714 u16 Cmd;
715 int i;
716
717 if (cdr.Stat) {
718 CDR_LOG_I("cmd %02x with irqstat %x\n",
719 cdr.CmdInProgress, cdr.Stat);
720 return;
721 }
722 if (cdr.Irq1Pending) {
723 // hand out the "newest" sector, according to nocash
724 cdrUpdateTransferBuf(CDR_getBuffer());
725 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
726 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
727 cdr.CmdInProgress, cdr.Irq1Pending);
728 SetResultSize(1);
729 cdr.Result[0] = cdr.Irq1Pending;
730 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
731 cdr.Irq1Pending = 0;
732 setIrq(0x1003);
733 return;
734 }
735
736 // default response
737 SetResultSize(1);
738 cdr.Result[0] = cdr.StatP;
739 cdr.Stat = Acknowledge;
740
741 Cmd = cdr.CmdInProgress;
742 cdr.CmdInProgress = 0;
743 ParamC = cdr.ParamC;
744
745 if (Cmd < 0x100) {
746 cdr.Ctrl &= ~0x80;
747 cdr.ParamC = 0;
748 cdr.Cmd = 0;
749 }
750
751 switch (cdr.DriveState) {
752 case DRIVESTATE_PREPARE_CD:
753 if (Cmd > 2) {
754 // Syphon filter 2 expects commands to work shortly after it sees
755 // STATUS_ROTATING, so give up trying to emulate the startup seq
756 cdr.DriveState = DRIVESTATE_STANDBY;
757 cdr.StatP &= ~STATUS_SEEK;
758 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
759 break;
760 }
761 // fallthrough
762 case DRIVESTATE_LID_OPEN:
763 case DRIVESTATE_RESCAN_CD:
764 // no disk or busy with the initial scan, allowed cmds are limited
765 not_ready = CMD_WHILE_NOT_READY;
766 break;
767 }
768
769 switch (Cmd | not_ready) {
770 case CdlNop:
771 case CdlNop + CMD_WHILE_NOT_READY:
772 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
773 cdr.StatP &= ~STATUS_SHELLOPEN;
774 break;
775
776 case CdlSetloc:
777 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
778 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
779
780 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
781 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))
782 {
783 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
784 if (++cdr.errorRetryhack > 100)
785 break;
786 error = ERROR_INVALIDARG;
787 goto set_error;
788 }
789 else
790 {
791 for (i = 0; i < 3; i++)
792 set_loc[i] = btoi(cdr.Param[i]);
793 memcpy(cdr.SetSector, set_loc, 3);
794 cdr.SetSector[3] = 0;
795 cdr.SetlocPending = 1;
796 cdr.errorRetryhack = 0;
797 }
798 break;
799
800 do_CdlPlay:
801 case CdlPlay:
802 StopCdda();
803 StopReading();
804
805 cdr.FastBackward = 0;
806 cdr.FastForward = 0;
807
808 // BIOS CD Player
809 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
810
811 if (ParamC != 0 && cdr.Param[0] != 0) {
812 int track = btoi( cdr.Param[0] );
813
814 if (track <= cdr.ResultTN[1])
815 cdr.CurTrack = track;
816
817 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
818
819 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
820 for (i = 0; i < 3; i++)
821 set_loc[i] = cdr.ResultTD[2 - i];
822 seekTime = cdrSeekTime(set_loc);
823 memcpy(cdr.SetSectorPlay, set_loc, 3);
824 }
825 }
826 else if (cdr.SetlocPending) {
827 seekTime = cdrSeekTime(cdr.SetSector);
828 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
829 }
830 else {
831 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
832 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
833 }
834 cdr.SetlocPending = 0;
835
836 /*
837 Rayman: detect track changes
838 - fixes logo freeze
839
840 Twisted Metal 2: skip PREGAP + starting accurate SubQ
841 - plays tracks without retry play
842
843 Wild 9: skip PREGAP + starting accurate SubQ
844 - plays tracks without retry play
845 */
846 Find_CurTrack(cdr.SetSectorPlay);
847 generate_subq(cdr.SetSectorPlay);
848 cdr.LocL[0] = LOCL_INVALID;
849 cdr.SubqForwardSectors = 1;
850 cdr.TrackChanged = FALSE;
851 cdr.FirstSector = 1;
852 cdr.ReportDelay = 60;
853 cdr.sectorsRead = 0;
854
855 if (!Config.Cdda)
856 CDR_play(cdr.SetSectorPlay);
857
858 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
859
860 // BIOS player - set flag again
861 cdr.Play = TRUE;
862
863 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
864 start_rotating = 1;
865 break;
866
867 case CdlForward:
868 // TODO: error 80 if stopped
869 cdr.Stat = Complete;
870
871 // GameShark CD Player: Calls 2x + Play 2x
872 cdr.FastForward = 1;
873 cdr.FastBackward = 0;
874 break;
875
876 case CdlBackward:
877 cdr.Stat = Complete;
878
879 // GameShark CD Player: Calls 2x + Play 2x
880 cdr.FastBackward = 1;
881 cdr.FastForward = 0;
882 break;
883
884 case CdlStandby:
885 if (cdr.DriveState != DRIVESTATE_STOPPED) {
886 error = ERROR_INVALIDARG;
887 goto set_error;
888 }
889 second_resp_time = cdReadTime * 125 / 2;
890 start_rotating = 1;
891 break;
892
893 case CdlStandby + CMD_PART2:
894 cdr.Stat = Complete;
895 break;
896
897 case CdlStop:
898 if (cdr.Play) {
899 // grab time for current track
900 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
901
902 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
903 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
904 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
905 }
906
907 StopCdda();
908 StopReading();
909 SetPlaySeekRead(cdr.StatP, 0);
910 cdr.StatP &= ~STATUS_ROTATING;
911 cdr.LocL[0] = LOCL_INVALID;
912
913 second_resp_time = 0x800;
914 if (cdr.DriveState == DRIVESTATE_STANDBY)
915 second_resp_time = cdReadTime * 30 / 2;
916
917 cdr.DriveState = DRIVESTATE_STOPPED;
918 break;
919
920 case CdlStop + CMD_PART2:
921 cdr.Stat = Complete;
922 break;
923
924 case CdlPause:
925 StopCdda();
926 StopReading();
927
928 // how the drive maintains the position while paused is quite
929 // complicated, this is the minimum to make "Bedlam" happy
930 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
931 cdr.sectorsRead = 0;
932
933 /*
934 Gundam Battle Assault 2: much slower (*)
935 - Fixes boot, gameplay
936
937 Hokuto no Ken 2: slower
938 - Fixes intro + subtitles
939
940 InuYasha - Feudal Fairy Tale: slower
941 - Fixes battles
942 */
943 /* Gameblabla - Tightening the timings (as taken from Duckstation).
944 * The timings from Duckstation are based upon hardware tests.
945 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
946 * seems to be timing sensitive as it can depend on the CPU's clock speed.
947 * */
948 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
949 {
950 second_resp_time = 7000;
951 }
952 else
953 {
954 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
955 }
956 SetPlaySeekRead(cdr.StatP, 0);
957 break;
958
959 case CdlPause + CMD_PART2:
960 cdr.Stat = Complete;
961 break;
962
963 case CdlReset:
964 case CdlReset + CMD_WHILE_NOT_READY:
965 StopCdda();
966 StopReading();
967 SetPlaySeekRead(cdr.StatP, 0);
968 cdr.LocL[0] = LOCL_INVALID;
969 cdr.Muted = FALSE;
970 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
971 second_resp_time = not_ready ? 70000 : 4100000;
972 start_rotating = 1;
973 break;
974
975 case CdlReset + CMD_PART2:
976 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
977 cdr.Stat = Complete;
978 break;
979
980 case CdlMute:
981 cdr.Muted = TRUE;
982 break;
983
984 case CdlDemute:
985 cdr.Muted = FALSE;
986 break;
987
988 case CdlSetfilter:
989 cdr.File = cdr.Param[0];
990 cdr.Channel = cdr.Param[1];
991 break;
992
993 case CdlSetmode:
994 case CdlSetmode + CMD_WHILE_NOT_READY:
995 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
996 cdr.Mode = cdr.Param[0];
997 break;
998
999 case CdlGetparam:
1000 case CdlGetparam + CMD_WHILE_NOT_READY:
1001 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1002 SetResultSize(5);
1003 cdr.Result[1] = cdr.Mode;
1004 cdr.Result[2] = 0;
1005 cdr.Result[3] = cdr.File;
1006 cdr.Result[4] = cdr.Channel;
1007 break;
1008
1009 case CdlGetlocL:
1010 if (cdr.LocL[0] == LOCL_INVALID) {
1011 error = 0x80;
1012 goto set_error;
1013 }
1014 SetResultSize(8);
1015 memcpy(cdr.Result, cdr.LocL, 8);
1016 break;
1017
1018 case CdlGetlocP:
1019 SetResultSize(8);
1020 memcpy(&cdr.Result, &cdr.subq, 8);
1021 break;
1022
1023 case CdlReadT: // SetSession?
1024 // really long
1025 second_resp_time = cdReadTime * 290 / 4;
1026 start_rotating = 1;
1027 break;
1028
1029 case CdlReadT + CMD_PART2:
1030 cdr.Stat = Complete;
1031 break;
1032
1033 case CdlGetTN:
1034 SetResultSize(3);
1035 if (CDR_getTN(cdr.ResultTN) == -1) {
1036 cdr.Stat = DiskError;
1037 cdr.Result[0] |= STATUS_ERROR;
1038 } else {
1039 cdr.Stat = Acknowledge;
1040 cdr.Result[1] = itob(cdr.ResultTN[0]);
1041 cdr.Result[2] = itob(cdr.ResultTN[1]);
1042 }
1043 break;
1044
1045 case CdlGetTD:
1046 cdr.Track = btoi(cdr.Param[0]);
1047 SetResultSize(4);
1048 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1049 cdr.Stat = DiskError;
1050 cdr.Result[0] |= STATUS_ERROR;
1051 } else {
1052 cdr.Stat = Acknowledge;
1053 cdr.Result[0] = cdr.StatP;
1054 cdr.Result[1] = itob(cdr.ResultTD[2]);
1055 cdr.Result[2] = itob(cdr.ResultTD[1]);
1056 /* According to Nocash's documentation, the function doesn't care about ff.
1057 * This can be seen also in Mednafen's implementation. */
1058 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1059 }
1060 break;
1061
1062 case CdlSeekL:
1063 case CdlSeekP:
1064 StopCdda();
1065 StopReading();
1066 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1067
1068 seekTime = cdrSeekTime(cdr.SetSector);
1069 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1070 /*
1071 Crusaders of Might and Magic = 0.5x-4x
1072 - fix cutscene speech start
1073
1074 Eggs of Steel = 2x-?
1075 - fix new game
1076
1077 Medievil = ?-4x
1078 - fix cutscene speech
1079
1080 Rockman X5 = 0.5-4x
1081 - fix capcom logo
1082 */
1083 second_resp_time = cdReadTime + seekTime;
1084 start_rotating = 1;
1085 break;
1086
1087 case CdlSeekL + CMD_PART2:
1088 case CdlSeekP + CMD_PART2:
1089 SetPlaySeekRead(cdr.StatP, 0);
1090 cdr.Result[0] = cdr.StatP;
1091 cdr.Stat = Complete;
1092
1093 Find_CurTrack(cdr.SetSectorPlay);
1094 read_ok = ReadTrack(cdr.SetSectorPlay);
1095 if (read_ok && (buf = CDR_getBuffer()))
1096 memcpy(cdr.LocL, buf, 8);
1097 UpdateSubq(cdr.SetSectorPlay);
1098 cdr.TrackChanged = FALSE;
1099 cdr.LastReadSeekCycles = psxRegs.cycle;
1100 break;
1101
1102 case CdlTest:
1103 case CdlTest + CMD_WHILE_NOT_READY:
1104 switch (cdr.Param[0]) {
1105 case 0x20: // System Controller ROM Version
1106 SetResultSize(4);
1107 memcpy(cdr.Result, Test20, 4);
1108 break;
1109 case 0x22:
1110 SetResultSize(8);
1111 memcpy(cdr.Result, Test22, 4);
1112 break;
1113 case 0x23: case 0x24:
1114 SetResultSize(8);
1115 memcpy(cdr.Result, Test23, 4);
1116 break;
1117 }
1118 break;
1119
1120 case CdlID:
1121 second_resp_time = 20480;
1122 break;
1123
1124 case CdlID + CMD_PART2:
1125 SetResultSize(8);
1126 cdr.Result[0] = cdr.StatP;
1127 cdr.Result[1] = 0;
1128 cdr.Result[2] = 0;
1129 cdr.Result[3] = 0;
1130
1131 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1132 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1133 cdr.Result[1] = 0xc0;
1134 }
1135 else {
1136 if (stat.Type == 2)
1137 cdr.Result[1] |= 0x10;
1138 if (CdromId[0] == '\0')
1139 cdr.Result[1] |= 0x80;
1140 }
1141 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1142
1143 /* This adds the string "PCSX" in Playstation bios boot screen */
1144 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1145 cdr.Stat = Complete;
1146 break;
1147
1148 case CdlInit:
1149 case CdlInit + CMD_WHILE_NOT_READY:
1150 StopCdda();
1151 StopReading();
1152 SetPlaySeekRead(cdr.StatP, 0);
1153 // yes, it really sets STATUS_SHELLOPEN
1154 cdr.StatP |= STATUS_SHELLOPEN;
1155 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1156 set_event(PSXINT_CDRLID, 20480);
1157 start_rotating = 1;
1158 break;
1159
1160 case CdlGetQ:
1161 case CdlGetQ + CMD_WHILE_NOT_READY:
1162 break;
1163
1164 case CdlReadToc:
1165 case CdlReadToc + CMD_WHILE_NOT_READY:
1166 cdr.LocL[0] = LOCL_INVALID;
1167 second_resp_time = cdReadTime * 180 / 4;
1168 start_rotating = 1;
1169 break;
1170
1171 case CdlReadToc + CMD_PART2:
1172 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1173 cdr.Stat = Complete;
1174 break;
1175
1176 case CdlReadN:
1177 case CdlReadS:
1178 if (cdr.Reading && !cdr.SetlocPending)
1179 break;
1180
1181 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1182
1183 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1184 // Read* acts as play for cdda tracks in cdda mode
1185 goto do_CdlPlay;
1186
1187 StopCdda();
1188 if (cdr.SetlocPending) {
1189 seekTime = cdrSeekTime(cdr.SetSector);
1190 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1191 cdr.SetlocPending = 0;
1192 }
1193 cdr.Reading = 1;
1194 cdr.FirstSector = 1;
1195
1196 // Fighting Force 2 - update subq time immediately
1197 // - fixes new game
1198 UpdateSubq(cdr.SetSectorPlay);
1199 cdr.LocL[0] = LOCL_INVALID;
1200 cdr.SubqForwardSectors = 1;
1201 cdr.sectorsRead = 0;
1202
1203 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1204 cycles += seekTime;
1205 if (Config.hacks.cdr_read_timing)
1206 cycles = cdrAlignTimingHack(cycles);
1207 CDRPLAYREAD_INT(cycles, 1);
1208
1209 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1210 start_rotating = 1;
1211 break;
1212
1213 case CdlSync:
1214 default:
1215 error = ERROR_INVALIDCMD;
1216 // FALLTHROUGH
1217
1218 set_error:
1219 SetResultSize(2);
1220 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1221 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1222 cdr.Stat = DiskError;
1223 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1224 break;
1225 }
1226
1227 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1228 cdr.DriveState = DRIVESTATE_STANDBY;
1229 cdr.StatP |= STATUS_ROTATING;
1230 }
1231
1232 if (second_resp_time) {
1233 cdr.CmdInProgress = Cmd | 0x100;
1234 set_event(PSXINT_CDR, second_resp_time);
1235 }
1236 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1237 cdr.CmdInProgress = cdr.Cmd;
1238 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1239 }
1240
1241 setIrq(Cmd);
1242}
1243
1244#ifdef HAVE_ARMV7
1245 #define ssat32_to_16(v) \
1246 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1247#else
1248 #define ssat32_to_16(v) do { \
1249 if (v < -32768) v = -32768; \
1250 else if (v > 32767) v = 32767; \
1251 } while (0)
1252#endif
1253
1254static void cdrPrepCdda(s16 *buf, int samples)
1255{
1256#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1257 int i;
1258 for (i = 0; i < samples; i++) {
1259 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1260 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1261 }
1262#endif
1263}
1264
1265static void cdrAttenuate(s16 *buf, int samples, int stereo)
1266{
1267 int i, l, r;
1268 int ll = cdr.AttenuatorLeftToLeft;
1269 int lr = cdr.AttenuatorLeftToRight;
1270 int rl = cdr.AttenuatorRightToLeft;
1271 int rr = cdr.AttenuatorRightToRight;
1272
1273 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1274 return;
1275
1276 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1277 return;
1278
1279 if (stereo) {
1280 for (i = 0; i < samples; i++) {
1281 l = buf[i * 2];
1282 r = buf[i * 2 + 1];
1283 l = (l * ll + r * rl) >> 7;
1284 r = (r * rr + l * lr) >> 7;
1285 ssat32_to_16(l);
1286 ssat32_to_16(r);
1287 buf[i * 2] = l;
1288 buf[i * 2 + 1] = r;
1289 }
1290 }
1291 else {
1292 for (i = 0; i < samples; i++) {
1293 l = buf[i];
1294 l = l * (ll + rl) >> 7;
1295 //r = r * (rr + lr) >> 7;
1296 ssat32_to_16(l);
1297 //ssat32_to_16(r);
1298 buf[i] = l;
1299 }
1300 }
1301}
1302
1303static void cdrReadInterruptSetResult(unsigned char result)
1304{
1305 if (cdr.Stat) {
1306 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1307 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1308 cdr.CmdInProgress, cdr.Stat);
1309 cdr.Irq1Pending = result;
1310 return;
1311 }
1312 SetResultSize(1);
1313 cdr.Result[0] = result;
1314 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1315 setIrq(0x1004);
1316}
1317
1318static void cdrUpdateTransferBuf(const u8 *buf)
1319{
1320 if (!buf)
1321 return;
1322 memcpy(cdr.Transfer, buf, DATA_SIZE);
1323 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1324 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1325 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1326 if (cdr.FifoOffset < 2048 + 12)
1327 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1328}
1329
1330static void cdrReadInterrupt(void)
1331{
1332 u8 *buf = NULL, *hdr;
1333 u8 subqPos[3];
1334 int read_ok;
1335
1336 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1337 msfiAdd(subqPos, cdr.SubqForwardSectors);
1338 UpdateSubq(subqPos);
1339 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1340 cdr.SubqForwardSectors++;
1341 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1342 return;
1343 }
1344
1345 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1346 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1347 cdr.sectorsRead++;
1348
1349 read_ok = ReadTrack(cdr.SetSectorPlay);
1350 if (read_ok)
1351 buf = CDR_getBuffer();
1352 if (buf == NULL)
1353 read_ok = 0;
1354
1355 if (!read_ok) {
1356 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1357 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1358 return;
1359 }
1360 memcpy(cdr.LocL, buf, 8);
1361
1362 if (!cdr.Stat && !cdr.Irq1Pending)
1363 cdrUpdateTransferBuf(buf);
1364
1365 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1366 hdr = buf + 4;
1367 // Firemen 2: Multi-XA files - briefings, cutscenes
1368 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1369 cdr.File = hdr[0];
1370 cdr.Channel = hdr[1];
1371 }
1372
1373 /* Gameblabla
1374 * Skips playing on channel 255.
1375 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1376 * TODO : Check if this is the proper behaviour.
1377 * */
1378 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1379 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1380 if (!ret) {
1381 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1382 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1383 cdr.FirstSector = 0;
1384 }
1385 else cdr.FirstSector = -1;
1386 }
1387 }
1388
1389 /*
1390 Croc 2: $40 - only FORM1 (*)
1391 Judge Dredd: $C8 - only FORM1 (*)
1392 Sim Theme Park - no adpcm at all (zero)
1393 */
1394
1395 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1396 cdrReadInterruptSetResult(cdr.StatP);
1397
1398 msfiAdd(cdr.SetSectorPlay, 1);
1399
1400 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1401}
1402
1403/*
1404cdrRead0:
1405 bit 0,1 - mode
1406 bit 2 - unknown
1407 bit 3 - unknown
1408 bit 4 - unknown
1409 bit 5 - 1 result ready
1410 bit 6 - 1 dma ready
1411 bit 7 - 1 command being processed
1412*/
1413
1414unsigned char cdrRead0(void) {
1415 if (cdr.ResultReady)
1416 cdr.Ctrl |= 0x20;
1417 else
1418 cdr.Ctrl &= ~0x20;
1419
1420 cdr.Ctrl |= 0x40; // data fifo not empty
1421
1422 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1423 cdr.Ctrl |= 0x18;
1424
1425 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1426
1427 return psxHu8(0x1800) = cdr.Ctrl;
1428}
1429
1430void cdrWrite0(unsigned char rt) {
1431 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1432
1433 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1434}
1435
1436unsigned char cdrRead1(void) {
1437 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1438 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1439 else
1440 psxHu8(0x1801) = 0;
1441 cdr.ResultP++;
1442 if (cdr.ResultP == cdr.ResultC)
1443 cdr.ResultReady = 0;
1444
1445 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1446
1447 return psxHu8(0x1801);
1448}
1449
1450void cdrWrite1(unsigned char rt) {
1451 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1452 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1453
1454 switch (cdr.Ctrl & 3) {
1455 case 0:
1456 break;
1457 case 3:
1458 cdr.AttenuatorRightToRightT = rt;
1459 return;
1460 default:
1461 return;
1462 }
1463
1464#ifdef CDR_LOG_CMD_IRQ
1465 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1466 if (cdr.ParamC) {
1467 int i;
1468 SysPrintf(" Param[%d] = {", cdr.ParamC);
1469 for (i = 0; i < cdr.ParamC; i++)
1470 SysPrintf(" %x,", cdr.Param[i]);
1471 SysPrintf("}\n");
1472 } else {
1473 SysPrintf("\n");
1474 }
1475#endif
1476
1477 cdr.ResultReady = 0;
1478 cdr.Ctrl |= 0x80;
1479
1480 if (!cdr.CmdInProgress) {
1481 cdr.CmdInProgress = rt;
1482 // should be something like 12k + controller delays
1483 set_event(PSXINT_CDR, 5000);
1484 }
1485 else {
1486 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1487 rt, cdr.Cmd, cdr.CmdInProgress);
1488 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1489 cdr.CmdInProgress = rt;
1490 }
1491
1492 cdr.Cmd = rt;
1493}
1494
1495unsigned char cdrRead2(void) {
1496 unsigned char ret = cdr.Transfer[0x920];
1497
1498 if (cdr.FifoOffset < cdr.FifoSize)
1499 ret = cdr.Transfer[cdr.FifoOffset++];
1500 else
1501 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1502
1503 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1504 return ret;
1505}
1506
1507void cdrWrite2(unsigned char rt) {
1508 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1509 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1510
1511 switch (cdr.Ctrl & 3) {
1512 case 0:
1513 if (cdr.ParamC < 8) // FIXME: size and wrapping
1514 cdr.Param[cdr.ParamC++] = rt;
1515 return;
1516 case 1:
1517 cdr.Reg2 = rt;
1518 setIrq(0x1005);
1519 return;
1520 case 2:
1521 cdr.AttenuatorLeftToLeftT = rt;
1522 return;
1523 case 3:
1524 cdr.AttenuatorRightToLeftT = rt;
1525 return;
1526 }
1527}
1528
1529unsigned char cdrRead3(void) {
1530 if (cdr.Ctrl & 0x1)
1531 psxHu8(0x1803) = cdr.Stat | 0xE0;
1532 else
1533 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1534
1535 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1536 return psxHu8(0x1803);
1537}
1538
1539void cdrWrite3(unsigned char rt) {
1540 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1541 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1542
1543 switch (cdr.Ctrl & 3) {
1544 case 0:
1545 break; // transfer
1546 case 1:
1547 if (cdr.Stat & rt) {
1548 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1549 + psxRegs.intCycle[PSXINT_CDR].cycle;
1550 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1551#ifdef CDR_LOG_CMD_IRQ
1552 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1553 !!pending, cdr.CmdInProgress,
1554 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1555#endif
1556 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1557 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1558 {
1559 s32 c = 2048;
1560 if (cdr.CmdInProgress) {
1561 c = 2048 - (psxRegs.cycle - nextCycle);
1562 c = MAX_VALUE(c, 512);
1563 }
1564 set_event(PSXINT_CDR, c);
1565 }
1566 }
1567 cdr.Stat &= ~rt;
1568
1569 if (rt & 0x40)
1570 cdr.ParamC = 0;
1571 return;
1572 case 2:
1573 cdr.AttenuatorLeftToRightT = rt;
1574 return;
1575 case 3:
1576 if (rt & 0x20) {
1577 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1578 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1579 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1580 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1581 }
1582 return;
1583 }
1584
1585 // test: Viewpoint
1586 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1587 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1588 }
1589 else if (rt & 0x80) {
1590 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1591 case MODE_SIZE_2328:
1592 case 0x00:
1593 cdr.FifoOffset = 12;
1594 cdr.FifoSize = 2048 + 12;
1595 break;
1596
1597 case MODE_SIZE_2340:
1598 default:
1599 cdr.FifoOffset = 0;
1600 cdr.FifoSize = 2340;
1601 break;
1602 }
1603 }
1604 else if (!(rt & 0xc0))
1605 cdr.FifoOffset = DATA_SIZE; // fifo empty
1606}
1607
1608void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1609 u32 cdsize, max_words;
1610 int size;
1611 u8 *ptr;
1612
1613#if 0
1614 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1615 if (cdr.FifoOffset == 0) {
1616 ptr = cdr.Transfer;
1617 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1618 }
1619 SysPrintf("\n");
1620#endif
1621
1622 switch (chcr & 0x71000000) {
1623 case 0x11000000:
1624 ptr = getDmaRam(madr, &max_words);
1625 if (ptr == INVALID_PTR) {
1626 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1627 break;
1628 }
1629
1630 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1631
1632 /*
1633 GS CDX: Enhancement CD crash
1634 - Setloc 0:0:0
1635 - CdlPlay
1636 - Spams DMA3 and gets buffer overrun
1637 */
1638 size = DATA_SIZE - cdr.FifoOffset;
1639 if (size > cdsize)
1640 size = cdsize;
1641 if (size > max_words * 4)
1642 size = max_words * 4;
1643 if (size > 0)
1644 {
1645 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1646 cdr.FifoOffset += size;
1647 }
1648 if (size < cdsize) {
1649 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1650 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1651 }
1652 psxCpu->Clear(madr, cdsize / 4);
1653
1654 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1655
1656 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1657 if (chcr & 0x100) {
1658 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1659 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1660 }
1661 else {
1662 // halted
1663 psxRegs.cycle += (cdsize/4) * 24 - 20;
1664 }
1665 return;
1666
1667 default:
1668 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1669 break;
1670 }
1671
1672 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1673 DMA_INTERRUPT(3);
1674}
1675
1676void cdrDmaInterrupt(void)
1677{
1678 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1679 {
1680 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1681 DMA_INTERRUPT(3);
1682 }
1683}
1684
1685static void getCdInfo(void)
1686{
1687 u8 tmp;
1688
1689 CDR_getTN(cdr.ResultTN);
1690 CDR_getTD(0, cdr.SetSectorEnd);
1691 tmp = cdr.SetSectorEnd[0];
1692 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1693 cdr.SetSectorEnd[2] = tmp;
1694}
1695
1696void cdrReset() {
1697 memset(&cdr, 0, sizeof(cdr));
1698 cdr.CurTrack = 1;
1699 cdr.File = 1;
1700 cdr.Channel = 1;
1701 cdr.Reg2 = 0x1f;
1702 cdr.Stat = NoIntr;
1703 cdr.FifoOffset = DATA_SIZE; // fifo empty
1704 if (CdromId[0] == '\0') {
1705 cdr.DriveState = DRIVESTATE_STOPPED;
1706 cdr.StatP = 0;
1707 }
1708 else {
1709 cdr.DriveState = DRIVESTATE_STANDBY;
1710 cdr.StatP = STATUS_ROTATING;
1711 }
1712
1713 // BIOS player - default values
1714 cdr.AttenuatorLeftToLeft = 0x80;
1715 cdr.AttenuatorLeftToRight = 0x00;
1716 cdr.AttenuatorRightToLeft = 0x00;
1717 cdr.AttenuatorRightToRight = 0x80;
1718
1719 getCdInfo();
1720}
1721
1722int cdrFreeze(void *f, int Mode) {
1723 u32 tmp;
1724 u8 tmpp[3];
1725
1726 if (Mode == 0 && !Config.Cdda)
1727 CDR_stop();
1728
1729 cdr.freeze_ver = 0x63647202;
1730 gzfreeze(&cdr, sizeof(cdr));
1731
1732 if (Mode == 1) {
1733 cdr.ParamP = cdr.ParamC;
1734 tmp = cdr.FifoOffset;
1735 }
1736
1737 gzfreeze(&tmp, sizeof(tmp));
1738
1739 if (Mode == 0) {
1740 getCdInfo();
1741
1742 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1743 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1744 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1745 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1746
1747 // read right sub data
1748 tmpp[0] = btoi(cdr.Prev[0]);
1749 tmpp[1] = btoi(cdr.Prev[1]);
1750 tmpp[2] = btoi(cdr.Prev[2]);
1751 cdr.Prev[0]++;
1752 ReadTrack(tmpp);
1753
1754 if (cdr.Play) {
1755 if (cdr.freeze_ver < 0x63647202)
1756 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1757
1758 Find_CurTrack(cdr.SetSectorPlay);
1759 if (!Config.Cdda)
1760 CDR_play(cdr.SetSectorPlay);
1761 }
1762
1763 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1764 // old versions did not latch Reg2, have to fixup..
1765 if (cdr.Reg2 == 0) {
1766 SysPrintf("cdrom: fixing up old savestate\n");
1767 cdr.Reg2 = 7;
1768 }
1769 // also did not save Attenuator..
1770 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1771 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1772 {
1773 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1774 }
1775 }
1776 }
1777
1778 return 0;
1779}
1780
1781void LidInterrupt(void) {
1782 getCdInfo();
1783 cdrLidSeekInterrupt();
1784}