cdrom: ignore repeated read cmds
[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.Stat = Complete;
1010
1011 Find_CurTrack(cdr.SetSectorPlay);
1012 read_ok = ReadTrack(cdr.SetSectorPlay);
1013 if (read_ok && (buf = CDR_getBuffer()))
1014 memcpy(cdr.LocL, buf, 8);
1015 cdr.TrackChanged = FALSE;
1016 break;
1017
1018 case CdlTest:
1019 case CdlTest + CMD_WHILE_NOT_READY:
1020 switch (cdr.Param[0]) {
1021 case 0x20: // System Controller ROM Version
1022 SetResultSize(4);
1023 memcpy(cdr.Result, Test20, 4);
1024 break;
1025 case 0x22:
1026 SetResultSize(8);
1027 memcpy(cdr.Result, Test22, 4);
1028 break;
1029 case 0x23: case 0x24:
1030 SetResultSize(8);
1031 memcpy(cdr.Result, Test23, 4);
1032 break;
1033 }
1034 break;
1035
1036 case CdlID:
1037 second_resp_time = 20480;
1038 break;
1039
1040 case CdlID + CMD_PART2:
1041 SetResultSize(8);
1042 cdr.Result[0] = cdr.StatP;
1043 cdr.Result[1] = 0;
1044 cdr.Result[2] = 0;
1045 cdr.Result[3] = 0;
1046
1047 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1048 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1049 cdr.Result[1] = 0xc0;
1050 }
1051 else {
1052 if (stat.Type == 2)
1053 cdr.Result[1] |= 0x10;
1054 if (CdromId[0] == '\0')
1055 cdr.Result[1] |= 0x80;
1056 }
1057 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1058
1059 /* This adds the string "PCSX" in Playstation bios boot screen */
1060 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1061 cdr.Stat = Complete;
1062 break;
1063
1064 case CdlInit:
1065 case CdlInit + CMD_WHILE_NOT_READY:
1066 StopCdda();
1067 StopReading();
1068 SetPlaySeekRead(cdr.StatP, 0);
1069 // yes, it really sets STATUS_SHELLOPEN
1070 cdr.StatP |= STATUS_SHELLOPEN;
1071 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1072 CDRLID_INT(20480);
1073 start_rotating = 1;
1074 break;
1075
1076 case CdlGetQ:
1077 case CdlGetQ + CMD_WHILE_NOT_READY:
1078 break;
1079
1080 case CdlReadToc:
1081 case CdlReadToc + CMD_WHILE_NOT_READY:
1082 cdr.LocL[0] = LOCL_INVALID;
1083 second_resp_time = cdReadTime * 180 / 4;
1084 start_rotating = 1;
1085 break;
1086
1087 case CdlReadToc + CMD_PART2:
1088 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1089 cdr.Stat = Complete;
1090 break;
1091
1092 case CdlReadN:
1093 case CdlReadS:
1094 if (cdr.Reading && !cdr.SetlocPending)
1095 break;
1096
1097 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1098
1099 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1100 // Read* acts as play for cdda tracks in cdda mode
1101 goto do_CdlPlay;
1102
1103 StopCdda();
1104 if (cdr.SetlocPending) {
1105 seekTime = cdrSeekTime(cdr.SetSector);
1106 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1107 cdr.SetlocPending = 0;
1108 }
1109 cdr.Reading = 1;
1110 cdr.FirstSector = 1;
1111
1112 // Fighting Force 2 - update subq time immediately
1113 // - fixes new game
1114 ReadTrack(cdr.SetSectorPlay);
1115 cdr.LocL[0] = LOCL_INVALID;
1116
1117 CDRPLAYREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1118
1119 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1120 start_rotating = 1;
1121 break;
1122
1123 case CdlSync:
1124 default:
1125 error = ERROR_INVALIDCMD;
1126 // FALLTHROUGH
1127
1128 set_error:
1129 CDR_LOG_I("cdrom: cmd %02x error %02x\n", Cmd, error);
1130 SetResultSize(2);
1131 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1132 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1133 cdr.Stat = DiskError;
1134 break;
1135 }
1136
1137 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1138 cdr.DriveState = DRIVESTATE_STANDBY;
1139 cdr.StatP |= STATUS_ROTATING;
1140 }
1141
1142 if (second_resp_time) {
1143 cdr.CmdInProgress = Cmd | 0x100;
1144 CDR_INT(second_resp_time);
1145 }
1146 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1147 cdr.CmdInProgress = cdr.Cmd;
1148 CDR_LOG_I("%u cdrom: cmd %02x came before %02x finished\n",
1149 psxRegs.cycle, cdr.Cmd, Cmd);
1150 }
1151
1152 setIrq(Cmd);
1153}
1154
1155#ifdef HAVE_ARMV7
1156 #define ssat32_to_16(v) \
1157 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1158#else
1159 #define ssat32_to_16(v) do { \
1160 if (v < -32768) v = -32768; \
1161 else if (v > 32767) v = 32767; \
1162 } while (0)
1163#endif
1164
1165static void cdrPrepCdda(s16 *buf, int samples)
1166{
1167#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1168 int i;
1169 for (i = 0; i < samples; i++) {
1170 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1171 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1172 }
1173#endif
1174}
1175
1176static void cdrAttenuate(s16 *buf, int samples, int stereo)
1177{
1178 int i, l, r;
1179 int ll = cdr.AttenuatorLeftToLeft;
1180 int lr = cdr.AttenuatorLeftToRight;
1181 int rl = cdr.AttenuatorRightToLeft;
1182 int rr = cdr.AttenuatorRightToRight;
1183
1184 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1185 return;
1186
1187 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1188 return;
1189
1190 if (stereo) {
1191 for (i = 0; i < samples; i++) {
1192 l = buf[i * 2];
1193 r = buf[i * 2 + 1];
1194 l = (l * ll + r * rl) >> 7;
1195 r = (r * rr + l * lr) >> 7;
1196 ssat32_to_16(l);
1197 ssat32_to_16(r);
1198 buf[i * 2] = l;
1199 buf[i * 2 + 1] = r;
1200 }
1201 }
1202 else {
1203 for (i = 0; i < samples; i++) {
1204 l = buf[i];
1205 l = l * (ll + rl) >> 7;
1206 //r = r * (rr + lr) >> 7;
1207 ssat32_to_16(l);
1208 //ssat32_to_16(r);
1209 buf[i] = l;
1210 }
1211 }
1212}
1213
1214static void cdrReadInterruptSetResult(unsigned char result)
1215{
1216 if (cdr.Stat) {
1217 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1218 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1219 cdr.CmdInProgress, cdr.Stat);
1220 cdr.Irq1Pending = result;
1221 return;
1222 }
1223 SetResultSize(1);
1224 cdr.Result[0] = result;
1225 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1226 setIrq(0x1004);
1227}
1228
1229static void cdrUpdateTransferBuf(const u8 *buf)
1230{
1231 if (!buf)
1232 return;
1233 memcpy(cdr.Transfer, buf, DATA_SIZE);
1234 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1235 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1236 if (cdr.FifoOffset < 2048 + 12)
1237 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1238}
1239
1240static void cdrReadInterrupt(void)
1241{
1242 u8 *buf = NULL, *hdr;
1243 int read_ok;
1244
1245 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1246
1247 read_ok = ReadTrack(cdr.SetSectorPlay);
1248 if (read_ok)
1249 buf = CDR_getBuffer();
1250 if (buf == NULL)
1251 read_ok = 0;
1252
1253 if (!read_ok) {
1254 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1255 memset(cdr.Transfer, 0, DATA_SIZE);
1256 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1257 return;
1258 }
1259 memcpy(cdr.LocL, buf, 8);
1260
1261 if (!cdr.Irq1Pending)
1262 cdrUpdateTransferBuf(buf);
1263
1264 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1265 hdr = buf + 4;
1266 // Firemen 2: Multi-XA files - briefings, cutscenes
1267 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1268 cdr.File = hdr[0];
1269 cdr.Channel = hdr[1];
1270 }
1271
1272 /* Gameblabla
1273 * Skips playing on channel 255.
1274 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1275 * TODO : Check if this is the proper behaviour.
1276 * */
1277 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1278 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1279 if (!ret) {
1280 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1281 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1282 cdr.FirstSector = 0;
1283 }
1284 else cdr.FirstSector = -1;
1285 }
1286 }
1287
1288 /*
1289 Croc 2: $40 - only FORM1 (*)
1290 Judge Dredd: $C8 - only FORM1 (*)
1291 Sim Theme Park - no adpcm at all (zero)
1292 */
1293
1294 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1295 cdrReadInterruptSetResult(cdr.StatP);
1296
1297 cdr.SetSectorPlay[2]++;
1298 if (cdr.SetSectorPlay[2] == 75) {
1299 cdr.SetSectorPlay[2] = 0;
1300 cdr.SetSectorPlay[1]++;
1301 if (cdr.SetSectorPlay[1] == 60) {
1302 cdr.SetSectorPlay[1] = 0;
1303 cdr.SetSectorPlay[0]++;
1304 }
1305 }
1306
1307 if (!cdr.Irq1Pending) {
1308 // update for CdlGetlocP
1309 ReadTrack(cdr.SetSectorPlay);
1310 }
1311
1312 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1313}
1314
1315/*
1316cdrRead0:
1317 bit 0,1 - mode
1318 bit 2 - unknown
1319 bit 3 - unknown
1320 bit 4 - unknown
1321 bit 5 - 1 result ready
1322 bit 6 - 1 dma ready
1323 bit 7 - 1 command being processed
1324*/
1325
1326unsigned char cdrRead0(void) {
1327 if (cdr.ResultReady)
1328 cdr.Ctrl |= 0x20;
1329 else
1330 cdr.Ctrl &= ~0x20;
1331
1332 cdr.Ctrl |= 0x40; // data fifo not empty
1333
1334 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1335 cdr.Ctrl |= 0x18;
1336
1337 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1338
1339 return psxHu8(0x1800) = cdr.Ctrl;
1340}
1341
1342void cdrWrite0(unsigned char rt) {
1343 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1344
1345 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1346}
1347
1348unsigned char cdrRead1(void) {
1349 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1350 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1351 else
1352 psxHu8(0x1801) = 0;
1353 cdr.ResultP++;
1354 if (cdr.ResultP == cdr.ResultC)
1355 cdr.ResultReady = 0;
1356
1357 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1358
1359 return psxHu8(0x1801);
1360}
1361
1362void cdrWrite1(unsigned char rt) {
1363 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1364 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1365
1366 switch (cdr.Ctrl & 3) {
1367 case 0:
1368 break;
1369 case 3:
1370 cdr.AttenuatorRightToRightT = rt;
1371 return;
1372 default:
1373 return;
1374 }
1375
1376#ifdef CDR_LOG_CMD_IRQ
1377 SysPrintf("%u cdrom: CD1 write: %x (%s)", psxRegs.cycle, rt, CmdName[rt]);
1378 if (cdr.ParamC) {
1379 int i;
1380 SysPrintf(" Param[%d] = {", cdr.ParamC);
1381 for (i = 0; i < cdr.ParamC; i++)
1382 SysPrintf(" %x,", cdr.Param[i]);
1383 SysPrintf("}\n");
1384 } else {
1385 SysPrintf("\n");
1386 }
1387#endif
1388
1389 cdr.ResultReady = 0;
1390 cdr.Ctrl |= 0x80;
1391
1392 if (!cdr.CmdInProgress) {
1393 cdr.CmdInProgress = rt;
1394 // should be something like 12k + controller delays
1395 CDR_INT(5000);
1396 }
1397 else {
1398 CDR_LOG_I("%u cdrom: cmd while busy: %02x, prev %02x, busy %02x\n",
1399 psxRegs.cycle, rt, cdr.Cmd, cdr.CmdInProgress);
1400 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1401 cdr.CmdInProgress = rt;
1402 }
1403
1404 cdr.Cmd = rt;
1405}
1406
1407unsigned char cdrRead2(void) {
1408 unsigned char ret = 0;
1409
1410 if (cdr.FifoOffset < cdr.FifoSize)
1411 ret = cdr.Transfer[cdr.FifoOffset++];
1412 else
1413 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1414
1415 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1416 return ret;
1417}
1418
1419void cdrWrite2(unsigned char rt) {
1420 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1421 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1422
1423 switch (cdr.Ctrl & 3) {
1424 case 0:
1425 if (cdr.ParamC < 8) // FIXME: size and wrapping
1426 cdr.Param[cdr.ParamC++] = rt;
1427 return;
1428 case 1:
1429 cdr.Reg2 = rt;
1430 setIrq(0x1005);
1431 return;
1432 case 2:
1433 cdr.AttenuatorLeftToLeftT = rt;
1434 return;
1435 case 3:
1436 cdr.AttenuatorRightToLeftT = rt;
1437 return;
1438 }
1439}
1440
1441unsigned char cdrRead3(void) {
1442 if (cdr.Ctrl & 0x1)
1443 psxHu8(0x1803) = cdr.Stat | 0xE0;
1444 else
1445 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1446
1447 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1448 return psxHu8(0x1803);
1449}
1450
1451void cdrWrite3(unsigned char rt) {
1452 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1453 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1454
1455 switch (cdr.Ctrl & 3) {
1456 case 0:
1457 break; // transfer
1458 case 1:
1459 if (cdr.Stat & rt) {
1460#ifdef CDR_LOG_CMD_IRQ
1461 SysPrintf("%u cdrom: ack %02x (w %02x)\n",
1462 psxRegs.cycle, cdr.Stat & rt, rt);
1463#endif
1464 // note: Croc vs Discworld Noir
1465 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1466 (cdr.CmdInProgress || cdr.Irq1Pending))
1467 CDR_INT(850); // 711-993
1468 }
1469 cdr.Stat &= ~rt;
1470
1471 if (rt & 0x40)
1472 cdr.ParamC = 0;
1473 return;
1474 case 2:
1475 cdr.AttenuatorLeftToRightT = rt;
1476 return;
1477 case 3:
1478 if (rt & 0x20) {
1479 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1480 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1481 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1482 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1483 }
1484 return;
1485 }
1486
1487 // test: Viewpoint
1488 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1489 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1490 }
1491 else if (rt & 0x80) {
1492 switch (cdr.Mode & 0x30) {
1493 case MODE_SIZE_2328:
1494 case 0x00:
1495 cdr.FifoOffset = 12;
1496 cdr.FifoSize = 2048 + 12;
1497 break;
1498
1499 case MODE_SIZE_2340:
1500 default:
1501 cdr.FifoOffset = 0;
1502 cdr.FifoSize = 2340;
1503 break;
1504 }
1505 }
1506 else if (!(rt & 0xc0))
1507 cdr.FifoOffset = DATA_SIZE; // fifo empty
1508}
1509
1510void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1511 u32 cdsize;
1512 int size;
1513 u8 *ptr;
1514
1515 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1516
1517 switch (chcr & 0x71000000) {
1518 case 0x11000000:
1519 ptr = (u8 *)PSXM(madr);
1520 if (ptr == NULL) {
1521 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1522 break;
1523 }
1524
1525 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1526
1527 /*
1528 GS CDX: Enhancement CD crash
1529 - Setloc 0:0:0
1530 - CdlPlay
1531 - Spams DMA3 and gets buffer overrun
1532 */
1533 size = DATA_SIZE - cdr.FifoOffset;
1534 if (size > cdsize)
1535 size = cdsize;
1536 if (size > 0)
1537 {
1538 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1539 cdr.FifoOffset += size;
1540 psxCpu->Clear(madr, size / 4);
1541 }
1542 if (size < cdsize)
1543 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1544
1545 CDRDMA_INT((cdsize/4) * 24);
1546
1547 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1548 if (chcr & 0x100) {
1549 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1550 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1551 }
1552 else {
1553 // halted
1554 psxRegs.cycle += (cdsize/4) * 24 - 20;
1555 }
1556 return;
1557
1558 default:
1559 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1560 break;
1561 }
1562
1563 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1564 DMA_INTERRUPT(3);
1565}
1566
1567void cdrDmaInterrupt(void)
1568{
1569 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1570 {
1571 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1572 DMA_INTERRUPT(3);
1573 }
1574}
1575
1576static void getCdInfo(void)
1577{
1578 u8 tmp;
1579
1580 CDR_getTN(cdr.ResultTN);
1581 CDR_getTD(0, cdr.SetSectorEnd);
1582 tmp = cdr.SetSectorEnd[0];
1583 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1584 cdr.SetSectorEnd[2] = tmp;
1585}
1586
1587void cdrReset() {
1588 memset(&cdr, 0, sizeof(cdr));
1589 cdr.CurTrack = 1;
1590 cdr.File = 1;
1591 cdr.Channel = 1;
1592 cdr.Reg2 = 0x1f;
1593 cdr.Stat = NoIntr;
1594 cdr.FifoOffset = DATA_SIZE; // fifo empty
1595 if (CdromId[0] == '\0') {
1596 cdr.DriveState = DRIVESTATE_STOPPED;
1597 cdr.StatP = 0;
1598 }
1599 else {
1600 cdr.DriveState = DRIVESTATE_STANDBY;
1601 cdr.StatP = STATUS_ROTATING;
1602 }
1603
1604 // BIOS player - default values
1605 cdr.AttenuatorLeftToLeft = 0x80;
1606 cdr.AttenuatorLeftToRight = 0x00;
1607 cdr.AttenuatorRightToLeft = 0x00;
1608 cdr.AttenuatorRightToRight = 0x80;
1609
1610 getCdInfo();
1611}
1612
1613int cdrFreeze(void *f, int Mode) {
1614 u32 tmp;
1615 u8 tmpp[3];
1616
1617 if (Mode == 0 && !Config.Cdda)
1618 CDR_stop();
1619
1620 cdr.freeze_ver = 0x63647202;
1621 gzfreeze(&cdr, sizeof(cdr));
1622
1623 if (Mode == 1) {
1624 cdr.ParamP = cdr.ParamC;
1625 tmp = cdr.FifoOffset;
1626 }
1627
1628 gzfreeze(&tmp, sizeof(tmp));
1629
1630 if (Mode == 0) {
1631 getCdInfo();
1632
1633 cdr.FifoOffset = tmp;
1634 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1635
1636 // read right sub data
1637 tmpp[0] = btoi(cdr.Prev[0]);
1638 tmpp[1] = btoi(cdr.Prev[1]);
1639 tmpp[2] = btoi(cdr.Prev[2]);
1640 cdr.Prev[0]++;
1641 ReadTrack(tmpp);
1642
1643 if (cdr.Play) {
1644 if (cdr.freeze_ver < 0x63647202)
1645 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1646
1647 Find_CurTrack(cdr.SetSectorPlay);
1648 if (!Config.Cdda)
1649 CDR_play(cdr.SetSectorPlay);
1650 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1651 CDRPLAYREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1652 }
1653
1654 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1655 // old versions did not latch Reg2, have to fixup..
1656 if (cdr.Reg2 == 0) {
1657 SysPrintf("cdrom: fixing up old savestate\n");
1658 cdr.Reg2 = 7;
1659 }
1660 // also did not save Attenuator..
1661 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1662 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1663 {
1664 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1665 }
1666 }
1667 }
1668
1669 return 0;
1670}
1671
1672void LidInterrupt(void) {
1673 getCdInfo();
1674 cdrLidSeekInterrupt();
1675}