cdrom: report read errors correctly
[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 if (read_ok)
467 memcpy(cdr.Prev, tmp, 3);
468
469 if (CheckSBI(time))
470 return read_ok;
471
472 subq = (struct SubQ *)CDR_getBufferSub();
473 if (subq != NULL && cdr.CurTrack == 1) {
474 crc = calcCrc((u8 *)subq + 12, 10);
475 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
476 cdr.subq.Track = subq->TrackNumber;
477 cdr.subq.Index = subq->IndexNumber;
478 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
479 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
480 }
481 else {
482 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
483 tmp[0], tmp[1], tmp[2]);
484 }
485 }
486 else {
487 generate_subq(time);
488 }
489
490 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
491 cdr.subq.Track, cdr.subq.Index,
492 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
493 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
494
495 return read_ok;
496}
497
498static void cdrPlayInterrupt_Autopause()
499{
500 u32 abs_lev_max = 0;
501 boolean abs_lev_chselect;
502 u32 i;
503
504 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
505 CDR_LOG( "CDDA STOP\n" );
506
507 // Magic the Gathering
508 // - looping territory cdda
509
510 // ...?
511 //cdr.ResultReady = 1;
512 //cdr.Stat = DataReady;
513 cdr.Stat = DataEnd;
514 setIrq(0x1000); // 0x1000 just for logging purposes
515
516 StopCdda();
517 SetPlaySeekRead(cdr.StatP, 0);
518 }
519 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
520 cdr.Result[0] = cdr.StatP;
521 cdr.Result[1] = cdr.subq.Track;
522 cdr.Result[2] = cdr.subq.Index;
523
524 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
525
526 /* 8 is a hack. For accuracy, it should be 588. */
527 for (i = 0; i < 8; i++)
528 {
529 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
530 }
531 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
532 abs_lev_max |= abs_lev_chselect << 15;
533
534 if (cdr.subq.Absolute[2] & 0x10) {
535 cdr.Result[3] = cdr.subq.Relative[0];
536 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
537 cdr.Result[5] = cdr.subq.Relative[2];
538 }
539 else {
540 cdr.Result[3] = cdr.subq.Absolute[0];
541 cdr.Result[4] = cdr.subq.Absolute[1];
542 cdr.Result[5] = cdr.subq.Absolute[2];
543 }
544
545 cdr.Result[6] = abs_lev_max >> 0;
546 cdr.Result[7] = abs_lev_max >> 8;
547
548 // Rayman: Logo freeze (resultready + dataready)
549 cdr.ResultReady = 1;
550 cdr.Stat = DataReady;
551
552 SetResultSize(8);
553 setIrq(0x1001);
554 }
555}
556
557static int cdrSeekTime(unsigned char *target)
558{
559 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
560 int seekTime = abs(diff) * (cdReadTime / 200);
561 /*
562 * Gameblabla :
563 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
564 * and was unreliable for that game.
565 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
566 *
567 * Obviously, this isn't perfect but right now, it should be a bit better.
568 * Games to test this against if you change that setting :
569 * - Driver (titlescreen music delay and retry mission)
570 * - Worms Pinball (Will either not boot or crash in the memory card screen)
571 * - Viewpoint (short pauses if the delay in the ingame music is too long)
572 *
573 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
574 * However, 1000000 is not enough for Worms Pinball to reliably boot.
575 */
576 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
577 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
578 return seekTime;
579}
580
581static void cdrUpdateTransferBuf(const u8 *buf);
582static void cdrReadInterrupt(void);
583static void cdrPrepCdda(s16 *buf, int samples);
584static void cdrAttenuate(s16 *buf, int samples, int stereo);
585
586void cdrPlayReadInterrupt(void)
587{
588 if (cdr.Reading) {
589 cdrReadInterrupt();
590 return;
591 }
592
593 if (!cdr.Play) return;
594
595 CDR_LOG( "CDDA - %d:%d:%d\n",
596 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
597
598 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
599 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
600 StopCdda();
601 SetPlaySeekRead(cdr.StatP, 0);
602 cdr.TrackChanged = TRUE;
603 }
604 else {
605 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
606 }
607
608 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
609 cdrPlayInterrupt_Autopause();
610
611 if (!cdr.Muted && !Config.Cdda) {
612 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
613 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
614 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
615 cdr.FirstSector = 0;
616 }
617
618 cdr.SetSectorPlay[2]++;
619 if (cdr.SetSectorPlay[2] == 75) {
620 cdr.SetSectorPlay[2] = 0;
621 cdr.SetSectorPlay[1]++;
622 if (cdr.SetSectorPlay[1] == 60) {
623 cdr.SetSectorPlay[1] = 0;
624 cdr.SetSectorPlay[0]++;
625 }
626 }
627
628 // update for CdlGetlocP/autopause
629 generate_subq(cdr.SetSectorPlay);
630
631 CDRPLAYREAD_INT(cdReadTime, 0);
632}
633
634#define CMD_PART2 0x100
635#define CMD_WHILE_NOT_READY 0x200
636
637void cdrInterrupt(void) {
638 int start_rotating = 0;
639 int error = 0;
640 unsigned int seekTime = 0;
641 u32 second_resp_time = 0;
642 const void *buf;
643 u8 ParamC;
644 u8 set_loc[3];
645 int read_ok;
646 u16 not_ready = 0;
647 u16 Cmd;
648 int i;
649
650 if (cdr.Stat) {
651 CDR_LOG_I("%u cdrom: cmd %02x with irqstat %x\n",
652 psxRegs.cycle, cdr.CmdInProgress, cdr.Stat);
653 return;
654 }
655 if (cdr.Irq1Pending) {
656 // hand out the "newest" sector, according to nocash
657 cdrUpdateTransferBuf(CDR_getBuffer());
658 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
659 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
660 SetResultSize(1);
661 cdr.Result[0] = cdr.Irq1Pending;
662 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
663 cdr.Irq1Pending = 0;
664 setIrq(0x1003);
665 return;
666 }
667
668 // default response
669 SetResultSize(1);
670 cdr.Result[0] = cdr.StatP;
671 cdr.Stat = Acknowledge;
672
673 Cmd = cdr.CmdInProgress;
674 cdr.CmdInProgress = 0;
675 ParamC = cdr.ParamC;
676
677 if (Cmd < 0x100) {
678 cdr.Ctrl &= ~0x80;
679 cdr.ParamC = 0;
680 cdr.Cmd = 0;
681 }
682
683 switch (cdr.DriveState) {
684 case DRIVESTATE_PREPARE_CD:
685 if (Cmd > 2) {
686 // Syphon filter 2 expects commands to work shortly after it sees
687 // STATUS_ROTATING, so give up trying to emulate the startup seq
688 cdr.DriveState = DRIVESTATE_STANDBY;
689 cdr.StatP &= ~STATUS_SEEK;
690 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
691 break;
692 }
693 // fallthrough
694 case DRIVESTATE_LID_OPEN:
695 case DRIVESTATE_RESCAN_CD:
696 // no disk or busy with the initial scan, allowed cmds are limited
697 not_ready = CMD_WHILE_NOT_READY;
698 break;
699 }
700
701 switch (Cmd | not_ready) {
702 case CdlNop:
703 case CdlNop + CMD_WHILE_NOT_READY:
704 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
705 cdr.StatP &= ~STATUS_SHELLOPEN;
706 break;
707
708 case CdlSetloc:
709 case CdlSetloc + CMD_WHILE_NOT_READY:
710 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
711
712 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
713 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))
714 {
715 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
716 error = ERROR_INVALIDARG;
717 goto set_error;
718 }
719 else
720 {
721 for (i = 0; i < 3; i++)
722 set_loc[i] = btoi(cdr.Param[i]);
723 memcpy(cdr.SetSector, set_loc, 3);
724 cdr.SetSector[3] = 0;
725 cdr.SetlocPending = 1;
726 }
727 break;
728
729 do_CdlPlay:
730 case CdlPlay:
731 StopCdda();
732 StopReading();
733
734 cdr.FastBackward = 0;
735 cdr.FastForward = 0;
736
737 // BIOS CD Player
738 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
739
740 if (ParamC != 0 && cdr.Param[0] != 0) {
741 int track = btoi( cdr.Param[0] );
742
743 if (track <= cdr.ResultTN[1])
744 cdr.CurTrack = track;
745
746 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
747
748 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
749 for (i = 0; i < 3; i++)
750 set_loc[i] = cdr.ResultTD[2 - i];
751 seekTime = cdrSeekTime(set_loc);
752 memcpy(cdr.SetSectorPlay, set_loc, 3);
753 }
754 }
755 else if (cdr.SetlocPending) {
756 seekTime = cdrSeekTime(cdr.SetSector);
757 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
758 }
759 else {
760 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
761 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
762 }
763 cdr.SetlocPending = 0;
764
765 /*
766 Rayman: detect track changes
767 - fixes logo freeze
768
769 Twisted Metal 2: skip PREGAP + starting accurate SubQ
770 - plays tracks without retry play
771
772 Wild 9: skip PREGAP + starting accurate SubQ
773 - plays tracks without retry play
774 */
775 Find_CurTrack(cdr.SetSectorPlay);
776 ReadTrack(cdr.SetSectorPlay);
777 cdr.LocL[0] = LOCL_INVALID;
778 cdr.TrackChanged = FALSE;
779 cdr.FirstSector = 1;
780
781 if (!Config.Cdda)
782 CDR_play(cdr.SetSectorPlay);
783
784 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
785
786 // BIOS player - set flag again
787 cdr.Play = TRUE;
788
789 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
790 start_rotating = 1;
791 break;
792
793 case CdlForward:
794 // TODO: error 80 if stopped
795 cdr.Stat = Complete;
796
797 // GameShark CD Player: Calls 2x + Play 2x
798 cdr.FastForward = 1;
799 cdr.FastBackward = 0;
800 break;
801
802 case CdlBackward:
803 cdr.Stat = Complete;
804
805 // GameShark CD Player: Calls 2x + Play 2x
806 cdr.FastBackward = 1;
807 cdr.FastForward = 0;
808 break;
809
810 case CdlStandby:
811 if (cdr.DriveState != DRIVESTATE_STOPPED) {
812 error = ERROR_INVALIDARG;
813 goto set_error;
814 }
815 second_resp_time = cdReadTime * 125 / 2;
816 start_rotating = 1;
817 break;
818
819 case CdlStandby + CMD_PART2:
820 cdr.Stat = Complete;
821 break;
822
823 case CdlStop:
824 if (cdr.Play) {
825 // grab time for current track
826 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
827
828 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
829 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
830 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
831 }
832
833 StopCdda();
834 StopReading();
835 SetPlaySeekRead(cdr.StatP, 0);
836 cdr.StatP &= ~STATUS_ROTATING;
837 cdr.LocL[0] = LOCL_INVALID;
838
839 second_resp_time = 0x800;
840 if (cdr.DriveState == DRIVESTATE_STANDBY)
841 second_resp_time = cdReadTime * 30 / 2;
842
843 cdr.DriveState = DRIVESTATE_STOPPED;
844 break;
845
846 case CdlStop + CMD_PART2:
847 cdr.Stat = Complete;
848 break;
849
850 case CdlPause:
851 StopCdda();
852 StopReading();
853 /*
854 Gundam Battle Assault 2: much slower (*)
855 - Fixes boot, gameplay
856
857 Hokuto no Ken 2: slower
858 - Fixes intro + subtitles
859
860 InuYasha - Feudal Fairy Tale: slower
861 - Fixes battles
862 */
863 /* Gameblabla - Tightening the timings (as taken from Duckstation).
864 * The timings from Duckstation are based upon hardware tests.
865 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
866 * seems to be timing sensitive as it can depend on the CPU's clock speed.
867 * */
868 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
869 {
870 second_resp_time = 7000;
871 }
872 else
873 {
874 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
875 }
876 SetPlaySeekRead(cdr.StatP, 0);
877 break;
878
879 case CdlPause + CMD_PART2:
880 cdr.Stat = Complete;
881 break;
882
883 case CdlReset:
884 case CdlReset + CMD_WHILE_NOT_READY:
885 StopCdda();
886 StopReading();
887 SetPlaySeekRead(cdr.StatP, 0);
888 cdr.LocL[0] = LOCL_INVALID;
889 cdr.Muted = FALSE;
890 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
891 second_resp_time = not_ready ? 70000 : 4100000;
892 start_rotating = 1;
893 break;
894
895 case CdlReset + CMD_PART2:
896 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
897 cdr.Stat = Complete;
898 break;
899
900 case CdlMute:
901 cdr.Muted = TRUE;
902 break;
903
904 case CdlDemute:
905 cdr.Muted = FALSE;
906 break;
907
908 case CdlSetfilter:
909 cdr.File = cdr.Param[0];
910 cdr.Channel = cdr.Param[1];
911 break;
912
913 case CdlSetmode:
914 case CdlSetmode + CMD_WHILE_NOT_READY:
915 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
916 cdr.Mode = cdr.Param[0];
917 break;
918
919 case CdlGetparam:
920 case CdlGetparam + CMD_WHILE_NOT_READY:
921 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
922 SetResultSize(5);
923 cdr.Result[1] = cdr.Mode;
924 cdr.Result[2] = 0;
925 cdr.Result[3] = cdr.File;
926 cdr.Result[4] = cdr.Channel;
927 break;
928
929 case CdlGetlocL:
930 if (cdr.LocL[0] == LOCL_INVALID) {
931 error = 0x80;
932 goto set_error;
933 }
934 SetResultSize(8);
935 memcpy(cdr.Result, cdr.LocL, 8);
936 break;
937
938 case CdlGetlocP:
939 SetResultSize(8);
940 memcpy(&cdr.Result, &cdr.subq, 8);
941 break;
942
943 case CdlReadT: // SetSession?
944 // really long
945 second_resp_time = cdReadTime * 290 / 4;
946 start_rotating = 1;
947 break;
948
949 case CdlReadT + CMD_PART2:
950 cdr.Stat = Complete;
951 break;
952
953 case CdlGetTN:
954 SetResultSize(3);
955 if (CDR_getTN(cdr.ResultTN) == -1) {
956 cdr.Stat = DiskError;
957 cdr.Result[0] |= STATUS_ERROR;
958 } else {
959 cdr.Stat = Acknowledge;
960 cdr.Result[1] = itob(cdr.ResultTN[0]);
961 cdr.Result[2] = itob(cdr.ResultTN[1]);
962 }
963 break;
964
965 case CdlGetTD:
966 cdr.Track = btoi(cdr.Param[0]);
967 SetResultSize(4);
968 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
969 cdr.Stat = DiskError;
970 cdr.Result[0] |= STATUS_ERROR;
971 } else {
972 cdr.Stat = Acknowledge;
973 cdr.Result[0] = cdr.StatP;
974 cdr.Result[1] = itob(cdr.ResultTD[2]);
975 cdr.Result[2] = itob(cdr.ResultTD[1]);
976 /* According to Nocash's documentation, the function doesn't care about ff.
977 * This can be seen also in Mednafen's implementation. */
978 //cdr.Result[3] = itob(cdr.ResultTD[0]);
979 }
980 break;
981
982 case CdlSeekL:
983 case CdlSeekP:
984 StopCdda();
985 StopReading();
986 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
987
988 seekTime = cdrSeekTime(cdr.SetSector);
989 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
990 /*
991 Crusaders of Might and Magic = 0.5x-4x
992 - fix cutscene speech start
993
994 Eggs of Steel = 2x-?
995 - fix new game
996
997 Medievil = ?-4x
998 - fix cutscene speech
999
1000 Rockman X5 = 0.5-4x
1001 - fix capcom logo
1002 */
1003 second_resp_time = cdReadTime + seekTime;
1004 start_rotating = 1;
1005 break;
1006
1007 case CdlSeekL + CMD_PART2:
1008 case CdlSeekP + CMD_PART2:
1009 SetPlaySeekRead(cdr.StatP, 0);
1010 cdr.Result[0] = cdr.StatP;
1011 cdr.Stat = Complete;
1012
1013 Find_CurTrack(cdr.SetSectorPlay);
1014 read_ok = ReadTrack(cdr.SetSectorPlay);
1015 if (read_ok && (buf = CDR_getBuffer()))
1016 memcpy(cdr.LocL, buf, 8);
1017 cdr.TrackChanged = FALSE;
1018 break;
1019
1020 case CdlTest:
1021 case CdlTest + CMD_WHILE_NOT_READY:
1022 switch (cdr.Param[0]) {
1023 case 0x20: // System Controller ROM Version
1024 SetResultSize(4);
1025 memcpy(cdr.Result, Test20, 4);
1026 break;
1027 case 0x22:
1028 SetResultSize(8);
1029 memcpy(cdr.Result, Test22, 4);
1030 break;
1031 case 0x23: case 0x24:
1032 SetResultSize(8);
1033 memcpy(cdr.Result, Test23, 4);
1034 break;
1035 }
1036 break;
1037
1038 case CdlID:
1039 second_resp_time = 20480;
1040 break;
1041
1042 case CdlID + CMD_PART2:
1043 SetResultSize(8);
1044 cdr.Result[0] = cdr.StatP;
1045 cdr.Result[1] = 0;
1046 cdr.Result[2] = 0;
1047 cdr.Result[3] = 0;
1048
1049 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1050 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1051 cdr.Result[1] = 0xc0;
1052 }
1053 else {
1054 if (stat.Type == 2)
1055 cdr.Result[1] |= 0x10;
1056 if (CdromId[0] == '\0')
1057 cdr.Result[1] |= 0x80;
1058 }
1059 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1060
1061 /* This adds the string "PCSX" in Playstation bios boot screen */
1062 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1063 cdr.Stat = Complete;
1064 break;
1065
1066 case CdlInit:
1067 case CdlInit + CMD_WHILE_NOT_READY:
1068 StopCdda();
1069 StopReading();
1070 SetPlaySeekRead(cdr.StatP, 0);
1071 // yes, it really sets STATUS_SHELLOPEN
1072 cdr.StatP |= STATUS_SHELLOPEN;
1073 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1074 CDRLID_INT(20480);
1075 start_rotating = 1;
1076 break;
1077
1078 case CdlGetQ:
1079 case CdlGetQ + CMD_WHILE_NOT_READY:
1080 break;
1081
1082 case CdlReadToc:
1083 case CdlReadToc + CMD_WHILE_NOT_READY:
1084 cdr.LocL[0] = LOCL_INVALID;
1085 second_resp_time = cdReadTime * 180 / 4;
1086 start_rotating = 1;
1087 break;
1088
1089 case CdlReadToc + CMD_PART2:
1090 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1091 cdr.Stat = Complete;
1092 break;
1093
1094 case CdlReadN:
1095 case CdlReadS:
1096 if (cdr.Reading && !cdr.SetlocPending)
1097 break;
1098
1099 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1100
1101 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1102 // Read* acts as play for cdda tracks in cdda mode
1103 goto do_CdlPlay;
1104
1105 StopCdda();
1106 if (cdr.SetlocPending) {
1107 seekTime = cdrSeekTime(cdr.SetSector);
1108 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1109 cdr.SetlocPending = 0;
1110 }
1111 cdr.Reading = 1;
1112 cdr.FirstSector = 1;
1113
1114 // Fighting Force 2 - update subq time immediately
1115 // - fixes new game
1116 ReadTrack(cdr.SetSectorPlay);
1117 cdr.LocL[0] = LOCL_INVALID;
1118
1119 CDRPLAYREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1120
1121 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1122 start_rotating = 1;
1123 break;
1124
1125 case CdlSync:
1126 default:
1127 error = ERROR_INVALIDCMD;
1128 // FALLTHROUGH
1129
1130 set_error:
1131 CDR_LOG_I("cdrom: cmd %02x error %02x\n", Cmd, error);
1132 SetResultSize(2);
1133 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1134 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1135 cdr.Stat = DiskError;
1136 break;
1137 }
1138
1139 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1140 cdr.DriveState = DRIVESTATE_STANDBY;
1141 cdr.StatP |= STATUS_ROTATING;
1142 }
1143
1144 if (second_resp_time) {
1145 cdr.CmdInProgress = Cmd | 0x100;
1146 CDR_INT(second_resp_time);
1147 }
1148 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1149 cdr.CmdInProgress = cdr.Cmd;
1150 CDR_LOG_I("%u cdrom: cmd %02x came before %02x finished\n",
1151 psxRegs.cycle, cdr.Cmd, Cmd);
1152 }
1153
1154 setIrq(Cmd);
1155}
1156
1157#ifdef HAVE_ARMV7
1158 #define ssat32_to_16(v) \
1159 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1160#else
1161 #define ssat32_to_16(v) do { \
1162 if (v < -32768) v = -32768; \
1163 else if (v > 32767) v = 32767; \
1164 } while (0)
1165#endif
1166
1167static void cdrPrepCdda(s16 *buf, int samples)
1168{
1169#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1170 int i;
1171 for (i = 0; i < samples; i++) {
1172 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1173 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1174 }
1175#endif
1176}
1177
1178static void cdrAttenuate(s16 *buf, int samples, int stereo)
1179{
1180 int i, l, r;
1181 int ll = cdr.AttenuatorLeftToLeft;
1182 int lr = cdr.AttenuatorLeftToRight;
1183 int rl = cdr.AttenuatorRightToLeft;
1184 int rr = cdr.AttenuatorRightToRight;
1185
1186 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1187 return;
1188
1189 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1190 return;
1191
1192 if (stereo) {
1193 for (i = 0; i < samples; i++) {
1194 l = buf[i * 2];
1195 r = buf[i * 2 + 1];
1196 l = (l * ll + r * rl) >> 7;
1197 r = (r * rr + l * lr) >> 7;
1198 ssat32_to_16(l);
1199 ssat32_to_16(r);
1200 buf[i * 2] = l;
1201 buf[i * 2 + 1] = r;
1202 }
1203 }
1204 else {
1205 for (i = 0; i < samples; i++) {
1206 l = buf[i];
1207 l = l * (ll + rl) >> 7;
1208 //r = r * (rr + lr) >> 7;
1209 ssat32_to_16(l);
1210 //ssat32_to_16(r);
1211 buf[i] = l;
1212 }
1213 }
1214}
1215
1216static void cdrReadInterruptSetResult(unsigned char result)
1217{
1218 if (cdr.Stat) {
1219 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1220 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1221 cdr.CmdInProgress, cdr.Stat);
1222 cdr.Irq1Pending = result;
1223 return;
1224 }
1225 SetResultSize(1);
1226 cdr.Result[0] = result;
1227 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1228 setIrq(0x1004);
1229}
1230
1231static void cdrUpdateTransferBuf(const u8 *buf)
1232{
1233 if (!buf)
1234 return;
1235 memcpy(cdr.Transfer, buf, DATA_SIZE);
1236 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1237 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1238 if (cdr.FifoOffset < 2048 + 12)
1239 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1240}
1241
1242static void cdrReadInterrupt(void)
1243{
1244 u8 *buf = NULL, *hdr;
1245 int read_ok;
1246
1247 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1248
1249 read_ok = ReadTrack(cdr.SetSectorPlay);
1250 if (read_ok)
1251 buf = CDR_getBuffer();
1252 if (buf == NULL)
1253 read_ok = 0;
1254
1255 if (!read_ok) {
1256 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
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 == INVALID_PTR) {
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}