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