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