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