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