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