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