some big endian fixes
[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 seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
549 /*
550 * Gameblabla :
551 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
552 * and was unreliable for that game.
553 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
554 *
555 * Obviously, this isn't perfect but right now, it should be a bit better.
556 * Games to test this against if you change that setting :
557 * - Driver (titlescreen music delay and retry mission)
558 * - Worms Pinball (Will either not boot or crash in the memory card screen)
559 * - Viewpoint (short pauses if the delay in the ingame music is too long)
560 *
561 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
562 * However, 1000000 is not enough for Worms Pinball to reliably boot.
563 */
564 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
565 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
566 return seekTime;
567}
568
569static void cdrReadInterrupt(void);
570static void cdrPrepCdda(s16 *buf, int samples);
571static void cdrAttenuate(s16 *buf, int samples, int stereo);
572
573void cdrPlaySeekReadInterrupt(void)
574{
575 if (cdr.Reading) {
576 cdrReadInterrupt();
577 return;
578 }
579
580 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
581 if (cdr.Stat) {
582 CDR_LOG_I("cdrom: seek stat hack\n");
583 CDRPLAYSEEKREAD_INT(0x1000, 1);
584 return;
585 }
586 SetResultSize(1);
587 cdr.StatP |= STATUS_ROTATING;
588 SetPlaySeekRead(cdr.StatP, 0);
589 cdr.Result[0] = cdr.StatP;
590 cdr.Stat = Complete;
591 setIrq(0x202);
592
593 Find_CurTrack(cdr.SetSectorPlay);
594 ReadTrack(cdr.SetSectorPlay);
595 cdr.TrackChanged = FALSE;
596 return;
597 }
598
599 if (!cdr.Play) return;
600
601 CDR_LOG( "CDDA - %d:%d:%d\n",
602 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
603
604 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
605 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
606 StopCdda();
607 SetPlaySeekRead(cdr.StatP, 0);
608 cdr.TrackChanged = TRUE;
609 }
610 else {
611 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
612 }
613
614 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
615 cdrPlayInterrupt_Autopause();
616
617 if (!cdr.Muted && !Config.Cdda) {
618 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
619 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
620 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
621 cdr.FirstSector = 0;
622 }
623
624 cdr.SetSectorPlay[2]++;
625 if (cdr.SetSectorPlay[2] == 75) {
626 cdr.SetSectorPlay[2] = 0;
627 cdr.SetSectorPlay[1]++;
628 if (cdr.SetSectorPlay[1] == 60) {
629 cdr.SetSectorPlay[1] = 0;
630 cdr.SetSectorPlay[0]++;
631 }
632 }
633
634 // update for CdlGetlocP/autopause
635 generate_subq(cdr.SetSectorPlay);
636
637 CDRPLAYSEEKREAD_INT(cdReadTime, 0);
638}
639
640void cdrInterrupt(void) {
641 int no_busy_error = 0;
642 int start_rotating = 0;
643 int error = 0;
644 unsigned int seekTime = 0;
645 u32 second_resp_time = 0;
646 u8 ParamC;
647 u8 set_loc[3];
648 u16 Cmd;
649 int i;
650
651 if (cdr.Stat) {
652 CDR_LOG_I("cdrom: cmd %02x with irqstat %x\n", cdr.CmdInProgress, cdr.Stat);
653 return;
654 }
655
656 cdr.Ctrl &= ~0x80;
657
658 // default response
659 SetResultSize(1);
660 cdr.Result[0] = cdr.StatP;
661 cdr.Stat = Acknowledge;
662
663 Cmd = cdr.CmdInProgress;
664 cdr.CmdInProgress = 0;
665 ParamC = cdr.ParamC;
666
667 if (Cmd < 0x100) {
668 cdr.Cmd = 0;
669 cdr.ParamC = 0;
670 }
671
672 switch (Cmd) {
673 case CdlNop:
674 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
675 cdr.StatP &= ~STATUS_SHELLOPEN;
676 no_busy_error = 1;
677 break;
678
679 case CdlSetloc:
680 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
681
682 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
683 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))
684 {
685 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
686 error = ERROR_INVALIDARG;
687 goto set_error;
688 }
689 else
690 {
691 for (i = 0; i < 3; i++)
692 set_loc[i] = btoi(cdr.Param[i]);
693 memcpy(cdr.SetSector, set_loc, 3);
694 cdr.SetSector[3] = 0;
695 cdr.SetlocPending = 1;
696 }
697 break;
698
699 do_CdlPlay:
700 case CdlPlay:
701 StopCdda();
702 StopReading();
703
704 cdr.FastBackward = 0;
705 cdr.FastForward = 0;
706
707 // BIOS CD Player
708 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
709
710 if (ParamC != 0 && cdr.Param[0] != 0) {
711 int track = btoi( cdr.Param[0] );
712
713 if (track <= cdr.ResultTN[1])
714 cdr.CurTrack = track;
715
716 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
717
718 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
719 for (i = 0; i < 3; i++)
720 set_loc[i] = cdr.ResultTD[2 - i];
721 seekTime = cdrSeekTime(set_loc);
722 memcpy(cdr.SetSectorPlay, set_loc, 3);
723 }
724 }
725 else if (cdr.SetlocPending) {
726 seekTime = cdrSeekTime(cdr.SetSector);
727 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
728 }
729 else {
730 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
731 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
732 }
733 cdr.SetlocPending = 0;
734
735 /*
736 Rayman: detect track changes
737 - fixes logo freeze
738
739 Twisted Metal 2: skip PREGAP + starting accurate SubQ
740 - plays tracks without retry play
741
742 Wild 9: skip PREGAP + starting accurate SubQ
743 - plays tracks without retry play
744 */
745 Find_CurTrack(cdr.SetSectorPlay);
746 ReadTrack(cdr.SetSectorPlay);
747 cdr.TrackChanged = FALSE;
748 cdr.FirstSector = 1;
749
750 if (!Config.Cdda)
751 CDR_play(cdr.SetSectorPlay);
752
753 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
754
755 // BIOS player - set flag again
756 cdr.Play = TRUE;
757
758 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
759 start_rotating = 1;
760 break;
761
762 case CdlForward:
763 // TODO: error 80 if stopped
764 cdr.Stat = Complete;
765
766 // GameShark CD Player: Calls 2x + Play 2x
767 cdr.FastForward = 1;
768 cdr.FastBackward = 0;
769 break;
770
771 case CdlBackward:
772 cdr.Stat = Complete;
773
774 // GameShark CD Player: Calls 2x + Play 2x
775 cdr.FastBackward = 1;
776 cdr.FastForward = 0;
777 break;
778
779 case CdlStandby:
780 if (cdr.DriveState != DRIVESTATE_STOPPED) {
781 error = ERROR_INVALIDARG;
782 goto set_error;
783 }
784 second_resp_time = cdReadTime * 125 / 2;
785 start_rotating = 1;
786 break;
787
788 case CdlStandby + 0x100:
789 cdr.Stat = Complete;
790 break;
791
792 case CdlStop:
793 if (cdr.Play) {
794 // grab time for current track
795 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
796
797 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
798 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
799 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
800 }
801
802 StopCdda();
803 StopReading();
804 SetPlaySeekRead(cdr.StatP, 0);
805 cdr.StatP &= ~STATUS_ROTATING;
806
807 second_resp_time = 0x800;
808 if (cdr.DriveState == DRIVESTATE_STANDBY)
809 second_resp_time = cdReadTime * 30 / 2;
810
811 cdr.DriveState = DRIVESTATE_STOPPED;
812 break;
813
814 case CdlStop + 0x100:
815 cdr.Stat = Complete;
816 break;
817
818 case CdlPause:
819 StopCdda();
820 StopReading();
821 /*
822 Gundam Battle Assault 2: much slower (*)
823 - Fixes boot, gameplay
824
825 Hokuto no Ken 2: slower
826 - Fixes intro + subtitles
827
828 InuYasha - Feudal Fairy Tale: slower
829 - Fixes battles
830 */
831 /* Gameblabla - Tightening the timings (as taken from Duckstation).
832 * The timings from Duckstation are based upon hardware tests.
833 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
834 * seems to be timing sensitive as it can depend on the CPU's clock speed.
835 * */
836 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
837 {
838 second_resp_time = 7000;
839 }
840 else
841 {
842 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
843 }
844 SetPlaySeekRead(cdr.StatP, 0);
845 cdr.Ctrl |= 0x80;
846 break;
847
848 case CdlPause + 0x100:
849 cdr.Stat = Complete;
850 break;
851
852 case CdlReset:
853 StopCdda();
854 StopReading();
855 SetPlaySeekRead(cdr.StatP, 0);
856 cdr.Muted = FALSE;
857 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
858 second_resp_time = 4100000;
859 no_busy_error = 1;
860 start_rotating = 1;
861 break;
862
863 case CdlReset + 0x100:
864 cdr.Stat = Complete;
865 break;
866
867 case CdlMute:
868 cdr.Muted = TRUE;
869 break;
870
871 case CdlDemute:
872 cdr.Muted = FALSE;
873 break;
874
875 case CdlSetfilter:
876 cdr.File = cdr.Param[0];
877 cdr.Channel = cdr.Param[1];
878 break;
879
880 case CdlSetmode:
881 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
882 cdr.Mode = cdr.Param[0];
883 no_busy_error = 1;
884 break;
885
886 case CdlGetparam:
887 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
888 SetResultSize(5);
889 cdr.Result[1] = cdr.Mode;
890 cdr.Result[2] = 0;
891 cdr.Result[3] = cdr.File;
892 cdr.Result[4] = cdr.Channel;
893 no_busy_error = 1;
894 break;
895
896 case CdlGetlocL:
897 SetResultSize(8);
898 memcpy(cdr.Result, cdr.Transfer, 8);
899 break;
900
901 case CdlGetlocP:
902 SetResultSize(8);
903 memcpy(&cdr.Result, &cdr.subq, 8);
904 break;
905
906 case CdlReadT: // SetSession?
907 // really long
908 second_resp_time = cdReadTime * 290 / 4;
909 start_rotating = 1;
910 break;
911
912 case CdlReadT + 0x100:
913 cdr.Stat = Complete;
914 break;
915
916 case CdlGetTN:
917 SetResultSize(3);
918 if (CDR_getTN(cdr.ResultTN) == -1) {
919 cdr.Stat = DiskError;
920 cdr.Result[0] |= STATUS_ERROR;
921 } else {
922 cdr.Stat = Acknowledge;
923 cdr.Result[1] = itob(cdr.ResultTN[0]);
924 cdr.Result[2] = itob(cdr.ResultTN[1]);
925 }
926 break;
927
928 case CdlGetTD:
929 cdr.Track = btoi(cdr.Param[0]);
930 SetResultSize(4);
931 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
932 cdr.Stat = DiskError;
933 cdr.Result[0] |= STATUS_ERROR;
934 } else {
935 cdr.Stat = Acknowledge;
936 cdr.Result[0] = cdr.StatP;
937 cdr.Result[1] = itob(cdr.ResultTD[2]);
938 cdr.Result[2] = itob(cdr.ResultTD[1]);
939 /* According to Nocash's documentation, the function doesn't care about ff.
940 * This can be seen also in Mednafen's implementation. */
941 //cdr.Result[3] = itob(cdr.ResultTD[0]);
942 }
943 break;
944
945 case CdlSeekL:
946 case CdlSeekP:
947 StopCdda();
948 StopReading();
949 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
950
951 seekTime = cdrSeekTime(cdr.SetSector);
952 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
953 /*
954 Crusaders of Might and Magic = 0.5x-4x
955 - fix cutscene speech start
956
957 Eggs of Steel = 2x-?
958 - fix new game
959
960 Medievil = ?-4x
961 - fix cutscene speech
962
963 Rockman X5 = 0.5-4x
964 - fix capcom logo
965 */
966 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
967 start_rotating = 1;
968 break;
969
970 case CdlTest:
971 switch (cdr.Param[0]) {
972 case 0x20: // System Controller ROM Version
973 SetResultSize(4);
974 memcpy(cdr.Result, Test20, 4);
975 break;
976 case 0x22:
977 SetResultSize(8);
978 memcpy(cdr.Result, Test22, 4);
979 break;
980 case 0x23: case 0x24:
981 SetResultSize(8);
982 memcpy(cdr.Result, Test23, 4);
983 break;
984 }
985 no_busy_error = 1;
986 break;
987
988 case CdlID:
989 second_resp_time = 20480;
990 break;
991
992 case CdlID + 0x100:
993 SetResultSize(8);
994 cdr.Result[0] = cdr.StatP;
995 cdr.Result[1] = 0;
996 cdr.Result[2] = 0;
997 cdr.Result[3] = 0;
998
999 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1000 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1001 cdr.Result[1] = 0xc0;
1002 }
1003 else {
1004 if (stat.Type == 2)
1005 cdr.Result[1] |= 0x10;
1006 if (CdromId[0] == '\0')
1007 cdr.Result[1] |= 0x80;
1008 }
1009 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1010
1011 /* This adds the string "PCSX" in Playstation bios boot screen */
1012 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1013 cdr.Stat = Complete;
1014 break;
1015
1016 case CdlInit:
1017 StopCdda();
1018 StopReading();
1019 SetPlaySeekRead(cdr.StatP, 0);
1020 // yes, it really sets STATUS_SHELLOPEN
1021 cdr.StatP |= STATUS_SHELLOPEN;
1022 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1023 CDRLID_INT(20480);
1024 no_busy_error = 1;
1025 start_rotating = 1;
1026 break;
1027
1028 case CdlGetQ:
1029 no_busy_error = 1;
1030 break;
1031
1032 case CdlReadToc:
1033 second_resp_time = cdReadTime * 180 / 4;
1034 no_busy_error = 1;
1035 start_rotating = 1;
1036 break;
1037
1038 case CdlReadToc + 0x100:
1039 cdr.Stat = Complete;
1040 no_busy_error = 1;
1041 break;
1042
1043 case CdlReadN:
1044 case CdlReadS:
1045 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1046
1047 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1048 // Read* acts as play for cdda tracks in cdda mode
1049 goto do_CdlPlay;
1050
1051 StopCdda();
1052 if (cdr.SetlocPending) {
1053 seekTime = cdrSeekTime(cdr.SetSector);
1054 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1055 cdr.SetlocPending = 0;
1056 }
1057 cdr.Reading = 1;
1058 cdr.FirstSector = 1;
1059
1060 // Fighting Force 2 - update subq time immediately
1061 // - fixes new game
1062 ReadTrack(cdr.SetSectorPlay);
1063
1064 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1065
1066 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1067 start_rotating = 1;
1068 break;
1069 case CdlSync:
1070 default:
1071 CDR_LOG_I("Invalid command: %02x\n", Cmd);
1072 error = ERROR_INVALIDCMD;
1073 // FALLTHROUGH
1074
1075 set_error:
1076 SetResultSize(2);
1077 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1078 cdr.Result[1] = error;
1079 cdr.Stat = DiskError;
1080 break;
1081 }
1082
1083 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1084 cdr.DriveState = DRIVESTATE_STANDBY;
1085 cdr.StatP |= STATUS_ROTATING;
1086 }
1087
1088 if (!no_busy_error) {
1089 switch (cdr.DriveState) {
1090 case DRIVESTATE_LID_OPEN:
1091 case DRIVESTATE_RESCAN_CD:
1092 case DRIVESTATE_PREPARE_CD:
1093 SetResultSize(2);
1094 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1095 cdr.Result[1] = ERROR_NOTREADY;
1096 cdr.Stat = DiskError;
1097 break;
1098 }
1099 }
1100
1101 if (second_resp_time) {
1102 cdr.CmdInProgress = Cmd | 0x100;
1103 CDR_INT(second_resp_time);
1104 }
1105 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1106 cdr.CmdInProgress = cdr.Cmd;
1107 CDR_LOG_I("cdrom: cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1108 }
1109
1110 setIrq(Cmd);
1111}
1112
1113#ifdef HAVE_ARMV7
1114 #define ssat32_to_16(v) \
1115 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1116#else
1117 #define ssat32_to_16(v) do { \
1118 if (v < -32768) v = -32768; \
1119 else if (v > 32767) v = 32767; \
1120 } while (0)
1121#endif
1122
1123static void cdrPrepCdda(s16 *buf, int samples)
1124{
1125#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1126 int i;
1127 for (i = 0; i < samples; i++) {
1128 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1129 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1130 }
1131#endif
1132}
1133
1134static void cdrAttenuate(s16 *buf, int samples, int stereo)
1135{
1136 int i, l, r;
1137 int ll = cdr.AttenuatorLeftToLeft;
1138 int lr = cdr.AttenuatorLeftToRight;
1139 int rl = cdr.AttenuatorRightToLeft;
1140 int rr = cdr.AttenuatorRightToRight;
1141
1142 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1143 return;
1144
1145 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1146 return;
1147
1148 if (stereo) {
1149 for (i = 0; i < samples; i++) {
1150 l = buf[i * 2];
1151 r = buf[i * 2 + 1];
1152 l = (l * ll + r * rl) >> 7;
1153 r = (r * rr + l * lr) >> 7;
1154 ssat32_to_16(l);
1155 ssat32_to_16(r);
1156 buf[i * 2] = l;
1157 buf[i * 2 + 1] = r;
1158 }
1159 }
1160 else {
1161 for (i = 0; i < samples; i++) {
1162 l = buf[i];
1163 l = l * (ll + rl) >> 7;
1164 //r = r * (rr + lr) >> 7;
1165 ssat32_to_16(l);
1166 //ssat32_to_16(r);
1167 buf[i] = l;
1168 }
1169 }
1170}
1171
1172static void cdrReadInterruptSetResult(unsigned char result)
1173{
1174 if (cdr.Stat) {
1175 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1176 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1177 cdr.CmdInProgress, cdr.Stat);
1178 cdr.Irq1Pending = result;
1179 return;
1180 }
1181 SetResultSize(1);
1182 cdr.Result[0] = result;
1183 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1184 setIrq(0x203);
1185}
1186
1187static void cdrUpdateTransferBuf(const u8 *buf)
1188{
1189 if (!buf)
1190 return;
1191 memcpy(cdr.Transfer, buf, DATA_SIZE);
1192 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1193 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1194 if (cdr.FifoOffset < 2048 + 12)
1195 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1196}
1197
1198static void cdrReadInterrupt(void)
1199{
1200 u8 *buf = NULL, *hdr;
1201
1202 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1203
1204 ReadTrack(cdr.SetSectorPlay);
1205 if (cdr.NoErr)
1206 buf = CDR_getBuffer();
1207 if (buf == NULL)
1208 cdr.NoErr = 0;
1209
1210 if (!cdr.NoErr) {
1211 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1212 memset(cdr.Transfer, 0, DATA_SIZE);
1213 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1214 return;
1215 }
1216
1217 if (!cdr.Irq1Pending)
1218 cdrUpdateTransferBuf(buf);
1219
1220 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1221 hdr = buf + 4;
1222 // Firemen 2: Multi-XA files - briefings, cutscenes
1223 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1224 cdr.File = hdr[0];
1225 cdr.Channel = hdr[1];
1226 }
1227
1228 /* Gameblabla
1229 * Skips playing on channel 255.
1230 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1231 * TODO : Check if this is the proper behaviour.
1232 * */
1233 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1234 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1235 if (!ret) {
1236 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1237 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1238 cdr.FirstSector = 0;
1239 }
1240 else cdr.FirstSector = -1;
1241 }
1242 }
1243
1244 /*
1245 Croc 2: $40 - only FORM1 (*)
1246 Judge Dredd: $C8 - only FORM1 (*)
1247 Sim Theme Park - no adpcm at all (zero)
1248 */
1249
1250 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1251 cdrReadInterruptSetResult(cdr.StatP);
1252
1253 cdr.SetSectorPlay[2]++;
1254 if (cdr.SetSectorPlay[2] == 75) {
1255 cdr.SetSectorPlay[2] = 0;
1256 cdr.SetSectorPlay[1]++;
1257 if (cdr.SetSectorPlay[1] == 60) {
1258 cdr.SetSectorPlay[1] = 0;
1259 cdr.SetSectorPlay[0]++;
1260 }
1261 }
1262
1263 if (!cdr.Irq1Pending) {
1264 // update for CdlGetlocP
1265 ReadTrack(cdr.SetSectorPlay);
1266 }
1267
1268 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1269}
1270
1271static void doMissedIrqs(void)
1272{
1273 if (cdr.Irq1Pending)
1274 {
1275 // hand out the "newest" sector, according to nocash
1276 cdrUpdateTransferBuf(CDR_getBuffer());
1277 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
1278 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1279 SetResultSize(1);
1280 cdr.Result[0] = cdr.Irq1Pending;
1281 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
1282 cdr.Irq1Pending = 0;
1283 setIrq(0x205);
1284 return;
1285 }
1286 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) && cdr.CmdInProgress)
1287 CDR_INT(256);
1288}
1289
1290/*
1291cdrRead0:
1292 bit 0,1 - mode
1293 bit 2 - unknown
1294 bit 3 - unknown
1295 bit 4 - unknown
1296 bit 5 - 1 result ready
1297 bit 6 - 1 dma ready
1298 bit 7 - 1 command being processed
1299*/
1300
1301unsigned char cdrRead0(void) {
1302 if (cdr.ResultReady)
1303 cdr.Ctrl |= 0x20;
1304 else
1305 cdr.Ctrl &= ~0x20;
1306
1307 cdr.Ctrl |= 0x40; // data fifo not empty
1308
1309 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1310 cdr.Ctrl |= 0x18;
1311
1312 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1313
1314 return psxHu8(0x1800) = cdr.Ctrl;
1315}
1316
1317void cdrWrite0(unsigned char rt) {
1318 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1319
1320 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1321}
1322
1323unsigned char cdrRead1(void) {
1324 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1325 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1326 else
1327 psxHu8(0x1801) = 0;
1328 cdr.ResultP++;
1329 if (cdr.ResultP == cdr.ResultC)
1330 cdr.ResultReady = 0;
1331
1332 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1333
1334 return psxHu8(0x1801);
1335}
1336
1337void cdrWrite1(unsigned char rt) {
1338 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1339 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1340
1341 switch (cdr.Ctrl & 3) {
1342 case 0:
1343 break;
1344 case 3:
1345 cdr.AttenuatorRightToRightT = rt;
1346 return;
1347 default:
1348 return;
1349 }
1350
1351#ifdef CDR_LOG_CMD_IRQ
1352 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1353 if (cdr.ParamC) {
1354 int i;
1355 SysPrintf(" Param[%d] = {", cdr.ParamC);
1356 for (i = 0; i < cdr.ParamC; i++)
1357 SysPrintf(" %x,", cdr.Param[i]);
1358 SysPrintf("}\n");
1359 } else {
1360 SysPrintf("\n");
1361 }
1362#endif
1363
1364 cdr.ResultReady = 0;
1365 cdr.Ctrl |= 0x80;
1366
1367 if (!cdr.CmdInProgress) {
1368 cdr.CmdInProgress = rt;
1369 // should be something like 12k + controller delays
1370 CDR_INT(5000);
1371 }
1372 else {
1373 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1374 rt, cdr.Cmd, cdr.CmdInProgress);
1375 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1376 cdr.CmdInProgress = rt;
1377 }
1378
1379 cdr.Cmd = rt;
1380}
1381
1382unsigned char cdrRead2(void) {
1383 unsigned char ret = 0;
1384
1385 if (cdr.FifoOffset < cdr.FifoSize)
1386 ret = cdr.Transfer[cdr.FifoOffset++];
1387 else
1388 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1389
1390 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1391 return ret;
1392}
1393
1394void cdrWrite2(unsigned char rt) {
1395 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1396 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1397
1398 switch (cdr.Ctrl & 3) {
1399 case 0:
1400 if (cdr.ParamC < 8) // FIXME: size and wrapping
1401 cdr.Param[cdr.ParamC++] = rt;
1402 return;
1403 case 1:
1404 cdr.Reg2 = rt;
1405 setIrq(0x204);
1406 return;
1407 case 2:
1408 cdr.AttenuatorLeftToLeftT = rt;
1409 return;
1410 case 3:
1411 cdr.AttenuatorRightToLeftT = rt;
1412 return;
1413 }
1414}
1415
1416unsigned char cdrRead3(void) {
1417 if (cdr.Ctrl & 0x1)
1418 psxHu8(0x1803) = cdr.Stat | 0xE0;
1419 else
1420 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1421
1422 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1423 return psxHu8(0x1803);
1424}
1425
1426void cdrWrite3(unsigned char rt) {
1427 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1428 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1429
1430 switch (cdr.Ctrl & 3) {
1431 case 0:
1432 break; // transfer
1433 case 1:
1434#ifdef CDR_LOG_CMD_IRQ
1435 if (cdr.Stat & rt)
1436 SysPrintf("ack %02x\n", cdr.Stat & rt);
1437#endif
1438 cdr.Stat &= ~rt;
1439
1440 if (rt & 0x40)
1441 cdr.ParamC = 0;
1442 doMissedIrqs();
1443 return;
1444 case 2:
1445 cdr.AttenuatorLeftToRightT = rt;
1446 return;
1447 case 3:
1448 if (rt & 0x20) {
1449 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1450 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1451 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1452 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1453 }
1454 return;
1455 }
1456
1457 // test: Viewpoint
1458 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1459 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1460 }
1461 else if (rt & 0x80) {
1462 switch (cdr.Mode & 0x30) {
1463 case MODE_SIZE_2328:
1464 case 0x00:
1465 cdr.FifoOffset = 12;
1466 cdr.FifoSize = 2048 + 12;
1467 break;
1468
1469 case MODE_SIZE_2340:
1470 default:
1471 cdr.FifoOffset = 0;
1472 cdr.FifoSize = 2340;
1473 break;
1474 }
1475 }
1476 else if (!(rt & 0xc0))
1477 cdr.FifoOffset = DATA_SIZE; // fifo empty
1478}
1479
1480void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1481 u32 cdsize;
1482 int size;
1483 u8 *ptr;
1484
1485 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1486
1487 switch (chcr & 0x71000000) {
1488 case 0x11000000:
1489 ptr = (u8 *)PSXM(madr);
1490 if (ptr == NULL) {
1491 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1492 break;
1493 }
1494
1495 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1496
1497 /*
1498 GS CDX: Enhancement CD crash
1499 - Setloc 0:0:0
1500 - CdlPlay
1501 - Spams DMA3 and gets buffer overrun
1502 */
1503 size = DATA_SIZE - cdr.FifoOffset;
1504 if (size > cdsize)
1505 size = cdsize;
1506 if (size > 0)
1507 {
1508 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1509 cdr.FifoOffset += size;
1510 psxCpu->Clear(madr, size / 4);
1511 }
1512 if (size < cdsize)
1513 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1514
1515 CDRDMA_INT((cdsize/4) * 24);
1516
1517 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1518 if (chcr & 0x100) {
1519 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1520 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1521 }
1522 else {
1523 // halted
1524 psxRegs.cycle += (cdsize/4) * 24 - 20;
1525 }
1526 return;
1527
1528 default:
1529 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1530 break;
1531 }
1532
1533 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1534 DMA_INTERRUPT(3);
1535}
1536
1537void cdrDmaInterrupt(void)
1538{
1539 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1540 {
1541 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1542 DMA_INTERRUPT(3);
1543 }
1544}
1545
1546static void getCdInfo(void)
1547{
1548 u8 tmp;
1549
1550 CDR_getTN(cdr.ResultTN);
1551 CDR_getTD(0, cdr.SetSectorEnd);
1552 tmp = cdr.SetSectorEnd[0];
1553 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1554 cdr.SetSectorEnd[2] = tmp;
1555}
1556
1557void cdrReset() {
1558 memset(&cdr, 0, sizeof(cdr));
1559 cdr.CurTrack = 1;
1560 cdr.File = 1;
1561 cdr.Channel = 1;
1562 cdr.Reg2 = 0x1f;
1563 cdr.Stat = NoIntr;
1564 cdr.DriveState = DRIVESTATE_STANDBY;
1565 cdr.StatP = STATUS_ROTATING;
1566 cdr.FifoOffset = DATA_SIZE; // fifo empty
1567
1568 // BIOS player - default values
1569 cdr.AttenuatorLeftToLeft = 0x80;
1570 cdr.AttenuatorLeftToRight = 0x00;
1571 cdr.AttenuatorRightToLeft = 0x00;
1572 cdr.AttenuatorRightToRight = 0x80;
1573
1574 getCdInfo();
1575}
1576
1577int cdrFreeze(void *f, int Mode) {
1578 u32 tmp;
1579 u8 tmpp[3];
1580
1581 if (Mode == 0 && !Config.Cdda)
1582 CDR_stop();
1583
1584 cdr.freeze_ver = 0x63647202;
1585 gzfreeze(&cdr, sizeof(cdr));
1586
1587 if (Mode == 1) {
1588 cdr.ParamP = cdr.ParamC;
1589 tmp = cdr.FifoOffset;
1590 }
1591
1592 gzfreeze(&tmp, sizeof(tmp));
1593
1594 if (Mode == 0) {
1595 getCdInfo();
1596
1597 cdr.FifoOffset = tmp;
1598 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1599
1600 // read right sub data
1601 tmpp[0] = btoi(cdr.Prev[0]);
1602 tmpp[1] = btoi(cdr.Prev[1]);
1603 tmpp[2] = btoi(cdr.Prev[2]);
1604 cdr.Prev[0]++;
1605 ReadTrack(tmpp);
1606
1607 if (cdr.Play) {
1608 if (cdr.freeze_ver < 0x63647202)
1609 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1610
1611 Find_CurTrack(cdr.SetSectorPlay);
1612 if (!Config.Cdda)
1613 CDR_play(cdr.SetSectorPlay);
1614 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1615 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1616 }
1617
1618 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1619 // old versions did not latch Reg2, have to fixup..
1620 if (cdr.Reg2 == 0) {
1621 SysPrintf("cdrom: fixing up old savestate\n");
1622 cdr.Reg2 = 7;
1623 }
1624 // also did not save Attenuator..
1625 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1626 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1627 {
1628 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1629 }
1630 }
1631 }
1632
1633 return 0;
1634}
1635
1636void LidInterrupt(void) {
1637 getCdInfo();
1638 cdrLidSeekInterrupt();
1639}