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