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