1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
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. *
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. *
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 ***************************************************************************/
21 * Handles all CD-ROM registers and functions.
27 #include "arm_features.h"
31 #define CDR_LOG SysPrintf
36 #define CDR_LOG_I SysPrintf
38 #define CDR_LOG_I(...)
41 #define CDR_LOG_IO SysPrintf
43 #define CDR_LOG_IO(...)
45 //#define CDR_LOG_CMD_IRQ
49 unsigned char Reg1Mode;
51 unsigned char CmdProcess;
57 unsigned char Transfer[DATA_SIZE];
61 unsigned char Relative[3];
62 unsigned char Absolute[3];
64 unsigned char TrackChanged;
65 unsigned char pad1[3];
66 unsigned int freeze_ver;
68 unsigned char Prev[4];
69 unsigned char Param[8];
70 unsigned char Result[16];
74 unsigned char ResultC;
75 unsigned char ResultP;
76 unsigned char ResultReady;
79 unsigned char SetlocPending;
82 unsigned char ResultTN[6];
83 unsigned char ResultTD[4];
84 unsigned char SetSectorPlay[4];
85 unsigned char SetSectorEnd[4];
86 unsigned char SetSector[4];
90 int Mode, File, Channel;
110 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
111 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
112 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
113 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
115 static unsigned char *pTransfer;
116 static s16 read_buf[CD_FRAMESIZE_RAW/2];
118 /* CD-ROM magic numbers */
119 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
124 #define CdlBackward 5
132 #define CdlSetfilter 13
133 #define CdlSetmode 14
134 #define CdlGetparam 15
135 #define CdlGetlocL 16
136 #define CdlGetlocP 17
142 #define CdlSetclock 23
143 #define CdlGetclock 24
149 #define CdlReadToc 30
151 #ifdef CDR_LOG_CMD_IRQ
152 static const char * const CmdName[0x100] = {
153 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
154 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
155 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
156 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
157 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
158 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
159 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
160 "CdlInit", NULL, "CDlReadToc", NULL
164 unsigned char Test04[] = { 0 };
165 unsigned char Test05[] = { 0 };
166 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
167 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
168 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
174 #define Acknowledge 3
179 #define MODE_SPEED (1<<7) // 0x80
180 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
181 #define MODE_SIZE_2340 (1<<5) // 0x20
182 #define MODE_SIZE_2328 (1<<4) // 0x10
183 #define MODE_SIZE_2048 (0<<4) // 0x00
184 #define MODE_SF (1<<3) // 0x08 channel on/off
185 #define MODE_REPORT (1<<2) // 0x04
186 #define MODE_AUTOPAUSE (1<<1) // 0x02
187 #define MODE_CDDA (1<<0) // 0x01
190 #define STATUS_PLAY (1<<7) // 0x80
191 #define STATUS_SEEK (1<<6) // 0x40
192 #define STATUS_READ (1<<5) // 0x20
193 #define STATUS_SHELLOPEN (1<<4) // 0x10
194 #define STATUS_UNKNOWN3 (1<<3) // 0x08
195 #define STATUS_UNKNOWN2 (1<<2) // 0x04
196 #define STATUS_ROTATING (1<<1) // 0x02
197 #define STATUS_ERROR (1<<0) // 0x01
200 #define ERROR_NOTREADY (1<<7) // 0x80
201 #define ERROR_INVALIDCMD (1<<6) // 0x40
202 #define ERROR_INVALIDARG (1<<5) // 0x20
204 // 1x = 75 sectors per second
205 // PSXCLK = 1 sec in the ps
206 // so (PSXCLK / 75) = cdr read time (linuzappz)
207 #define cdReadTime (PSXCLK / 75)
210 DRIVESTATE_STANDBY = 0, // pause, play, read
212 DRIVESTATE_RESCAN_CD,
213 DRIVESTATE_PREPARE_CD,
217 static struct CdrStat stat;
219 static unsigned int msf2sec(const u8 *msf) {
220 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
223 // for that weird psemu API..
224 static unsigned int fsm2sec(const u8 *msf) {
225 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
228 static void sec2msf(unsigned int s, u8 *msf) {
229 msf[0] = s / 75 / 60;
230 s = s - msf[0] * 75 * 60;
237 #define CDR_INT(eCycle) { \
238 psxRegs.interrupt |= (1 << PSXINT_CDR); \
239 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
240 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
241 new_dyna_set_event(PSXINT_CDR, eCycle); \
244 // cdrPlaySeekReadInterrupt
245 #define CDRPLAYSEEKREAD_INT(eCycle) { \
246 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
247 psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
248 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
249 new_dyna_set_event(PSXINT_CDREAD, eCycle); \
252 // cdrLidSeekInterrupt
253 #define CDRLID_INT(eCycle) { \
254 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
255 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
256 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
257 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
260 #define StopReading() { \
262 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
265 #define StopCdda() { \
266 if (cdr.Play && !Config.Cdda) CDR_stop(); \
268 cdr.FastForward = 0; \
269 cdr.FastBackward = 0; \
272 #define SetPlaySeekRead(x, f) { \
273 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
277 #define SetResultSize(size) { \
279 cdr.ResultC = size; \
280 cdr.ResultReady = 1; \
283 static void setIrq(int log_cmd)
285 if (cdr.Stat & cdr.Reg2)
286 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
288 #ifdef CDR_LOG_CMD_IRQ
291 SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ",
292 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
293 for (i = 0; i < cdr.ResultC; i++)
294 SysPrintf("%02x ", cdr.Result[i]);
300 // timing used in this function was taken from tests on real hardware
301 // (yes it's slow, but you probably don't want to modify it)
302 void cdrLidSeekInterrupt(void)
304 switch (cdr.DriveState) {
306 case DRIVESTATE_STANDBY:
309 SetPlaySeekRead(cdr.StatP, 0);
311 if (CDR_getStatus(&stat) == -1)
314 if (stat.Status & STATUS_SHELLOPEN)
316 cdr.DriveState = DRIVESTATE_LID_OPEN;
321 case DRIVESTATE_LID_OPEN:
322 if (CDR_getStatus(&stat) == -1)
323 stat.Status &= ~STATUS_SHELLOPEN;
326 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
327 cdr.StatP |= STATUS_SHELLOPEN;
329 // could generate error irq here, but real hardware
330 // only sometimes does that
331 // (not done when lots of commands are sent?)
333 CDRLID_INT(cdReadTime * 30);
336 else if (cdr.StatP & STATUS_ROTATING) {
337 cdr.StatP &= ~STATUS_ROTATING;
339 else if (!(stat.Status & STATUS_SHELLOPEN)) {
343 // cdr.StatP STATUS_SHELLOPEN is "sticky"
344 // and is only cleared by CdlNop
346 cdr.DriveState = DRIVESTATE_RESCAN_CD;
347 CDRLID_INT(cdReadTime * 105);
352 CDRLID_INT(cdReadTime * 3);
355 case DRIVESTATE_RESCAN_CD:
356 cdr.StatP |= STATUS_ROTATING;
357 cdr.DriveState = DRIVESTATE_PREPARE_CD;
359 // this is very long on real hardware, over 6 seconds
360 // make it a bit faster here...
361 CDRLID_INT(cdReadTime * 150);
364 case DRIVESTATE_PREPARE_CD:
365 cdr.StatP |= STATUS_SEEK;
367 cdr.DriveState = DRIVESTATE_STANDBY;
368 CDRLID_INT(cdReadTime * 26);
373 static void Find_CurTrack(const u8 *time)
377 current = msf2sec(time);
379 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
380 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
381 sect = fsm2sec(cdr.ResultTD);
382 if (sect - current >= 150)
387 static void generate_subq(const u8 *time)
389 unsigned char start[3], next[3];
390 unsigned int this_s, start_s, next_s, pregap;
393 CDR_getTD(cdr.CurTrack, start);
394 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
396 CDR_getTD(cdr.CurTrack + 1, next);
399 // last track - cd size
401 next[0] = cdr.SetSectorEnd[2];
402 next[1] = cdr.SetSectorEnd[1];
403 next[2] = cdr.SetSectorEnd[0];
406 this_s = msf2sec(time);
407 start_s = fsm2sec(start);
408 next_s = fsm2sec(next);
410 cdr.TrackChanged = FALSE;
412 if (next_s - this_s < pregap) {
413 cdr.TrackChanged = TRUE;
420 relative_s = this_s - start_s;
421 if (relative_s < 0) {
423 relative_s = -relative_s;
425 sec2msf(relative_s, cdr.subq.Relative);
427 cdr.subq.Track = itob(cdr.CurTrack);
428 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
429 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
430 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
431 cdr.subq.Absolute[0] = itob(time[0]);
432 cdr.subq.Absolute[1] = itob(time[1]);
433 cdr.subq.Absolute[2] = itob(time[2]);
436 static void ReadTrack(const u8 *time) {
437 unsigned char tmp[3];
441 tmp[0] = itob(time[0]);
442 tmp[1] = itob(time[1]);
443 tmp[2] = itob(time[2]);
445 if (memcmp(cdr.Prev, tmp, 3) == 0)
448 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
450 cdr.NoErr = CDR_readTrack(tmp);
451 memcpy(cdr.Prev, tmp, 3);
456 subq = (struct SubQ *)CDR_getBufferSub();
457 if (subq != NULL && cdr.CurTrack == 1) {
458 crc = calcCrc((u8 *)subq + 12, 10);
459 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
460 cdr.subq.Track = subq->TrackNumber;
461 cdr.subq.Index = subq->IndexNumber;
462 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
463 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
466 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
467 tmp[0], tmp[1], tmp[2]);
474 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
475 cdr.subq.Track, cdr.subq.Index,
476 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
477 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
480 static void AddIrqQueue(unsigned short irq, unsigned long ecycle) {
482 if (irq == cdr.Irq || irq + 0x100 == cdr.Irq) {
488 CDR_LOG_I("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
497 static void cdrPlayInterrupt_Autopause()
500 boolean abs_lev_chselect;
503 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
504 CDR_LOG( "CDDA STOP\n" );
506 // Magic the Gathering
507 // - looping territory cdda
510 //cdr.ResultReady = 1;
511 //cdr.Stat = DataReady;
516 SetPlaySeekRead(cdr.StatP, 0);
518 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
519 cdr.Result[0] = cdr.StatP;
520 cdr.Result[1] = cdr.subq.Track;
521 cdr.Result[2] = cdr.subq.Index;
523 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
525 /* 8 is a hack. For accuracy, it should be 588. */
526 for (i = 0; i < 8; i++)
528 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
530 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
531 abs_lev_max |= abs_lev_chselect << 15;
533 if (cdr.subq.Absolute[2] & 0x10) {
534 cdr.Result[3] = cdr.subq.Relative[0];
535 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
536 cdr.Result[5] = cdr.subq.Relative[2];
539 cdr.Result[3] = cdr.subq.Absolute[0];
540 cdr.Result[4] = cdr.subq.Absolute[1];
541 cdr.Result[5] = cdr.subq.Absolute[2];
544 cdr.Result[6] = abs_lev_max >> 0;
545 cdr.Result[7] = abs_lev_max >> 8;
547 // Rayman: Logo freeze (resultready + dataready)
549 cdr.Stat = DataReady;
556 static int cdrSeekTime(unsigned char *target)
558 int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
561 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
562 * and was unreliable for that game.
563 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
565 * Obviously, this isn't perfect but right now, it should be a bit better.
566 * Games to test this against if you change that setting :
567 * - Driver (titlescreen music delay and retry mission)
568 * - Worms Pinball (Will either not boot or crash in the memory card screen)
569 * - Viewpoint (short pauses if the delay in the ingame music is too long)
571 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
572 * However, 1000000 is not enough for Worms Pinball to reliably boot.
574 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
575 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
579 static void cdrReadInterrupt(void);
581 void cdrPlaySeekReadInterrupt(void)
588 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
590 CDR_LOG_I("cdrom: seek stat hack\n");
591 CDRPLAYSEEKREAD_INT(0x1000);
595 cdr.StatP |= STATUS_ROTATING;
596 SetPlaySeekRead(cdr.StatP, 0);
597 cdr.Result[0] = cdr.StatP;
603 Find_CurTrack(cdr.SetSectorPlay);
604 ReadTrack(cdr.SetSectorPlay);
605 cdr.TrackChanged = FALSE;
609 if (!cdr.Play) return;
611 CDR_LOG( "CDDA - %d:%d:%d\n",
612 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
614 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
615 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
617 SetPlaySeekRead(cdr.StatP, 0);
618 cdr.TrackChanged = TRUE;
621 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
624 if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
625 cdrPlayInterrupt_Autopause();
627 if (!cdr.Muted && !Config.Cdda) {
628 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
629 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
633 cdr.SetSectorPlay[2]++;
634 if (cdr.SetSectorPlay[2] == 75) {
635 cdr.SetSectorPlay[2] = 0;
636 cdr.SetSectorPlay[1]++;
637 if (cdr.SetSectorPlay[1] == 60) {
638 cdr.SetSectorPlay[1] = 0;
639 cdr.SetSectorPlay[0]++;
643 CDRPLAYSEEKREAD_INT(cdReadTime);
645 // update for CdlGetlocP/autopause
646 generate_subq(cdr.SetSectorPlay);
649 void cdrInterrupt(void) {
651 int no_busy_error = 0;
652 int start_rotating = 0;
655 unsigned int seekTime = 0;
661 CDR_LOG_I("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat);
670 cdr.Result[0] = cdr.StatP;
671 cdr.Stat = Acknowledge;
673 if (cdr.IrqRepeated) {
675 if (cdr.eCycle > psxRegs.cycle) {
685 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
686 cdr.StatP &= ~STATUS_SHELLOPEN;
691 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
693 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
694 if (((cdr.Param[0] & 0x0F) > 0x09) || (cdr.Param[0] > 0x99) || ((cdr.Param[1] & 0x0F) > 0x09) || (cdr.Param[1] >= 0x60) || ((cdr.Param[2] & 0x0F) > 0x09) || (cdr.Param[2] >= 0x75))
696 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
697 error = ERROR_INVALIDARG;
702 for (i = 0; i < 3; i++)
703 set_loc[i] = btoi(cdr.Param[i]);
704 memcpy(cdr.SetSector, set_loc, 3);
705 cdr.SetSector[3] = 0;
706 cdr.SetlocPending = 1;
715 cdr.FastBackward = 0;
719 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
721 if (cdr.ParamC != 0 && cdr.Param[0] != 0) {
722 int track = btoi( cdr.Param[0] );
724 if (track <= cdr.ResultTN[1])
725 cdr.CurTrack = track;
727 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
729 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
730 for (i = 0; i < 3; i++)
731 set_loc[i] = cdr.ResultTD[2 - i];
732 seekTime = cdrSeekTime(set_loc);
733 memcpy(cdr.SetSectorPlay, set_loc, 3);
736 else if (cdr.SetlocPending) {
737 seekTime = cdrSeekTime(cdr.SetSector);
738 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
741 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
742 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
744 cdr.SetlocPending = 0;
747 Rayman: detect track changes
750 Twisted Metal 2: skip PREGAP + starting accurate SubQ
751 - plays tracks without retry play
753 Wild 9: skip PREGAP + starting accurate SubQ
754 - plays tracks without retry play
756 Find_CurTrack(cdr.SetSectorPlay);
757 ReadTrack(cdr.SetSectorPlay);
758 cdr.TrackChanged = FALSE;
762 CDR_play(cdr.SetSectorPlay);
764 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
766 // BIOS player - set flag again
769 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
774 // TODO: error 80 if stopped
777 // GameShark CD Player: Calls 2x + Play 2x
779 cdr.FastBackward = 0;
785 // GameShark CD Player: Calls 2x + Play 2x
786 cdr.FastBackward = 1;
791 if (cdr.DriveState != DRIVESTATE_STOPPED) {
792 error = ERROR_INVALIDARG;
795 AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
799 case CdlStandby + 0x100:
805 // grab time for current track
806 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
808 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
809 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
810 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
815 SetPlaySeekRead(cdr.StatP, 0);
816 cdr.StatP &= ~STATUS_ROTATING;
819 if (cdr.DriveState == DRIVESTATE_STANDBY)
820 delay = cdReadTime * 30 / 2;
822 cdr.DriveState = DRIVESTATE_STOPPED;
823 AddIrqQueue(CdlStop + 0x100, delay);
826 case CdlStop + 0x100:
834 Gundam Battle Assault 2: much slower (*)
835 - Fixes boot, gameplay
837 Hokuto no Ken 2: slower
838 - Fixes intro + subtitles
840 InuYasha - Feudal Fairy Tale: slower
843 /* Gameblabla - Tightening the timings (as taken from Duckstation).
844 * The timings from Duckstation are based upon hardware tests.
845 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
846 * seems to be timing sensitive as it can depend on the CPU's clock speed.
848 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
854 delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000));
856 AddIrqQueue(CdlPause + 0x100, delay);
857 SetPlaySeekRead(cdr.StatP, 0);
861 case CdlPause + 0x100:
868 SetPlaySeekRead(cdr.StatP, 0);
870 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
871 AddIrqQueue(CdlReset + 0x100, 4100000);
876 case CdlReset + 0x100:
889 cdr.File = cdr.Param[0];
890 cdr.Channel = cdr.Param[1];
898 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
900 cdr.Result[1] = cdr.Mode;
902 cdr.Result[3] = cdr.File;
903 cdr.Result[4] = cdr.Channel;
909 memcpy(cdr.Result, cdr.Transfer, 8);
914 memcpy(&cdr.Result, &cdr.subq, 8);
917 case CdlReadT: // SetSession?
919 AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
923 case CdlReadT + 0x100:
929 if (CDR_getTN(cdr.ResultTN) == -1) {
930 cdr.Stat = DiskError;
931 cdr.Result[0] |= STATUS_ERROR;
933 cdr.Stat = Acknowledge;
934 cdr.Result[1] = itob(cdr.ResultTN[0]);
935 cdr.Result[2] = itob(cdr.ResultTN[1]);
940 cdr.Track = btoi(cdr.Param[0]);
942 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
943 cdr.Stat = DiskError;
944 cdr.Result[0] |= STATUS_ERROR;
946 cdr.Stat = Acknowledge;
947 cdr.Result[0] = cdr.StatP;
948 cdr.Result[1] = itob(cdr.ResultTD[2]);
949 cdr.Result[2] = itob(cdr.ResultTD[1]);
950 /* According to Nocash's documentation, the function doesn't care about ff.
951 * This can be seen also in Mednafen's implementation. */
952 //cdr.Result[3] = itob(cdr.ResultTD[0]);
960 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
962 seekTime = cdrSeekTime(cdr.SetSector);
963 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
965 Crusaders of Might and Magic = 0.5x-4x
966 - fix cutscene speech start
972 - fix cutscene speech
977 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
982 switch (cdr.Param[0]) {
983 case 0x20: // System Controller ROM Version
985 memcpy(cdr.Result, Test20, 4);
989 memcpy(cdr.Result, Test22, 4);
991 case 0x23: case 0x24:
993 memcpy(cdr.Result, Test23, 4);
1000 AddIrqQueue(CdlID + 0x100, 20480);
1005 cdr.Result[0] = cdr.StatP;
1010 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1011 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1012 cdr.Result[1] = 0xc0;
1016 cdr.Result[1] |= 0x10;
1017 if (CdromId[0] == '\0')
1018 cdr.Result[1] |= 0x80;
1020 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1022 /* This adds the string "PCSX" in Playstation bios boot screen */
1023 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1024 cdr.Stat = Complete;
1030 SetPlaySeekRead(cdr.StatP, 0);
1031 // yes, it really sets STATUS_SHELLOPEN
1032 cdr.StatP |= STATUS_SHELLOPEN;
1033 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1044 AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
1049 case CdlReadToc + 0x100:
1050 cdr.Stat = Complete;
1056 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1058 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1059 // Read* acts as play for cdda tracks in cdda mode
1063 if (cdr.SetlocPending) {
1064 seekTime = cdrSeekTime(cdr.SetSector);
1065 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1066 cdr.SetlocPending = 0;
1069 cdr.FirstSector = 1;
1071 // Fighting Force 2 - update subq time immediately
1073 ReadTrack(cdr.SetSectorPlay);
1075 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
1077 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1082 CDR_LOG_I("Invalid command: %02x\n", Irq);
1083 error = ERROR_INVALIDCMD;
1088 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1089 cdr.Result[1] = error;
1090 cdr.Stat = DiskError;
1094 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1095 cdr.DriveState = DRIVESTATE_STANDBY;
1096 cdr.StatP |= STATUS_ROTATING;
1099 if (!no_busy_error) {
1100 switch (cdr.DriveState) {
1101 case DRIVESTATE_LID_OPEN:
1102 case DRIVESTATE_RESCAN_CD:
1103 case DRIVESTATE_PREPARE_CD:
1105 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1106 cdr.Result[1] = ERROR_NOTREADY;
1107 cdr.Stat = DiskError;
1118 #define ssat32_to_16(v) \
1119 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1121 #define ssat32_to_16(v) do { \
1122 if (v < -32768) v = -32768; \
1123 else if (v > 32767) v = 32767; \
1127 void cdrAttenuate(s16 *buf, int samples, int stereo)
1130 int ll = cdr.AttenuatorLeftToLeft;
1131 int lr = cdr.AttenuatorLeftToRight;
1132 int rl = cdr.AttenuatorRightToLeft;
1133 int rr = cdr.AttenuatorRightToRight;
1135 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1138 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1142 for (i = 0; i < samples; i++) {
1145 l = (l * ll + r * rl) >> 7;
1146 r = (r * rr + l * lr) >> 7;
1154 for (i = 0; i < samples; i++) {
1156 l = l * (ll + rl) >> 7;
1157 //r = r * (rr + lr) >> 7;
1165 static void cdrReadInterrupt(void)
1169 if (cdr.Irq || cdr.Stat) {
1170 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1171 CDRPLAYSEEKREAD_INT(2048);
1177 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1178 cdr.Result[0] = cdr.StatP;
1180 ReadTrack(cdr.SetSectorPlay);
1182 buf = CDR_getBuffer();
1187 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1188 memset(cdr.Transfer, 0, DATA_SIZE);
1189 cdr.Stat = DiskError;
1190 cdr.Result[0] |= STATUS_ERROR;
1195 memcpy(cdr.Transfer, buf, DATA_SIZE);
1196 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1199 CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1201 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1202 // Firemen 2: Multi-XA files - briefings, cutscenes
1203 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1204 cdr.File = cdr.Transfer[4 + 0];
1205 cdr.Channel = cdr.Transfer[4 + 1];
1209 * Skips playing on channel 255.
1210 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1211 * TODO : Check if this is the proper behaviour.
1213 if((cdr.Transfer[4 + 2] & 0x4) &&
1214 (cdr.Transfer[4 + 1] == cdr.Channel) &&
1215 (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1216 int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1218 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1219 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1220 cdr.FirstSector = 0;
1222 else cdr.FirstSector = -1;
1226 cdr.SetSectorPlay[2]++;
1227 if (cdr.SetSectorPlay[2] == 75) {
1228 cdr.SetSectorPlay[2] = 0;
1229 cdr.SetSectorPlay[1]++;
1230 if (cdr.SetSectorPlay[1] == 60) {
1231 cdr.SetSectorPlay[1] = 0;
1232 cdr.SetSectorPlay[0]++;
1238 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1241 Croc 2: $40 - only FORM1 (*)
1242 Judge Dredd: $C8 - only FORM1 (*)
1243 Sim Theme Park - no adpcm at all (zero)
1246 if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1247 cdr.Stat = DataReady;
1251 // update for CdlGetlocP
1252 ReadTrack(cdr.SetSectorPlay);
1261 bit 5 - 1 result ready
1263 bit 7 - 1 command being processed
1266 unsigned char cdrRead0(void) {
1267 if (cdr.ResultReady)
1275 // cdr.Ctrl &= ~0x40;
1277 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1280 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1282 return psxHu8(0x1800) = cdr.Ctrl;
1285 void cdrWrite0(unsigned char rt) {
1286 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1288 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1291 unsigned char cdrRead1(void) {
1292 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1293 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1297 if (cdr.ResultP == cdr.ResultC)
1298 cdr.ResultReady = 0;
1300 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1302 return psxHu8(0x1801);
1305 void cdrWrite1(unsigned char rt) {
1306 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1307 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1309 switch (cdr.Ctrl & 3) {
1313 cdr.AttenuatorRightToRightT = rt;
1322 #ifdef CDR_LOG_CMD_IRQ
1323 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1326 SysPrintf(" Param[%d] = {", cdr.ParamC);
1327 for (i = 0; i < cdr.ParamC; i++)
1328 SysPrintf(" %x,", cdr.Param[i]);
1335 cdr.ResultReady = 0;
1337 // cdr.Stat = NoIntr;
1338 AddIrqQueue(cdr.Cmd, 0x800);
1342 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1344 cdr.Mode = cdr.Param[0];
1349 unsigned char cdrRead2(void) {
1352 if (cdr.Readed == 0) {
1358 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1362 void cdrWrite2(unsigned char rt) {
1363 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1364 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1366 switch (cdr.Ctrl & 3) {
1368 if (cdr.ParamC < 8) // FIXME: size and wrapping
1369 cdr.Param[cdr.ParamC++] = rt;
1376 cdr.AttenuatorLeftToLeftT = rt;
1379 cdr.AttenuatorRightToLeftT = rt;
1384 unsigned char cdrRead3(void) {
1386 psxHu8(0x1803) = cdr.Stat | 0xE0;
1388 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1390 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1391 return psxHu8(0x1803);
1394 void cdrWrite3(unsigned char rt) {
1395 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1396 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1398 switch (cdr.Ctrl & 3) {
1408 cdr.AttenuatorLeftToRightT = rt;
1412 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1413 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1414 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1415 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1420 if ((rt & 0x80) && cdr.Readed == 0) {
1422 pTransfer = cdr.Transfer;
1424 switch (cdr.Mode & 0x30) {
1425 case MODE_SIZE_2328:
1430 case MODE_SIZE_2340:
1440 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1445 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1450 if (cdr.Readed == 0) {
1451 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1455 cdsize = (bcr & 0xffff) * 4;
1457 // Ape Escape: bcr = 0001 / 0000
1461 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1462 case MODE_SIZE_2340: cdsize = 2340; break;
1463 case MODE_SIZE_2328: cdsize = 2328; break;
1465 case MODE_SIZE_2048: cdsize = 2048; break;
1470 ptr = (u8 *)PSXM(madr);
1471 if (ptr == INVALID_PTR) {
1472 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1477 GS CDX: Enhancement CD crash
1480 - Spams DMA3 and gets buffer overrun
1482 size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1487 memcpy(ptr, pTransfer, size);
1490 psxCpu->Clear(madr, cdsize / 4);
1491 pTransfer += cdsize;
1493 if( chcr == 0x11400100 ) {
1494 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1495 CDRDMA_INT( (cdsize/4) / 4 );
1497 else if( chcr == 0x11000000 ) {
1498 // CDRDMA_INT( (cdsize/4) * 1 );
1500 psxRegs.cycle += (cdsize/4) * 24/2;
1506 CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1510 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1514 void cdrDmaInterrupt(void)
1516 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1518 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1523 static void getCdInfo(void)
1527 CDR_getTN(cdr.ResultTN);
1528 CDR_getTD(0, cdr.SetSectorEnd);
1529 tmp = cdr.SetSectorEnd[0];
1530 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1531 cdr.SetSectorEnd[2] = tmp;
1535 memset(&cdr, 0, sizeof(cdr));
1541 cdr.DriveState = DRIVESTATE_STANDBY;
1542 cdr.StatP = STATUS_ROTATING;
1543 pTransfer = cdr.Transfer;
1545 // BIOS player - default values
1546 cdr.AttenuatorLeftToLeft = 0x80;
1547 cdr.AttenuatorLeftToRight = 0x00;
1548 cdr.AttenuatorRightToLeft = 0x00;
1549 cdr.AttenuatorRightToRight = 0x80;
1554 int cdrFreeze(void *f, int Mode) {
1558 if (Mode == 0 && !Config.Cdda)
1561 cdr.freeze_ver = 0x63647202;
1562 gzfreeze(&cdr, sizeof(cdr));
1565 cdr.ParamP = cdr.ParamC;
1566 tmp = pTransfer - cdr.Transfer;
1569 gzfreeze(&tmp, sizeof(tmp));
1574 pTransfer = cdr.Transfer + tmp;
1576 // read right sub data
1577 tmpp[0] = btoi(cdr.Prev[0]);
1578 tmpp[1] = btoi(cdr.Prev[1]);
1579 tmpp[2] = btoi(cdr.Prev[2]);
1584 if (cdr.freeze_ver < 0x63647202)
1585 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1587 Find_CurTrack(cdr.SetSectorPlay);
1589 CDR_play(cdr.SetSectorPlay);
1590 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1591 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1594 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1595 // old versions did not latch Reg2, have to fixup..
1596 if (cdr.Reg2 == 0) {
1597 SysPrintf("cdrom: fixing up old savestate\n");
1600 // also did not save Attenuator..
1601 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1602 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1604 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1612 void LidInterrupt(void) {
1614 cdrLidSeekInterrupt();