cdrom: clean up command handling
[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(...)
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 Readed;
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 u32 unused4;
99
100 u16 CmdInProgress;
101 u16 unused5;
102 u32 unused6;
103
104 u8 unused7;
105
106 u8 DriveState;
107 u8 FastForward;
108 u8 FastBackward;
109 u8 unused8;
110
111 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
112 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
113 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
114 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
115} cdr;
116static unsigned char *pTransfer;
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
210enum drive_state {
211 DRIVESTATE_STANDBY = 0, // pause, play, read
212 DRIVESTATE_LID_OPEN,
213 DRIVESTATE_RESCAN_CD,
214 DRIVESTATE_PREPARE_CD,
215 DRIVESTATE_STOPPED,
216};
217
218static struct CdrStat stat;
219
220static unsigned int msf2sec(const u8 *msf) {
221 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
222}
223
224// for that weird psemu API..
225static unsigned int fsm2sec(const u8 *msf) {
226 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
227}
228
229static void sec2msf(unsigned int s, u8 *msf) {
230 msf[0] = s / 75 / 60;
231 s = s - msf[0] * 75 * 60;
232 msf[1] = s / 75;
233 s = s - msf[1] * 75;
234 msf[2] = s;
235}
236
237// cdrInterrupt
238#define CDR_INT(eCycle) { \
239 psxRegs.interrupt |= (1 << PSXINT_CDR); \
240 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
241 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
242 new_dyna_set_event(PSXINT_CDR, eCycle); \
243}
244
245// cdrPlaySeekReadInterrupt
246#define CDRPLAYSEEKREAD_INT(eCycle, isFirst) { \
247 u32 e_ = eCycle; \
248 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
249 if (isFirst) \
250 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
251 else \
252 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
253 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
254 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
255}
256
257// cdrLidSeekInterrupt
258#define CDRLID_INT(eCycle) { \
259 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
260 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
261 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
262 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
263}
264
265#define StopReading() { \
266 cdr.Reading = 0; \
267 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
268}
269
270#define StopCdda() { \
271 if (cdr.Play && !Config.Cdda) CDR_stop(); \
272 cdr.Play = FALSE; \
273 cdr.FastForward = 0; \
274 cdr.FastBackward = 0; \
275}
276
277#define SetPlaySeekRead(x, f) { \
278 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
279 x |= f; \
280}
281
282#define SetResultSize(size) { \
283 cdr.ResultP = 0; \
284 cdr.ResultC = size; \
285 cdr.ResultReady = 1; \
286}
287
288static void setIrq(int log_cmd)
289{
290 if (cdr.Stat & cdr.Reg2)
291 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
292
293#ifdef CDR_LOG_CMD_IRQ
294 if (cdr.Stat)
295 {
296 int i;
297 SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ",
298 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
299 for (i = 0; i < cdr.ResultC; i++)
300 SysPrintf("%02x ", cdr.Result[i]);
301 SysPrintf("\n");
302 }
303#endif
304}
305
306// timing used in this function was taken from tests on real hardware
307// (yes it's slow, but you probably don't want to modify it)
308void cdrLidSeekInterrupt(void)
309{
310 switch (cdr.DriveState) {
311 default:
312 case DRIVESTATE_STANDBY:
313 StopCdda();
314 StopReading();
315 SetPlaySeekRead(cdr.StatP, 0);
316
317 if (CDR_getStatus(&stat) == -1)
318 return;
319
320 if (stat.Status & STATUS_SHELLOPEN)
321 {
322 cdr.DriveState = DRIVESTATE_LID_OPEN;
323 CDRLID_INT(0x800);
324 }
325 break;
326
327 case DRIVESTATE_LID_OPEN:
328 if (CDR_getStatus(&stat) == -1)
329 stat.Status &= ~STATUS_SHELLOPEN;
330
331 // 02, 12, 10
332 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
333 cdr.StatP |= STATUS_SHELLOPEN;
334
335 // could generate error irq here, but real hardware
336 // only sometimes does that
337 // (not done when lots of commands are sent?)
338
339 CDRLID_INT(cdReadTime * 30);
340 break;
341 }
342 else if (cdr.StatP & STATUS_ROTATING) {
343 cdr.StatP &= ~STATUS_ROTATING;
344 }
345 else if (!(stat.Status & STATUS_SHELLOPEN)) {
346 // closed now
347 CheckCdrom();
348
349 // cdr.StatP STATUS_SHELLOPEN is "sticky"
350 // and is only cleared by CdlNop
351
352 cdr.DriveState = DRIVESTATE_RESCAN_CD;
353 CDRLID_INT(cdReadTime * 105);
354 break;
355 }
356
357 // recheck for close
358 CDRLID_INT(cdReadTime * 3);
359 break;
360
361 case DRIVESTATE_RESCAN_CD:
362 cdr.StatP |= STATUS_ROTATING;
363 cdr.DriveState = DRIVESTATE_PREPARE_CD;
364
365 // this is very long on real hardware, over 6 seconds
366 // make it a bit faster here...
367 CDRLID_INT(cdReadTime * 150);
368 break;
369
370 case DRIVESTATE_PREPARE_CD:
371 cdr.StatP |= STATUS_SEEK;
372
373 cdr.DriveState = DRIVESTATE_STANDBY;
374 CDRLID_INT(cdReadTime * 26);
375 break;
376 }
377}
378
379static void Find_CurTrack(const u8 *time)
380{
381 int current, sect;
382
383 current = msf2sec(time);
384
385 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
386 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
387 sect = fsm2sec(cdr.ResultTD);
388 if (sect - current >= 150)
389 break;
390 }
391}
392
393static void generate_subq(const u8 *time)
394{
395 unsigned char start[3], next[3];
396 unsigned int this_s, start_s, next_s, pregap;
397 int relative_s;
398
399 CDR_getTD(cdr.CurTrack, start);
400 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
401 pregap = 150;
402 CDR_getTD(cdr.CurTrack + 1, next);
403 }
404 else {
405 // last track - cd size
406 pregap = 0;
407 next[0] = cdr.SetSectorEnd[2];
408 next[1] = cdr.SetSectorEnd[1];
409 next[2] = cdr.SetSectorEnd[0];
410 }
411
412 this_s = msf2sec(time);
413 start_s = fsm2sec(start);
414 next_s = fsm2sec(next);
415
416 cdr.TrackChanged = FALSE;
417
418 if (next_s - this_s < pregap) {
419 cdr.TrackChanged = TRUE;
420 cdr.CurTrack++;
421 start_s = next_s;
422 }
423
424 cdr.subq.Index = 1;
425
426 relative_s = this_s - start_s;
427 if (relative_s < 0) {
428 cdr.subq.Index = 0;
429 relative_s = -relative_s;
430 }
431 sec2msf(relative_s, cdr.subq.Relative);
432
433 cdr.subq.Track = itob(cdr.CurTrack);
434 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
435 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
436 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
437 cdr.subq.Absolute[0] = itob(time[0]);
438 cdr.subq.Absolute[1] = itob(time[1]);
439 cdr.subq.Absolute[2] = itob(time[2]);
440}
441
442static void ReadTrack(const u8 *time) {
443 unsigned char tmp[3];
444 struct SubQ *subq;
445 u16 crc;
446
447 tmp[0] = itob(time[0]);
448 tmp[1] = itob(time[1]);
449 tmp[2] = itob(time[2]);
450
451 if (memcmp(cdr.Prev, tmp, 3) == 0)
452 return;
453
454 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
455
456 cdr.NoErr = CDR_readTrack(tmp);
457 memcpy(cdr.Prev, tmp, 3);
458
459 if (CheckSBI(time))
460 return;
461
462 subq = (struct SubQ *)CDR_getBufferSub();
463 if (subq != NULL && cdr.CurTrack == 1) {
464 crc = calcCrc((u8 *)subq + 12, 10);
465 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
466 cdr.subq.Track = subq->TrackNumber;
467 cdr.subq.Index = subq->IndexNumber;
468 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
469 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
470 }
471 else {
472 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
473 tmp[0], tmp[1], tmp[2]);
474 }
475 }
476 else {
477 generate_subq(time);
478 }
479
480 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
481 cdr.subq.Track, cdr.subq.Index,
482 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
483 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
484}
485
486static void cdrPlayInterrupt_Autopause()
487{
488 u32 abs_lev_max = 0;
489 boolean abs_lev_chselect;
490 u32 i;
491
492 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
493 CDR_LOG( "CDDA STOP\n" );
494
495 // Magic the Gathering
496 // - looping territory cdda
497
498 // ...?
499 //cdr.ResultReady = 1;
500 //cdr.Stat = DataReady;
501 cdr.Stat = DataEnd;
502 setIrq(0x200);
503
504 StopCdda();
505 SetPlaySeekRead(cdr.StatP, 0);
506 }
507 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
508 cdr.Result[0] = cdr.StatP;
509 cdr.Result[1] = cdr.subq.Track;
510 cdr.Result[2] = cdr.subq.Index;
511
512 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
513
514 /* 8 is a hack. For accuracy, it should be 588. */
515 for (i = 0; i < 8; i++)
516 {
517 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
518 }
519 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
520 abs_lev_max |= abs_lev_chselect << 15;
521
522 if (cdr.subq.Absolute[2] & 0x10) {
523 cdr.Result[3] = cdr.subq.Relative[0];
524 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
525 cdr.Result[5] = cdr.subq.Relative[2];
526 }
527 else {
528 cdr.Result[3] = cdr.subq.Absolute[0];
529 cdr.Result[4] = cdr.subq.Absolute[1];
530 cdr.Result[5] = cdr.subq.Absolute[2];
531 }
532
533 cdr.Result[6] = abs_lev_max >> 0;
534 cdr.Result[7] = abs_lev_max >> 8;
535
536 // Rayman: Logo freeze (resultready + dataready)
537 cdr.ResultReady = 1;
538 cdr.Stat = DataReady;
539
540 SetResultSize(8);
541 setIrq(0x201);
542 }
543}
544
545static int cdrSeekTime(unsigned char *target)
546{
547 int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
548 /*
549 * Gameblabla :
550 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
551 * and was unreliable for that game.
552 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
553 *
554 * Obviously, this isn't perfect but right now, it should be a bit better.
555 * Games to test this against if you change that setting :
556 * - Driver (titlescreen music delay and retry mission)
557 * - Worms Pinball (Will either not boot or crash in the memory card screen)
558 * - Viewpoint (short pauses if the delay in the ingame music is too long)
559 *
560 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
561 * However, 1000000 is not enough for Worms Pinball to reliably boot.
562 */
563 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
564 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
565 return seekTime;
566}
567
568static void cdrReadInterrupt(void);
569
570void cdrPlaySeekReadInterrupt(void)
571{
572 if (cdr.Reading) {
573 cdrReadInterrupt();
574 return;
575 }
576
577 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
578 if (cdr.Stat) {
579 CDR_LOG_I("cdrom: seek stat hack\n");
580 CDRPLAYSEEKREAD_INT(0x1000, 1);
581 return;
582 }
583 SetResultSize(1);
584 cdr.StatP |= STATUS_ROTATING;
585 SetPlaySeekRead(cdr.StatP, 0);
586 cdr.Result[0] = cdr.StatP;
587 if (cdr.Stat == 0) {
588 cdr.Stat = Complete;
589 setIrq(0x202);
590 }
591
592 Find_CurTrack(cdr.SetSectorPlay);
593 ReadTrack(cdr.SetSectorPlay);
594 cdr.TrackChanged = FALSE;
595 return;
596 }
597
598 if (!cdr.Play) return;
599
600 CDR_LOG( "CDDA - %d:%d:%d\n",
601 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
602
603 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
604 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
605 StopCdda();
606 SetPlaySeekRead(cdr.StatP, 0);
607 cdr.TrackChanged = TRUE;
608 }
609 else {
610 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
611 }
612
613 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
614 cdrPlayInterrupt_Autopause();
615
616 if (!cdr.Muted && !Config.Cdda) {
617 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
618 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
619 cdr.FirstSector = 0;
620 }
621
622 cdr.SetSectorPlay[2]++;
623 if (cdr.SetSectorPlay[2] == 75) {
624 cdr.SetSectorPlay[2] = 0;
625 cdr.SetSectorPlay[1]++;
626 if (cdr.SetSectorPlay[1] == 60) {
627 cdr.SetSectorPlay[1] = 0;
628 cdr.SetSectorPlay[0]++;
629 }
630 }
631
632 CDRPLAYSEEKREAD_INT(cdReadTime, 0);
633
634 // update for CdlGetlocP/autopause
635 generate_subq(cdr.SetSectorPlay);
636}
637
638void cdrInterrupt(void) {
639 int no_busy_error = 0;
640 int start_rotating = 0;
641 int error = 0;
642 unsigned int seekTime = 0;
643 u32 second_resp_time = 0;
644 u8 ParamC;
645 u8 set_loc[3];
646 u16 Cmd;
647 int i;
648
649 // Reschedule IRQ
650 if (cdr.Stat) {
651 CDR_LOG_I("cdrom: cmd %02x with irqstat %x\n", cdr.CmdInProgress, cdr.Stat);
652 CDR_INT(1000);
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 CDR_INT(256);
1109 }
1110
1111 setIrq(Cmd);
1112}
1113
1114#ifdef HAVE_ARMV7
1115 #define ssat32_to_16(v) \
1116 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1117#else
1118 #define ssat32_to_16(v) do { \
1119 if (v < -32768) v = -32768; \
1120 else if (v > 32767) v = 32767; \
1121 } while (0)
1122#endif
1123
1124void cdrAttenuate(s16 *buf, int samples, int stereo)
1125{
1126 int i, l, r;
1127 int ll = cdr.AttenuatorLeftToLeft;
1128 int lr = cdr.AttenuatorLeftToRight;
1129 int rl = cdr.AttenuatorRightToLeft;
1130 int rr = cdr.AttenuatorRightToRight;
1131
1132 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1133 return;
1134
1135 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1136 return;
1137
1138 if (stereo) {
1139 for (i = 0; i < samples; i++) {
1140 l = buf[i * 2];
1141 r = buf[i * 2 + 1];
1142 l = (l * ll + r * rl) >> 7;
1143 r = (r * rr + l * lr) >> 7;
1144 ssat32_to_16(l);
1145 ssat32_to_16(r);
1146 buf[i * 2] = l;
1147 buf[i * 2 + 1] = r;
1148 }
1149 }
1150 else {
1151 for (i = 0; i < samples; i++) {
1152 l = buf[i];
1153 l = l * (ll + rl) >> 7;
1154 //r = r * (rr + lr) >> 7;
1155 ssat32_to_16(l);
1156 //ssat32_to_16(r);
1157 buf[i] = l;
1158 }
1159 }
1160}
1161
1162static void cdrReadInterrupt(void)
1163{
1164 u8 *buf;
1165
1166 if (cdr.Stat) {
1167 CDR_LOG_I("cdrom: read stat hack %02x %02x\n", cdr.Cmd, cdr.Stat);
1168 CDRPLAYSEEKREAD_INT(2048, 1);
1169 return;
1170 }
1171
1172 SetResultSize(1);
1173 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1174 cdr.Result[0] = cdr.StatP;
1175
1176 ReadTrack(cdr.SetSectorPlay);
1177
1178 buf = CDR_getBuffer();
1179 if (buf == NULL)
1180 cdr.NoErr = 0;
1181
1182 if (!cdr.NoErr) {
1183 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1184 memset(cdr.Transfer, 0, DATA_SIZE);
1185 cdr.Stat = DiskError;
1186 cdr.Result[0] |= STATUS_ERROR;
1187 setIrq(0x205);
1188 return;
1189 }
1190
1191 memcpy(cdr.Transfer, buf, DATA_SIZE);
1192 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1193
1194
1195 CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1196
1197 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1198 // Firemen 2: Multi-XA files - briefings, cutscenes
1199 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1200 cdr.File = cdr.Transfer[4 + 0];
1201 cdr.Channel = cdr.Transfer[4 + 1];
1202 }
1203
1204 /* Gameblabla
1205 * Skips playing on channel 255.
1206 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1207 * TODO : Check if this is the proper behaviour.
1208 * */
1209 if((cdr.Transfer[4 + 2] & 0x4) &&
1210 (cdr.Transfer[4 + 1] == cdr.Channel) &&
1211 (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1212 int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1213 if (!ret) {
1214 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1215 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1216 cdr.FirstSector = 0;
1217 }
1218 else cdr.FirstSector = -1;
1219 }
1220 }
1221
1222 cdr.SetSectorPlay[2]++;
1223 if (cdr.SetSectorPlay[2] == 75) {
1224 cdr.SetSectorPlay[2] = 0;
1225 cdr.SetSectorPlay[1]++;
1226 if (cdr.SetSectorPlay[1] == 60) {
1227 cdr.SetSectorPlay[1] = 0;
1228 cdr.SetSectorPlay[0]++;
1229 }
1230 }
1231
1232 cdr.Readed = 0;
1233
1234 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1235
1236 /*
1237 Croc 2: $40 - only FORM1 (*)
1238 Judge Dredd: $C8 - only FORM1 (*)
1239 Sim Theme Park - no adpcm at all (zero)
1240 */
1241
1242 if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1243 cdr.Stat = DataReady;
1244 setIrq(0x203);
1245 }
1246
1247 // update for CdlGetlocP
1248 ReadTrack(cdr.SetSectorPlay);
1249}
1250
1251/*
1252cdrRead0:
1253 bit 0,1 - mode
1254 bit 2 - unknown
1255 bit 3 - unknown
1256 bit 4 - unknown
1257 bit 5 - 1 result ready
1258 bit 6 - 1 dma ready
1259 bit 7 - 1 command being processed
1260*/
1261
1262unsigned char cdrRead0(void) {
1263 if (cdr.ResultReady)
1264 cdr.Ctrl |= 0x20;
1265 else
1266 cdr.Ctrl &= ~0x20;
1267
1268 cdr.Ctrl |= 0x40; // data fifo not empty
1269
1270 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1271 cdr.Ctrl |= 0x18;
1272
1273 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1274
1275 return psxHu8(0x1800) = cdr.Ctrl;
1276}
1277
1278void cdrWrite0(unsigned char rt) {
1279 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1280
1281 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1282}
1283
1284unsigned char cdrRead1(void) {
1285 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1286 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1287 else
1288 psxHu8(0x1801) = 0;
1289 cdr.ResultP++;
1290 if (cdr.ResultP == cdr.ResultC)
1291 cdr.ResultReady = 0;
1292
1293 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1294
1295 return psxHu8(0x1801);
1296}
1297
1298void cdrWrite1(unsigned char rt) {
1299 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1300 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1301
1302 switch (cdr.Ctrl & 3) {
1303 case 0:
1304 break;
1305 case 3:
1306 cdr.AttenuatorRightToRightT = rt;
1307 return;
1308 default:
1309 return;
1310 }
1311
1312#ifdef CDR_LOG_CMD_IRQ
1313 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1314 if (cdr.ParamC) {
1315 int i;
1316 SysPrintf(" Param[%d] = {", cdr.ParamC);
1317 for (i = 0; i < cdr.ParamC; i++)
1318 SysPrintf(" %x,", cdr.Param[i]);
1319 SysPrintf("}\n");
1320 } else {
1321 SysPrintf("\n");
1322 }
1323#endif
1324
1325 cdr.ResultReady = 0;
1326 cdr.Ctrl |= 0x80;
1327
1328 if (!cdr.CmdInProgress) {
1329 cdr.CmdInProgress = rt;
1330 // should be something like 12k + controller delays
1331 CDR_INT(5000);
1332 }
1333 else {
1334 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1335 rt, cdr.Cmd, cdr.CmdInProgress);
1336 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1337 cdr.CmdInProgress = rt;
1338 }
1339
1340 cdr.Cmd = rt;
1341}
1342
1343unsigned char cdrRead2(void) {
1344 unsigned char ret;
1345
1346 if (cdr.Readed == 0) {
1347 ret = 0;
1348 } else {
1349 ret = *pTransfer++;
1350 }
1351
1352 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1353 return ret;
1354}
1355
1356void cdrWrite2(unsigned char rt) {
1357 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1358 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1359
1360 switch (cdr.Ctrl & 3) {
1361 case 0:
1362 if (cdr.ParamC < 8) // FIXME: size and wrapping
1363 cdr.Param[cdr.ParamC++] = rt;
1364 return;
1365 case 1:
1366 cdr.Reg2 = rt;
1367 setIrq(0x204);
1368 return;
1369 case 2:
1370 cdr.AttenuatorLeftToLeftT = rt;
1371 return;
1372 case 3:
1373 cdr.AttenuatorRightToLeftT = rt;
1374 return;
1375 }
1376}
1377
1378unsigned char cdrRead3(void) {
1379 if (cdr.Ctrl & 0x1)
1380 psxHu8(0x1803) = cdr.Stat | 0xE0;
1381 else
1382 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1383
1384 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1385 return psxHu8(0x1803);
1386}
1387
1388void cdrWrite3(unsigned char rt) {
1389 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1390 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1391
1392 switch (cdr.Ctrl & 3) {
1393 case 0:
1394 break; // transfer
1395 case 1:
1396#ifdef CDR_LOG_CMD_IRQ
1397 if (cdr.Stat & rt)
1398 SysPrintf("ack %02x\n", cdr.Stat & rt);
1399#endif
1400 cdr.Stat &= ~rt;
1401
1402 if (rt & 0x40)
1403 cdr.ParamC = 0;
1404 return;
1405 case 2:
1406 cdr.AttenuatorLeftToRightT = rt;
1407 return;
1408 case 3:
1409 if (rt & 0x20) {
1410 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1411 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1412 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1413 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1414 }
1415 return;
1416 }
1417
1418 if ((rt & 0x80) && cdr.Readed == 0) {
1419 cdr.Readed = 1;
1420 pTransfer = cdr.Transfer;
1421
1422 switch (cdr.Mode & 0x30) {
1423 case MODE_SIZE_2328:
1424 case 0x00:
1425 pTransfer += 12;
1426 break;
1427
1428 case MODE_SIZE_2340:
1429 pTransfer += 0;
1430 break;
1431
1432 default:
1433 break;
1434 }
1435 }
1436}
1437
1438void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1439 u32 cdsize;
1440 int size;
1441 u8 *ptr;
1442
1443 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1444
1445 switch (chcr) {
1446 case 0x11000000:
1447 case 0x11400100:
1448 if (cdr.Readed == 0) {
1449 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1450 break;
1451 }
1452
1453 cdsize = (bcr & 0xffff) * 4;
1454
1455 // Ape Escape: bcr = 0001 / 0000
1456 // - fix boot
1457 if( cdsize == 0 )
1458 {
1459 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1460 case MODE_SIZE_2340: cdsize = 2340; break;
1461 case MODE_SIZE_2328: cdsize = 2328; break;
1462 default:
1463 case MODE_SIZE_2048: cdsize = 2048; break;
1464 }
1465 }
1466
1467
1468 ptr = (u8 *)PSXM(madr);
1469 if (ptr == NULL) {
1470 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1471 break;
1472 }
1473
1474 /*
1475 GS CDX: Enhancement CD crash
1476 - Setloc 0:0:0
1477 - CdlPlay
1478 - Spams DMA3 and gets buffer overrun
1479 */
1480 size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1481 if (size > cdsize)
1482 size = cdsize;
1483 if (size > 0)
1484 {
1485 memcpy(ptr, pTransfer, size);
1486 }
1487
1488 psxCpu->Clear(madr, cdsize / 4);
1489 pTransfer += cdsize;
1490
1491 if( chcr == 0x11400100 ) {
1492 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1493 CDRDMA_INT( (cdsize/4) / 4 );
1494 }
1495 else if( chcr == 0x11000000 ) {
1496 // CDRDMA_INT( (cdsize/4) * 1 );
1497 // halted
1498 psxRegs.cycle += (cdsize/4) * 24/2;
1499 CDRDMA_INT(16);
1500 }
1501 return;
1502
1503 default:
1504 CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1505 break;
1506 }
1507
1508 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1509 DMA_INTERRUPT(3);
1510}
1511
1512void cdrDmaInterrupt(void)
1513{
1514 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1515 {
1516 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1517 DMA_INTERRUPT(3);
1518 }
1519}
1520
1521static void getCdInfo(void)
1522{
1523 u8 tmp;
1524
1525 CDR_getTN(cdr.ResultTN);
1526 CDR_getTD(0, cdr.SetSectorEnd);
1527 tmp = cdr.SetSectorEnd[0];
1528 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1529 cdr.SetSectorEnd[2] = tmp;
1530}
1531
1532void cdrReset() {
1533 memset(&cdr, 0, sizeof(cdr));
1534 cdr.CurTrack = 1;
1535 cdr.File = 1;
1536 cdr.Channel = 1;
1537 cdr.Reg2 = 0x1f;
1538 cdr.Stat = NoIntr;
1539 cdr.DriveState = DRIVESTATE_STANDBY;
1540 cdr.StatP = STATUS_ROTATING;
1541 pTransfer = cdr.Transfer;
1542
1543 // BIOS player - default values
1544 cdr.AttenuatorLeftToLeft = 0x80;
1545 cdr.AttenuatorLeftToRight = 0x00;
1546 cdr.AttenuatorRightToLeft = 0x00;
1547 cdr.AttenuatorRightToRight = 0x80;
1548
1549 getCdInfo();
1550}
1551
1552int cdrFreeze(void *f, int Mode) {
1553 u32 tmp;
1554 u8 tmpp[3];
1555
1556 if (Mode == 0 && !Config.Cdda)
1557 CDR_stop();
1558
1559 cdr.freeze_ver = 0x63647202;
1560 gzfreeze(&cdr, sizeof(cdr));
1561
1562 if (Mode == 1) {
1563 cdr.ParamP = cdr.ParamC;
1564 tmp = pTransfer - cdr.Transfer;
1565 }
1566
1567 gzfreeze(&tmp, sizeof(tmp));
1568
1569 if (Mode == 0) {
1570 getCdInfo();
1571
1572 pTransfer = cdr.Transfer + tmp;
1573
1574 // read right sub data
1575 tmpp[0] = btoi(cdr.Prev[0]);
1576 tmpp[1] = btoi(cdr.Prev[1]);
1577 tmpp[2] = btoi(cdr.Prev[2]);
1578 cdr.Prev[0]++;
1579 ReadTrack(tmpp);
1580
1581 if (cdr.Play) {
1582 if (cdr.freeze_ver < 0x63647202)
1583 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1584
1585 Find_CurTrack(cdr.SetSectorPlay);
1586 if (!Config.Cdda)
1587 CDR_play(cdr.SetSectorPlay);
1588 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1589 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1590 }
1591
1592 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1593 // old versions did not latch Reg2, have to fixup..
1594 if (cdr.Reg2 == 0) {
1595 SysPrintf("cdrom: fixing up old savestate\n");
1596 cdr.Reg2 = 7;
1597 }
1598 // also did not save Attenuator..
1599 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1600 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1601 {
1602 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1603 }
1604 }
1605 }
1606
1607 return 0;
1608}
1609
1610void LidInterrupt(void) {
1611 getCdInfo();
1612 cdrLidSeekInterrupt();
1613}