spu: cleanup some irq hacks
[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.Muted && !Config.Cdda) {
628 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
629 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
630 cdr.FirstSector = 0;
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 cdr.FirstSector = 1;
760
761 if (!Config.Cdda)
762 CDR_play(cdr.SetSectorPlay);
763
764 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
765
766 // BIOS player - set flag again
767 cdr.Play = TRUE;
768
769 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
770 start_rotating = 1;
771 break;
772
773 case CdlForward:
774 // TODO: error 80 if stopped
775 cdr.Stat = Complete;
776
777 // GameShark CD Player: Calls 2x + Play 2x
778 cdr.FastForward = 1;
779 cdr.FastBackward = 0;
780 break;
781
782 case CdlBackward:
783 cdr.Stat = Complete;
784
785 // GameShark CD Player: Calls 2x + Play 2x
786 cdr.FastBackward = 1;
787 cdr.FastForward = 0;
788 break;
789
790 case CdlStandby:
791 if (cdr.DriveState != DRIVESTATE_STOPPED) {
792 error = ERROR_INVALIDARG;
793 goto set_error;
794 }
795 AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
796 start_rotating = 1;
797 break;
798
799 case CdlStandby + 0x100:
800 cdr.Stat = Complete;
801 break;
802
803 case CdlStop:
804 if (cdr.Play) {
805 // grab time for current track
806 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
807
808 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
809 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
810 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
811 }
812
813 StopCdda();
814 StopReading();
815 SetPlaySeekRead(cdr.StatP, 0);
816 cdr.StatP &= ~STATUS_ROTATING;
817
818 delay = 0x800;
819 if (cdr.DriveState == DRIVESTATE_STANDBY)
820 delay = cdReadTime * 30 / 2;
821
822 cdr.DriveState = DRIVESTATE_STOPPED;
823 AddIrqQueue(CdlStop + 0x100, delay);
824 break;
825
826 case CdlStop + 0x100:
827 cdr.Stat = Complete;
828 break;
829
830 case CdlPause:
831 StopCdda();
832 StopReading();
833 /*
834 Gundam Battle Assault 2: much slower (*)
835 - Fixes boot, gameplay
836
837 Hokuto no Ken 2: slower
838 - Fixes intro + subtitles
839
840 InuYasha - Feudal Fairy Tale: slower
841 - Fixes battles
842 */
843 /* Gameblabla - Tightening the timings (as taken from Duckstation).
844 * The timings from Duckstation are based upon hardware tests.
845 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
846 * seems to be timing sensitive as it can depend on the CPU's clock speed.
847 * */
848 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
849 {
850 delay = 7000;
851 }
852 else
853 {
854 delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000));
855 }
856 AddIrqQueue(CdlPause + 0x100, delay);
857 SetPlaySeekRead(cdr.StatP, 0);
858 cdr.Ctrl |= 0x80;
859 break;
860
861 case CdlPause + 0x100:
862 cdr.Stat = Complete;
863 break;
864
865 case CdlReset:
866 StopCdda();
867 StopReading();
868 SetPlaySeekRead(cdr.StatP, 0);
869 cdr.Muted = FALSE;
870 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
871 AddIrqQueue(CdlReset + 0x100, 4100000);
872 no_busy_error = 1;
873 start_rotating = 1;
874 break;
875
876 case CdlReset + 0x100:
877 cdr.Stat = Complete;
878 break;
879
880 case CdlMute:
881 cdr.Muted = TRUE;
882 break;
883
884 case CdlDemute:
885 cdr.Muted = FALSE;
886 break;
887
888 case CdlSetfilter:
889 cdr.File = cdr.Param[0];
890 cdr.Channel = cdr.Param[1];
891 break;
892
893 case CdlSetmode:
894 no_busy_error = 1;
895 break;
896
897 case CdlGetparam:
898 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
899 SetResultSize(5);
900 cdr.Result[1] = cdr.Mode;
901 cdr.Result[2] = 0;
902 cdr.Result[3] = cdr.File;
903 cdr.Result[4] = cdr.Channel;
904 no_busy_error = 1;
905 break;
906
907 case CdlGetlocL:
908 SetResultSize(8);
909 memcpy(cdr.Result, cdr.Transfer, 8);
910 break;
911
912 case CdlGetlocP:
913 SetResultSize(8);
914 memcpy(&cdr.Result, &cdr.subq, 8);
915 break;
916
917 case CdlReadT: // SetSession?
918 // really long
919 AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
920 start_rotating = 1;
921 break;
922
923 case CdlReadT + 0x100:
924 cdr.Stat = Complete;
925 break;
926
927 case CdlGetTN:
928 SetResultSize(3);
929 if (CDR_getTN(cdr.ResultTN) == -1) {
930 cdr.Stat = DiskError;
931 cdr.Result[0] |= STATUS_ERROR;
932 } else {
933 cdr.Stat = Acknowledge;
934 cdr.Result[1] = itob(cdr.ResultTN[0]);
935 cdr.Result[2] = itob(cdr.ResultTN[1]);
936 }
937 break;
938
939 case CdlGetTD:
940 cdr.Track = btoi(cdr.Param[0]);
941 SetResultSize(4);
942 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
943 cdr.Stat = DiskError;
944 cdr.Result[0] |= STATUS_ERROR;
945 } else {
946 cdr.Stat = Acknowledge;
947 cdr.Result[0] = cdr.StatP;
948 cdr.Result[1] = itob(cdr.ResultTD[2]);
949 cdr.Result[2] = itob(cdr.ResultTD[1]);
950 /* According to Nocash's documentation, the function doesn't care about ff.
951 * This can be seen also in Mednafen's implementation. */
952 //cdr.Result[3] = itob(cdr.ResultTD[0]);
953 }
954 break;
955
956 case CdlSeekL:
957 case CdlSeekP:
958 StopCdda();
959 StopReading();
960 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
961
962 seekTime = cdrSeekTime(cdr.SetSector);
963 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
964 /*
965 Crusaders of Might and Magic = 0.5x-4x
966 - fix cutscene speech start
967
968 Eggs of Steel = 2x-?
969 - fix new game
970
971 Medievil = ?-4x
972 - fix cutscene speech
973
974 Rockman X5 = 0.5-4x
975 - fix capcom logo
976 */
977 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
978 start_rotating = 1;
979 break;
980
981 case CdlTest:
982 switch (cdr.Param[0]) {
983 case 0x20: // System Controller ROM Version
984 SetResultSize(4);
985 memcpy(cdr.Result, Test20, 4);
986 break;
987 case 0x22:
988 SetResultSize(8);
989 memcpy(cdr.Result, Test22, 4);
990 break;
991 case 0x23: case 0x24:
992 SetResultSize(8);
993 memcpy(cdr.Result, Test23, 4);
994 break;
995 }
996 no_busy_error = 1;
997 break;
998
999 case CdlID:
1000 AddIrqQueue(CdlID + 0x100, 20480);
1001 break;
1002
1003 case CdlID + 0x100:
1004 SetResultSize(8);
1005 cdr.Result[0] = cdr.StatP;
1006 cdr.Result[1] = 0;
1007 cdr.Result[2] = 0;
1008 cdr.Result[3] = 0;
1009
1010 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1011 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1012 cdr.Result[1] = 0xc0;
1013 }
1014 else {
1015 if (stat.Type == 2)
1016 cdr.Result[1] |= 0x10;
1017 if (CdromId[0] == '\0')
1018 cdr.Result[1] |= 0x80;
1019 }
1020 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1021
1022 /* This adds the string "PCSX" in Playstation bios boot screen */
1023 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1024 cdr.Stat = Complete;
1025 break;
1026
1027 case CdlInit:
1028 StopCdda();
1029 StopReading();
1030 SetPlaySeekRead(cdr.StatP, 0);
1031 // yes, it really sets STATUS_SHELLOPEN
1032 cdr.StatP |= STATUS_SHELLOPEN;
1033 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1034 CDRLID_INT(20480);
1035 no_busy_error = 1;
1036 start_rotating = 1;
1037 break;
1038
1039 case CdlGetQ:
1040 no_busy_error = 1;
1041 break;
1042
1043 case CdlReadToc:
1044 AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
1045 no_busy_error = 1;
1046 start_rotating = 1;
1047 break;
1048
1049 case CdlReadToc + 0x100:
1050 cdr.Stat = Complete;
1051 no_busy_error = 1;
1052 break;
1053
1054 case CdlReadN:
1055 case CdlReadS:
1056 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1057
1058 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1059 // Read* acts as play for cdda tracks in cdda mode
1060 goto do_CdlPlay;
1061
1062 StopCdda();
1063 if (cdr.SetlocPending) {
1064 seekTime = cdrSeekTime(cdr.SetSector);
1065 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1066 cdr.SetlocPending = 0;
1067 }
1068 cdr.Reading = 1;
1069 cdr.FirstSector = 1;
1070
1071 // Fighting Force 2 - update subq time immediately
1072 // - fixes new game
1073 ReadTrack(cdr.SetSectorPlay);
1074
1075 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
1076
1077 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1078 start_rotating = 1;
1079 break;
1080 case CdlSync:
1081 default:
1082 CDR_LOG_I("Invalid command: %02x\n", Irq);
1083 error = ERROR_INVALIDCMD;
1084 // FALLTHROUGH
1085
1086 set_error:
1087 SetResultSize(2);
1088 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1089 cdr.Result[1] = error;
1090 cdr.Stat = DiskError;
1091 break;
1092 }
1093
1094 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1095 cdr.DriveState = DRIVESTATE_STANDBY;
1096 cdr.StatP |= STATUS_ROTATING;
1097 }
1098
1099 if (!no_busy_error) {
1100 switch (cdr.DriveState) {
1101 case DRIVESTATE_LID_OPEN:
1102 case DRIVESTATE_RESCAN_CD:
1103 case DRIVESTATE_PREPARE_CD:
1104 SetResultSize(2);
1105 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1106 cdr.Result[1] = ERROR_NOTREADY;
1107 cdr.Stat = DiskError;
1108 break;
1109 }
1110 }
1111
1112finish:
1113 setIrq(Irq);
1114 cdr.ParamC = 0;
1115}
1116
1117#ifdef HAVE_ARMV7
1118 #define ssat32_to_16(v) \
1119 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1120#else
1121 #define ssat32_to_16(v) do { \
1122 if (v < -32768) v = -32768; \
1123 else if (v > 32767) v = 32767; \
1124 } while (0)
1125#endif
1126
1127void cdrAttenuate(s16 *buf, int samples, int stereo)
1128{
1129 int i, l, r;
1130 int ll = cdr.AttenuatorLeftToLeft;
1131 int lr = cdr.AttenuatorLeftToRight;
1132 int rl = cdr.AttenuatorRightToLeft;
1133 int rr = cdr.AttenuatorRightToRight;
1134
1135 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1136 return;
1137
1138 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1139 return;
1140
1141 if (stereo) {
1142 for (i = 0; i < samples; i++) {
1143 l = buf[i * 2];
1144 r = buf[i * 2 + 1];
1145 l = (l * ll + r * rl) >> 7;
1146 r = (r * rr + l * lr) >> 7;
1147 ssat32_to_16(l);
1148 ssat32_to_16(r);
1149 buf[i * 2] = l;
1150 buf[i * 2 + 1] = r;
1151 }
1152 }
1153 else {
1154 for (i = 0; i < samples; i++) {
1155 l = buf[i];
1156 l = l * (ll + rl) >> 7;
1157 //r = r * (rr + lr) >> 7;
1158 ssat32_to_16(l);
1159 //ssat32_to_16(r);
1160 buf[i] = l;
1161 }
1162 }
1163}
1164
1165static void cdrReadInterrupt(void)
1166{
1167 u8 *buf;
1168
1169 if (cdr.Irq || cdr.Stat) {
1170 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1171 CDRPLAYSEEKREAD_INT(2048);
1172 return;
1173 }
1174
1175 cdr.OCUP = 1;
1176 SetResultSize(1);
1177 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1178 cdr.Result[0] = cdr.StatP;
1179
1180 ReadTrack(cdr.SetSectorPlay);
1181
1182 buf = CDR_getBuffer();
1183 if (buf == NULL)
1184 cdr.NoErr = 0;
1185
1186 if (!cdr.NoErr) {
1187 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1188 memset(cdr.Transfer, 0, DATA_SIZE);
1189 cdr.Stat = DiskError;
1190 cdr.Result[0] |= STATUS_ERROR;
1191 setIrq(0x205);
1192 return;
1193 }
1194
1195 memcpy(cdr.Transfer, buf, DATA_SIZE);
1196 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1197
1198
1199 CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1200
1201 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1202 // Firemen 2: Multi-XA files - briefings, cutscenes
1203 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1204 cdr.File = cdr.Transfer[4 + 0];
1205 cdr.Channel = cdr.Transfer[4 + 1];
1206 }
1207
1208 /* Gameblabla
1209 * Skips playing on channel 255.
1210 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1211 * TODO : Check if this is the proper behaviour.
1212 * */
1213 if((cdr.Transfer[4 + 2] & 0x4) &&
1214 (cdr.Transfer[4 + 1] == cdr.Channel) &&
1215 (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1216 int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1217 if (!ret) {
1218 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1219 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1220 cdr.FirstSector = 0;
1221 }
1222 else cdr.FirstSector = -1;
1223 }
1224 }
1225
1226 cdr.SetSectorPlay[2]++;
1227 if (cdr.SetSectorPlay[2] == 75) {
1228 cdr.SetSectorPlay[2] = 0;
1229 cdr.SetSectorPlay[1]++;
1230 if (cdr.SetSectorPlay[1] == 60) {
1231 cdr.SetSectorPlay[1] = 0;
1232 cdr.SetSectorPlay[0]++;
1233 }
1234 }
1235
1236 cdr.Readed = 0;
1237
1238 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1239
1240 /*
1241 Croc 2: $40 - only FORM1 (*)
1242 Judge Dredd: $C8 - only FORM1 (*)
1243 Sim Theme Park - no adpcm at all (zero)
1244 */
1245
1246 if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1247 cdr.Stat = DataReady;
1248 setIrq(0x203);
1249 }
1250
1251 // update for CdlGetlocP
1252 ReadTrack(cdr.SetSectorPlay);
1253}
1254
1255/*
1256cdrRead0:
1257 bit 0,1 - mode
1258 bit 2 - unknown
1259 bit 3 - unknown
1260 bit 4 - unknown
1261 bit 5 - 1 result ready
1262 bit 6 - 1 dma ready
1263 bit 7 - 1 command being processed
1264*/
1265
1266unsigned char cdrRead0(void) {
1267 if (cdr.ResultReady)
1268 cdr.Ctrl |= 0x20;
1269 else
1270 cdr.Ctrl &= ~0x20;
1271
1272 if (cdr.OCUP)
1273 cdr.Ctrl |= 0x40;
1274// else
1275// cdr.Ctrl &= ~0x40;
1276
1277 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1278 cdr.Ctrl |= 0x18;
1279
1280 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1281
1282 return psxHu8(0x1800) = cdr.Ctrl;
1283}
1284
1285void cdrWrite0(unsigned char rt) {
1286 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1287
1288 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1289}
1290
1291unsigned char cdrRead1(void) {
1292 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1293 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1294 else
1295 psxHu8(0x1801) = 0;
1296 cdr.ResultP++;
1297 if (cdr.ResultP == cdr.ResultC)
1298 cdr.ResultReady = 0;
1299
1300 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1301
1302 return psxHu8(0x1801);
1303}
1304
1305void cdrWrite1(unsigned char rt) {
1306 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1307 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1308
1309 switch (cdr.Ctrl & 3) {
1310 case 0:
1311 break;
1312 case 3:
1313 cdr.AttenuatorRightToRightT = rt;
1314 return;
1315 default:
1316 return;
1317 }
1318
1319 cdr.Cmd = rt;
1320 cdr.OCUP = 0;
1321
1322#ifdef CDR_LOG_CMD_IRQ
1323 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1324 if (cdr.ParamC) {
1325 int i;
1326 SysPrintf(" Param[%d] = {", cdr.ParamC);
1327 for (i = 0; i < cdr.ParamC; i++)
1328 SysPrintf(" %x,", cdr.Param[i]);
1329 SysPrintf("}\n");
1330 } else {
1331 SysPrintf("\n");
1332 }
1333#endif
1334
1335 cdr.ResultReady = 0;
1336 cdr.Ctrl |= 0x80;
1337 // cdr.Stat = NoIntr;
1338 AddIrqQueue(cdr.Cmd, 0x800);
1339
1340 switch (cdr.Cmd) {
1341 case CdlSetmode:
1342 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1343
1344 cdr.Mode = cdr.Param[0];
1345 break;
1346 }
1347}
1348
1349unsigned char cdrRead2(void) {
1350 unsigned char ret;
1351
1352 if (cdr.Readed == 0) {
1353 ret = 0;
1354 } else {
1355 ret = *pTransfer++;
1356 }
1357
1358 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1359 return ret;
1360}
1361
1362void cdrWrite2(unsigned char rt) {
1363 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1364 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1365
1366 switch (cdr.Ctrl & 3) {
1367 case 0:
1368 if (cdr.ParamC < 8) // FIXME: size and wrapping
1369 cdr.Param[cdr.ParamC++] = rt;
1370 return;
1371 case 1:
1372 cdr.Reg2 = rt;
1373 setIrq(0x204);
1374 return;
1375 case 2:
1376 cdr.AttenuatorLeftToLeftT = rt;
1377 return;
1378 case 3:
1379 cdr.AttenuatorRightToLeftT = rt;
1380 return;
1381 }
1382}
1383
1384unsigned char cdrRead3(void) {
1385 if (cdr.Ctrl & 0x1)
1386 psxHu8(0x1803) = cdr.Stat | 0xE0;
1387 else
1388 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1389
1390 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1391 return psxHu8(0x1803);
1392}
1393
1394void cdrWrite3(unsigned char rt) {
1395 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1396 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1397
1398 switch (cdr.Ctrl & 3) {
1399 case 0:
1400 break; // transfer
1401 case 1:
1402 cdr.Stat &= ~rt;
1403
1404 if (rt & 0x40)
1405 cdr.ParamC = 0;
1406 return;
1407 case 2:
1408 cdr.AttenuatorLeftToRightT = rt;
1409 return;
1410 case 3:
1411 if (rt & 0x20) {
1412 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1413 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1414 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1415 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1416 }
1417 return;
1418 }
1419
1420 if ((rt & 0x80) && cdr.Readed == 0) {
1421 cdr.Readed = 1;
1422 pTransfer = cdr.Transfer;
1423
1424 switch (cdr.Mode & 0x30) {
1425 case MODE_SIZE_2328:
1426 case 0x00:
1427 pTransfer += 12;
1428 break;
1429
1430 case MODE_SIZE_2340:
1431 pTransfer += 0;
1432 break;
1433
1434 default:
1435 break;
1436 }
1437 }
1438}
1439
1440void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1441 u32 cdsize;
1442 int size;
1443 u8 *ptr;
1444
1445 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1446
1447 switch (chcr) {
1448 case 0x11000000:
1449 case 0x11400100:
1450 if (cdr.Readed == 0) {
1451 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1452 break;
1453 }
1454
1455 cdsize = (bcr & 0xffff) * 4;
1456
1457 // Ape Escape: bcr = 0001 / 0000
1458 // - fix boot
1459 if( cdsize == 0 )
1460 {
1461 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1462 case MODE_SIZE_2340: cdsize = 2340; break;
1463 case MODE_SIZE_2328: cdsize = 2328; break;
1464 default:
1465 case MODE_SIZE_2048: cdsize = 2048; break;
1466 }
1467 }
1468
1469
1470 ptr = (u8 *)PSXM(madr);
1471 if (ptr == NULL) {
1472 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1473 break;
1474 }
1475
1476 /*
1477 GS CDX: Enhancement CD crash
1478 - Setloc 0:0:0
1479 - CdlPlay
1480 - Spams DMA3 and gets buffer overrun
1481 */
1482 size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1483 if (size > cdsize)
1484 size = cdsize;
1485 if (size > 0)
1486 {
1487 memcpy(ptr, pTransfer, size);
1488 }
1489
1490 psxCpu->Clear(madr, cdsize / 4);
1491 pTransfer += cdsize;
1492
1493 if( chcr == 0x11400100 ) {
1494 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1495 CDRDMA_INT( (cdsize/4) / 4 );
1496 }
1497 else if( chcr == 0x11000000 ) {
1498 // CDRDMA_INT( (cdsize/4) * 1 );
1499 // halted
1500 psxRegs.cycle += (cdsize/4) * 24/2;
1501 CDRDMA_INT(16);
1502 }
1503 return;
1504
1505 default:
1506 CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1507 break;
1508 }
1509
1510 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1511 DMA_INTERRUPT(3);
1512}
1513
1514void cdrDmaInterrupt(void)
1515{
1516 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1517 {
1518 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1519 DMA_INTERRUPT(3);
1520 }
1521}
1522
1523static void getCdInfo(void)
1524{
1525 u8 tmp;
1526
1527 CDR_getTN(cdr.ResultTN);
1528 CDR_getTD(0, cdr.SetSectorEnd);
1529 tmp = cdr.SetSectorEnd[0];
1530 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1531 cdr.SetSectorEnd[2] = tmp;
1532}
1533
1534void cdrReset() {
1535 memset(&cdr, 0, sizeof(cdr));
1536 cdr.CurTrack = 1;
1537 cdr.File = 1;
1538 cdr.Channel = 1;
1539 cdr.Reg2 = 0x1f;
1540 cdr.Stat = NoIntr;
1541 cdr.DriveState = DRIVESTATE_STANDBY;
1542 cdr.StatP = STATUS_ROTATING;
1543 pTransfer = cdr.Transfer;
1544
1545 // BIOS player - default values
1546 cdr.AttenuatorLeftToLeft = 0x80;
1547 cdr.AttenuatorLeftToRight = 0x00;
1548 cdr.AttenuatorRightToLeft = 0x00;
1549 cdr.AttenuatorRightToRight = 0x80;
1550
1551 getCdInfo();
1552}
1553
1554int cdrFreeze(void *f, int Mode) {
1555 u32 tmp;
1556 u8 tmpp[3];
1557
1558 if (Mode == 0 && !Config.Cdda)
1559 CDR_stop();
1560
1561 cdr.freeze_ver = 0x63647202;
1562 gzfreeze(&cdr, sizeof(cdr));
1563
1564 if (Mode == 1) {
1565 cdr.ParamP = cdr.ParamC;
1566 tmp = pTransfer - cdr.Transfer;
1567 }
1568
1569 gzfreeze(&tmp, sizeof(tmp));
1570
1571 if (Mode == 0) {
1572 getCdInfo();
1573
1574 pTransfer = cdr.Transfer + tmp;
1575
1576 // read right sub data
1577 tmpp[0] = btoi(cdr.Prev[0]);
1578 tmpp[1] = btoi(cdr.Prev[1]);
1579 tmpp[2] = btoi(cdr.Prev[2]);
1580 cdr.Prev[0]++;
1581 ReadTrack(tmpp);
1582
1583 if (cdr.Play) {
1584 if (cdr.freeze_ver < 0x63647202)
1585 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1586
1587 Find_CurTrack(cdr.SetSectorPlay);
1588 if (!Config.Cdda)
1589 CDR_play(cdr.SetSectorPlay);
1590 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1591 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1592 }
1593
1594 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1595 // old versions did not latch Reg2, have to fixup..
1596 if (cdr.Reg2 == 0) {
1597 SysPrintf("cdrom: fixing up old savestate\n");
1598 cdr.Reg2 = 7;
1599 }
1600 // also did not save Attenuator..
1601 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1602 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1603 {
1604 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1605 }
1606 }
1607 }
1608
1609 return 0;
1610}
1611
1612void LidInterrupt(void) {
1613 getCdInfo();
1614 cdrLidSeekInterrupt();
1615}