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.
29 #include "psxevents.h"
30 #include "arm_features.h"
34 #define CDR_LOG SysPrintf
39 #define CDR_LOG_I SysPrintf
41 #define CDR_LOG_I(fmt, ...) \
42 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
45 #define CDR_LOG_IO SysPrintf
47 #define CDR_LOG_IO(...)
49 //#define CDR_LOG_CMD_IRQ
52 // unused members maintain savesate compatibility
53 unsigned char unused0;
54 unsigned char unused1;
56 unsigned char unused2;
62 unsigned char Transfer[DATA_SIZE];
66 unsigned char Relative[3];
67 unsigned char Absolute[3];
69 unsigned char TrackChanged;
70 unsigned char ReportDelay;
71 unsigned char unused3;
72 unsigned short sectorsRead;
73 unsigned int freeze_ver;
75 unsigned char Prev[4];
76 unsigned char Param[8];
77 unsigned char Result[16];
81 unsigned char ResultC;
82 unsigned char ResultP;
83 unsigned char ResultReady;
85 unsigned char SubqForwardSectors;
86 unsigned char SetlocPending;
89 unsigned char ResultTN[6];
90 unsigned char ResultTD[4];
91 unsigned char SetSectorPlay[4];
92 unsigned char SetSectorEnd[4];
93 unsigned char SetSector[4];
97 int Mode, File, Channel;
98 unsigned char LocL[8];
109 u32 LastReadSeekCycles;
118 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
119 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
120 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
121 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
123 static s16 read_buf[CD_FRAMESIZE_RAW/2];
125 /* CD-ROM magic numbers */
126 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
131 #define CdlBackward 5
139 #define CdlSetfilter 13
140 #define CdlSetmode 14
141 #define CdlGetparam 15
142 #define CdlGetlocL 16
143 #define CdlGetlocP 17
149 #define CdlSetclock 23
150 #define CdlGetclock 24
156 #define CdlReadToc 30
158 #ifdef CDR_LOG_CMD_IRQ
159 static const char * const CmdName[0x100] = {
160 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
161 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
162 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
163 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
164 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
165 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
166 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
167 "CdlInit", NULL, "CDlReadToc", NULL
171 unsigned char Test04[] = { 0 };
172 unsigned char Test05[] = { 0 };
173 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
174 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
175 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
181 #define Acknowledge 3
186 #define MODE_SPEED (1<<7) // 0x80
187 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
188 #define MODE_SIZE_2340 (1<<5) // 0x20
189 #define MODE_SIZE_2328 (1<<4) // 0x10
190 #define MODE_SIZE_2048 (0<<4) // 0x00
191 #define MODE_SF (1<<3) // 0x08 channel on/off
192 #define MODE_REPORT (1<<2) // 0x04
193 #define MODE_AUTOPAUSE (1<<1) // 0x02
194 #define MODE_CDDA (1<<0) // 0x01
197 #define STATUS_PLAY (1<<7) // 0x80
198 #define STATUS_SEEK (1<<6) // 0x40
199 #define STATUS_READ (1<<5) // 0x20
200 #define STATUS_SHELLOPEN (1<<4) // 0x10
201 #define STATUS_UNKNOWN3 (1<<3) // 0x08
202 #define STATUS_UNKNOWN2 (1<<2) // 0x04
203 #define STATUS_ROTATING (1<<1) // 0x02
204 #define STATUS_ERROR (1<<0) // 0x01
207 #define ERROR_NOTREADY (1<<7) // 0x80
208 #define ERROR_INVALIDCMD (1<<6) // 0x40
209 #define ERROR_INVALIDARG (1<<5) // 0x20
211 // 1x = 75 sectors per second
212 // PSXCLK = 1 sec in the ps
213 // so (PSXCLK / 75) = cdr read time (linuzappz)
214 #define cdReadTime (PSXCLK / 75)
216 #define LOCL_INVALID 0xff
217 #define SUBQ_FORWARD_SECTORS 2u
220 DRIVESTATE_STANDBY = 0, // pause, play, read
222 DRIVESTATE_RESCAN_CD,
223 DRIVESTATE_PREPARE_CD,
227 static struct CdrStat stat;
229 static unsigned int msf2sec(const u8 *msf) {
230 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
233 // for that weird psemu API..
234 static unsigned int fsm2sec(const u8 *msf) {
235 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
238 static void sec2msf(unsigned int s, u8 *msf) {
239 msf[0] = s / 75 / 60;
240 s = s - msf[0] * 75 * 60;
246 // cdrPlayReadInterrupt
247 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
249 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
251 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
253 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
254 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
255 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
258 #define StopReading() { \
260 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
263 #define StopCdda() { \
264 if (cdr.Play && !Config.Cdda) CDR_stop(); \
266 cdr.FastForward = 0; \
267 cdr.FastBackward = 0; \
270 #define SetPlaySeekRead(x, f) { \
271 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
275 #define SetResultSize(size) { \
277 cdr.ResultC = size; \
278 cdr.ResultReady = 1; \
281 static void setIrq(int log_cmd)
283 if (cdr.Stat & cdr.Reg2)
284 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
286 #ifdef CDR_LOG_CMD_IRQ
290 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
291 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
292 for (i = 0; i < cdr.ResultC; i++)
293 SysPrintf("%02x ", cdr.Result[i]);
299 // timing used in this function was taken from tests on real hardware
300 // (yes it's slow, but you probably don't want to modify it)
301 void cdrLidSeekInterrupt(void)
303 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
305 switch (cdr.DriveState) {
307 case DRIVESTATE_STANDBY:
310 SetPlaySeekRead(cdr.StatP, 0);
312 if (CDR_getStatus(&stat) == -1)
315 if (stat.Status & STATUS_SHELLOPEN)
317 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
318 cdr.DriveState = DRIVESTATE_LID_OPEN;
319 set_event(PSXINT_CDRLID, 0x800);
323 case DRIVESTATE_LID_OPEN:
324 if (CDR_getStatus(&stat) == -1)
325 stat.Status &= ~STATUS_SHELLOPEN;
328 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
329 cdr.StatP |= STATUS_SHELLOPEN;
331 // could generate error irq here, but real hardware
332 // only sometimes does that
333 // (not done when lots of commands are sent?)
335 set_event(PSXINT_CDRLID, cdReadTime * 30);
338 else if (cdr.StatP & STATUS_ROTATING) {
339 cdr.StatP &= ~STATUS_ROTATING;
341 else if (!(stat.Status & STATUS_SHELLOPEN)) {
345 // cdr.StatP STATUS_SHELLOPEN is "sticky"
346 // and is only cleared by CdlNop
348 cdr.DriveState = DRIVESTATE_RESCAN_CD;
349 set_event(PSXINT_CDRLID, cdReadTime * 105);
354 set_event(PSXINT_CDRLID, cdReadTime * 3);
357 case DRIVESTATE_RESCAN_CD:
358 cdr.StatP |= STATUS_ROTATING;
359 cdr.DriveState = DRIVESTATE_PREPARE_CD;
361 // this is very long on real hardware, over 6 seconds
362 // make it a bit faster here...
363 set_event(PSXINT_CDRLID, cdReadTime * 150);
366 case DRIVESTATE_PREPARE_CD:
367 if (cdr.StatP & STATUS_SEEK) {
368 SetPlaySeekRead(cdr.StatP, 0);
369 cdr.DriveState = DRIVESTATE_STANDBY;
372 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
373 set_event(PSXINT_CDRLID, cdReadTime * 26);
379 static void Find_CurTrack(const u8 *time)
383 current = msf2sec(time);
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)
393 static void generate_subq(const u8 *time)
395 unsigned char start[3], next[3];
396 unsigned int this_s, start_s, next_s, pregap;
399 CDR_getTD(cdr.CurTrack, start);
400 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
402 CDR_getTD(cdr.CurTrack + 1, next);
405 // last track - cd size
407 next[0] = cdr.SetSectorEnd[2];
408 next[1] = cdr.SetSectorEnd[1];
409 next[2] = cdr.SetSectorEnd[0];
412 this_s = msf2sec(time);
413 start_s = fsm2sec(start);
414 next_s = fsm2sec(next);
416 cdr.TrackChanged = FALSE;
418 if (next_s - this_s < pregap) {
419 cdr.TrackChanged = TRUE;
426 relative_s = this_s - start_s;
427 if (relative_s < 0) {
429 relative_s = -relative_s;
431 sec2msf(relative_s, cdr.subq.Relative);
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]);
442 static int ReadTrack(const u8 *time)
444 unsigned char tmp[3];
447 tmp[0] = itob(time[0]);
448 tmp[1] = itob(time[1]);
449 tmp[2] = itob(time[2]);
451 if (memcmp(cdr.Prev, tmp, 3) == 0)
454 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
456 read_ok = CDR_readTrack(tmp);
458 memcpy(cdr.Prev, tmp, 3);
462 static void UpdateSubq(const u8 *time)
464 const struct SubQ *subq;
465 int s = MSF2SECT(time[0], time[1], time[2]);
471 subq = (struct SubQ *)CDR_getBufferSub(s);
472 if (subq != NULL && cdr.CurTrack == 1) {
473 crc = calcCrc((u8 *)subq + 12, 10);
474 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
475 cdr.subq.Track = subq->TrackNumber;
476 cdr.subq.Index = subq->IndexNumber;
477 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
478 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
481 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
482 time[0], time[1], time[2]);
489 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
490 cdr.subq.Track, cdr.subq.Index,
491 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
492 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
495 static void cdrPlayInterrupt_Autopause()
498 boolean abs_lev_chselect;
501 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
502 CDR_LOG( "CDDA STOP\n" );
504 // Magic the Gathering
505 // - looping territory cdda
508 //cdr.ResultReady = 1;
509 //cdr.Stat = DataReady;
511 setIrq(0x1000); // 0x1000 just for logging purposes
514 SetPlaySeekRead(cdr.StatP, 0);
516 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
517 ((cdr.subq.Absolute[2] & 0x0f) == 0 || 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;
559 static int cdrSeekTime(unsigned char *target)
561 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
562 int seekTime = abs(diff) * (cdReadTime / 2000);
563 seekTime = MAX_VALUE(seekTime, 20000);
565 // need this stupidly long penalty or else Spyro2 intro desyncs
566 if ((s32)(psxRegs.cycle - cdr.LastReadSeekCycles) > cdReadTime * 8)
567 seekTime += cdReadTime * 25;
569 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
570 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
574 static u32 cdrAlignTimingHack(u32 cycles)
577 * timing hack for T'ai Fu - Wrath of the Tiger:
578 * The game has a bug where it issues some cdc commands from a low priority
579 * vint handler, however there is a higher priority default bios handler
580 * that acks the vint irq and returns, so game's handler is not reached
581 * (see bios irq handler chains at e004 and the game's irq handling func
582 * at 80036810). For the game to work, vint has to arrive after the bios
583 * vint handler rejects some other irq (of which only cd and rcnt2 are
584 * active), but before the game's handler loop reads I_STAT. The time
585 * window for this is quite small (~1k cycles of so). Apparently this
586 * somehow happens naturally on the real hardware.
588 * Note: always enforcing this breaks other games like Crash PAL version
589 * (inputs get dropped because bios handler doesn't see interrupts).
592 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
594 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
595 vint_rel += PSXCLK / 60;
596 while ((s32)(vint_rel - cycles) < 0)
597 vint_rel += PSXCLK / 60;
601 static void cdrUpdateTransferBuf(const u8 *buf);
602 static void cdrReadInterrupt(void);
603 static void cdrPrepCdda(s16 *buf, int samples);
604 static void cdrAttenuate(s16 *buf, int samples, int stereo);
606 static void msfiAdd(u8 *msfi, u32 count)
620 static void msfiSub(u8 *msfi, u32 count)
624 if ((s8)msfi[2] < 0) {
627 if ((s8)msfi[1] < 0) {
634 void cdrPlayReadInterrupt(void)
636 cdr.LastReadSeekCycles = psxRegs.cycle;
643 if (!cdr.Play) return;
645 CDR_LOG( "CDDA - %d:%d:%d\n",
646 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
648 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
649 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
651 SetPlaySeekRead(cdr.StatP, 0);
652 cdr.TrackChanged = TRUE;
655 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
658 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
659 cdrPlayInterrupt_Autopause();
661 if (!cdr.Muted && !Config.Cdda) {
662 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
663 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
664 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
668 msfiAdd(cdr.SetSectorPlay, 1);
670 // update for CdlGetlocP/autopause
671 generate_subq(cdr.SetSectorPlay);
673 CDRPLAYREAD_INT(cdReadTime, 0);
676 #define CMD_PART2 0x100
677 #define CMD_WHILE_NOT_READY 0x200
679 void cdrInterrupt(void) {
680 int start_rotating = 0;
682 u32 cycles, seekTime = 0;
683 u32 second_resp_time = 0;
693 CDR_LOG_I("cmd %02x with irqstat %x\n",
694 cdr.CmdInProgress, cdr.Stat);
697 if (cdr.Irq1Pending) {
698 // hand out the "newest" sector, according to nocash
699 cdrUpdateTransferBuf(CDR_getBuffer());
700 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
701 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
702 cdr.CmdInProgress, cdr.Irq1Pending);
704 cdr.Result[0] = cdr.Irq1Pending;
705 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
713 cdr.Result[0] = cdr.StatP;
714 cdr.Stat = Acknowledge;
716 Cmd = cdr.CmdInProgress;
717 cdr.CmdInProgress = 0;
726 switch (cdr.DriveState) {
727 case DRIVESTATE_PREPARE_CD:
729 // Syphon filter 2 expects commands to work shortly after it sees
730 // STATUS_ROTATING, so give up trying to emulate the startup seq
731 cdr.DriveState = DRIVESTATE_STANDBY;
732 cdr.StatP &= ~STATUS_SEEK;
733 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
737 case DRIVESTATE_LID_OPEN:
738 case DRIVESTATE_RESCAN_CD:
739 // no disk or busy with the initial scan, allowed cmds are limited
740 not_ready = CMD_WHILE_NOT_READY;
744 switch (Cmd | not_ready) {
746 case CdlNop + CMD_WHILE_NOT_READY:
747 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
748 cdr.StatP &= ~STATUS_SHELLOPEN;
752 case CdlSetloc + CMD_WHILE_NOT_READY:
753 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
755 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
756 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))
758 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
759 if (++cdr.errorRetryhack > 100)
761 error = ERROR_INVALIDARG;
766 for (i = 0; i < 3; i++)
767 set_loc[i] = btoi(cdr.Param[i]);
768 memcpy(cdr.SetSector, set_loc, 3);
769 cdr.SetSector[3] = 0;
770 cdr.SetlocPending = 1;
771 cdr.errorRetryhack = 0;
780 cdr.FastBackward = 0;
784 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
786 if (ParamC != 0 && cdr.Param[0] != 0) {
787 int track = btoi( cdr.Param[0] );
789 if (track <= cdr.ResultTN[1])
790 cdr.CurTrack = track;
792 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
794 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
795 for (i = 0; i < 3; i++)
796 set_loc[i] = cdr.ResultTD[2 - i];
797 seekTime = cdrSeekTime(set_loc);
798 memcpy(cdr.SetSectorPlay, set_loc, 3);
801 else if (cdr.SetlocPending) {
802 seekTime = cdrSeekTime(cdr.SetSector);
803 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
806 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
807 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
809 cdr.SetlocPending = 0;
812 Rayman: detect track changes
815 Twisted Metal 2: skip PREGAP + starting accurate SubQ
816 - plays tracks without retry play
818 Wild 9: skip PREGAP + starting accurate SubQ
819 - plays tracks without retry play
821 Find_CurTrack(cdr.SetSectorPlay);
822 generate_subq(cdr.SetSectorPlay);
823 cdr.LocL[0] = LOCL_INVALID;
824 cdr.SubqForwardSectors = 1;
825 cdr.TrackChanged = FALSE;
827 cdr.ReportDelay = 60;
831 CDR_play(cdr.SetSectorPlay);
833 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
835 // BIOS player - set flag again
838 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
843 // TODO: error 80 if stopped
846 // GameShark CD Player: Calls 2x + Play 2x
848 cdr.FastBackward = 0;
854 // GameShark CD Player: Calls 2x + Play 2x
855 cdr.FastBackward = 1;
860 if (cdr.DriveState != DRIVESTATE_STOPPED) {
861 error = ERROR_INVALIDARG;
864 second_resp_time = cdReadTime * 125 / 2;
868 case CdlStandby + CMD_PART2:
874 // grab time for current track
875 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
877 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
878 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
879 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
884 SetPlaySeekRead(cdr.StatP, 0);
885 cdr.StatP &= ~STATUS_ROTATING;
886 cdr.LocL[0] = LOCL_INVALID;
888 second_resp_time = 0x800;
889 if (cdr.DriveState == DRIVESTATE_STANDBY)
890 second_resp_time = cdReadTime * 30 / 2;
892 cdr.DriveState = DRIVESTATE_STOPPED;
895 case CdlStop + CMD_PART2:
903 // how the drive maintains the position while paused is quite
904 // complicated, this is the minimum to make "Bedlam" happy
905 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
909 Gundam Battle Assault 2: much slower (*)
910 - Fixes boot, gameplay
912 Hokuto no Ken 2: slower
913 - Fixes intro + subtitles
915 InuYasha - Feudal Fairy Tale: slower
918 /* Gameblabla - Tightening the timings (as taken from Duckstation).
919 * The timings from Duckstation are based upon hardware tests.
920 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
921 * seems to be timing sensitive as it can depend on the CPU's clock speed.
923 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
925 second_resp_time = 7000;
929 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
931 SetPlaySeekRead(cdr.StatP, 0);
934 case CdlPause + CMD_PART2:
939 case CdlReset + CMD_WHILE_NOT_READY:
942 SetPlaySeekRead(cdr.StatP, 0);
943 cdr.LocL[0] = LOCL_INVALID;
945 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
946 second_resp_time = not_ready ? 70000 : 4100000;
950 case CdlReset + CMD_PART2:
951 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
964 cdr.File = cdr.Param[0];
965 cdr.Channel = cdr.Param[1];
969 case CdlSetmode + CMD_WHILE_NOT_READY:
970 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
971 cdr.Mode = cdr.Param[0];
975 case CdlGetparam + CMD_WHILE_NOT_READY:
976 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
978 cdr.Result[1] = cdr.Mode;
980 cdr.Result[3] = cdr.File;
981 cdr.Result[4] = cdr.Channel;
985 if (cdr.LocL[0] == LOCL_INVALID) {
990 memcpy(cdr.Result, cdr.LocL, 8);
995 memcpy(&cdr.Result, &cdr.subq, 8);
998 case CdlReadT: // SetSession?
1000 second_resp_time = cdReadTime * 290 / 4;
1004 case CdlReadT + CMD_PART2:
1005 cdr.Stat = Complete;
1010 if (CDR_getTN(cdr.ResultTN) == -1) {
1011 cdr.Stat = DiskError;
1012 cdr.Result[0] |= STATUS_ERROR;
1014 cdr.Stat = Acknowledge;
1015 cdr.Result[1] = itob(cdr.ResultTN[0]);
1016 cdr.Result[2] = itob(cdr.ResultTN[1]);
1021 cdr.Track = btoi(cdr.Param[0]);
1023 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1024 cdr.Stat = DiskError;
1025 cdr.Result[0] |= STATUS_ERROR;
1027 cdr.Stat = Acknowledge;
1028 cdr.Result[0] = cdr.StatP;
1029 cdr.Result[1] = itob(cdr.ResultTD[2]);
1030 cdr.Result[2] = itob(cdr.ResultTD[1]);
1031 /* According to Nocash's documentation, the function doesn't care about ff.
1032 * This can be seen also in Mednafen's implementation. */
1033 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1041 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1043 seekTime = cdrSeekTime(cdr.SetSector);
1044 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1046 Crusaders of Might and Magic = 0.5x-4x
1047 - fix cutscene speech start
1049 Eggs of Steel = 2x-?
1053 - fix cutscene speech
1058 second_resp_time = cdReadTime + seekTime;
1062 case CdlSeekL + CMD_PART2:
1063 case CdlSeekP + CMD_PART2:
1064 SetPlaySeekRead(cdr.StatP, 0);
1065 cdr.Result[0] = cdr.StatP;
1066 cdr.Stat = Complete;
1068 Find_CurTrack(cdr.SetSectorPlay);
1069 read_ok = ReadTrack(cdr.SetSectorPlay);
1070 if (read_ok && (buf = CDR_getBuffer()))
1071 memcpy(cdr.LocL, buf, 8);
1072 UpdateSubq(cdr.SetSectorPlay);
1073 cdr.TrackChanged = FALSE;
1074 cdr.LastReadSeekCycles = psxRegs.cycle;
1078 case CdlTest + CMD_WHILE_NOT_READY:
1079 switch (cdr.Param[0]) {
1080 case 0x20: // System Controller ROM Version
1082 memcpy(cdr.Result, Test20, 4);
1086 memcpy(cdr.Result, Test22, 4);
1088 case 0x23: case 0x24:
1090 memcpy(cdr.Result, Test23, 4);
1096 second_resp_time = 20480;
1099 case CdlID + CMD_PART2:
1101 cdr.Result[0] = cdr.StatP;
1106 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1107 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1108 cdr.Result[1] = 0xc0;
1112 cdr.Result[1] |= 0x10;
1113 if (CdromId[0] == '\0')
1114 cdr.Result[1] |= 0x80;
1116 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1118 /* This adds the string "PCSX" in Playstation bios boot screen */
1119 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1120 cdr.Stat = Complete;
1124 case CdlInit + CMD_WHILE_NOT_READY:
1127 SetPlaySeekRead(cdr.StatP, 0);
1128 // yes, it really sets STATUS_SHELLOPEN
1129 cdr.StatP |= STATUS_SHELLOPEN;
1130 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1131 set_event(PSXINT_CDRLID, 20480);
1136 case CdlGetQ + CMD_WHILE_NOT_READY:
1140 case CdlReadToc + CMD_WHILE_NOT_READY:
1141 cdr.LocL[0] = LOCL_INVALID;
1142 second_resp_time = cdReadTime * 180 / 4;
1146 case CdlReadToc + CMD_PART2:
1147 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1148 cdr.Stat = Complete;
1153 if (cdr.Reading && !cdr.SetlocPending)
1156 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1158 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1159 // Read* acts as play for cdda tracks in cdda mode
1163 if (cdr.SetlocPending) {
1164 seekTime = cdrSeekTime(cdr.SetSector);
1165 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1166 cdr.SetlocPending = 0;
1169 cdr.FirstSector = 1;
1171 // Fighting Force 2 - update subq time immediately
1173 UpdateSubq(cdr.SetSectorPlay);
1174 cdr.LocL[0] = LOCL_INVALID;
1175 cdr.SubqForwardSectors = 1;
1176 cdr.sectorsRead = 0;
1178 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1180 if (Config.hacks.cdr_read_timing)
1181 cycles = cdrAlignTimingHack(cycles);
1182 CDRPLAYREAD_INT(cycles, 1);
1184 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1190 error = ERROR_INVALIDCMD;
1194 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1196 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1197 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1198 cdr.Stat = DiskError;
1202 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1203 cdr.DriveState = DRIVESTATE_STANDBY;
1204 cdr.StatP |= STATUS_ROTATING;
1207 if (second_resp_time) {
1208 cdr.CmdInProgress = Cmd | 0x100;
1209 set_event(PSXINT_CDR, second_resp_time);
1211 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1212 cdr.CmdInProgress = cdr.Cmd;
1213 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1220 #define ssat32_to_16(v) \
1221 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1223 #define ssat32_to_16(v) do { \
1224 if (v < -32768) v = -32768; \
1225 else if (v > 32767) v = 32767; \
1229 static void cdrPrepCdda(s16 *buf, int samples)
1231 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1233 for (i = 0; i < samples; i++) {
1234 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1235 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1240 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1243 int ll = cdr.AttenuatorLeftToLeft;
1244 int lr = cdr.AttenuatorLeftToRight;
1245 int rl = cdr.AttenuatorRightToLeft;
1246 int rr = cdr.AttenuatorRightToRight;
1248 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1251 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1255 for (i = 0; i < samples; i++) {
1258 l = (l * ll + r * rl) >> 7;
1259 r = (r * rr + l * lr) >> 7;
1267 for (i = 0; i < samples; i++) {
1269 l = l * (ll + rl) >> 7;
1270 //r = r * (rr + lr) >> 7;
1278 static void cdrReadInterruptSetResult(unsigned char result)
1281 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1282 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1283 cdr.CmdInProgress, cdr.Stat);
1284 cdr.Irq1Pending = result;
1288 cdr.Result[0] = result;
1289 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1293 static void cdrUpdateTransferBuf(const u8 *buf)
1297 memcpy(cdr.Transfer, buf, DATA_SIZE);
1298 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1299 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1300 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1301 if (cdr.FifoOffset < 2048 + 12)
1302 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1305 static void cdrReadInterrupt(void)
1307 u8 *buf = NULL, *hdr;
1311 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1312 msfiAdd(subqPos, cdr.SubqForwardSectors);
1313 UpdateSubq(subqPos);
1314 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1315 cdr.SubqForwardSectors++;
1316 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1320 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1321 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1324 read_ok = ReadTrack(cdr.SetSectorPlay);
1326 buf = CDR_getBuffer();
1331 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1332 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1335 memcpy(cdr.LocL, buf, 8);
1337 if (!cdr.Stat && !cdr.Irq1Pending)
1338 cdrUpdateTransferBuf(buf);
1340 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1342 // Firemen 2: Multi-XA files - briefings, cutscenes
1343 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1345 cdr.Channel = hdr[1];
1349 * Skips playing on channel 255.
1350 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1351 * TODO : Check if this is the proper behaviour.
1353 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1354 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1356 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1357 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1358 cdr.FirstSector = 0;
1360 else cdr.FirstSector = -1;
1365 Croc 2: $40 - only FORM1 (*)
1366 Judge Dredd: $C8 - only FORM1 (*)
1367 Sim Theme Park - no adpcm at all (zero)
1370 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1371 cdrReadInterruptSetResult(cdr.StatP);
1373 msfiAdd(cdr.SetSectorPlay, 1);
1375 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1384 bit 5 - 1 result ready
1386 bit 7 - 1 command being processed
1389 unsigned char cdrRead0(void) {
1390 if (cdr.ResultReady)
1395 cdr.Ctrl |= 0x40; // data fifo not empty
1397 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1400 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1402 return psxHu8(0x1800) = cdr.Ctrl;
1405 void cdrWrite0(unsigned char rt) {
1406 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1408 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1411 unsigned char cdrRead1(void) {
1412 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1413 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1417 if (cdr.ResultP == cdr.ResultC)
1418 cdr.ResultReady = 0;
1420 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1422 return psxHu8(0x1801);
1425 void cdrWrite1(unsigned char rt) {
1426 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1427 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1429 switch (cdr.Ctrl & 3) {
1433 cdr.AttenuatorRightToRightT = rt;
1439 #ifdef CDR_LOG_CMD_IRQ
1440 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1443 SysPrintf(" Param[%d] = {", cdr.ParamC);
1444 for (i = 0; i < cdr.ParamC; i++)
1445 SysPrintf(" %x,", cdr.Param[i]);
1452 cdr.ResultReady = 0;
1455 if (!cdr.CmdInProgress) {
1456 cdr.CmdInProgress = rt;
1457 // should be something like 12k + controller delays
1458 set_event(PSXINT_CDR, 5000);
1461 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1462 rt, cdr.Cmd, cdr.CmdInProgress);
1463 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1464 cdr.CmdInProgress = rt;
1470 unsigned char cdrRead2(void) {
1471 unsigned char ret = cdr.Transfer[0x920];
1473 if (cdr.FifoOffset < cdr.FifoSize)
1474 ret = cdr.Transfer[cdr.FifoOffset++];
1476 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1478 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1482 void cdrWrite2(unsigned char rt) {
1483 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1484 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1486 switch (cdr.Ctrl & 3) {
1488 if (cdr.ParamC < 8) // FIXME: size and wrapping
1489 cdr.Param[cdr.ParamC++] = rt;
1496 cdr.AttenuatorLeftToLeftT = rt;
1499 cdr.AttenuatorRightToLeftT = rt;
1504 unsigned char cdrRead3(void) {
1506 psxHu8(0x1803) = cdr.Stat | 0xE0;
1508 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1510 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1511 return psxHu8(0x1803);
1514 void cdrWrite3(unsigned char rt) {
1515 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1516 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1518 switch (cdr.Ctrl & 3) {
1522 if (cdr.Stat & rt) {
1523 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1524 + psxRegs.intCycle[PSXINT_CDR].cycle;
1525 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1526 #ifdef CDR_LOG_CMD_IRQ
1527 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1528 !!pending, cdr.CmdInProgress,
1529 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1531 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1532 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1535 if (cdr.CmdInProgress) {
1536 c = 2048 - (psxRegs.cycle - nextCycle);
1537 c = MAX_VALUE(c, 512);
1539 set_event(PSXINT_CDR, c);
1548 cdr.AttenuatorLeftToRightT = rt;
1552 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1553 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1554 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1555 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1561 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1562 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1564 else if (rt & 0x80) {
1565 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1566 case MODE_SIZE_2328:
1568 cdr.FifoOffset = 12;
1569 cdr.FifoSize = 2048 + 12;
1572 case MODE_SIZE_2340:
1575 cdr.FifoSize = 2340;
1579 else if (!(rt & 0xc0))
1580 cdr.FifoOffset = DATA_SIZE; // fifo empty
1583 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1584 u32 cdsize, max_words;
1589 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1590 if (cdr.FifoOffset == 0) {
1592 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1597 switch (chcr & 0x71000000) {
1599 ptr = getDmaRam(madr, &max_words);
1600 if (ptr == INVALID_PTR) {
1601 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1605 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1608 GS CDX: Enhancement CD crash
1611 - Spams DMA3 and gets buffer overrun
1613 size = DATA_SIZE - cdr.FifoOffset;
1616 if (size > max_words * 4)
1617 size = max_words * 4;
1620 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1621 cdr.FifoOffset += size;
1623 if (size < cdsize) {
1624 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1625 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1627 psxCpu->Clear(madr, cdsize / 4);
1629 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1631 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1633 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1634 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1638 psxRegs.cycle += (cdsize/4) * 24 - 20;
1643 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1647 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1651 void cdrDmaInterrupt(void)
1653 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1655 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1660 static void getCdInfo(void)
1664 CDR_getTN(cdr.ResultTN);
1665 CDR_getTD(0, cdr.SetSectorEnd);
1666 tmp = cdr.SetSectorEnd[0];
1667 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1668 cdr.SetSectorEnd[2] = tmp;
1672 memset(&cdr, 0, sizeof(cdr));
1678 cdr.FifoOffset = DATA_SIZE; // fifo empty
1679 if (CdromId[0] == '\0') {
1680 cdr.DriveState = DRIVESTATE_STOPPED;
1684 cdr.DriveState = DRIVESTATE_STANDBY;
1685 cdr.StatP = STATUS_ROTATING;
1688 // BIOS player - default values
1689 cdr.AttenuatorLeftToLeft = 0x80;
1690 cdr.AttenuatorLeftToRight = 0x00;
1691 cdr.AttenuatorRightToLeft = 0x00;
1692 cdr.AttenuatorRightToRight = 0x80;
1697 int cdrFreeze(void *f, int Mode) {
1701 if (Mode == 0 && !Config.Cdda)
1704 cdr.freeze_ver = 0x63647202;
1705 gzfreeze(&cdr, sizeof(cdr));
1708 cdr.ParamP = cdr.ParamC;
1709 tmp = cdr.FifoOffset;
1712 gzfreeze(&tmp, sizeof(tmp));
1717 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1718 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1719 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1720 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1722 // read right sub data
1723 tmpp[0] = btoi(cdr.Prev[0]);
1724 tmpp[1] = btoi(cdr.Prev[1]);
1725 tmpp[2] = btoi(cdr.Prev[2]);
1730 if (cdr.freeze_ver < 0x63647202)
1731 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1733 Find_CurTrack(cdr.SetSectorPlay);
1735 CDR_play(cdr.SetSectorPlay);
1738 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1739 // old versions did not latch Reg2, have to fixup..
1740 if (cdr.Reg2 == 0) {
1741 SysPrintf("cdrom: fixing up old savestate\n");
1744 // also did not save Attenuator..
1745 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1746 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1748 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1756 void LidInterrupt(void) {
1758 cdrLidSeekInterrupt();