diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index d87cf75be..be1323ca1 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -21,6 +21,10 @@ jobs: - uses: n1hility/cancel-previous-runs@v2 with: token: ${{ secrets.GITHUB_TOKEN }} + - run: | + wget https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1/releases/download/8.0.K/UNIROM_BOOTDISC_8.0.K.zip + - run: | + unzip -o UNIROM_BOOTDISC_8.0.K.zip - run: | make -j 2 all pcsx-redux-tests tools make -C src/mips/tests -j 2 PCSX_TESTS=true @@ -59,6 +63,12 @@ jobs: echo '}' >> version.json cp version.json AppDir/usr/share/pcsx-redux/resources appimage-builder --skip-tests + - run: | + make -C src/mips/openbios clean + make -C src/mips/tests -j 2 PCSX_TESTS=true + cp ./openbios.bin src/mips/openbios/ + - run: | + xvfb-run catchsegv ./pcsx-redux -stdout -lua_stdout -testmode -no-gui-logs -iso UNIROM_BOOTDISC_8.0.K.bin -exec "dofile 'src/mips/tests/cdrom/create-test-iso.lua'" - name: Test run: | export GTEST_OUTPUT=xml:${TEST_RESULTS}/ diff --git a/.github/workflows/linux-coverage.yml b/.github/workflows/linux-coverage.yml index 0a36e64f7..9ec3ea25d 100644 --- a/.github/workflows/linux-coverage.yml +++ b/.github/workflows/linux-coverage.yml @@ -32,13 +32,20 @@ jobs: - uses: n1hility/cancel-previous-runs@v2 with: token: ${{ secrets.GITHUB_TOKEN }} + - run: | + wget https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1/releases/download/8.0.K/UNIROM_BOOTDISC_8.0.K.zip + - run: | + unzip -o UNIROM_BOOTDISC_8.0.K.zip - run: | make -j 2 all pcsx-redux-tests make -C src/mips/openbios -j 2 cp src/mips/openbios/openbios.bin . + - run: | make -C src/mips/openbios clean make -C src/mips/tests -j 2 PCSX_TESTS=true cp ./openbios.bin src/mips/openbios/ + - run: | + xvfb-run catchsegv ./pcsx-redux -stdout -lua_stdout -testmode -no-gui-logs -iso UNIROM_BOOTDISC_8.0.K.bin -exec "dofile 'src/mips/tests/cdrom/create-test-iso.lua'" - name: Test run: | xvfb-run catchsegv ./pcsx-redux-tests diff --git a/.gitignore b/.gitignore index f52871f1f..d3e7fe708 100644 --- a/.gitignore +++ b/.gitignore @@ -110,9 +110,15 @@ sstate.proto # Sentry Native files .sentry-native -# NOPS leftovers +# nops' configuration file comport.txt +# Cue sheets. +*.cue + +# Various editors' backup files +*.bak + # NVIDIA Nsight GPUCache diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 2e433cdc6..8ed9339e1 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,7 +9,8 @@ "compilerPath": "/usr/bin/clang-9", "cStandard": "c11", "cppStandard": "c++17", - "intelliSenseMode": "clang-x64" + "intelliSenseMode": "clang-x64", + "configurationProvider": "ms-vscode.makefile-tools" } ], "version": 4 diff --git a/src/cdrom/cdriso.cc b/src/cdrom/cdriso.cc index 46ff24dbf..12a697344 100644 --- a/src/cdrom/cdriso.cc +++ b/src/cdrom/cdriso.cc @@ -422,6 +422,53 @@ PCSX::IEC60908b::MSF PCSX::CDRIso::getPregap(uint8_t track) { return IEC60908b::MSF(0, 2, 0); } +bool PCSX::CDRIso::getLocP(const PCSX::IEC60908b::MSF msf, uint8_t locP[8]) { + // TODO: actually check subchannels from iso. + auto length = getTD(0) + IEC60908b::MSF{0, 2, 0}; + if (msf >= length) return false; + unsigned track; + for (track = m_numtracks; track != 1; track--) { + if (m_ti[track].start <= msf) break; + } + + bool inPregap = false; + IEC60908b::MSF nextPregapStart; + if (track != m_numtracks) { + if (msf >= (m_ti[track + 1].start - m_ti[track + 1].pregap)) { + track++; + inPregap = true; + } + } + locP[0] = IEC60908b::itob(track); + locP[1] = inPregap ? 0 : 1; + IEC60908b::MSF relative; + if (inPregap) { + relative = m_ti[track].start - msf; + } else { + relative = msf - m_ti[track].start; + } + relative.toBCD(locP + 2); + msf.toBCD(locP + 5); + + return true; +} + +unsigned PCSX::CDRIso::getTrack(const PCSX::IEC60908b::MSF msf) { + auto length = getTD(0) + IEC60908b::MSF{0, 2, 0}; + if (msf >= length) return 0; + unsigned track; + for (track = m_numtracks; track != 1; track--) { + if (m_ti[track].start <= msf) break; + } + IEC60908b::MSF nextPregapStart; + if (track != m_numtracks) { + if (msf >= (m_ti[track + 1].start - m_ti[track + 1].pregap)) { + track++; + } + } + return track; +} + // Decode 'raw' subchannel data from being packed bitwise. // Essentially is a bitwise matrix transposition. void PCSX::CDRIso::decodeRawSubData() { diff --git a/src/cdrom/cdriso.h b/src/cdrom/cdriso.h index 9fbfc3e7a..b509a0a5f 100644 --- a/src/cdrom/cdriso.h +++ b/src/cdrom/cdriso.h @@ -48,10 +48,16 @@ class CDRIso { enum class TrackType { CLOSED = 0, DATA = 1, CDDA = 2 }; TrackType getTrackType(unsigned track) { return m_ti[track].type; } const std::filesystem::path& getIsoPath() { return m_isoPath; } - uint8_t getTN() { return std::max(m_numtracks, 1); } + uint8_t getTN() { return std::max(m_numtracks, 1U); } IEC60908b::MSF getTD(uint8_t track); IEC60908b::MSF getLength(uint8_t track); IEC60908b::MSF getPregap(uint8_t track); + bool getLocP(const IEC60908b::MSF msf, uint8_t locP[8]); + unsigned getTrack(const IEC60908b::MSF msf); + + bool failed(); + + /* Probably have to change those */ bool readTrack(const IEC60908b::MSF time); unsigned readSectors(uint32_t lba, void* buffer, unsigned count); uint8_t* getBuffer(); @@ -59,8 +65,6 @@ class CDRIso { bool readCDDA(const IEC60908b::MSF msf, unsigned char* buffer); PPF* getPPF() { return &m_ppf; } - bool failed(); - unsigned m_cdrIsoMultidiskCount; unsigned m_cdrIsoMultidiskSelect; @@ -132,7 +136,6 @@ class CDRIso { ECMFILELUT* m_ecm_savetable = nullptr; static inline const size_t ECM_SECTOR_SIZE[4] = {1, 2352, 2336, 2336}; - static inline const uint8_t ZEROADDRESS[4] = {0, 0, 0, 0}; struct trackinfo { TrackType type = TrackType::CLOSED; @@ -146,7 +149,7 @@ class CDRIso { static constexpr unsigned MAXTRACKS = 100; /* How many tracks can a CD hold? */ - int m_numtracks = 0; + unsigned m_numtracks = 0; struct trackinfo m_ti[MAXTRACKS]; // redump.org SBI files diff --git a/src/cdrom/iso9660-builder.cc b/src/cdrom/iso9660-builder.cc index da85b95ab..2a2f0252a 100644 --- a/src/cdrom/iso9660-builder.cc +++ b/src/cdrom/iso9660-builder.cc @@ -68,6 +68,7 @@ PCSX::IEC60908b::MSF PCSX::ISO9660Builder::writeSectorAt(const uint8_t* sectorDa msf.toBCD(ptr + 12); ptr[15] = 2; memcpy(ptr + 16, sectorData, 2336); + compute_edcecc(ptr); m_out->writeAt(std::move(slice), lba * IEC60908b::FRAMESIZE_RAW); break; case SectorMode::M2_FORM1: diff --git a/src/core/DynaRec_aa64/recompiler.cc b/src/core/DynaRec_aa64/recompiler.cc index 0402b8b88..898b8bd42 100644 --- a/src/core/DynaRec_aa64/recompiler.cc +++ b/src/core/DynaRec_aa64/recompiler.cc @@ -245,7 +245,8 @@ DynarecCallback DynaRecCPU::recompile(DynarecCallback* callback, uint32_t pc, bo m_pc = pc & ~3; const auto startingPC = m_pc; - int count = 0; // How many instructions have we compiled? + unsigned count = 0; // How many instructions have we compiled? + unsigned extra = 0; // How many instructions from rom? #if defined(__APPLE__) gen.setRW(); // Mark code cache as readable/writeable before emitting code @@ -288,6 +289,7 @@ DynarecCallback DynaRecCPU::recompile(DynarecCallback* callback, uint32_t pc, bo uint32_t code = m_regs.code = *p; // Actually read the instruction m_pc += 4; // Increment recompiler PC count++; // Increment instruction count + if ((m_pc & 0xffc00000) == 0xbfc00000) extra++; const auto func = m_recBSC[code >> 26]; // Look up the opcode in our decoding LUT (*this.*func)(code); // Jump into the handler to recompile it @@ -305,7 +307,7 @@ DynarecCallback DynaRecCPU::recompile(DynarecCallback* callback, uint32_t pc, bo } gen.Ldr(x0, MemOperand(contextPointer, CYCLE_OFFSET)); // Fetch cycle count from memory - gen.Add(x0, x0, count * PCSX::Emulator::BIAS); // Add block cycles + gen.Add(x0, x0, (count + extra * PCSX::Emulator::ROM_EXTRA_BIAS) * PCSX::Emulator::BIAS); // Add block cycles gen.Str(x0, MemOperand(contextPointer, CYCLE_OFFSET)); // Store cycles back to memory // Link block else return to dispatcher diff --git a/src/core/DynaRec_x64/recompiler.cc b/src/core/DynaRec_x64/recompiler.cc index 4e2bff32d..035d00f6c 100644 --- a/src/core/DynaRec_x64/recompiler.cc +++ b/src/core/DynaRec_x64/recompiler.cc @@ -331,6 +331,7 @@ DynarecCallback DynaRecCPU::recompile(uint32_t pc, bool fullLoadDelayEmulation, const auto startingPC = m_pc; unsigned count = 0; // How many instructions have we compiled? + unsigned extra = 0; // How many instructions in rom? DynarecCallback* callback = getBlockPointer(m_pc); // Pointer to where we'll store the addr of the emitted code if (align) { @@ -392,7 +393,7 @@ DynarecCallback DynaRecCPU::recompile(uint32_t pc, bool fullLoadDelayEmulation, } }; - const auto compileInstruction = [this, &count, &memory]() -> bool { + const auto compileInstruction = [this, &count, &memory, &extra]() -> bool { m_inDelaySlot = m_nextIsDelaySlot; m_nextIsDelaySlot = false; @@ -402,6 +403,7 @@ DynarecCallback DynaRecCPU::recompile(uint32_t pc, bool fullLoadDelayEmulation, uint32_t code = m_regs.code = *ptr; m_pc += 4; // Increment recompiler PC count++; // Increment instruction count + if ((m_pc & 0xffc00000) == 0xbfc00000) extra++; const auto func = m_recBSC[code >> 26]; // Look up the opcode in our decoding LUT (*this.*func)(code); // Jump into the handler to recompile it @@ -450,7 +452,8 @@ DynarecCallback DynaRecCPU::recompile(uint32_t pc, bool fullLoadDelayEmulation, endProfiling(); } - gen.add(qword[contextPointer + CYCLE_OFFSET], count * PCSX::Emulator::BIAS); // Add block cycles; + gen.add(qword[contextPointer + CYCLE_OFFSET], + (count + extra * PCSX::Emulator::ROM_EXTRA_BIAS) * PCSX::Emulator::BIAS); // Add block cycles; if (m_linkedPC && ENABLE_BLOCK_LINKING && m_linkedPC.value() != startingPC) { handleLinking(); } else { diff --git a/src/core/cdrom.cc b/src/core/cdrom.cc index eaac186fc..d5bac8ddf 100644 --- a/src/core/cdrom.cc +++ b/src/core/cdrom.cc @@ -23,6 +23,8 @@ #include "core/cdrom.h" +#include + #include "cdrom/iso9660-reader.h" #include "core/debug.h" #include "core/psxdma.h" @@ -30,15 +32,20 @@ #include "magic_enum/include/magic_enum/magic_enum_all.hpp" #include "spu/interface.h" #include "support/strings-helpers.h" +#include "supportpsx/iec-60908b.h" namespace { -class CDRomImpl : public PCSX::CDRom { - /* CD-ROM magic numbers */ +using namespace std::literals; + +// The buffer/decoder chip the PSX CPU will talk to is the CXD1199, which +// datasheet can be found at https://archive.org/details/cxd-1199 + +class CDRomImpl final : public PCSX::CDRom { enum Commands { CdlSync = 0, - CdlGetStat = 1, - CdlSetloc = 2, + CdlNop = 1, + CdlSetLoc = 2, CdlPlay = 3, CdlForward = 4, CdlBackward = 5, @@ -46,1578 +53,1162 @@ class CDRomImpl : public PCSX::CDRom { CdlStandby = 7, CdlStop = 8, CdlPause = 9, - CdlReset = 10, + CdlInit = 10, CdlMute = 11, CdlDemute = 12, - CdlSetfilter = 13, - CdlSetmode = 14, - CdlGetparam = 15, - CdlGetlocL = 16, - CdlGetlocP = 17, + CdlSetFilter = 13, + CdlSetMode = 14, + CdlGetParam = 15, + CdlGetLocL = 16, + CdlGetLocP = 17, CdlReadT = 18, CdlGetTN = 19, CdlGetTD = 20, CdlSeekL = 21, CdlSeekP = 22, - CdlSetclock = 23, - CdlGetclock = 24, + CdlSetClock = 23, + CdlGetClock = 24, CdlTest = 25, CdlID = 26, CdlReadS = 27, - CdlInit = 28, + CdlReset = 28, CdlGetQ = 29, CdlReadToc = 30, }; - static const size_t cdCmdEnumCount = magic_enum::enum_count(); - - static const inline uint8_t Test20[] = {0x98, 0x06, 0x10, 0xC3}; - static const inline uint8_t Test22[] = {0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F}; - static const inline uint8_t Test23[] = {0x43, 0x58, 0x44, 0x32, 0x39, 0x34, 0x30, 0x51}; - static const unsigned irqReschedule = 0x100; - - // m_stat: - enum { - NoIntr = 0, - DataReady = 1, - Complete = 2, - Acknowledge = 3, - DataEnd = 4, - DiskError = 5, - }; - - /* m_ctrl */ - enum { - BUSYSTS = 1 << 7, // 0x80 Command/parameter transmission busy (1=Busy) - DRQSTS = 1 << 6, // 0x40 Data fifo empty (0=Empty) - RSLRRDY = 1 << 5, // 0x20 Response fifo empty (0=Empty) - PRMWRDY = 1 << 4, // 0x10 Parameter fifo full (0=Full) - PRMEMPT = 1 << 3, // 0x08 Parameter fifo empty (1=Empty) - ADPBUSY = 1 << 2 // 0x04 XA-ADPCM fifo empty (0=Empty) - }; - - /* Modes flags */ - enum { - MODE_SPEED = 1 << 7, // 0x80 - MODE_STRSND = 1 << 6, // 0x40 ADPCM on/off - MODE_SIZE_2340 = 1 << 5, // 0x20 - MODE_SIZE_2328 = 1 << 4, // 0x10 - MODE_SIZE_2048 = 0 << 4, // 0x00 - MODE_SF = 1 << 3, // 0x08 channel on/off - MODE_REPORT = 1 << 2, // 0x04 - MODE_AUTOPAUSE = 1 << 1, // 0x02 - MODE_CDDA = 1 << 0, // 0x01 - }; - - /* Status flags */ - enum { - STATUS_PLAY = 1 << 7, // 0x80 - STATUS_SEEK = 1 << 6, // 0x40 - STATUS_READ = 1 << 5, // 0x20 - STATUS_SHELLOPEN = 1 << 4, // 0x10 - STATUS_UNKNOWN3 = 1 << 3, // 0x08 - STATUS_UNKNOWN2 = 1 << 2, // 0x04 - STATUS_ROTATING = 1 << 1, // 0x02 - STATUS_ERROR = 1 << 0, // 0x01 - }; - - /* Errors */ - enum { - ERROR_NOTREADY = 1 << 7, // 0x80 - ERROR_INVALIDCMD = 1 << 6, // 0x40 - ERROR_INVALIDARG = 1 << 5, // 0x20 - }; - -// 1x = 75 sectors per second -// PCSX::g_emulator->m_psxClockSpeed = 1 sec in the ps -// so (PCSX::g_emulator->m_psxClockSpeed / 75) = m_cdr read time (linuzappz) -#define cdReadTime (PCSX::g_emulator->m_psxClockSpeed / 75) - - enum drive_state { - DRIVESTATE_STANDBY = 0, - DRIVESTATE_LID_OPEN, - DRIVESTATE_RESCAN_CD, - DRIVESTATE_PREPARE_CD, - DRIVESTATE_STOPPED, - }; - - // for m_seeked - enum seeked_state { - SEEK_PENDING = 0, - SEEK_DONE = 1, - }; - - struct PCSX::CdrStat cdr_stat; - - static const uint32_t H_SPUirqAddr = 0x1f801da4; - static const uint32_t H_SPUctrl = 0x1f801daa; - - // interrupt - static inline void scheduleCDIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_CDR, eCycle); - } - - // readInterrupt - static inline void scheduleCDReadIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_CDREAD, eCycle); - } - - // decodedBufferInterrupt - static inline void scheduleDecodeBufferIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_CDRDBUF, eCycle); - } - - // lidSeekInterrupt - static inline void scheduleCDLidIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_CDRLID, eCycle); + std::string_view commandName(uint8_t command) { + if (command > c_cdCmdEnumCount) { + return "Unknown"; + } else { + return magic_enum::enum_names()[command]; + } } - // playInterrupt - static inline void scheduleCDPlayIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_CDRPLAY, eCycle); + static constexpr size_t c_cdCmdEnumCount = magic_enum::enum_count(); + static constexpr bool isValidBCD(uint8_t value) { return (value & 0x0f) <= 9 && (value & 0xf0) <= 0x90; } + static std::optional getMSF(const uint8_t *msf) { + bool validBCD = isValidBCD(msf[0]) && isValidBCD(msf[1]) && isValidBCD(msf[2]); + if (!validBCD) return {}; + uint8_t m = PCSX::IEC60908b::btoi(msf[0]); + uint8_t s = PCSX::IEC60908b::btoi(msf[1]); + uint8_t f = PCSX::IEC60908b::btoi(msf[2]); + + if (s >= 60) return {}; + if (f >= 75) return {}; + return PCSX::IEC60908b::MSF(m, s, f); } - static inline void scheduleCDDMAIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_CDRDMA, eCycle); + uint32_t rand() { + m_seed *= 14726776315600504853ull; + return m_seed >> 9; } - inline void StopReading() { - if (m_reading) { - m_reading = 0; - PCSX::g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_CDREAD); - } - m_statP &= ~(STATUS_READ | STATUS_SEEK); + void reset() override { + m_seed = 9223521712174600777ull; + m_dataFIFOIndex = 0; + m_dataFIFOSize = 0; + m_registerAddress = 0; + m_currentPosition.reset(); + m_seekPosition.reset(); + m_speed = Speed::Simple; + m_speedChanged = false; + m_status = Status::Idle; + m_dataRequested = false; + m_interruptCauseMask = 0x1f; + m_subheaderFilter = false; + m_realtime = false; + m_commandFifo.clear(); + m_commandExecuting.clear(); + m_responseFifo[0].clear(); + m_responseFifo[1].clear(); + m_readingState = ReadingState::None; } - inline void StopCdda() { - if (m_play) { - m_statP &= ~STATUS_PLAY; - m_play = false; - m_fastForward = 0; - m_fastBackward = 0; - // PCSX::g_emulator->m_spu->registerCallback(SPUirq); + void fifoScheduledCallback() override { + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (m_responseFifo[0].valueEmpty() && !m_responseFifo[1].empty()) { + m_responseFifo[0] = m_responseFifo[1]; + m_responseFifo[1].clear(); + PCSX::g_emulator->m_mem->setIRQ(4); + if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, + "CD-Rom: response fifo sliding one response, triggering IRQ.\n"); + } } + maybeStartCommand(); } - inline void SetResultSize(uint8_t size) { - m_resultP = 0; - m_resultC = size; - m_resultReady = 1; - } - - inline void setIrq(void) { - if (m_stat & m_reg2) PCSX::g_emulator->m_mem->setIRQ(4); + void commandsScheduledCallback() override { + auto command = m_commandExecuting.value; + auto handler = c_commandsHandlers[command]; + (this->*handler)(m_commandExecuting, false); } - void adjustTransferIndex(void) { - size_t bufSize = 0; + void readScheduledCallback() override { + static const std::chrono::nanoseconds c_retryDelay = 50us; + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (m_readingState == ReadingState::Seeking) { + auto seekDelay = computeSeekDelay(m_currentPosition, m_seekPosition, SeekType::DATA, true); + m_status = Status::Seeking; + if (m_speedChanged) { + m_speedChanged = false; + seekDelay += 650ms; + } + m_currentPosition = m_seekPosition; + scheduleRead(seekDelay + computeReadDelay()); + m_readingState = ReadingState::Reading; + return; + } else if (m_readingState == ReadingState::Reading) { + m_readingState = ReadingState::None; + m_status = Status::ReadingData; + } else if ((m_status == Status::Idle) || (m_status == Status::Seeking)) { + m_readingType = ReadingType::None; + if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: readInterrupt: cancelling read.\n"); + } + return; + } - switch (m_mode & (MODE_SIZE_2340 | MODE_SIZE_2328)) { - case MODE_SIZE_2340: - bufSize = 2340; - break; - case MODE_SIZE_2328: - bufSize = 12 + 2328; - break; - case MODE_SIZE_2048: + switch (m_status) { + case Status::ReadingData: { + unsigned track = m_iso->getTrack(m_currentPosition); + if (m_iso->getTrackType(track) == PCSX::CDRIso::TrackType::CDDA) { + m_status = Status::Idle; + maybeEnqueueError(4, 4); + } else if (track == 0) { + m_status = Status::Idle; + maybeEnqueueError(4, 0x10); + } else { + m_invalidLocL = false; + m_iso->readTrack(m_currentPosition); + auto buffer = m_iso->getBuffer(); + memcpy(m_lastLocL, buffer, sizeof(m_lastLocL)); + uint32_t size = 0; + bool passToData = true; + if ((buffer[3] == 2) && m_realtime) { + PCSX::IEC60908b::SubHeaders subHeaders; + subHeaders.fromBuffer(buffer + 4); + if (subHeaders.isRealTime() && subHeaders.isAudio()) { + passToData = false; + if (m_subheaderFilter) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: filtering not supported yet.\n"); + PCSX::g_system->pause(); + } + // TODO: play XA sector. + } + } + if (passToData) { + switch (m_readSpan) { + case ReadSpan::S2048: + size = 2048; + if (buffer[3] == 1) { + memcpy(m_dataFIFO, buffer + 4, 2048); + } else { + memcpy(m_dataFIFO, buffer + 12, 2048); + } + break; + case ReadSpan::S2328: + size = 2328; + memcpy(m_dataFIFO, buffer + 12, 2328); + break; + case ReadSpan::S2340: + size = 2340; + memcpy(m_dataFIFO, buffer, 2340); + break; + } + } + auto readDelay = computeReadDelay(); + m_dataFIFOIndex = 0; + m_dataFIFOPending = size; + if (m_dataRequested) m_dataFIFOSize = size; + m_currentPosition++; + if (debug) { + std::string msfFormat = fmt::format("{}", m_currentPosition); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: readInterrupt: advancing to %s.\n", + msfFormat); + } + if (passToData) { + QueueElement ready; + ready.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::DataReady, ready); + } + scheduleRead(readDelay); + } + } break; default: - bufSize = 12 + 2048; + PCSX::g_system->log(PCSX::LogClass::CDROM, "unsupported yet\n"); + PCSX::g_system->pause(); break; } + } - if (m_transferIndex >= bufSize) m_transferIndex -= bufSize; - - // FIFO empty - if (m_transferIndex == 0) { - m_ctrl &= ~DRQSTS; // Data fifo empty - m_read = 0; + void scheduledDmaCallback() override { + auto &mem = PCSX::g_emulator->m_mem; + if (mem->isDMABusy<3>()) { + mem->clearDMABusy<3>(); + mem->dmaInterrupt<3>(); } } - // FIXME: do this in SPU instead - void decodedBufferInterrupt() final { -#if 0 - return; -#endif - - // check dbuf IRQ still active - if (!m_play) return; - if ((PCSX::g_emulator->m_spu->readRegister(H_SPUctrl) & 0x40) == 0) return; - if ((PCSX::g_emulator->m_spu->readRegister(H_SPUirqAddr) * 8) >= 0x800) return; - - // turn off plugin SPU IRQ decoded buffer handling - // PCSX::g_emulator->m_spu->registerCallback(0); + void scheduleFifo(uint32_t cycles) { PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::CDRFIFO, cycles); } + void scheduleFifo(std::chrono::nanoseconds delay) { scheduleFifo(PCSX::psxRegisters::durationToCycles(delay)); } - /* - Vib Ribbon - - 000-3FF = left CDDA - 400-7FF = right CDDA + void schedule(uint32_t cycles) { PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::CDRCOMMANDS, cycles); } + void schedule(std::chrono::nanoseconds delay) { schedule(PCSX::psxRegisters::durationToCycles(delay)); } - Assume IRQ every wrap - */ + void scheduleRead(uint32_t cycles) { PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::CDREAD, cycles); } + void scheduleRead(std::chrono::nanoseconds delay) { scheduleRead(PCSX::psxRegisters::durationToCycles(delay)); } - // signal CDDA data ready - PCSX::g_emulator->m_mem->setIRQ(0x200); - - // time for next full buffer - // scheduleDecodeBufferIRQ( PCSX::g_emulator->m_psxClockSpeed / 44100 * 0x200 ); - scheduleDecodeBufferIRQ(PCSX::g_emulator->m_psxClockSpeed / 44100 * 0x100); - } + void scheduleDMA(uint32_t cycles) { PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::CDRDMA, cycles); } + void scheduleDMA(std::chrono::nanoseconds delay) { scheduleDMA(PCSX::psxRegisters::durationToCycles(delay)); } - void getStatus(PCSX::CdrStat *stat) { - if (isLidOpened()) { - stat->Status = 0x10; - } else { - stat->Status = 0; + bool maybeEnqueueResponse(QueueElement &response) { + if (m_responseFifo[0].valueEmpty() && !m_responseFifo[1].empty()) { + m_responseFifo[0] = m_responseFifo[1]; + m_responseFifo[1] = response; + return true; } - if (m_play) { - stat->Type = 0x02; - stat->Status |= 0x80; - } else { - // BIOS - boot ID (CD type) - stat->Type = magic_enum::enum_integer(m_iso->getTrackType(1)); + if (m_responseFifo[0].empty()) { + m_responseFifo[0] = response; + return true; + } else if (m_responseFifo[1].empty()) { + m_responseFifo[1] = response; } - - // relative -> absolute time - stat->Time = m_setSectorPlay; + return false; } - // timing used in this function was taken from tests on real hardware - // (yes it's slow, but you probably don't want to modify it) - void lidSeekInterrupt() final { - switch (m_driveState) { - default: - case DRIVESTATE_STANDBY: - m_statP &= ~STATUS_SEEK; - - getStatus(&cdr_stat); - - if (cdr_stat.Status & STATUS_SHELLOPEN) { - StopCdda(); - m_driveState = DRIVESTATE_LID_OPEN; - scheduleCDLidIRQ(8 * irqReschedule); - } - break; - - case DRIVESTATE_LID_OPEN: - getStatus(&cdr_stat); - - // 02, 12, 10 - if (!(m_statP & STATUS_SHELLOPEN)) { - StopReading(); - m_statP |= STATUS_SHELLOPEN; - - // could generate error irq here, but real hardware - // only sometimes does that - // (not done when lots of commands are sent?) - - scheduleCDLidIRQ(cdReadTime * 30); - break; - } else if (m_statP & STATUS_ROTATING) { - m_statP &= ~STATUS_ROTATING; - } else if (!(cdr_stat.Status & STATUS_SHELLOPEN)) { - // closed now - check(); - - // m_statP STATUS_SHELLOPEN is "sticky" - // and is only cleared by CdlGetStat - - m_driveState = DRIVESTATE_RESCAN_CD; - scheduleCDLidIRQ(cdReadTime * 105); - break; + void maybeTriggerIRQ(Cause cause, QueueElement &element) { + uint8_t causeValue = static_cast(cause); + uint8_t bit = 1 << (causeValue - 1); + if (m_interruptCauseMask & bit) { + element.setValue(cause); + bool actuallyTriggering = false; + if (maybeEnqueueResponse(element)) { + PCSX::g_emulator->m_mem->setIRQ(4); + actuallyTriggering = true; + } + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + if (actuallyTriggering) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] triggering IRQ with cause %d\n", + regs.pc, regs.cycle, causeValue); + } else { + PCSX::g_system->log(PCSX::LogClass::CDROM, + "CD-Rom: %08x.%08x] wanted to trigger IRQ with cause %d, but queue is full\n", + regs.pc, regs.cycle, causeValue); } + } + } else { + if (PCSX::g_emulator->settings.get() + .get()) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: wanted to trigger IRQ but cause %d is masked...\n", + causeValue); + } + } + } - // recheck for close - scheduleCDLidIRQ(cdReadTime * 3); + uint8_t getStatus(bool resetLid = false) { + bool lidOpen = isLidOpen(); + if (resetLid && !lidOpen) m_wasLidOpened = false; + uint8_t v1 = m_motorOn && !lidOpen ? 0x02 : 0; + uint8_t v4 = m_wasLidOpened ? 0x10 : 0; + uint8_t v567 = 0; + switch (m_status) { + case Status::ReadingData: + v567 = 0x20; break; - - case DRIVESTATE_RESCAN_CD: - m_statP |= STATUS_ROTATING; - m_driveState = DRIVESTATE_PREPARE_CD; - - // this is very long on real hardware, over 6 seconds - // make it a bit faster here... - scheduleCDLidIRQ(cdReadTime * 150); + case Status::Seeking: + v567 = 0x40; break; - - case DRIVESTATE_PREPARE_CD: - m_statP |= STATUS_SEEK; - - m_driveState = DRIVESTATE_STANDBY; - scheduleCDLidIRQ(cdReadTime * 26); + case Status::PlayingCDDA: + v567 = 0x80; break; } + return v1 | v4 | v567; } - void Find_CurTrack(const MSF time) { - int current, sect; - - current = time.toLBA(); - - for (m_curTrack = 1; m_curTrack < m_iso->getTN(); m_curTrack++) { - sect = static_cast(m_iso->getTD(m_curTrack + 1).toLBA()); - if (sect - current >= 150) break; - } - CDROM_LOG("Find_CurTrack *** %02d %02d\n", m_curTrack, current); - } - - void generate_subq(const MSF time) { - unsigned int this_s, start_s, next_s, pregap; - int relative_s; - - MSF start = m_iso->getTD(m_curTrack); - MSF next; - if (m_curTrack + 1 <= m_iso->getTN()) { - pregap = 150; - next = m_iso->getTD(m_curTrack + 1); - } else { - // last track - cd size - pregap = 0; - next = m_setSectorEnd; + uint8_t read0() override { + // HSTS (host status) register + /* + bit 7: BUSYSTS (busy status) + This is high when the host writes a command into the command register and low when the sub + CPU sets the CLRBUSY bit (bit 6) of the CLRCTL register. + */ + uint8_t v7 = m_commandFifo.hasValue ? 0x80 : 0; + /* + bit 6: DRQSTS (data request status) + Indicates to the host that the buffer memory data transfer request status is established. When + transferring data in the I/O mode, the host should confirm that this bit is high before accessing the + WRDATA or RDDATA register. + */ + uint8_t v6 = m_dataFIFOSize != m_dataFIFOIndex ? 0x40 : 0; + /* + bit 5: RSLRRDY (result read ready) + The result register is not empty when this bit is high. At this time, the host can read the result + register. + */ + uint8_t v5 = !m_responseFifo[0].isPayloadAtEnd() ? 0x20 : 0; + /* + bit 4: PRMWRDY (parameter write ready) + The PARAMETER register is not full when this bit is high. At this time, the host writes data into the + PARAMETER register. + */ + uint8_t v4 = !m_commandFifo.isPayloadFull() ? 0x10 : 0; + /* + bit 3: PRMEMPT (parameter empty) + The PARAMETER register is empty when this bit is high. + */ + uint8_t v3 = m_commandFifo.isPayloadEmpty() ? 0x08 : 0; + /* + bit 2: ADPBUSY (ADPCM busy) + This bit is set high for ADPCM decoding. + */ + uint8_t v2 = 0; /* adpcmPlaying */ + /* + bits 1, 0: RA1, 0 + The values of the RA1 and 0 bits for the ADDRESS register can be read from these bits. + */ + uint8_t v01 = m_registerAddress & 3; + + uint8_t ret = v01 | v2 | v3 | v4 | v5 | v6 | v7; + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] r0: %02x\n", regs.pc, regs.cycle, ret); } - this_s = time.toLBA(); - start_s = start.toLBA(); - next_s = next.toLBA(); - - m_trackChanged = false; - - if (next_s - this_s < pregap) { - m_trackChanged = true; - m_curTrack++; - start_s = next_s; - } + return ret; + } - m_subq.index = 1; + uint8_t read1() override { + // RESULT + uint8_t ret = m_responseFifo[0].readPayloadByte(); + maybeScheduleNextCommand(); - relative_s = this_s - start_s; - if (relative_s < 0) { - m_subq.index = 0; - relative_s = -relative_s; + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] r1: %02x\n", regs.pc, regs.cycle, ret); } - m_subq.track = PCSX::IEC60908b::itob(m_curTrack); - MSF(relative_s).toBCD(m_subq.relative); - time.toBCD(m_subq.absolute); + return ret; } - void readTrack(MSF time) { - if (m_prev == time) return; - - CDROM_LOG("ReadTrack *** %02i:%02i:%02i\n", time.m, time.s, time.f); - - m_prev.reset(); - if (m_iso->getTrackType(m_curTrack) == PCSX::CDRIso::TrackType::CDDA) { - m_suceeded = false; + uint8_t read2() override { + // RD DATA + uint8_t ret = 0; + if (!dataFIFOHasData()) { + ret = 0; } else { - m_suceeded = m_iso->readTrack(time); - if (m_suceeded) m_prev = time; + ret = m_dataFIFO[m_dataFIFOIndex++]; } - const PCSX::IEC60908b::Sub *sub = m_iso->getBufferSub(); - if (sub && m_curTrack == 1) { - uint16_t calcCRC = PCSX::IEC60908b::subqCRC(sub->Q); - uint16_t actualCRC = sub->CRC[0]; - actualCRC <<= 8; - actualCRC |= sub->CRC[1]; - if (calcCRC == actualCRC) { - m_subq.track = sub->TrackNumber; - m_subq.index = sub->IndexNumber; - memcpy(m_subq.relative, sub->RelativeAddress, 3); - memcpy(m_subq.absolute, sub->AbsoluteAddress, 3); - } else { - CDROM_IO_LOG("subq bad crc @%02i:%02i:%02i\n", time.m, time.s, time.f); - } - } else { - generate_subq(time); + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] r2: %02x\n", regs.pc, regs.cycle, ret); } - - CDROM_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n", m_subq.track, m_subq.index, m_subq.relative[0], - m_subq.relative[1], m_subq.relative[2], m_subq.absolute[0], m_subq.absolute[1], m_subq.absolute[2]); + return ret; } - void AddIrqQueue(unsigned short irq, unsigned long ecycle) { - if (m_irq != 0) { - if (irq == m_irq || irq + 0x100 == m_irq) { - m_irqRepeated = 1; - scheduleCDIRQ(ecycle); - return; - } - CDROM_IO_LOG("cdr: override cmd %02x -> %02x\n", m_irq, irq); - } - - m_irq = irq; - m_eCycle = ecycle; + uint8_t read3() override { + /* + * bit 4: BFWRDY (buffer write ready) + * The BFWRDY status is established if there is area where writing is possible in the buffer of 1 sector + * or more for sound map playback. It is established in any of the following cases: + * (1) When the host has set the SMEN bit (bit 5) of the HCHPCTL register high + * (2) When there is sound map data area of 1 sector or more in the buffer memory (when the buffer + * is not full) after the sound map data equivalent to 1 sector from the host has been written into + * the buffer memory + * (3) When an area for writing the sound map data has been created in the buffer memory by the + * completion of the sound map ADPCM decoding of one sector + * bit 3: BFEMPT (buffer empty) + * The BFEMPT status is established when there is no more sector data in the buffer memory upon + * completion of the sound map ADPCM decoding of one sector for sound map playback. + * bits 2 to 0: INTSTS#2 to 0 + * The values of these bits are those of the corresponding bits for the sub CPU HIFCTL register. + */ - scheduleCDIRQ(ecycle); + uint8_t ret = 0; + switch (m_registerAddress & 1) { + case 0: { + // HINT MSK (host interrupt mask) + ret = m_interruptCauseMask; + } break; + case 1: { + // HINT STS (host interrupt status) + ret = m_responseFifo[0].getValue(); + } break; + } + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] r3.%i: %02x\n", regs.pc, regs.cycle, + m_registerAddress & 1, ret); + } + return ret | 0xe0; } - void cdrPlayInterrupt_Autopause() { - if ((m_mode & MODE_AUTOPAUSE) && m_trackChanged) { - CDROM_LOG("CDDA STOP\n"); - // Magic the Gathering - // - looping territory cdda - - // ...? - // m_resultReady = 1; - // m_stat = DataReady; - m_stat = DataEnd; - setIrq(); - - StopCdda(); - } else if (m_mode & MODE_REPORT) { - m_result[0] = m_statP; - m_result[1] = m_subq.track; - m_result[2] = m_subq.index; - unsigned abs_lev_chselect = m_subq.absolute[1] & 0x01; - uint32_t abs_lev_max = 0; - int16_t *data = reinterpret_cast(m_transfer); - for (unsigned i = 0; i < 588; i++) { - abs_lev_max = std::max(abs_lev_max, std::abs(data[i * 2 + abs_lev_chselect])); - } - abs_lev_max = std::min(abs_lev_max, 32767U); - abs_lev_max |= abs_lev_chselect << 15; - - if (m_subq.absolute[2] & 0x10) { - m_result[3] = m_subq.relative[0]; - m_result[4] = m_subq.relative[1] | 0x80; - m_result[5] = m_subq.relative[2]; - } else { - m_result[3] = m_subq.absolute[0]; - m_result[4] = m_subq.absolute[1]; - m_result[5] = m_subq.absolute[2]; - } - - m_result[6] = abs_lev_max & 0xff; - m_result[7] = abs_lev_max >> 8; - - // Rayman: Logo freeze (resultready + dataready) - m_resultReady = 1; - m_stat = DataReady; - - SetResultSize(8); - setIrq(); + void write0(uint8_t value) override { + // ADDRESS + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] w0: %02x\n", regs.pc, regs.cycle, value); } + m_registerAddress = value & 3; } - // also handles seek - void playInterrupt() final { - if (m_seeked == SEEK_PENDING) { - if (m_stat) { - scheduleCDPlayIRQ(irqReschedule); - return; - } - SetResultSize(1); - m_statP |= STATUS_ROTATING; - m_statP &= ~STATUS_SEEK; - m_result[0] = m_statP; - m_seeked = SEEK_DONE; - if (m_irq == 0) { - m_stat = Complete; - m_suceeded = true; - setIrq(); - } - - if (m_setlocPending) { - m_setSectorPlay = m_setSector; - m_setlocPending = 0; - m_locationChanged = true; - } - Find_CurTrack(m_setSectorPlay); - readTrack(m_setSectorPlay); - m_trackChanged = false; + void write1(uint8_t value) override { + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] w1.%i: %02x\n", regs.pc, regs.cycle, + m_registerAddress, value); } - - if (!m_play) return; - CDROM_LOG("CDDA - %02d:%02d:%02d\n", m_setSectorPlay.m, m_setSectorPlay.s, m_setSectorPlay.f); - if (m_setSectorPlay == m_setSectorEnd) { - StopCdda(); - m_trackChanged = true; + switch (m_registerAddress) { + case 0: + // COMMAND + m_commandFifo.value = value; + if (!m_commandFifo.hasValue) { + if (PCSX::g_emulator->settings.get() + .get()) { + logCDROM(m_commandFifo); + } + scheduleFifo(797us); + } + m_commandFifo.hasValue = true; + break; + case 1: { + // WR DATA + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: w1:1 not available yet\n"); + PCSX::g_system->pause(); + } break; + case 2: { + /* + CI (coding information) + This sets the coding information for sound map playback. The bit allocation is the same as that for the + coding information bytes of the sub header. + bits 7, 5, 3, 1: Reserved + bit 6: EMPHASIS + High: Emphasis ON + Low : Emphasis OFF + bit 4: BITLNGTH + High: 8 bits + Low : 4 bits + bit 2: FS + High: 18.9 kHz + Low : 37.8 kHz + bit 0: S/M (stereo/mono) + High: Stereo + Low : Mono + */ + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: w1:2 not available yet\n"); + PCSX::g_system->pause(); + } break; + case 3: { + // ATV2 Right-to-Right + m_atv[2] = value; + } break; } + } - m_iso->readCDDA(m_setSectorPlay, m_transfer); - if (!m_irq && !m_stat && (m_mode & (MODE_AUTOPAUSE | MODE_REPORT))) cdrPlayInterrupt_Autopause(); - - if (!m_play) return; - - if (!m_muted) { - attenuate((int16_t *)m_transfer, PCSX::IEC60908b::FRAMESIZE_RAW / 4, 1); - PCSX::g_emulator->m_spu->playCDDAchannel((short *)m_transfer, PCSX::IEC60908b::FRAMESIZE_RAW); + void write2(uint8_t value) override { + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] w2.%i: %02x\n", regs.pc, regs.cycle, + m_registerAddress, value); } - - m_setSectorPlay++; - - if (m_locationChanged) { - scheduleCDPlayIRQ(cdReadTime * 30); - m_locationChanged = false; - } else { - scheduleCDPlayIRQ(cdReadTime); + switch (m_registerAddress) { + case 0: { + // PARAMETER + m_commandFifo.pushPayloadData(value); + } break; + case 1: { + // HINT MSK (host interrupt mask) + m_interruptCauseMask = value; + } break; + case 2: { + // ATV0 Left-to-Left + m_atv[0] = value; + } break; + case 3: { + // ATV3 Right-to-Left + m_atv[3] = value; + } break; } - - // update for CdlGetlocP/autopause - generate_subq(m_setSectorPlay); } - void interrupt() final { - uint16_t irq = m_irq; - int no_busy_error = 0; - int start_rotating = 0; - int error = 0; - int delay; - - // Reschedule IRQ - if (m_stat) { - scheduleCDIRQ(irqReschedule); - return; - } - - m_ctrl &= ~BUSYSTS; // Command/parameter transmission not busy - - // default response - SetResultSize(1); - m_result[0] = m_statP; - m_stat = Acknowledge; - - if (m_irqRepeated) { - m_irqRepeated = 0; + void write3(uint8_t value) override { + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { auto ®s = PCSX::g_emulator->m_cpu->m_regs; - auto diff = regs.intTargets[PCSX::PSXINT_CDR] - regs.cycle; - if (m_eCycle > diff) { - scheduleCDIRQ(m_eCycle); - goto finish; - } - } - - m_irq = 0; - CDROM_IO_LOG("CDRINT %x %x %x %x\n", m_seeked, m_stat, irq, m_irqRepeated); - if ((irq & 0x100) && PCSX ::g_emulator->settings.get() - .get()) { - logCDROM(irq); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] w3.%i: %02x\n", regs.pc, regs.cycle, + m_registerAddress, value); } - - switch (irq) { - case CdlGetStat: - if (m_driveState != DRIVESTATE_LID_OPEN) m_statP &= ~STATUS_SHELLOPEN; - no_busy_error = 1; - break; - - case CdlSetloc: - break; - - do_CdlPlay: - case CdlPlay: - StopCdda(); - if (m_seeked == SEEK_PENDING) { - // XXX: wrong, should seek instead.. - m_seeked = SEEK_DONE; - } - if (m_setlocPending) { - m_setSectorPlay = m_setSector; - m_setlocPending = 0; - m_locationChanged = true; - } - - // BIOS CD Player - // - Pause player, hit Track 01/02/../xx (Setloc issued!!) - - if (m_paramC == 0 || m_param[0] == 0) { - CDROM_LOG("PLAY Resume @ %d:%d:%d\n", m_setSectorPlay.m, m_setSectorPlay.s, m_setSectorPlay.f); - } else { - int track = PCSX::IEC60908b::btoi(m_param[0]); - if (track <= m_iso->getTN()) m_curTrack = track; - CDROM_LOG("PLAY track %d\n", m_curTrack); - m_setSectorPlay = m_iso->getTD(m_curTrack); - } - - /* - Rayman: detect track changes - - fixes logo freeze - - Twisted Metal 2: skip PREGAP + starting accurate SubQ - - plays tracks without retry play - - Wild 9: skip PREGAP + starting accurate SubQ - - plays tracks without retry play - */ - Find_CurTrack(m_setSectorPlay); - readTrack(m_setSectorPlay); - m_trackChanged = false; - - // Vib Ribbon: gameplay checks flag - m_statP &= ~STATUS_SEEK; - m_result[0] = m_statP; - - m_statP |= STATUS_PLAY; - - // BIOS player - set flag again - m_play = true; - - scheduleCDPlayIRQ(cdReadTime); - start_rotating = 1; - break; - - case CdlForward: - // TODO: error 80 if stopped - m_stat = Complete; - m_suceeded = true; - - // GameShark CD Player: Calls 2x + Play 2x - if (m_fastForward == 0) { - m_fastForward = 2; - } else { - m_fastForward++; - } - - m_fastBackward = 0; - break; - - case CdlBackward: - m_stat = Complete; - m_suceeded = true; - - // GameShark CD Player: Calls 2x + Play 2x - if (m_fastBackward == 0) { - m_fastBackward = 2; - } else { - m_fastBackward++; - } - - m_fastForward = 0; - break; - - case CdlStandby: - if (m_driveState != DRIVESTATE_STOPPED) { - error = ERROR_INVALIDARG; - goto set_error; - } - AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2); - start_rotating = 1; - break; - - case CdlStandby + 0x100: - m_stat = Complete; - m_suceeded = true; - break; - - case CdlStop: - if (m_play) { - // grab time for current track - m_setSectorPlay = m_iso->getTD(m_curTrack); - } - - StopCdda(); - StopReading(); - - delay = 0x800; - if (m_driveState == DRIVESTATE_STANDBY) delay = cdReadTime * 30 / 2; - - m_driveState = DRIVESTATE_STOPPED; - AddIrqQueue(CdlStop + 0x100, delay); - break; - - case CdlStop + 0x100: - m_statP &= ~STATUS_ROTATING; - m_result[0] = m_statP; - m_stat = Complete; - m_suceeded = true; - break; - - case CdlPause: + switch (m_registerAddress) { + case 0: { + // clang-format off /* - Gundam Battle Assault 2: much slower (*) - - Fixes boot, gameplay - - Hokuto no Ken 2: slower - - Fixes intro + subtitles - - InuYasha - Feudal Fairy Tale: slower - - Fixes battles + HCHPCTL (host chip control) register + bit 7: BFRD (buffer read) + The transfer of (drive) data from the buffer memory to the host is started by setting this bit high. + The bit is automatically set low upon completion of the transfer. + bit 6: BFWR (buffer write) + The transfer of data from the host to the buffer memory is started by setting this bit high. The bit is + automatically set low upon completion of the transfer. + bit 5: SMEN (sound map En) + This is set high to perform sound map ADPCM playback. */ - /* - * Gameblabla - - * The timings are based on hardware tests and were taken from Duckstation. - * A couple of notes : - * Gundam Battle Assault 2 in PAL mode (this includes the PAL release) needs a high enough delay - * if not, the game will either crash after the FMV intro or upon starting a new game. - * - */ - if (m_driveState == DRIVESTATE_STANDBY) { - /* Gameblabla - - * Dead or Alive needs this condition and a shorter delay otherwise : if you pause ingame, music - * will not resume. */ - delay = 7000; - } else { - delay = (((m_mode & MODE_SPEED) ? 2 : 1) * (1000000)); - scheduleCDPlayIRQ((m_mode & MODE_SPEED) ? cdReadTime / 2 : cdReadTime); - } - AddIrqQueue(CdlPause + 0x100, delay); - m_ctrl |= BUSYSTS; // Command/parameter transmission busy - break; - - case CdlPause + 0x100: - m_statP &= ~STATUS_READ; - m_result[0] = m_statP; - m_stat = Complete; - m_suceeded = true; - break; - - case CdlReset: - m_muted = false; - m_mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */ - AddIrqQueue(CdlReset + 0x100, 4100000); // 4100000 is from Mednafen - no_busy_error = 1; - start_rotating = 1; - break; - - case CdlReset + 0x100: - m_stat = Complete; - m_suceeded = true; - break; - - case CdlMute: - m_muted = true; - break; - - case CdlDemute: - m_muted = false; - break; - - case CdlSetfilter: - m_file = m_param[0]; - m_channel = m_param[1]; - break; - - case CdlSetmode: - no_busy_error = 1; - break; - - case CdlGetparam: - SetResultSize(5); - m_result[1] = m_mode; - m_result[2] = 0; - m_result[3] = m_file; - m_result[4] = m_channel; - no_busy_error = 1; - break; - - case CdlGetlocL: - SetResultSize(8); - memcpy(m_result, m_transfer, 8); - break; - - case CdlGetlocP: - SetResultSize(8); - memcpy(&m_result, &m_subq, 8); - - if (!m_play && m_iso->CheckSBI(m_result + 5)) memset(m_result + 2, 0, 6); - if (!m_play && !m_reading) m_result[1] = 0; // HACK? - break; - - case CdlReadT: // SetSession? - // really long - AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4); - start_rotating = 1; - break; - - case CdlReadT + 0x100: - m_stat = Complete; - m_suceeded = true; - break; - - case CdlGetTN: - if (m_iso->failed()) { - m_stat = DiskError; - m_result[0] |= STATUS_ERROR; + // clang-format on + bool bfrd = value & 0x80; + bool bfwr = value & 0x40; + bool smen = value & 0x20; + if (bfrd) { + m_dataRequested = true; + m_dataFIFOSize = m_dataFIFOPending; } else { - SetResultSize(3); - m_stat = Acknowledge; - m_result[1] = 1; - m_result[2] = PCSX::IEC60908b::itob(m_iso->getTN()); + m_dataRequested = false; + m_dataFIFOSize = 0; + m_dataFIFOIndex = 0; } - break; - - case CdlGetTD: { - if (m_iso->failed()) { - m_stat = DiskError; - m_result[0] |= STATUS_ERROR; - } else { - m_track = PCSX::IEC60908b::btoi(m_param[0]); - SetResultSize(4); - m_stat = Acknowledge; - MSF td = m_iso->getTD(m_track); - m_result[0] = m_statP; - m_result[1] = PCSX::IEC60908b::itob(td.m); - m_result[2] = PCSX::IEC60908b::itob(td.s); - } - break; - } - - case CdlSeekL: - case CdlSeekP: - StopCdda(); - StopReading(); - m_statP |= STATUS_SEEK; - + m_soundMapEnabled = smen; + } break; + case 1: { + // clang-format off /* - Crusaders of Might and Magic = 0.5x-4x - - fix cutscene speech start - - Eggs of Steel = 2x-? - - fix new game - - Medievil = ?-4x - - fix cutscene speech - - Rockman X5 = 0.5-4x - - fix capcom logo + HCLRCTL (host clear control) + When each bit of this register is set high, the chip, status, register, interrupt status and interrupt request to + the host generated by the status are cleared. + bit 7: CHPRST (chip reset) + The inside of the IC is initialized by setting this bit high. The bit is automatically set low upon + completion of the initialization of the IC. There is therefore no need for the host to reset low. When + the inside of the IC is initialized by setting bit high, the XHRS pin is set low. + bit 6: CLRPRM (clear parameter) + The parameter register is cleared by setting this bit high. The bit is automatically set low upon + completion of the clearing for the parameter register. There is therefore no need for the host to + reset low. + bit 5: SMADPCLR (sound map ADPCM clear) + This bit is set high to terminate sound map ADPCM decoding forcibly. + (1) When this bit has been set high for sound map ADPCM playback (when both SMEN and + ADPBSY (HSTS register bit 2) are high): + • ADPCM decoding during playback is suspended. (Noise may be generated). + • The sound map and buffer management circuits in the IC are cleared, making the buffer + empty. The BFEMPT interrupt status is established. + (Note) Set the SMEN bit low at the same time as this bit is set high. + (2) Setting this bit high when the sound map ADPCM playback is not being performed has no + effect whatsoever + bit 4: CLRBFWRDY (clear buffer write ready interrupt) + bit 3: CLRBFEMPT (clear buffer write empty interrupt) + bits 2 to 0: CLRINT#2 to 0 (clear interrupt #2 to 0) + bit 4 clears the corresponding interrupt status. */ - scheduleCDPlayIRQ(m_seeked == SEEK_DONE ? 0x800 : cdReadTime * 4); - m_seeked = SEEK_PENDING; - start_rotating = 1; - break; - - case CdlTest: - switch (m_param[0]) { - case 0x20: // System Controller ROM Version - SetResultSize(4); - memcpy(m_result, Test20, 4); - break; - case 0x22: - SetResultSize(8); - memcpy(m_result, Test22, 4); - break; - case 0x23: - case 0x24: - SetResultSize(8); - memcpy(m_result, Test23, 4); - break; + // clang-format on + bool ack = false; + // cause ack + if (value == 0x07) { + // partial ack? + ack = true; } - no_busy_error = 1; - break; - - case CdlID: - AddIrqQueue(CdlID + 0x100, 20480); - break; - - case CdlID + 0x100: - SetResultSize(8); - - if (m_iso->failed()) { - m_result[0] = 0x08; - m_result[1] = 0x40; - memset((char *)&m_result[2], 0, 6); - m_stat = DiskError; - break; + if (value == 0x1f) { + // all ack? + ack = true; } - - m_result[0] = m_statP; - m_result[1] = 0; - m_result[2] = 0; - m_result[3] = 0; - - // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed - getStatus(&cdr_stat); - if (cdr_stat.Type == 0 || cdr_stat.Type == 0xff) { - m_result[1] = 0xc0; - } else { - // Audio, unlicensed - if (cdr_stat.Type == 2) m_result[1] |= (0x10 | 0x80); + if (ack) { + if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: acked %02x (was %i)\n", value, + m_responseFifo[0].value); + } + m_responseFifo[0].valueRead = true; + maybeScheduleNextCommand(); + return; } - m_result[0] |= (m_result[1] >> 4) & 0x08; - - strncpy((char *)&m_result[4], "PCSX", 4); - m_stat = Complete; - m_suceeded = true; - break; - - case CdlInit: - // yes, it really sets STATUS_SHELLOPEN - m_statP |= STATUS_SHELLOPEN; - m_driveState = DRIVESTATE_RESCAN_CD; - scheduleCDLidIRQ(20480); - no_busy_error = 1; - start_rotating = 1; - break; - - case CdlGetQ: - // TODO? - CDROM_LOG("got CdlGetQ\n"); - break; - - case CdlReadToc: - AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4); - no_busy_error = 1; - start_rotating = 1; - break; - - case CdlReadToc + 0x100: - m_stat = Complete; - m_suceeded = true; - no_busy_error = 1; - break; - - case CdlReadN: - case CdlReadS: - if (m_setlocPending) { - m_setSectorPlay = m_setSector; - m_setlocPending = 0; - m_locationChanged = true; + if (value & 0x10) { + // request ack? + // TODO: act on this? } - Find_CurTrack(m_setSectorPlay); - - if ((m_mode & MODE_CDDA) && m_curTrack > 1) { - // Read* acts as play for cdda tracks in cdda mode - goto do_CdlPlay; + if (value & 0x08) { + // ?? + // TODO: act on this? } - - m_reading = 1; - m_firstSector = 1; - - // Fighting Force 2 - update m_subq time immediately - // - fixes new game - readTrack(m_setSectorPlay); - - // Crusaders of Might and Magic - update getlocl now - // - fixes cutscene speech - { - uint8_t *buf = m_iso->getBuffer(); - if (buf != NULL) memcpy(m_transfer, buf, 8); + if (value & 0x40) { + m_commandFifo.payloadSize = 0; + return; } - + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: w3:1(%02x) not available yet\n", value); + PCSX::g_system->pause(); + } break; + case 2: { + // ATV1 Left-to-Right + m_atv[1] = value; + } break; + case 3: { + // clang-format off /* - Duke Nukem: Land of the Babes - seek then delay read for one frame - - fixes cutscenes - C-12 - Final Resistance - doesn't like seek + ADPCTL (ADPCM control) register + bit 5: CHNGATV (change ATV register) + The host sets this bit high after the changes of the ATV 3 to 0 registers have been completed. The + attenuator value in the IC is switched for the first time. There is no need for the host to set this bit + low. The bit used to set the ATV3 to 0 registers of the host and to synchronize the IC audio + playback. + bit 0: ADPMUTE (ADPCM mute) + Set high to mute the ADPCM sound for ADPCM decoding. + bits 7, 6, 4 to 1: Reserved */ - - // It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now. - // - // For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect - // the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK - // flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game - // tries to protect itself against errors by preventing from issuing a GetLocP while it knows the - // last status was "seek". But this makes the logic just softlock as it'll never get a notification - // about the fact the drive is done seeking and the read actually started. - // - // In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is - // done right away. It's rather when it's done seeking, and the read has actually started. This probably - // requires a bit more work to make sure seek delays are processed properly. - // - // Checked with a few games, this seems to work fine. - if ((m_seeked != SEEK_DONE) && false) { - m_statP |= STATUS_SEEK; - m_statP &= ~STATUS_READ; - - // Crusaders of Might and Magic - use short time - // - fix cutscene speech (startup) - - // ??? - use more accurate seek time later - scheduleCDReadIRQ((m_mode & 0x80) ? (cdReadTime) : cdReadTime * 2); - } else { - m_statP |= STATUS_READ; - m_statP &= ~STATUS_SEEK; - - scheduleCDReadIRQ((m_mode & 0x80) ? (cdReadTime) : cdReadTime * 2); - } - - m_result[0] = m_statP; - start_rotating = 1; - break; - case CdlSync: - default: - CDROM_LOG("Invalid command: %02x\n", irq); - error = ERROR_INVALIDCMD; - - set_error: - SetResultSize(2); - m_result[0] = m_statP | STATUS_ERROR; - m_result[1] = error; - m_stat = DiskError; - break; + // clang-format on + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: w3:3 not available yet\n"); + } break; } + } - if (m_driveState == DRIVESTATE_STOPPED && start_rotating) { - m_driveState = DRIVESTATE_STANDBY; - m_statP |= STATUS_ROTATING; + void dma(uint32_t madr, uint32_t bcr, uint32_t chcr) override { + uint32_t size = bcr & 0xffff; + size *= 4; + if (size == 0) size = 0xffffffff; + size = std::min(m_dataFIFOSize - m_dataFIFOIndex, size); + PCSX::IO memFile = PCSX::g_emulator->m_mem->getMemoryAsFile(); + memFile->wSeek(madr); + memFile->write(m_dataFIFO + m_dataFIFOIndex, size); + m_dataFIFOIndex += size; + PCSX::g_emulator->m_cpu->Clear(madr, size / 4); + if (PCSX::g_emulator->settings.get() + .get()) { + PCSX::g_emulator->m_debug->checkDMAwrite(3, madr, size); } - - if (!no_busy_error) { - switch (m_driveState) { - case DRIVESTATE_LID_OPEN: - case DRIVESTATE_RESCAN_CD: - case DRIVESTATE_PREPARE_CD: - SetResultSize(2); - m_result[0] = m_statP | STATUS_ERROR; - m_result[1] = ERROR_NOTREADY; - m_stat = DiskError; - break; - } + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (debug) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; + PCSX::g_system->log(PCSX::LogClass::CDROM, + "CD-Rom: %08x.%08x] DMA transfer requested to address %08x, size %08x\n", regs.pc, + regs.cycle, madr, size); } - - finish: - setIrq(); - m_paramC = 0; - - { - CDROM_IO_LOG("CDR IRQ %d cmd %02x stat %02x: ", !!(m_stat & m_reg2), irq, m_stat); - for (int i = 0; i < m_resultC; i++) CDROM_IO_LOG("%02x ", m_result[i]); - CDROM_IO_LOG("\n"); + if (chcr == 0x11400100) { + scheduleDMA(size / 16); + } else { + scheduleDMA(size / 4); } } - static constexpr inline int ssat32_to_16(int v) { - if (v < -32768) { - v = -32768; - } else if (v > 32767) { - v = 32767; - } - return v; + void maybeEnqueueError(uint8_t mask1, uint8_t mask2) { + QueueElement error; + error.pushPayloadData(getStatus() | mask1); + error.pushPayloadData(mask2); + maybeTriggerIRQ(Cause::Error, error); } - void attenuate(int16_t *buf, int samples, int stereo) final { - int i, l, r; - int ll = m_attenuatorLeftToLeft; - int lr = m_attenuatorLeftToRight; - int rl = m_attenuatorRightToLeft; - int rr = m_attenuatorRightToRight; - - if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88) return; - - if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40) return; - - if (stereo) { - for (i = 0; i < samples; i++) { - l = buf[i * 2]; - r = buf[i * 2 + 1]; - l = (l * ll + r * rl) >> 7; - r = (r * rr + l * lr) >> 7; - buf[i * 2] = ssat32_to_16(l); - buf[i * 2 + 1] = ssat32_to_16(r); - } - } else { - for (i = 0; i < samples; i++) { - l = buf[i]; - l = l * (ll + rl) >> 7; - buf[i] = ssat32_to_16(l); + void maybeStartCommand() { + if (m_commandFifo.empty()) return; + auto command = m_commandFifo.value; + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (!m_responseFifo[0].valueRead || !m_responseFifo[1].empty()) { + if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, + "CD-Rom: command %s (%i) pending, but response fifo full; won't start.\n", + commandName(command), command); } - } - } - - void readInterrupt() final { - uint8_t *buf; - - if (!m_reading) return; - - if (m_irq || m_stat) { - scheduleCDReadIRQ(irqReschedule); return; } - - uint32_t istat = PCSX::g_emulator->m_mem->readHardwareRegister(); - uint32_t imask = PCSX::g_emulator->m_mem->readHardwareRegister(); - - if ((istat & imask & 4) && !m_readRescheduled) { - // HACK: with PCSX::Emulator::BIAS 2, emulated CPU is often slower than real thing, - // game may be unfinished with prev data read, so reschedule - // (Brave Fencer Musashi) - scheduleCDReadIRQ(cdReadTime / 2); - m_readRescheduled = 1; + if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: Starting command %s (%i)\n", commandName(command), + command); + } + static constexpr unsigned c_commandMax = sizeof(c_commandsArgumentsCount) / sizeof(c_commandsArgumentsCount[0]); + if (command >= c_commandMax) { + maybeEnqueueError(1, 0x40); + maybeScheduleNextCommand(); + m_commandFifo.clear(); return; } - - SetResultSize(1); - m_statP |= STATUS_READ | STATUS_ROTATING; - m_statP &= ~STATUS_SEEK; - m_result[0] = m_statP; - m_seeked = SEEK_DONE; - - readTrack(m_setSectorPlay); - - buf = m_iso->getBuffer(); - if (buf == NULL) m_suceeded = false; - - if (!m_suceeded) { - CDROM_LOG("readInterrupt() Log: err\n"); - memset(m_transfer, 0, PCSX::IEC60908b::DATA_SIZE); - m_stat = DiskError; - m_result[0] |= STATUS_ERROR; - setIrq(); + auto expectedCount = c_commandsArgumentsCount[command]; + if ((expectedCount >= 0) && (expectedCount != m_commandFifo.payloadSize)) { + maybeEnqueueError(1, 0x20); + maybeScheduleNextCommand(); + m_commandFifo.clear(); return; } + auto handler = c_commandsHandlers[command]; + if (handler) { + if ((this->*handler)(m_commandFifo, true)) m_commandExecuting = m_commandFifo; + } else { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: Unsupported command %i (%s).\n", command, + commandName(command)); + PCSX::g_system->pause(); + maybeEnqueueError(1, 0x40); + maybeScheduleNextCommand(); + } + m_commandFifo.clear(); + } - memcpy(m_transfer, buf, PCSX::IEC60908b::DATA_SIZE); - m_ctrl |= DRQSTS; // Data fifo not empty - - CDROM_LOG("readInterrupt() Log: cdr.m_transfer %x:%x:%x\n", m_transfer[0], m_transfer[1], m_transfer[2]); - - if ((!m_muted) && (m_mode & MODE_STRSND) && (PCSX::g_emulator->settings.get()) && - (m_firstSector != -1)) { // CD-XA - // Firemen 2: Multi-XA files - briefings, cutscenes - if (m_firstSector == 1 && (m_mode & MODE_SF) == 0) { - m_file = m_transfer[4 + 0]; - m_channel = m_transfer[4 + 1]; - } - - /* Gameblabla - Ignore sectors with channel 255. - * This fixes the missing sound in Blue's Clues : Blue's Big Musical. - * (Taxi 2 is also said to be affected by the same issue) - * */ - if ((m_transfer[4 + 2] & 0x4) && (m_transfer[4 + 1] == m_channel) && (m_transfer[4 + 0] == m_file) && - m_channel != 255) { - int ret = xa_decode_sector(&m_xa, m_transfer + 4, m_firstSector); - if (!ret) { - attenuate(m_xa.pcm, m_xa.nsamples, m_xa.stereo); - PCSX::g_emulator->m_spu->playADPCMchannel(&m_xa); - m_firstSector = 0; - } else { - m_firstSector = -1; - } + void maybeScheduleNextCommand() { + const bool debug = PCSX::g_emulator->settings.get() + .get(); + if (!responseFifoFull()) { + if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: Scheduling queued next command to run.\n"); } + scheduleFifo(797us); + } else if (debug) { + PCSX::g_system->log(PCSX::LogClass::CDROM, + "CD-Rom: Won't schedule next command to run as response fifo is full.\n"); } + } - m_setSectorPlay++; - m_read = 0; - m_readRescheduled = 0; + enum class SeekType { DATA, CDDA }; - uint32_t delay = (m_mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime; - if (m_locationChanged) { - scheduleCDReadIRQ(delay * 30); - m_locationChanged = false; + std::chrono::nanoseconds computeSeekDelay(MSF from, MSF to, SeekType seekType, bool forReading = false) { + unsigned destTrack = m_iso->getTrack(to); + if (destTrack == 0) return 650ms; + if ((seekType == SeekType::DATA) && (m_iso->getTrackType(destTrack) == PCSX::CDRIso::TrackType::CDDA)) { + return 4s; + } + uint32_t distance = 0; + if (from > to) { + distance = (from - to).toLBA(); } else { - scheduleCDReadIRQ(delay); + distance = (to - from).toLBA(); } + // TODO: ought to be a decent approximation for now, + // but may require some tuning later on. + return std::chrono::microseconds(distance * 3) + (forReading ? 1ms : 167ms); + } - /* - Croc 2: $40 - only FORM1 (*) - Judge Dredd: $C8 - only FORM1 (*) - Sim Theme Park - no adpcm at all (zero) - */ - - if (!(m_mode & MODE_STRSND) || !(m_transfer[4 + 2] & 0x4)) { - m_stat = DataReady; - setIrq(); - } + std::chrono::nanoseconds computeReadDelay() { return m_speed == Speed::Simple ? 13333us : 6666us; } + + // "Command" 0, which doesn't actually exist. + bool cdlSync(const QueueElement &command, bool start) { + maybeEnqueueError(1, 0x40); + maybeScheduleNextCommand(); + return false; + } - // update for CdlGetlocP - readTrack(m_setSectorPlay); + // Command 1. + bool cdlNop(const QueueElement &command, bool start) { + QueueElement response; + response.pushPayloadData(getStatus(true)); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + return false; } - /* - read0: - 03 - bit 0,1 - mode - 04 - bit 2 - xa-adpcm fifo occupied - 08 - bit 3 - parameter fifo empty - 10 - bit 4 - parameter fifo safe to push to - 20 - bit 5 - 1 result ready - 40 - bit 6 - 1 dma ready - 80 - bit 7 - 1 command being processed - */ - - uint8_t read0(void) final { - if (m_resultReady) { - m_ctrl |= RSLRRDY; // Response fifo not empty + // Command 2. + bool cdlSetLoc(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + // What happens when issued during Read / Play? + auto maybeMSF = getMSF(command.payload); + QueueElement response; + Cause cause; + if (maybeMSF.has_value()) { + response.pushPayloadData(getStatus()); + cause = Cause::Acknowledge; + maybeTriggerIRQ(cause, response); + m_seekPosition = maybeMSF.value(); } else { - m_ctrl &= ~RSLRRDY; // Response fifo empty + maybeEnqueueError(1, 0x10); } - - m_ctrl |= (PRMEMPT | PRMWRDY); // Parameter fifo empty, parameter not fifo full - - CDROM_IO_LOG("cdr r0: %02x\n", m_ctrl); - return m_ctrl; + maybeScheduleNextCommand(); + return false; } - void write0(uint8_t rt) final { - CDROM_IO_LOG("cdr w0: %02x\n", rt); - m_ctrl = (rt & 3) | (m_ctrl & ~3); + // Command 6. + bool cdlReadN(const QueueElement &command, bool start) { + m_status = Status::Idle; + scheduleRead(20ms); + m_readingType = ReadingType::Normal; + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + m_readingState = ReadingState::Seeking; + return false; } - uint8_t read1(void) final { - uint8_t ret = 0; - if ((m_resultP & 0xf) < m_resultC) { - ret = m_result[m_resultP & 0xf]; + // Command 9. + bool cdlPause(const QueueElement &command, bool start) { + if (start) { + if (m_status == Status::Idle) { + schedule(200us); + } else { + schedule(m_speed == Speed::Simple ? 70ms : 35ms); + } + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + m_status = Status::Idle; + m_invalidLocL = true; + return true; + } else { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Complete, response); + maybeScheduleNextCommand(); + return false; } - m_resultP++; - if (m_resultP == m_resultC) m_resultReady = 0; - CDROM_IO_LOG("cdr r1: %02x\n", ret); - return ret; } - void write1(uint8_t rt) final { - MSF set_loc; - int i; - CDROM_IO_LOG("cdr w1: %02x\n", rt); - switch (m_ctrl & 3) { - case 0: - break; - case 3: - m_attenuatorRightToRightT = rt; - return; - default: - return; + // Command 10. + bool cdlInit(const QueueElement &command, bool start) { + if (start) { + QueueElement response; + m_motorOn = true; + m_speedChanged = false; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + schedule(120ms); + // TODO: figure out exactly the various states of the CD-Rom controller + // that are being reset, and their value. + m_currentPosition.reset(); + m_currentPosition.s = 2; + m_seekPosition.reset(); + m_seekPosition.s = 2; + m_invalidLocL = false; + m_speed = Speed::Simple; + m_status = Status::Idle; + m_interruptCauseMask = 0x1f; + m_readingState = ReadingState::None; + memset(m_lastLocP, 0, sizeof(m_lastLocP)); + // Probably need to cancel other scheduled tasks here. + return true; + } else { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Complete, response); + maybeScheduleNextCommand(); + return false; } + } - m_cmd = rt; - if (PCSX::g_emulator->settings.get() - .get()) { - logCDROM(rt); - } + // Command 11 + bool cdlMute(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: Mute - not yet implemented.\n"); + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + return false; + } - if constexpr (PCSX::CDROM_IO_LOGGER::c_enabled) { - std::string args; - if (m_paramC) { - args = fmt::format(" Param[{}] = {{", m_paramC); - for (i = 0; i < m_paramC; i++) args += fmt::format(" {:02x},", m_param[i]); - args += "}"; - } + // Command 12 + bool cdlDemute(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: Demute - not yet implemented.\n"); + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + return false; + } - if (rt > cdCmdEnumCount) { - CDROM_IO_LOG("CD1 write: %x (CdlUnknown) %s\n", rt, args); - } else { - CDROM_IO_LOG("CD1 write: %x (%s) %s\n", rt, magic_enum::enum_names()[rt], args); + // Command 14 + bool cdlSetMode(const QueueElement &command, bool start) { + uint8_t mode = command.payload[0]; + // TODO: add the rest of the mode bits. + if (mode & 0x80) { + if (m_speed == Speed::Simple) { + m_speed = Speed::Double; + m_speedChanged = true; + } + } else { + if (m_speed == Speed::Double) { + m_speed = Speed::Simple; + m_speedChanged = true; } } - - m_resultReady = 0; - m_ctrl |= BUSYSTS; // Command/parameter transmission busy - // m_stat = NoIntr; - AddIrqQueue(m_cmd, 0x800); - - switch (m_cmd) { - case CdlSetloc: - CDROM_LOG("CDROM setloc command (%02x, %02x, %02x)\n", m_param[0], m_param[1], m_param[2]); - // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75 - if (((m_param[0] & 0x0f) > 0x09) || (m_param[0] > 0x99) || ((m_param[1] & 0x0f) > 0x09) || - (m_param[1] >= 0x60) || ((m_param[2] & 0x0f) > 0x09) || (m_param[2] >= 0x75)) { - CDROM_LOG("Invalid/out of range seek to %02x:%02x:%02x\n", m_param[0], m_param[1], m_param[2]); - } else { - set_loc.fromBCD(m_param); - - i = m_setSectorPlay.toLBA(); - i = abs(i - int(set_loc.toLBA())); - if (i > 16) m_seeked = SEEK_PENDING; - - m_setSector = set_loc; - m_setlocPending = 1; - } + switch ((mode & 0x30) >> 4) { + case 0: + m_readSpan = ReadSpan::S2048; break; - - case CdlReadN: - case CdlReadS: - case CdlPause: - StopCdda(); - StopReading(); + case 1: + m_readSpan = ReadSpan::S2328; break; - - case CdlInit: - case CdlReset: - m_seeked = SEEK_DONE; - StopCdda(); - StopReading(); + case 2: + m_readSpan = ReadSpan::S2340; break; - - case CdlSetmode: - CDROM_LOG("write1() Log: Setmode %x\n", m_param[0]); - if ((m_mode != MODE_STRSND) && (m_param[0] == MODE_STRSND)) { - xa_decode_reset(&m_xa); - } - m_mode = m_param[0]; - - // Squaresoft on PlayStation 1998 Collector's CD Vol. 1 - // - fixes choppy movie sound - if (m_play && (m_mode & MODE_CDDA) == 0) StopCdda(); + case 3: + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: unsupported mode: %02x\n", mode); + PCSX::g_system->pause(); break; } + m_subheaderFilter = (mode & 0x08) != 0; + m_realtime = (mode & 0x40) != 0; + if (mode & 0x07) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: unsupported mode: %02x\n", mode); + PCSX::g_system->pause(); + } + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + return false; } - uint8_t read2(void) final { - unsigned char ret; - - if (m_read == 0) { - m_ctrl &= ~DRQSTS; // Data fifo empty - ret = 0; + // Command 16. + bool cdlGetLocL(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + if (m_invalidLocL) { + maybeEnqueueError(1, 0x80); } else { - ret = m_transfer[m_transferIndex]; - m_transferIndex++; - adjustTransferIndex(); + QueueElement response; + response.pushPayloadData(std::string_view((char *)m_lastLocL, sizeof(m_lastLocL))); + maybeTriggerIRQ(Cause::Acknowledge, response); } - CDROM_IO_LOG("cdr r2: %02x\n", ret); - return ret; + maybeScheduleNextCommand(); + return false; } - void write2(uint8_t rt) final { - CDROM_IO_LOG("cdr w2: %02x\n", rt); - switch (m_ctrl & 3) { - case 0: - if (m_paramC < 8) { // FIXME: size and wrapping - m_param[m_paramC++] = rt; - } - return; - case 1: - m_reg2 = rt; - setIrq(); - return; - case 2: - m_attenuatorLeftToLeftT = rt; - return; - case 3: - m_attenuatorRightToLeftT = rt; - return; - } + // Command 17. + bool cdlGetLocP(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + m_iso->getLocP(m_currentPosition, m_lastLocP); + QueueElement response; + response.pushPayloadData(std::string_view((char *)m_lastLocP, sizeof(m_lastLocP))); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + return false; } - uint8_t read3(void) final { - uint8_t ret; - if (m_ctrl & 0x1) { - ret = m_stat | 0xE0; + // Command 19. + bool cdlGetTN(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + QueueElement response; + response.pushPayloadData(getStatus()); + response.pushPayloadData(1); + response.pushPayloadData(PCSX::IEC60908b::itob(m_iso->getTN())); + maybeTriggerIRQ(Cause::Acknowledge, response); + maybeScheduleNextCommand(); + return false; + } + + // Command 20. + bool cdlGetTD(const QueueElement &command, bool start) { + // TODO: probably should error out if no disc or + // lid open? + auto track = PCSX::IEC60908b::btoi(command.payload[0]); + if (!isValidBCD(command.payload[0]) || (track > m_iso->getTN())) { + maybeEnqueueError(1, 0x10); } else { - ret = m_reg2 | 0xE0; + auto td = m_iso->getTD(track); + QueueElement response; + response.pushPayloadData(getStatus()); + response.pushPayloadData(PCSX::IEC60908b::itob(td.m)); + response.pushPayloadData(PCSX::IEC60908b::itob(td.s)); + maybeTriggerIRQ(Cause::Acknowledge, response); } - CDROM_IO_LOG("cdr r3: %02x\n", ret); - return ret; + maybeScheduleNextCommand(); + return false; } - void write3(uint8_t rt) final { - CDROM_IO_LOG("cdr w3: %02x\n", rt); - switch (m_ctrl & 3) { - case 0: - break; // transfer - case 1: - m_stat &= ~rt; - - if (rt & 0x40) m_paramC = 0; - return; - case 2: - m_attenuatorLeftToRightT = rt; - return; - case 3: - if (rt & 0x20) { - memcpy(&m_attenuatorLeftToLeft, &m_attenuatorLeftToLeftT, 4); - CDROM_IO_LOG("CD-XA Volume: %02x %02x | %02x %02x\n", m_attenuatorLeftToLeft, - m_attenuatorLeftToRight, m_attenuatorRightToLeft, m_attenuatorRightToRight); - } - return; + // Command 21. + bool cdlSeekL(const QueueElement &command, bool start) { + m_readingState = ReadingState::None; + if (start) { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + schedule(15ms); + return true; + } else if (m_status != Status::Seeking) { + auto seekDelay = computeSeekDelay(m_currentPosition, m_seekPosition, SeekType::DATA); + if (m_speedChanged) { + m_speedChanged = false; + seekDelay += 650ms; + } + m_status = Status::Seeking; + schedule(seekDelay); + return true; + } else { + m_status = Status::Idle; + m_currentPosition = m_seekPosition; + unsigned track = m_iso->getTrack(m_seekPosition); + if (m_iso->getTrackType(track) == PCSX::CDRIso::TrackType::CDDA) { + maybeEnqueueError(4, 4); + } else if (track == 0) { + maybeEnqueueError(4, 0x10); + } else { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Complete, response); + } + m_invalidLocL = true; + maybeScheduleNextCommand(); + return false; } + } - if ((rt & 0x80) && m_read == 0) { - m_read = 1; - m_transferIndex = 0; - - switch (m_mode & (MODE_SIZE_2340 | MODE_SIZE_2328)) { - case MODE_SIZE_2328: - case MODE_SIZE_2048: - m_transferIndex += 12; - break; - - case MODE_SIZE_2340: - m_transferIndex += 0; - break; - - default: - break; + // Command 22. + bool cdlSeekP(const QueueElement &command, bool start) { + m_readingState = ReadingState::None; + if (start) { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + schedule(15ms); + return true; + } else if (m_status != Status::Seeking) { + auto seekDelay = computeSeekDelay(m_currentPosition, m_seekPosition, SeekType::CDDA); + if (m_speedChanged) { + m_speedChanged = false; + seekDelay += 650ms; } + m_status = Status::Seeking; + schedule(seekDelay); + return true; + } else { + m_status = Status::Idle; + MSF fudge = m_seekPosition - MSF{m_seekPosition.toLBA() / 32768}; + m_currentPosition = fudge; + if (m_iso->getTrack(m_seekPosition) == 0) { + maybeEnqueueError(4, 0x10); + } else { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Complete, response); + } + m_invalidLocL = true; + maybeScheduleNextCommand(); + return false; } } - void dma(uint32_t madr, uint32_t bcr, uint32_t chcr) final { - uint32_t cdsize; - unsigned i; - uint8_t *ptr; - - CDROM_LOG("dma() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr); - - switch (chcr) { - case 0x11000000: - case 0x11400100: { - if (m_read == 0) { - CDROM_LOG("dma() Log: *** DMA 3 *** NOT READY\n"); - break; - } - - cdsize = (bcr & 0xffff) * 4; - - // Ape Escape: bcr = 0001 / 0000 - // - fix boot - if (cdsize == 0) { - switch (m_mode & (MODE_SIZE_2340 | MODE_SIZE_2328)) { - case MODE_SIZE_2340: - cdsize = 2340; - break; - case MODE_SIZE_2328: - cdsize = 2328; - break; - case MODE_SIZE_2048: - default: - cdsize = 2048; - break; - } - } - - PCSX::IO memFile = PCSX::g_emulator->m_mem->getMemoryAsFile(); - memFile->wSeek(madr); + // Command 25. + bool cdlTest(const QueueElement &command, bool start) { + static constexpr uint8_t c_test20[] = {0x94, 0x09, 0x19, 0xc0}; + if (command.isPayloadEmpty()) { + maybeEnqueueError(1, 0x20); + maybeScheduleNextCommand(); + return false; + } - /* - GS CDX: Enhancement CD crash - - Setloc 0:0:0 - - CdlPlay - - Spams DMA3 and gets buffer overrun - */ - for (i = 0; i < cdsize; ++i) { - memFile->write(m_transfer[m_transferIndex++]); - adjustTransferIndex(); - } - if (PCSX::g_emulator->settings.get() - .get()) { - PCSX::g_emulator->m_debug->checkDMAwrite(3, madr, cdsize); - } - PCSX::g_emulator->m_cpu->Clear(madr, cdsize / 4); - // burst vs normal - if (chcr == 0x11400100) { - scheduleCDDMAIRQ((cdsize / 4) / 4); - } else if (chcr == 0x11000000) { - scheduleCDDMAIRQ((cdsize / 4) * 1); + switch (command.payload[0]) { + case 0x20: + if (command.payloadSize == 1) { + QueueElement response; + response.pushPayloadData(std::string_view((const char *)c_test20, sizeof(c_test20))); + maybeTriggerIRQ(Cause::Acknowledge, response); + } else { + maybeEnqueueError(1, 0x20); } - return; - } - + break; default: - CDROM_LOG("dma() Log: Unknown cddma %x\n", chcr); + maybeEnqueueError(1, 0x10); break; } - - dmaInterrupt(); + maybeScheduleNextCommand(); + return false; } - void dmaInterrupt() final { - auto &mem = PCSX::g_emulator->m_mem; - if (mem->isDMABusy<3>()) { - mem->clearDMABusy<3>(); - mem->dmaInterrupt<3>(); + // Command 26. + bool cdlID(const QueueElement &command, bool start) { + if (start) { + QueueElement response; + response.pushPayloadData(getStatus()); + maybeTriggerIRQ(Cause::Acknowledge, response); + schedule(5ms); + return true; + } else { + // Adjust this response for various types of discs and situations. + QueueElement response; + response.pushPayloadData(getStatus()); + response.pushPayloadData(0x00); + response.pushPayloadData(0x20); + response.pushPayloadData(0x00); + response.pushPayloadData("PCSX"sv); + maybeTriggerIRQ(Cause::Complete, response); + maybeScheduleNextCommand(); + return false; } } - void getCdInfo(void) { m_setSectorEnd = m_iso->getTD(0); } - - void reset() final { - m_reg1Mode = 0; - m_cmdProcess = 0; - m_ctrl = 0; - - memset(m_transfer, 0, sizeof(m_transfer)); - m_prev.reset(); - memset(m_param, 0, sizeof(m_param)); - memset(m_result, 0, sizeof(m_result)); - - m_paramC = 0; - m_resultC = 0; - m_resultP = 0; - m_resultReady = 0; - m_cmd = 0; - m_read = 0; - m_setlocPending = 0; - m_locationChanged = false; - m_reading = 0; - - m_setSectorPlay.reset(); - m_setSectorEnd.reset(); - m_setSector.reset(); - m_track = 0; - m_play = false; - m_muted = false; - m_mode = 0; - m_suceeded = true; - m_firstSector = 0; - - memset(&m_xa, 0, sizeof(m_xa)); - - m_irq = 0; - m_irqRepeated = 0; - m_eCycle = 0; - - m_seeked = 0; - m_readRescheduled = 0; - - m_fastForward = 0; - m_fastBackward = 0; - - m_attenuatorLeftToLeftT = 0; - m_attenuatorLeftToRightT = 0; - m_attenuatorRightToRightT = 0; - m_attenuatorRightToLeftT = 0; - - m_subq.index = 0; - m_subq.relative[0] = 0; - m_subq.relative[1] = 0; - m_subq.relative[2] = 0; - m_subq.absolute[0] = 0; - m_subq.absolute[1] = 0; - m_subq.absolute[2] = 0; - m_trackChanged = false; - - m_curTrack = 1; - m_file = 1; - m_channel = 1; - m_transferIndex = 0; - m_reg2 = 0x1f; - m_stat = NoIntr; - m_driveState = DRIVESTATE_STANDBY; - m_statP = STATUS_ROTATING; - - // BIOS player - default values - m_attenuatorLeftToLeft = 0x80; - m_attenuatorLeftToRight = 0x00; - m_attenuatorRightToLeft = 0x00; - m_attenuatorRightToRight = 0x80; - - getCdInfo(); + // Command 27. + bool cdlReadS(const QueueElement &command, bool start) { + bool ret = cdlReadN(command, start); + m_readingType = ReadingType::Streaming; + return ret; } - void load() final { - getCdInfo(); - - // read right sub data - MSF tmp = m_prev; - readTrack(++tmp); + typedef bool (CDRomImpl::*CommandType)(const QueueElement &, bool); - if (m_play) { - Find_CurTrack(m_setSectorPlay); - } - } + const CommandType c_commandsHandlers[31] { +#if 0 + &CDRomImpl::cdlSync, &CDRomImpl::cdlNop, &CDRomImpl::cdlSetLoc, &CDRomImpl::cdlPlay, // 0 + &CDRomImpl::cdlForward, &CDRomImpl::cdlBackward, &CDRomImpl::cdlReadN, &CDRomImpl::cdlStandby, // 4 + &CDRomImpl::cdlStop, &CDRomImpl::cdlPause, &CDRomImpl::cdlInit, &CDRomImpl::cdlMute, // 8 + &CDRomImpl::cdlDemute, &CDRomImpl::cdlSetFilter, &CDRomImpl::cdlSetMode, &CDRomImpl::cdlGetParam, // 12 + &CDRomImpl::cdlGetLocL, &CDRomImpl::cdlGetLocP, &CDRomImpl::cdlReadT, &CDRomImpl::cdlGetTN, // 16 + &CDRomImpl::cdlGetTD, &CDRomImpl::cdlSeekL, &CDRomImpl::cdlSeekP, &CDRomImpl::cdlSetClock, // 20 + &CDRomImpl::cdlGetClock, &CDRomImpl::cdlTest, &CDRomImpl::cdlID, &CDRomImpl::cdlReadS, // 24 + &CDRomImpl::cdlReset, &CDRomImpl::cdlGetQ, &CDRomImpl::cdlReadTOC, // 28 +#else + &CDRomImpl::cdlSync, &CDRomImpl::cdlNop, &CDRomImpl::cdlSetLoc, nullptr, // 0 + nullptr, nullptr, &CDRomImpl::cdlReadN, nullptr, // 4 + nullptr, &CDRomImpl::cdlPause, &CDRomImpl::cdlInit, &CDRomImpl::cdlMute, // 8 + &CDRomImpl::cdlDemute, nullptr, &CDRomImpl::cdlSetMode, nullptr, // 12 + &CDRomImpl::cdlGetLocL, &CDRomImpl::cdlGetLocP, nullptr, &CDRomImpl::cdlGetTN, // 16 + &CDRomImpl::cdlGetTD, &CDRomImpl::cdlSeekL, &CDRomImpl::cdlSeekP, nullptr, // 20 + nullptr, &CDRomImpl::cdlTest, &CDRomImpl::cdlID, &CDRomImpl::cdlReadS, // 24 + nullptr, nullptr, nullptr, // 28 +#endif + }; - void lidInterrupt() final { - getCdInfo(); - StopCdda(); - lidSeekInterrupt(); - } + static constexpr int c_commandsArgumentsCount[31] = { + 0, 0, 3, -1, // 0 + 0, 0, 0, 0, // 4 + 0, 0, 0, 0, // 8 + 0, 2, 1, 0, // 12 + 0, 0, 1, 0, // 16 + 1, 0, 0, 0, // 20 + 0, -1, 0, 0, // 24 + 0, 0, 0, // 28 + }; - void logCDROM(int command) { - const auto delayedString = (command & 0x100) ? "[Delayed]" : ""; // log if this is a delayed CD-ROM IRQ - uint32_t pc = PCSX::g_emulator->m_cpu->m_regs.pc; + void logCDROM(const QueueElement &command) { + auto ®s = PCSX::g_emulator->m_cpu->m_regs; - switch (command & 0xff) { - // TODO: decode more commands + switch (command.value) { case CdlTest: - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlTest %02x\n", pc, delayedString, - m_param[0]); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlTest %02x\n", regs.pc, + regs.cycle, command.payload[0]); break; - case CdlSetloc: - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlSetloc %02x:%02x:%02x\n", pc, - delayedString, m_param[0], m_param[1], m_param[2]); + case CdlSetLoc: + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlSetloc %02x:%02x:%02x\n", + regs.pc, regs.cycle, command.payload[0], command.payload[1], command.payload[2]); break; case CdlPlay: - if (m_paramC == 0) { - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlPlay\n", pc, delayedString); + if (command.payloadSize == 0) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlPlay\n", regs.pc, + regs.cycle); } else { - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlPlay %i\n", pc, - delayedString, m_param[0]); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlPlay %i\n", regs.pc, + regs.cycle, command.payload[0]); } break; - case CdlSetfilter: + case CdlSetFilter: PCSX::g_system->log(PCSX::LogClass::CDROM, - "%08x [CDROM]%s Command: CdlSetfilter file: %i, channel: %i\n", pc, delayedString, - m_param[0], m_param[1]); + "CD-Rom: %08x.%08x] Command: CdlSetfilter file: %i, channel: %i\n", regs.pc, + regs.cycle, command.payload[0], command.payload[1]); break; - case CdlSetmode: { - auto mode = m_param[0]; + case CdlSetMode: { + auto mode = command.payload[0]; std::string modeDecode = mode & 1 ? "CDDA" : "DATA"; if (mode & 2) modeDecode += " Autopause"; if (mode & 4) modeDecode += " Report"; @@ -1638,26 +1229,26 @@ class CDRomImpl : public PCSX::CDRom { } if (mode & 0x40) modeDecode += " RealTimePlay"; modeDecode += mode & 0x80 ? " @2x" : " @1x"; - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlSetmode %02x (%s)\n", pc, - delayedString, m_param[0], modeDecode); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlSetmode %02x (%s)\n", + regs.pc, regs.cycle, command.payload[0], modeDecode); } break; case CdlGetTN: - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlGetTN (returns %i)\n", pc, - delayedString, m_iso->getTN()); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlGetTN (returns %i)\n", + regs.pc, regs.cycle, m_iso->getTN()); break; case CdlGetTD: { - auto ret = m_iso->getTD(m_param[0]); + auto ret = m_iso->getTD(command.payload[0]); PCSX::g_system->log(PCSX::LogClass::CDROM, - "%08x [CDROM]%s Command: CdlGetTD %i (returns %02i:%02i:%02i)\n", pc, delayedString, - m_param[0], ret.m, ret.s, ret.f); + "CD-Rom: %08x.%08x] Command: CdlGetTD %i (returns %02i:%02i:%02i)\n", regs.pc, + regs.cycle, command.payload[0], ret.m, ret.s, ret.f); } break; default: - if ((command & 0xff) > cdCmdEnumCount) { - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: CdlUnknown(0x%02X)\n", pc, - delayedString, command & 0xff); + if (command.value > c_cdCmdEnumCount) { + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: CdlUnknown(0x%02X)\n", + regs.pc, regs.cycle, command.value); } else { - PCSX::g_system->log(PCSX::LogClass::CDROM, "%08x [CDROM]%s Command: %s\n", pc, delayedString, - magic_enum::enum_names()[command & 0xff]); + PCSX::g_system->log(PCSX::LogClass::CDROM, "CD-Rom: %08x.%08x] Command: %s\n", regs.pc, regs.cycle, + commandName(command.value)); } break; } @@ -1667,7 +1258,8 @@ class CDRomImpl : public PCSX::CDRom { } // namespace PCSX::CDRom *PCSX::CDRom::factory() { return new CDRomImpl; } -void PCSX::CDRom::check() { + +void PCSX::CDRom::parseIso() { m_cdromId.clear(); m_cdromLabel.clear(); ISO9660Reader reader(m_iso); @@ -1715,3 +1307,14 @@ void PCSX::CDRom::check() { g_system->printf(_("CD-ROM ID: %.9s\n"), m_cdromId); g_system->printf(_("CD-ROM EXE Name: %.255s\n"), exename); } + +bool PCSX::CDRom::isLidOpen() { + if (m_lidCloseScheduled) { + const uint32_t cycle = g_emulator->m_cpu->m_regs.cycle; + if (((int32_t)(m_lidCloseAtCycles - cycle)) <= 0) { + m_lidCloseScheduled = false; + m_lidOpen = false; + } + } + return m_lidOpen; +} diff --git a/src/core/cdrom.h b/src/core/cdrom.h index 9c7483ff1..44fab7364 100644 --- a/src/core/cdrom.h +++ b/src/core/cdrom.h @@ -21,6 +21,7 @@ #include #include +#include #include "cdrom/cdriso.h" #include "core/decode_xa.h" @@ -38,21 +39,30 @@ namespace Widgets { class IsoBrowser; } -struct CdrStat { - uint32_t Type; - uint32_t Status; - IEC60908b::MSF Time; -}; - class CDRom { public: - using MSF = PCSX::IEC60908b::MSF; + using MSF = IEC60908b::MSF; CDRom() : m_iso(new CDRIso(new FailedFile)) {} virtual ~CDRom() {} static CDRom* factory(); - bool isLidOpened() { return m_lidOpenTime < 0 || m_lidOpenTime > (int64_t)time(nullptr); } - void setLidOpenTime(int64_t time) { m_lidOpenTime = time; } - void check(); + bool isLidOpen(); + void closeLid() { + m_lidOpen = false; + m_lidCloseScheduled = false; + } + void openLid() { + m_lidOpen = true; + m_wasLidOpened = true; + m_lidCloseScheduled = false; + } + void scheduleCloseLid() { + m_lidOpen = true; + m_wasLidOpened = true; + m_lidCloseScheduled = true; + using namespace std::chrono_literals; + m_lidCloseAtCycles = g_emulator->m_cpu->m_regs.getFutureCycle(1s); + } + void parseIso(); std::shared_ptr getIso() { return m_iso; } void clearIso() { @@ -68,24 +78,19 @@ class CDRom { const std::string& getCDRomLabel() { return m_cdromLabel; } virtual void reset() = 0; - virtual void attenuate(int16_t* buf, int samples, int stereo) = 0; - - virtual void interrupt() = 0; - virtual void readInterrupt() = 0; - virtual void decodedBufferInterrupt() = 0; - virtual void lidSeekInterrupt() = 0; - virtual void playInterrupt() = 0; - virtual void dmaInterrupt() = 0; - virtual void lidInterrupt() = 0; - virtual uint8_t read0(void) = 0; - virtual uint8_t read1(void) = 0; - virtual uint8_t read2(void) = 0; - virtual uint8_t read3(void) = 0; + + virtual void fifoScheduledCallback() = 0; + virtual void commandsScheduledCallback() = 0; + virtual void readScheduledCallback() = 0; + virtual void scheduledDmaCallback() = 0; + virtual uint8_t read0() = 0; + virtual uint8_t read1() = 0; + virtual uint8_t read2() = 0; + virtual uint8_t read3() = 0; virtual void write0(uint8_t rt) = 0; virtual void write1(uint8_t rt) = 0; virtual void write2(uint8_t rt) = 0; virtual void write3(uint8_t rt) = 0; - virtual void load() = 0; virtual void dma(uint32_t madr, uint32_t bcr, uint32_t chcr) = 0; @@ -93,74 +98,114 @@ class CDRom { protected: std::shared_ptr m_iso; - // savestate stuff starts here - uint8_t m_reg1Mode; - uint8_t m_reg2; - uint8_t m_cmdProcess; - uint8_t m_ctrl; - uint8_t m_stat; - - uint8_t m_statP; - - uint8_t m_transfer[PCSX::IEC60908b::FRAMESIZE_RAW]; - unsigned int m_transferIndex; - - MSF m_prev; - uint8_t m_param[8]; - uint8_t m_result[16]; - - uint8_t m_paramC; - uint8_t m_resultC; - uint8_t m_resultP; - uint8_t m_resultReady; - uint8_t m_cmd; - uint8_t m_read; - uint8_t m_setlocPending; - bool m_locationChanged; - uint32_t m_reading; - - MSF m_setSectorPlay; - MSF m_setSectorEnd; - MSF m_setSector; - uint8_t m_track; - bool m_play, m_muted; - int m_curTrack; - int m_mode, m_file, m_channel; - bool m_suceeded; - int m_firstSector; - - public: - // this belongs in the SPU, not here. - xa_decode_t m_xa; - - protected: - int64_t m_lidOpenTime = 0; - uint16_t m_irq; - uint8_t m_irqRepeated; - uint32_t m_eCycle; - - uint8_t m_seeked; - uint8_t m_readRescheduled; - - uint8_t m_driveState; - uint8_t m_fastForward; - uint8_t m_fastBackward; - - uint8_t m_attenuatorLeftToLeft, m_attenuatorLeftToRight; - uint8_t m_attenuatorRightToRight, m_attenuatorRightToLeft; - uint8_t m_attenuatorLeftToLeftT, m_attenuatorLeftToRightT; - uint8_t m_attenuatorRightToRightT, m_attenuatorRightToLeftT; - - struct { - uint8_t track; - uint8_t index; - uint8_t relative[3]; - uint8_t absolute[3]; - } m_subq; - bool m_trackChanged; - // end savestate friend SaveStates::SaveState SaveStates::constructSaveState(); + bool dataFIFOHasData() { return m_dataFIFOIndex != m_dataFIFOSize; } + + bool m_lidOpen = false; + bool m_wasLidOpened = false; + bool m_lidCloseScheduled = false; + uint32_t m_lidCloseAtCycles = 0; + + // to save/init + uint64_t m_seed = 9223521712174600777ull; + uint8_t m_dataFIFO[2352] = {0}; + uint32_t m_dataFIFOIndex = 0; + uint32_t m_dataFIFOSize = 0; + uint32_t m_dataFIFOPending = 0; + uint8_t m_registerAddress = 0; + bool m_motorOn = false; + bool m_speedChanged = false; + bool m_invalidLocL = false; + bool m_dataRequested = false; + bool m_subheaderFilter = false; + bool m_realtime = false; + enum class ReadingState : uint8_t { + None, + Seeking, + Reading, + } m_readingState = ReadingState::None; + bool m_startPlaying = false; + enum class ReadingType : uint8_t { + None, + Normal, + Streaming, + } m_readingType = ReadingType::None; + enum class Status : uint8_t { + Idle, + ReadingData, + Seeking, + PlayingCDDA, + } m_status = Status::Idle; + enum class Speed : uint8_t { Simple, Double } m_speed; + enum class ReadSpan : uint8_t { S2048, S2328, S2340 } m_readSpan; + uint8_t m_interruptCauseMask = 0x1f; + uint8_t m_atv[4] = {0}; + bool m_soundMapEnabled = false; + + enum class Cause : uint8_t { + None = 0, + DataReady = 1, + Complete = 2, + Acknowledge = 3, + End = 4, + Error = 5, + }; + + MSF m_currentPosition; + MSF m_seekPosition; + uint8_t m_lastLocP[8] = {0}; + uint8_t m_lastLocL[8] = {0}; + + struct QueueElement { + uint8_t payload[16]; + uint8_t value; + bool valueRead = false; + bool hasValue = false; + bool hitMax = false; + uint8_t payloadSize = 0; + uint8_t payloadIndex = 0; + bool isPayloadAtEnd() const { return payloadIndex == payloadSize; } + bool isPayloadEmpty() const { return (payloadSize == 0) || hitMax; } + bool isPayloadFull() const { return payloadSize == sizeof(payload); } + bool empty() const { return valueEmpty() && isPayloadEmpty(); } + bool valueEmpty() const { return !hasValue || valueRead; } + void clear() { + hasValue = false; + valueRead = false; + payloadSize = 0; + payloadIndex = 0; + } + void setValue(uint8_t newValue) { + value = newValue; + hasValue = true; + } + void setValue(Cause cause) { setValue(static_cast(cause)); } + void pushPayloadData(uint8_t value) { + if (payloadSize < sizeof(payload)) payload[payloadSize++] = value; + } + void pushPayloadData(std::string_view values) { + for (auto value : values) { + pushPayloadData(value); + } + } + uint8_t getValue() const { return valueRead ? 0 : value; } + uint8_t readPayloadByte() { + while (payloadIndex >= 16) payloadIndex -= 16; + uint8_t r = 0; + if (payloadIndex < payloadSize) { + r = payload[payloadIndex]; + } + if (++payloadIndex == payloadSize) hitMax = true; + return r; + } + }; + + QueueElement m_commandFifo; + QueueElement m_commandExecuting; + QueueElement m_responseFifo[2]; + bool responseFifoFull() { return !m_responseFifo[0].empty() && !m_responseFifo[1].empty(); } + private: friend class Widgets::IsoBrowser; std::string m_cdromId; diff --git a/src/core/gdb-server.cc b/src/core/gdb-server.cc index 34908b759..f59ffcdc7 100644 --- a/src/core/gdb-server.cc +++ b/src/core/gdb-server.cc @@ -754,7 +754,7 @@ void PCSX::GdbClient::processMonitorCommand(const std::string& cmd) { auto pathCmd = cmd.substr(8); auto pathView = StringsHelpers::trim(pathCmd); g_emulator->m_cdrom->setIso(new CDRIso(pathView)); - g_emulator->m_cdrom->check(); + g_emulator->m_cdrom->parseIso(); } } write("OK"); diff --git a/src/core/gte.cc b/src/core/gte.cc index cf9799466..0410d26f4 100644 --- a/src/core/gte.cc +++ b/src/core/gte.cc @@ -446,9 +446,9 @@ static int32_t Lm_D(int64_t a, int sf) { return LIM(gte_shift(a, sf), 0xffff, 0x int64_t PCSX::GTE::F(int64_t a) { s_mac0 = a; - if (a > S64(0x7fffffff)) FLAG |= (1 << 31) | (1 << 16); + if (a > 0x7fffffffLL) FLAG |= (1 << 31) | (1 << 16); - if (a < S64(-0x80000000)) FLAG |= (1 << 31) | (1 << 15); + if (a < -0x80000000LL) FLAG |= (1 << 31) | (1 << 15); return a; } diff --git a/src/core/isoffi.lua b/src/core/isoffi.lua index 6e8787740..d96c0275a 100644 --- a/src/core/isoffi.lua +++ b/src/core/isoffi.lua @@ -28,6 +28,7 @@ enum SectorMode { typedef struct { char opaque[?]; } LuaIso; typedef struct { char opaque[?]; } IsoReader; +typedef struct { uint8_t m, s, f; } MSF; void deleteIso(LuaIso* wrapper); bool isIsoFailed(LuaIso* wrapper); @@ -42,6 +43,8 @@ void deleteIsoReader(IsoReader* isoReader); bool isReaderFailed(IsoReader* reader); LuaFile* readerOpen(IsoReader* reader, const char* path); LuaFile* fileisoOpen(LuaIso* wrapper, uint32_t lba, uint32_t size, enum SectorMode mode); +uint32_t getIsoTN(LuaIso* wrapper); +MSF getIsoTD(LuaIso* wrapper, uint32_t tn); typedef struct { char opaque[?]; } ISO9660Builder; ISO9660Builder* createIsoBuilder(LuaFile* out); @@ -78,6 +81,12 @@ local function createIsoWrapper(wrapper) if mode == nil then mode = 'GUESS' end return Support.File._createFileWrapper(C.fileisoOpen(self._wrapper, lba, size, mode)) end, + getTD = function(self, tn) + return C.getIsoTD(self._wrapper, tn) + end, + getTN = function(self) + return C.getIsoTN(self._wrapper) + end, } return iso end diff --git a/src/core/luaiso.cc b/src/core/luaiso.cc index 3930d8a09..fc40b959b 100644 --- a/src/core/luaiso.cc +++ b/src/core/luaiso.cc @@ -28,6 +28,7 @@ #include "core/cdrom.h" #include "lua/luafile.h" #include "lua/luawrapper.h" +#include "supportpsx/iec-60908b.h" namespace { @@ -36,6 +37,10 @@ struct LuaIso { std::shared_ptr iso; }; +struct MSF { + uint8_t m, s, f; +}; + void deleteIso(LuaIso* wrapper) { delete wrapper; } bool isIsoFailed(LuaIso* wrapper) { return wrapper->iso->failed(); } void isoClearPPF(LuaIso* wrapper) { wrapper->iso->getPPF()->clear(); } @@ -56,6 +61,11 @@ PCSX::LuaFFI::LuaFile* readerOpen(PCSX::ISO9660Reader* reader, const char* path) PCSX::LuaFFI::LuaFile* fileisoOpen(LuaIso* wrapper, uint32_t lba, uint32_t size, PCSX::SectorMode mode) { return new PCSX::LuaFFI::LuaFile(new PCSX::CDRIsoFile(wrapper->iso, lba, size, mode)); } +uint32_t getIsoTN(LuaIso* wrapper) { return wrapper->iso->getTN(); } +MSF getIsoTD(LuaIso* wrapper, uint32_t tn) { + auto ret = wrapper->iso->getTD(tn); + return {ret.m, ret.s, ret.f}; +} PCSX::ISO9660Builder* createIsoBuilder(PCSX::LuaFFI::LuaFile* wrapper) { return new PCSX::ISO9660Builder(wrapper->file); @@ -92,6 +102,8 @@ static void registerAllSymbols(PCSX::Lua L) { REGISTER(L, getCurrentIso); REGISTER(L, openIso); REGISTER(L, openIsoFromFile); + REGISTER(L, getIsoTD); + REGISTER(L, getIsoTN); REGISTER(L, createIsoReader); REGISTER(L, deleteIsoReader); REGISTER(L, isReaderFailed); diff --git a/src/core/mdec.cc b/src/core/mdec.cc index fffd09b26..aa881dc1e 100644 --- a/src/core/mdec.cc +++ b/src/core/mdec.cc @@ -455,10 +455,10 @@ void PCSX::MDEC::dma0(uint32_t adr, uint32_t bcr, uint32_t chcr) { break; } - mdec0Interrupt(); + scheduledCallback0(); } -void PCSX::MDEC::mdec0Interrupt() { +void PCSX::MDEC::scheduledCallback0() { auto &mem = g_emulator->m_mem; mem->clearDMABusy<0>(); mem->dmaInterrupt<0>(); @@ -557,7 +557,7 @@ void PCSX::MDEC::dma1(uint32_t adr, uint32_t bcr, uint32_t chcr) { } } -void PCSX::MDEC::mdec1Interrupt() { +void PCSX::MDEC::scheduledCallback1() { /* Author : gschwind * * in that case we have done all decoding stuff @@ -585,11 +585,11 @@ void PCSX::MDEC::mdec1Interrupt() { /* this else if avoid to read outside memory */ if (mdec.rl >= mdec.rl_end) { mdec.reg1 &= ~MDEC1_STP; - mdec0Interrupt(); + scheduledCallback0(); mdec.reg1 &= ~MDEC1_BUSY; } else if (SWAP_LE16(*(mdec.rl)) == MDEC_END_OF_DATA) { mdec.reg1 &= ~MDEC1_STP; - mdec0Interrupt(); + scheduledCallback0(); mdec.reg1 &= ~MDEC1_BUSY; } diff --git a/src/core/mdec.h b/src/core/mdec.h index 551244c22..cc79ffb76 100644 --- a/src/core/mdec.h +++ b/src/core/mdec.h @@ -37,8 +37,8 @@ class MDEC { uint32_t read1(); void dma0(uint32_t madr, uint32_t bcr, uint32_t chcr); void dma1(uint32_t madr, uint32_t bcr, uint32_t chcr); - void mdec0Interrupt(); - void mdec1Interrupt(); + void scheduledCallback0(); + void scheduledCallback1(); static const unsigned DSIZE = 8; static const unsigned DSIZE2 = DSIZE * DSIZE; diff --git a/src/core/psxdma.h b/src/core/psxdma.h index 044474c84..7544beed5 100644 --- a/src/core/psxdma.h +++ b/src/core/psxdma.h @@ -23,23 +23,23 @@ #include "core/r3000a.h" static inline void scheduleGPUDMAIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_GPUDMA, eCycle); + PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::GPUDMA, eCycle); } static inline void scheduleSPUDMAIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_SPUDMA, eCycle); + PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::SPUDMA, eCycle); } static inline void scheduleMDECOUTDMAIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_MDECOUTDMA, eCycle); + PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::MDECOUTDMA, eCycle); } static inline void scheduleMDECINDMAIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_MDECINDMA, eCycle); + PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::MDECINDMA, eCycle); } static inline void scheduleGPUOTCDMAIRQ(uint32_t eCycle) { - PCSX::g_emulator->m_cpu->scheduleInterrupt(PCSX::PSXINT_GPUOTCDMA, eCycle); + PCSX::g_emulator->m_cpu->schedule(PCSX::Schedule::GPUOTCDMA, eCycle); } void dma4(uint32_t madr, uint32_t bcr, uint32_t chcr); diff --git a/src/core/psxemulator.h b/src/core/psxemulator.h index 8a4d0eb17..b5373b3bf 100644 --- a/src/core/psxemulator.h +++ b/src/core/psxemulator.h @@ -109,6 +109,7 @@ class Emulator { typedef Setting FirstChanceException; typedef Setting SkipISR; typedef Setting LoggingCDROM; + typedef Setting LoggingHWCDROM; typedef Setting GdbServer; typedef Setting GdbManifest; enum class GdbLog { @@ -143,12 +144,12 @@ class Emulator { Raw, }; typedef Setting SIO1ModeSetting; - typedef Settings + typedef Settings type; }; typedef SettingNested SettingDebugSettings; @@ -229,8 +230,9 @@ class Emulator { // Make the timing events trigger faster as we are currently assuming everything // takes one cycle, which is not the case on real hardware. // FIXME: Count the proper cycle and get rid of this - uint32_t m_psxClockSpeed = 33868800 /* 33.8688 MHz */; - enum { BIAS = 2 }; + static constexpr uint32_t m_psxClockSpeed = 33868800 /* 33.8688 MHz */; + static constexpr uint32_t BIAS = 2; + static constexpr uint32_t ROM_EXTRA_BIAS = 10; template requires((alignment == 1) || (alignment == 4)) diff --git a/src/core/psxinterpreter.cc b/src/core/psxinterpreter.cc index ac3931c75..3c56deefb 100644 --- a/src/core/psxinterpreter.cc +++ b/src/core/psxinterpreter.cc @@ -372,7 +372,7 @@ void InterpretedCPU::psxADDI(uint32_t code) { void InterpretedCPU::psxADDIU(uint32_t code) { if (!_Rt_) return; maybeCancelDelayedLoad(_Rt_); - uint32_t newValue = _u32(_rRs_) + _Imm_; + uint32_t newValue = _rRs_ + _Imm_; if (_Rt_ == 29) { if (_Rs_ == 29) { PCSX::g_emulator->m_callStacks->offsetSP(_rRt_, _Imm_); @@ -386,7 +386,7 @@ void InterpretedCPU::psxADDIU(uint32_t code) { void InterpretedCPU::psxANDI(uint32_t code) { if (!_Rt_) return; maybeCancelDelayedLoad(_Rt_); - uint32_t newValue = _u32(_rRs_) & _ImmU_; + uint32_t newValue = _rRs_ & _ImmU_; if (_Rt_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRt_, newValue); } @@ -395,7 +395,7 @@ void InterpretedCPU::psxANDI(uint32_t code) { void InterpretedCPU::psxORI(uint32_t code) { if (!_Rt_) return; maybeCancelDelayedLoad(_Rt_); - uint32_t newValue = _u32(_rRs_) | _ImmU_; + uint32_t newValue = _rRs_ | _ImmU_; if (_Rt_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRt_, newValue); } @@ -404,7 +404,7 @@ void InterpretedCPU::psxORI(uint32_t code) { void InterpretedCPU::psxXORI(uint32_t code) { if (!_Rt_) return; maybeCancelDelayedLoad(_Rt_); - uint32_t newValue = _u32(_rRs_) ^ _ImmU_; + uint32_t newValue = _rRs_ ^ _ImmU_; if (_Rt_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRt_, newValue); } @@ -413,7 +413,7 @@ void InterpretedCPU::psxXORI(uint32_t code) { void InterpretedCPU::psxSLTI(uint32_t code) { if (!_Rt_) return; maybeCancelDelayedLoad(_Rt_); - uint32_t newValue = _i32(_rRs_) < _Imm_; + uint32_t newValue = int32_t(_rRs_) < _Imm_; if (_Rt_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRt_, newValue); } @@ -422,7 +422,7 @@ void InterpretedCPU::psxSLTI(uint32_t code) { void InterpretedCPU::psxSLTIU(uint32_t code) { if (!_Rt_) return; maybeCancelDelayedLoad(_Rt_); - uint32_t newValue = _u32(_rRs_) < ((uint32_t)_Imm_); + uint32_t newValue = _rRs_ < ((uint32_t)_Imm_); if (_Rt_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRt_, newValue); } @@ -519,7 +519,7 @@ void InterpretedCPU::psxSUBU(uint32_t code) { void InterpretedCPU::psxAND(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRs_) & _u32(_rRt_); + uint32_t newValue = _rRs_ & _rRt_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -528,7 +528,7 @@ void InterpretedCPU::psxAND(uint32_t code) { void InterpretedCPU::psxOR(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRs_) | _u32(_rRt_); + uint32_t newValue = _rRs_ | _rRt_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -537,7 +537,7 @@ void InterpretedCPU::psxOR(uint32_t code) { void InterpretedCPU::psxXOR(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRs_) ^ _u32(_rRt_); + uint32_t newValue = _rRs_ ^ _rRt_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -546,7 +546,7 @@ void InterpretedCPU::psxXOR(uint32_t code) { void InterpretedCPU::psxNOR(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = ~(_u32(_rRs_) | _u32(_rRt_)); + uint32_t newValue = ~(_rRs_ | _rRt_); if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -555,7 +555,7 @@ void InterpretedCPU::psxNOR(uint32_t code) { void InterpretedCPU::psxSLT(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _i32(_rRs_) < _i32(_rRt_); + uint32_t newValue = int32_t(_rRs_) < int32_t(_rRt_); if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -564,7 +564,7 @@ void InterpretedCPU::psxSLT(uint32_t code) { void InterpretedCPU::psxSLTU(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRs_) < _u32(_rRt_); + uint32_t newValue = _rRs_ < _rRt_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -621,7 +621,7 @@ void InterpretedCPU::psxMULTU(uint32_t code) { * Format: OP rs, offset * *********************************************************/ #define RepZBranchi32(op) \ - if (_i32(_rRs_) op 0) { \ + if (int32_t(_rRs_) op 0) { \ doBranch(_BranchTarget_, false); \ } #define RepZBranchLinki32(op) \ @@ -649,7 +649,7 @@ void InterpretedCPU::psxBLTZAL(uint32_t code) { RepZBranchLinki32(<); } // Bra void InterpretedCPU::psxSLL(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRt_) << _Sa_; + uint32_t newValue = _rRt_ << _Sa_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -658,7 +658,7 @@ void InterpretedCPU::psxSLL(uint32_t code) { void InterpretedCPU::psxSRA(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _i32(_rRt_) >> _Sa_; + uint32_t newValue = int32_t(_rRt_) >> _Sa_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -667,7 +667,7 @@ void InterpretedCPU::psxSRA(uint32_t code) { void InterpretedCPU::psxSRL(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRt_) >> _Sa_; + uint32_t newValue = _rRt_ >> _Sa_; if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -681,7 +681,7 @@ void InterpretedCPU::psxSRL(uint32_t code) { void InterpretedCPU::psxSLLV(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRt_) << (_u32(_rRs_) & 0x1f); + uint32_t newValue = _rRt_ << (_rRs_ & 0x1f); if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -690,7 +690,7 @@ void InterpretedCPU::psxSLLV(uint32_t code) { void InterpretedCPU::psxSRAV(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _i32(_rRt_) >> (_u32(_rRs_) & 0x1f); + uint32_t newValue = int32_t(_rRt_) >> (_rRs_ & 0x1f); if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -699,7 +699,7 @@ void InterpretedCPU::psxSRAV(uint32_t code) { void InterpretedCPU::psxSRLV(uint32_t code) { if (!_Rd_) return; maybeCancelDelayedLoad(_Rd_); - uint32_t newValue = _u32(_rRt_) >> (_u32(_rRs_) & 0x1f); + uint32_t newValue = _rRt_ >> (_rRs_ & 0x1f); if (_Rd_ == 29) { PCSX::g_emulator->m_callStacks->setSP(_rRd_, newValue); } @@ -823,7 +823,7 @@ void InterpretedCPU::psxJR(uint32_t code) { } void InterpretedCPU::psxJALR(uint32_t code) { - uint32_t temp = _u32(_rRs_); + uint32_t temp = _rRs_; if (_Rd_) { maybeCancelDelayedLoad(_Rd_); uint32_t ra = m_regs.pc + 4; @@ -852,12 +852,12 @@ void InterpretedCPU::psxJALR(uint32_t code) { * Format: OP rt, offset(base) * *********************************************************/ -#define _oB_ (_u32(_rRs_) + _Imm_) +#define _oB_ (_rRs_ + _Imm_) void InterpretedCPU::psxLB(uint32_t code) { // load delay = 1 latency if (_Rt_) { - _i32(delayedLoadRef(_Rt_)) = (int8_t)PCSX::g_emulator->m_mem->read8(_oB_); + delayedLoadRef(_Rt_) = (int8_t)PCSX::g_emulator->m_mem->read8(_oB_); } else { PCSX::g_emulator->m_mem->read8(_oB_); } @@ -866,7 +866,7 @@ void InterpretedCPU::psxLB(uint32_t code) { void InterpretedCPU::psxLBU(uint32_t code) { // load delay = 1 latency if (_Rt_) { - _u32(delayedLoadRef(_Rt_)) = PCSX::g_emulator->m_mem->read8(_oB_); + delayedLoadRef(_Rt_) = PCSX::g_emulator->m_mem->read8(_oB_); } else { PCSX::g_emulator->m_mem->read8(_oB_); } @@ -883,7 +883,7 @@ void InterpretedCPU::psxLH(uint32_t code) { } if (_Rt_) { - _i32(delayedLoadRef(_Rt_)) = (short)PCSX::g_emulator->m_mem->read16(_oB_); + delayedLoadRef(_Rt_) = (short)PCSX::g_emulator->m_mem->read16(_oB_); } else { PCSX::g_emulator->m_mem->read16(_oB_); } @@ -900,7 +900,7 @@ void InterpretedCPU::psxLHU(uint32_t code) { } if (_Rt_) { - _u32(delayedLoadRef(_Rt_)) = PCSX::g_emulator->m_mem->read16(_oB_); + delayedLoadRef(_Rt_) = PCSX::g_emulator->m_mem->read16(_oB_); } else { PCSX::g_emulator->m_mem->read16(_oB_); } @@ -928,7 +928,7 @@ void InterpretedCPU::psxLW(uint32_t code) { } break; } - _u32(delayedLoadRef(_Rt_)) = val; + delayedLoadRef(_Rt_) = val; } } @@ -939,7 +939,7 @@ void InterpretedCPU::psxLWL(uint32_t code) { // load delay = 1 latency if (!_Rt_) return; - _u32(delayedLoadRef(_Rt_, LWL_MASK[shift])) = mem << LWL_SHIFT[shift]; + delayedLoadRef(_Rt_, LWL_MASK[shift]) = mem << LWL_SHIFT[shift]; /* Mem = 1234. Reg = abcd @@ -957,7 +957,7 @@ void InterpretedCPU::psxLWR(uint32_t code) { // load delay = 1 latency if (!_Rt_) return; - _u32(delayedLoadRef(_Rt_, LWR_MASK[shift])) = mem >> LWR_SHIFT[shift]; + delayedLoadRef(_Rt_, LWR_MASK[shift]) = mem >> LWR_SHIFT[shift]; /* Mem = 1234. Reg = abcd @@ -999,7 +999,7 @@ void InterpretedCPU::psxSWL(uint32_t code) { uint32_t shift = addr & 3; uint32_t mem = PCSX::g_emulator->m_mem->read32(addr & ~3); - PCSX::g_emulator->m_mem->write32(addr & ~3, (_u32(_rRt_) >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift])); + PCSX::g_emulator->m_mem->write32(addr & ~3, (_rRt_ >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift])); /* Mem = 1234. Reg = abcd 0 123a (reg >> 24) | (mem & 0xffffff00) @@ -1014,7 +1014,7 @@ void InterpretedCPU::psxSWR(uint32_t code) { uint32_t shift = addr & 3; uint32_t mem = PCSX::g_emulator->m_mem->read32(addr & ~3); - PCSX::g_emulator->m_mem->write32(addr & ~3, (_u32(_rRt_) << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift])); + PCSX::g_emulator->m_mem->write32(addr & ~3, (_rRt_ << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift])); /* Mem = 1234. Reg = abcd @@ -1032,13 +1032,13 @@ void InterpretedCPU::psxSWR(uint32_t code) { void InterpretedCPU::psxMFC0(uint32_t code) { // load delay = 1 latency if (!_Rt_) return; - _i32(delayedLoadRef(_Rt_)) = (int)m_regs.CP0.r[_Rd_]; + delayedLoadRef(_Rt_) = (int)m_regs.CP0.r[_Rd_]; } void InterpretedCPU::psxCFC0(uint32_t code) { // load delay = 1 latency if (!_Rt_) return; - _i32(delayedLoadRef(_Rt_)) = (int)m_regs.CP0.r[_Rd_]; + delayedLoadRef(_Rt_) = (int)m_regs.CP0.r[_Rd_]; } void InterpretedCPU::psxTestSWInts() { @@ -1070,8 +1070,8 @@ inline void InterpretedCPU::MTC0(int reg, uint32_t val) { } } -void InterpretedCPU::psxMTC0(uint32_t code) { MTC0(_Rd_, _u32(_rRt_)); } -void InterpretedCPU::psxCTC0(uint32_t code) { MTC0(_Rd_, _u32(_rRt_)); } +void InterpretedCPU::psxMTC0(uint32_t code) { MTC0(_Rd_, _rRt_); } +void InterpretedCPU::psxCTC0(uint32_t code) { MTC0(_Rd_, _rRt_); } void InterpretedCPU::psxMFC2(uint32_t code) { // load delay = 1 latency @@ -1615,6 +1615,7 @@ inline void InterpretedCPU::execBlock() { m_regs.pc += 4; m_regs.cycle += PCSX::Emulator::BIAS; + if ((m_regs.pc & 0xffc00000) == 0xbfc00000) m_regs.cycle += PCSX::Emulator::BIAS * 10; cIntFunc_t func = s_pPsxBSC[code >> 26]; (*this.*func)(code); diff --git a/src/core/r3000a.cc b/src/core/r3000a.cc index 416f7b3a2..c8e8a3eed 100644 --- a/src/core/r3000a.cc +++ b/src/core/r3000a.cc @@ -327,73 +327,49 @@ void PCSX::R3000Acpu::restorePCdrvFile(const std::filesystem::path& filename, ui } void PCSX::R3000Acpu::branchTest() { -#if 0 - if( SPU_async ) - { - static int init; - int elapsed; - - if( init == 0 ) { - // 10 apu cycles - // - Final Fantasy Tactics (distorted - dropped sound effects) - m_regs.intCycle[PSXINT_SPUASYNC].cycle = g_emulator->m_psxClockSpeed / 44100 * 10; - - init = 1; - } - - elapsed = m_regs.cycle - m_regs.intCycle[PSXINT_SPUASYNC].sCycle; - if (elapsed >= m_regs.intCycle[PSXINT_SPUASYNC].cycle) { - SPU_async( elapsed ); - - m_regs.intCycle[PSXINT_SPUASYNC].sCycle = m_regs.cycle; - } - } -#endif - const uint64_t cycle = m_regs.cycle; if (cycle >= g_emulator->m_counters->m_psxNextCounter) g_emulator->m_counters->update(); if (m_regs.spuInterrupt.exchange(false)) g_emulator->m_spu->interrupt(); - const uint32_t interrupts = m_regs.interrupt; + const uint32_t scheduleMask = m_regs.scheduleMask; int32_t lowestDistance = std::numeric_limits::max(); uint64_t lowestTarget = cycle; - uint64_t* targets = m_regs.intTargets; - - if ((interrupts != 0) && (m_regs.lowestTarget < cycle)) { -#define checkAndUpdate(irq, act) \ - { \ - constexpr uint32_t mask = 1 << irq; \ - if ((interrupts & mask) != 0) { \ - uint64_t target = targets[irq]; \ - int64_t dist = target - cycle; \ - if (dist > 0) { \ - if (lowestDistance > dist) { \ - lowestDistance = dist; \ - lowestTarget = target; \ - } \ - } else { \ - m_regs.interrupt &= ~mask; \ - PSXIRQ_LOG("Triggering interrupt %08x\n", magic_enum::enum_integer(irq)); \ - act(); \ - } \ - } \ + uint64_t* targets = m_regs.scheduleTargets; + + if ((scheduleMask != 0) && (m_regs.lowestTarget < cycle)) { +#define checkAndUpdate(irq_, act) \ + { \ + constexpr unsigned irq = static_cast(irq_); \ + constexpr uint32_t mask = 1 << irq; \ + if ((scheduleMask & mask) != 0) { \ + uint64_t target = targets[irq]; \ + int64_t dist = target - cycle; \ + if (dist > 0) { \ + if (lowestDistance > dist) { \ + lowestDistance = dist; \ + lowestTarget = target; \ + } \ + } else { \ + m_regs.scheduleMask &= ~mask; \ + PSXIRQ_LOG("Calling scheduled callback %08x\n", irq); \ + act(); \ + } \ + } \ } - checkAndUpdate(PSXINT_SIO, g_emulator->m_sio->interrupt); - checkAndUpdate(PSXINT_SIO1, g_emulator->m_sio1->interrupt); - checkAndUpdate(PSXINT_CDR, g_emulator->m_cdrom->interrupt); - checkAndUpdate(PSXINT_CDREAD, g_emulator->m_cdrom->readInterrupt); - checkAndUpdate(PSXINT_GPUDMA, GPU::gpuInterrupt); - checkAndUpdate(PSXINT_MDECOUTDMA, g_emulator->m_mdec->mdec1Interrupt); - checkAndUpdate(PSXINT_SPUDMA, spuInterrupt); - checkAndUpdate(PSXINT_MDECINDMA, g_emulator->m_mdec->mdec0Interrupt); - checkAndUpdate(PSXINT_GPUOTCDMA, gpuotcInterrupt); - checkAndUpdate(PSXINT_CDRDMA, g_emulator->m_cdrom->dmaInterrupt); - checkAndUpdate(PSXINT_CDRPLAY, g_emulator->m_cdrom->playInterrupt); - checkAndUpdate(PSXINT_CDRDBUF, g_emulator->m_cdrom->decodedBufferInterrupt); - checkAndUpdate(PSXINT_CDRLID, g_emulator->m_cdrom->lidSeekInterrupt); + checkAndUpdate(Schedule::SIO, g_emulator->m_sio->scheduledCallback); + checkAndUpdate(Schedule::SIO1, g_emulator->m_sio1->scheduledCallback); + checkAndUpdate(Schedule::CDRFIFO, g_emulator->m_cdrom->fifoScheduledCallback); + checkAndUpdate(Schedule::CDRCOMMANDS, g_emulator->m_cdrom->commandsScheduledCallback); + checkAndUpdate(Schedule::CDREAD, g_emulator->m_cdrom->readScheduledCallback); + checkAndUpdate(Schedule::GPUDMA, GPU::gpuInterrupt); + checkAndUpdate(Schedule::MDECOUTDMA, g_emulator->m_mdec->scheduledCallback1); + checkAndUpdate(Schedule::SPUDMA, spuInterrupt); + checkAndUpdate(Schedule::MDECINDMA, g_emulator->m_mdec->scheduledCallback0); + checkAndUpdate(Schedule::GPUOTCDMA, gpuotcInterrupt); + checkAndUpdate(Schedule::CDRDMA, g_emulator->m_cdrom->scheduledDmaCallback); m_regs.lowestTarget = lowestTarget; } auto& mem = g_emulator->m_mem; diff --git a/src/core/r3000a.h b/src/core/r3000a.h index 3bb1cef15..644d281e2 100644 --- a/src/core/r3000a.h +++ b/src/core/r3000a.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -174,22 +175,18 @@ typedef union { PAIR p[32]; } psxCP2Ctrl; -enum { - PSXINT_SIO = 0, - PSXINT_SIO1, - PSXINT_CDR, - PSXINT_CDREAD, - PSXINT_GPUDMA, - PSXINT_MDECOUTDMA, - PSXINT_SPUDMA, - PSXINT_GPUBUSY, - PSXINT_MDECINDMA, - PSXINT_GPUOTCDMA, - PSXINT_CDRDMA, - PSXINT_SPUASYNC, - PSXINT_CDRDBUF, - PSXINT_CDRLID, - PSXINT_CDRPLAY +enum class Schedule : unsigned { + SIO = 0, + SIO1, + CDRFIFO, + CDRCOMMANDS, + CDREAD, + GPUDMA, + MDECOUTDMA, + SPUDMA, + MDECINDMA, + GPUOTCDMA, + CDRDMA, }; struct psxRegisters { @@ -201,30 +198,21 @@ struct psxRegisters { uint32_t code; // The current instruction uint64_t cycle; uint64_t previousCycles; - uint32_t interrupt; + uint32_t scheduleMask; std::atomic spuInterrupt; - uint64_t intTargets[32]; + uint64_t scheduleTargets[32]; uint64_t lowestTarget; uint8_t iCacheAddr[0x1000]; uint8_t iCacheCode[0x1000]; + uint32_t getFutureCycle(std::chrono::nanoseconds delay) const { return cycle + durationToCycles(delay); } + std::chrono::nanoseconds getFutureTime(uint32_t futureCycle) const { + return std::chrono::nanoseconds(int32_t(futureCycle - cycle) * 1'000'000'000 / Emulator::m_psxClockSpeed); + } + static constexpr uint32_t durationToCycles(std::chrono::nanoseconds duration) { + return duration.count() * Emulator::m_psxClockSpeed / 1'000'000'000; + } }; -// U64 and S64 are used to wrap long integer constants. -#define U64(val) val##ULL -#define S64(val) val##LL - -#if defined(__BIGENDIAN__) - -#define _i32(x) reinterpret_cast(&x)[0] -#define _u32(x) reinterpret_cast(&x)[0] - -#else - -#define _i32(x) reinterpret_cast(&x)[0] -#define _u32(x) reinterpret_cast(&x)[0] - -#endif - // R3000A Instruction Macros #define _PC_ PCSX::g_emulator->m_cpu->m_regs.pc // The next PC to be executed @@ -315,25 +303,37 @@ class R3000Acpu { void psxSetPGXPMode(uint32_t pgxpMode); - void scheduleInterrupt(unsigned interrupt, uint32_t eCycle) { - PSXIRQ_LOG("Scheduling interrupt %08x at %08x\n", interrupt, eCycle); + void schedule(Schedule s_, uint32_t eCycle) { + unsigned s = static_cast(s_); + PSXIRQ_LOG("Scheduling callback %08x at %08x\n", s, eCycle); const uint64_t cycle = m_regs.cycle; - uint64_t target = uint64_t(cycle + eCycle * m_interruptScales[interrupt]); - m_regs.interrupt |= (1 << interrupt); - m_regs.intTargets[interrupt] = target; + uint64_t target = uint64_t(cycle + eCycle * m_scheduleScales[s]); + m_regs.scheduleMask |= (1 << s); + m_regs.scheduleTargets[s] = target; int64_t lowest = m_regs.lowestTarget - cycle; int64_t maybeNewLowest = target - cycle; if (maybeNewLowest < lowest) m_regs.lowestTarget = target; } + void unschedule(Schedule s_) { + unsigned s = static_cast(s_); + PSXIRQ_LOG("Unscheduling callback %08x\n", s); + m_regs.scheduleMask &= ~(1 << s); + } + + bool isScheduled(Schedule s_) { + unsigned s = static_cast(s_); + return (m_regs.scheduleMask & (1 << s)) != 0; + } + psxRegisters m_regs; - float m_interruptScales[15] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + float m_scheduleScales[15] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; bool m_shellStarted = false; virtual void Reset() { invalidateCache(); - m_regs.interrupt = 0; + m_regs.scheduleMask = 0; } bool m_inISR = false; bool m_nextIsDelaySlot = false; @@ -348,13 +348,14 @@ class R3000Acpu { bool fromLink = false; } m_delayedLoadInfo[2]; unsigned m_currentDelayedLoad = 0; - uint32_t &delayedLoadRef(unsigned reg, uint32_t mask = 0) { + template + T &delayedLoadRef(unsigned reg, uint32_t mask = 0) { if (reg >= 32) abort(); auto &delayedLoad = m_delayedLoadInfo[m_currentDelayedLoad]; delayedLoad.active = true; delayedLoad.index = reg; delayedLoad.mask = mask; - return delayedLoad.value; + return reinterpret_cast(delayedLoad.value); } void delayedLoad(unsigned reg, uint32_t value, uint32_t mask = 0) { auto &ref = delayedLoadRef(reg, mask); diff --git a/src/core/sio.cc b/src/core/sio.cc index d899dc6f8..7f60b3d9c 100644 --- a/src/core/sio.cc +++ b/src/core/sio.cc @@ -307,7 +307,7 @@ void PCSX::SIO::writeCtrl16(uint16_t value) { m_bufferIndex = 0; m_regs.status = StatusFlags::TX_DATACLEAR | StatusFlags::TX_FINISHED; g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); - PCSX::g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_SIO); + g_emulator->m_cpu->unschedule(Schedule::SIO); m_currentDevice = DeviceType::None; } @@ -348,7 +348,7 @@ uint16_t PCSX::SIO::readStatus16() { return hard; } -void PCSX::SIO::interrupt() { +void PCSX::SIO::scheduledCallback() { SIO0_LOG("Sio Interrupt (CP0.Status = %x)\n", PCSX::g_emulator->m_cpu->m_regs.CP0.n.Status); m_regs.status |= StatusFlags::IRQ; g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); diff --git a/src/core/sio.h b/src/core/sio.h index 8071c4730..47dfc9bff 100644 --- a/src/core/sio.h +++ b/src/core/sio.h @@ -93,7 +93,7 @@ class SIO { void acknowledge(); void init(); - void interrupt(); + void scheduledCallback(); void reset(); bool copyMcdFile(McdBlock block); @@ -234,7 +234,7 @@ class SIO { bool isReceiveIRQReady(); bool isTransmitReady(); static inline void scheduleInterrupt(uint64_t eCycle) { - g_emulator->m_cpu->scheduleInterrupt(PSXINT_SIO, eCycle); + g_emulator->m_cpu->schedule(Schedule::SIO, eCycle); #if 0 // Breaks Twisted Metal 2 intro m_statusReg &= ~RX_FIFONOTEMPTY; diff --git a/src/core/sio1.cc b/src/core/sio1.cc index 509ab396e..fdc8af274 100644 --- a/src/core/sio1.cc +++ b/src/core/sio1.cc @@ -126,7 +126,7 @@ void PCSX::SIO1::sio1StateMachine() { } } -void PCSX::SIO1::interrupt() { +void PCSX::SIO1::scheduledCallback() { SIO1_LOG("SIO1 Interrupt (CP0.Status = %x)\n", PCSX::g_emulator->m_cpu->m_regs.CP0.n.Status); g_emulator->m_mem->setIRQ(IRQ8_SIO); m_regs.status |= SR_IRQ; @@ -281,7 +281,7 @@ void PCSX::SIO1::writeCtrl16(uint16_t v) { m_sio1fifo.asA()->reset(); } - PCSX::g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_SIO1); + g_emulator->m_cpu->unschedule(Schedule::SIO1); } if (!(m_regs.control & CR_RXEN)) { diff --git a/src/core/sio1.h b/src/core/sio1.h index 0af0f707c..59616c64e 100644 --- a/src/core/sio1.h +++ b/src/core/sio1.h @@ -70,7 +70,7 @@ class SIO1 { enum class SIO1Mode { Raw, Protobuf }; SIO1Mode m_sio1Mode = SIO1Mode::Protobuf; SIO1Mode getSIO1Mode() { return m_sio1Mode; } - void interrupt(); + void scheduledCallback(); void poll() { if (fifoError()) return; @@ -93,7 +93,7 @@ class SIO1 { m_decodeState = READ_SIZE; messageSize = 0; initialMessage = true; - g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_SIO1); + g_emulator->m_cpu->unschedule(Schedule::SIO1); } void stopSIO1Connection() { @@ -247,7 +247,7 @@ class SIO1 { enum { READ_SIZE, READ_MESSAGE } m_decodeState = READ_SIZE; - inline void scheduleInterrupt(uint32_t eCycle) { g_emulator->m_cpu->scheduleInterrupt(PSXINT_SIO1, eCycle); } + inline void scheduleInterrupt(uint32_t eCycle) { g_emulator->m_cpu->schedule(Schedule::SIO1, eCycle); } void updateStat(); void transmitData(); diff --git a/src/core/sstate.cc b/src/core/sstate.cc index 27107c2a8..bd9e95f1a 100644 --- a/src/core/sstate.cc +++ b/src/core/sstate.cc @@ -31,68 +31,67 @@ #include "spu/interface.h" PCSX::SaveStates::SaveState PCSX::SaveStates::constructSaveState() { - // clang-format off - return SaveState { - SaveStateInfo { - VersionString {}, - Version {}, + return SaveState{ + SaveStateInfo{ + VersionString{}, + Version{}, }, - Thumbnail {}, - Memory { - RAM { g_emulator->m_mem->m_wram }, - ROM { g_emulator->m_mem->m_bios }, - EXP1 { g_emulator->m_mem->m_exp1 }, - HardwareMemory { g_emulator->m_mem->m_hard }, + Thumbnail{}, + Memory{ + RAM{g_emulator->m_mem->m_wram}, + ROM{g_emulator->m_mem->m_bios}, + EXP1{g_emulator->m_mem->m_exp1}, + HardwareMemory{g_emulator->m_mem->m_hard}, }, - Registers { - GPR { g_emulator->m_cpu->m_regs.GPR.r }, - CP0 { g_emulator->m_cpu->m_regs.CP0.r }, - CP2D { g_emulator->m_cpu->m_regs.CP2D.r }, - CP2C { g_emulator->m_cpu->m_regs.CP2C.r }, - PC { g_emulator->m_cpu->m_regs.pc }, - Code { g_emulator->m_cpu->m_regs.code }, - Cycle { g_emulator->m_cpu->m_regs.cycle }, - Interrupt { g_emulator->m_cpu->m_regs.interrupt }, - ICacheAddr { g_emulator->m_cpu->m_regs.iCacheAddr }, - ICacheCode { g_emulator->m_cpu->m_regs.iCacheCode }, - NextIsDelaySlot { g_emulator->m_cpu->m_nextIsDelaySlot }, - DelaySlotInfo1 { - DelaySlotIndex { g_emulator->m_cpu->m_delayedLoadInfo[0].index }, - DelaySlotValue { g_emulator->m_cpu->m_delayedLoadInfo[0].value }, - DelaySlotMask { g_emulator->m_cpu->m_delayedLoadInfo[0].mask }, - DelaySlotPcValue { g_emulator->m_cpu->m_delayedLoadInfo[0].pcValue }, - DelaySlotActive { g_emulator->m_cpu->m_delayedLoadInfo[0].active }, - DelaySlotPcActive { g_emulator->m_cpu->m_delayedLoadInfo[0].pcActive }, - DelaySlotFromLink { g_emulator->m_cpu->m_delayedLoadInfo[0].fromLink } + Registers{ + GPR{g_emulator->m_cpu->m_regs.GPR.r}, + CP0{g_emulator->m_cpu->m_regs.CP0.r}, + CP2D{g_emulator->m_cpu->m_regs.CP2D.r}, + CP2C{g_emulator->m_cpu->m_regs.CP2C.r}, + PC{g_emulator->m_cpu->m_regs.pc}, + Code{g_emulator->m_cpu->m_regs.code}, + Cycle{g_emulator->m_cpu->m_regs.cycle}, + ScheduleMask{g_emulator->m_cpu->m_regs.scheduleMask}, + ICacheAddr{g_emulator->m_cpu->m_regs.iCacheAddr}, + ICacheCode{g_emulator->m_cpu->m_regs.iCacheCode}, + NextIsDelaySlot{g_emulator->m_cpu->m_nextIsDelaySlot}, + DelaySlotInfo1{ + DelaySlotIndex{g_emulator->m_cpu->m_delayedLoadInfo[0].index}, + DelaySlotValue{g_emulator->m_cpu->m_delayedLoadInfo[0].value}, + DelaySlotMask{g_emulator->m_cpu->m_delayedLoadInfo[0].mask}, + DelaySlotPcValue{g_emulator->m_cpu->m_delayedLoadInfo[0].pcValue}, + DelaySlotActive{g_emulator->m_cpu->m_delayedLoadInfo[0].active}, + DelaySlotPcActive{g_emulator->m_cpu->m_delayedLoadInfo[0].pcActive}, + DelaySlotFromLink{g_emulator->m_cpu->m_delayedLoadInfo[0].fromLink}, }, - DelaySlotInfo2 { - DelaySlotIndex { g_emulator->m_cpu->m_delayedLoadInfo[1].index }, - DelaySlotValue { g_emulator->m_cpu->m_delayedLoadInfo[1].value }, - DelaySlotMask { g_emulator->m_cpu->m_delayedLoadInfo[1].mask }, - DelaySlotPcValue { g_emulator->m_cpu->m_delayedLoadInfo[1].pcValue }, - DelaySlotActive { g_emulator->m_cpu->m_delayedLoadInfo[1].active }, - DelaySlotPcActive { g_emulator->m_cpu->m_delayedLoadInfo[1].pcActive }, - DelaySlotFromLink { g_emulator->m_cpu->m_delayedLoadInfo[1].fromLink } + DelaySlotInfo2{ + DelaySlotIndex{g_emulator->m_cpu->m_delayedLoadInfo[1].index}, + DelaySlotValue{g_emulator->m_cpu->m_delayedLoadInfo[1].value}, + DelaySlotMask{g_emulator->m_cpu->m_delayedLoadInfo[1].mask}, + DelaySlotPcValue{g_emulator->m_cpu->m_delayedLoadInfo[1].pcValue}, + DelaySlotActive{g_emulator->m_cpu->m_delayedLoadInfo[1].active}, + DelaySlotPcActive{g_emulator->m_cpu->m_delayedLoadInfo[1].pcActive}, + DelaySlotFromLink{g_emulator->m_cpu->m_delayedLoadInfo[1].fromLink}, }, - CurrentDelayedLoad { g_emulator->m_cpu->m_currentDelayedLoad }, - IntTargetsField { g_emulator->m_cpu->m_regs.intTargets }, - InISR { g_emulator->m_cpu->m_inISR }, + CurrentDelayedLoad{g_emulator->m_cpu->m_currentDelayedLoad}, + ScheduleTargetsField{g_emulator->m_cpu->m_regs.scheduleTargets}, + InISR{g_emulator->m_cpu->m_inISR}, }, - GPU {}, - SPU {}, - SIO { - SIOBuffer { g_emulator->m_sio->m_buffer }, - SIOStatusReg { g_emulator->m_sio->m_regs.status }, - SIOModeReg { g_emulator->m_sio->m_regs.mode }, - SIOCtrlReg { g_emulator->m_sio->m_regs.control }, - SIOBaudReg { g_emulator->m_sio->m_regs.baud }, - SIOBufferMaxIndex { g_emulator->m_sio->m_maxBufferIndex }, - SIOBufferIndex { g_emulator->m_sio->m_bufferIndex }, - SIOPadState { g_emulator->m_sio->m_padState }, - SIOCurrentDevice { g_emulator->m_sio->m_currentDevice }, - SIOMCD1TempBuffer { g_emulator->m_sio->m_memoryCard[0].m_tempBuffer }, - SIOMCD1DirectoryFlag { g_emulator->m_sio->m_memoryCard[0].m_directoryFlag }, - SIOMCD1ChecksumIn{ g_emulator->m_sio->m_memoryCard[0].m_checksumIn}, + GPU{}, + SPU{}, + SIO{ + SIOBuffer{g_emulator->m_sio->m_buffer}, + SIOStatusReg{g_emulator->m_sio->m_regs.status}, + SIOModeReg{g_emulator->m_sio->m_regs.mode}, + SIOCtrlReg{g_emulator->m_sio->m_regs.control}, + SIOBaudReg{g_emulator->m_sio->m_regs.baud}, + SIOBufferMaxIndex{g_emulator->m_sio->m_maxBufferIndex}, + SIOBufferIndex{g_emulator->m_sio->m_bufferIndex}, + SIOPadState{g_emulator->m_sio->m_padState}, + SIOCurrentDevice{g_emulator->m_sio->m_currentDevice}, + SIOMCD1TempBuffer{g_emulator->m_sio->m_memoryCard[0].m_tempBuffer}, + SIOMCD1DirectoryFlag{g_emulator->m_sio->m_memoryCard[0].m_directoryFlag}, + SIOMCD1ChecksumIn{g_emulator->m_sio->m_memoryCard[0].m_checksumIn}, SIOMCD1ChecksumOut{g_emulator->m_sio->m_memoryCard[0].m_checksumOut}, SIOMCD1CommandTicks{g_emulator->m_sio->m_memoryCard[0].m_commandTicks}, SIOMCD1CurrentCommand{g_emulator->m_sio->m_memoryCard[0].m_currentCommand}, @@ -107,68 +106,15 @@ PCSX::SaveStates::SaveState PCSX::SaveStates::constructSaveState() { SIOMCD2Sector{g_emulator->m_sio->m_memoryCard[1].m_sector}, SIOMCD2DataOffset{g_emulator->m_sio->m_memoryCard[1].m_dataOffset}, }, - CDRom { - CDReg1Mode { g_emulator->m_cdrom->m_reg1Mode }, - CDReg2 { g_emulator->m_cdrom->m_reg2 }, - CDCmdProcess { g_emulator->m_cdrom->m_cmdProcess }, - CDCtrl { g_emulator->m_cdrom->m_ctrl }, - CDStat { g_emulator->m_cdrom->m_stat }, - CDStatP { g_emulator->m_cdrom->m_statP }, - CDTransfer { reinterpret_cast(g_emulator->m_cdrom->m_transfer) }, - CDTransferIndex { g_emulator->m_cdrom->m_transferIndex }, - CDPrev { g_emulator->m_cdrom->m_prev.data }, - CDParam { g_emulator->m_cdrom->m_param }, - CDResult { g_emulator->m_cdrom->m_result }, - CDParamC { g_emulator->m_cdrom->m_paramC }, - CDResultC { g_emulator->m_cdrom->m_resultC }, - CDResultP { g_emulator->m_cdrom->m_resultP }, - CDResultReady { g_emulator->m_cdrom->m_resultReady }, - CDCmd { g_emulator->m_cdrom->m_cmd }, - CDRead { g_emulator->m_cdrom->m_read }, - CDSetLocPending { g_emulator->m_cdrom->m_setlocPending }, - CDReading { g_emulator->m_cdrom->m_reading }, - CDSetSectorPlay { g_emulator->m_cdrom->m_setSectorPlay.data }, - CDSetSectorEnd { g_emulator->m_cdrom->m_setSectorEnd.data }, - CDSetSector { g_emulator->m_cdrom->m_setSector.data }, - CDTrack { g_emulator->m_cdrom->m_track }, - CDPlay { g_emulator->m_cdrom->m_play }, - CDMuted { g_emulator->m_cdrom->m_muted }, - CDCurTrack { g_emulator->m_cdrom->m_curTrack }, - CDMode { g_emulator->m_cdrom->m_mode }, - CDFile { g_emulator->m_cdrom->m_file }, - CDChannel { g_emulator->m_cdrom->m_channel }, - CDSuceeded { g_emulator->m_cdrom->m_suceeded }, - CDFirstSector { g_emulator->m_cdrom->m_firstSector }, - CDIRQ { g_emulator->m_cdrom->m_irq }, - CDIrqRepeated { g_emulator->m_cdrom->m_irqRepeated }, - CDECycle { g_emulator->m_cdrom->m_eCycle }, - CDSeeked { g_emulator->m_cdrom->m_seeked }, - CDReadRescheduled { g_emulator->m_cdrom->m_readRescheduled }, - CDDriveState { g_emulator->m_cdrom->m_driveState }, - CDFastForward { g_emulator->m_cdrom->m_fastForward }, - CDFastBackward { g_emulator->m_cdrom->m_fastBackward }, - CDAttenuatorLeftToLeft { g_emulator->m_cdrom->m_attenuatorLeftToLeft }, - CDAttenuatorLeftToRight { g_emulator->m_cdrom->m_attenuatorLeftToRight }, - CDAttenuatorRightToRight { g_emulator->m_cdrom->m_attenuatorRightToRight }, - CDAttenuatorRightToLeft { g_emulator->m_cdrom->m_attenuatorRightToLeft }, - CDAttenuatorLeftToLeftT { g_emulator->m_cdrom->m_attenuatorLeftToLeftT }, - CDAttenuatorLeftToRightT { g_emulator->m_cdrom->m_attenuatorLeftToRightT }, - CDAttenuatorRightToRightT { g_emulator->m_cdrom->m_attenuatorRightToRightT }, - CDAttenuatorRightToLeftT { g_emulator->m_cdrom->m_attenuatorRightToLeftT }, - CDSubQTrack { g_emulator->m_cdrom->m_subq.track }, - CDSubQIndex { g_emulator->m_cdrom->m_subq.index }, - CDSubQRelative { g_emulator->m_cdrom->m_subq.relative }, - CDSubQAbsolute { g_emulator->m_cdrom->m_subq.absolute }, - CDTrackChanged { g_emulator->m_cdrom->m_trackChanged }, - CDLocationChanged { g_emulator->m_cdrom->m_locationChanged }, + Hardware{}, + Counters{}, + MDEC{}, + PCdrvFilesField{}, + CallStacks{}, + CDRom{ + CDDataFIFO{g_emulator->m_cdrom->m_dataFIFO}, }, - Hardware {}, - Counters {}, - MDEC {}, - PCdrvFilesField {}, - CallStacks {}, }; - // clang-format on } namespace PCSX { @@ -302,13 +248,16 @@ bool PCSX::SaveStates::load(std::string_view data) { g_emulator->m_cpu->m_regs.pc &= ~3; g_emulator->m_gpu->deserialize(&wrapper); g_emulator->m_spu->load(state.get()); +#if 0 g_emulator->m_cdrom->load(); +#endif g_emulator->m_counters->deserialize(&wrapper); g_emulator->m_mdec->deserialize(&wrapper); auto& xa = state.get().get(); +#if 0 g_emulator->m_cdrom->m_xa.freq = xa.get().value; g_emulator->m_cdrom->m_xa.nbits = xa.get().value; g_emulator->m_cdrom->m_xa.nsamples = xa.get().value; @@ -321,6 +270,7 @@ bool PCSX::SaveStates::load(std::string_view data) { g_emulator->m_cdrom->m_xa.right.y1 = right.get().value; xa.get().copyTo(reinterpret_cast(g_emulator->m_cdrom->m_xa.pcm)); g_emulator->m_spu->playADPCMchannel(&g_emulator->m_cdrom->m_xa); +#endif g_emulator->m_cpu->closeAllPCdrvFiles(); for (auto& file : state.get().value) { diff --git a/src/core/sstate.h b/src/core/sstate.h index 317b51685..cb60e026d 100644 --- a/src/core/sstate.h +++ b/src/core/sstate.h @@ -56,7 +56,7 @@ typedef Protobuf::RepeatedFieldRef typedef Protobuf::FieldRef PC; typedef Protobuf::FieldRef Code; typedef Protobuf::FieldRef Cycle; -typedef Protobuf::FieldRef Interrupt; +typedef Protobuf::FieldRef ScheduleMask; // skip id 9 typedef Protobuf::FieldPtr, TYPESTRING("icache_addr"), 10> ICacheAddr; typedef Protobuf::FieldPtr, TYPESTRING("icache_code"), 11> ICacheCode; @@ -75,11 +75,11 @@ typedef Protobuf::Message DelaySlotInfo1; typedef Protobuf::MessageField DelaySlotInfo2; typedef Protobuf::FieldRef CurrentDelayedLoad; -typedef Protobuf::RepeatedFieldRef IntTargetsField; +typedef Protobuf::RepeatedFieldRef ScheduleTargetsField; typedef Protobuf::FieldRef InISR; -typedef Protobuf::Message + ScheduleTargetsField, InISR> Registers; typedef Protobuf::MessageField RegistersField; @@ -176,76 +176,7 @@ typedef Protobuf::Message SIO; typedef Protobuf::MessageField SIOField; - -// skip id 1 -typedef Protobuf::FieldRef CDReg1Mode; -typedef Protobuf::FieldRef CDReg2; -typedef Protobuf::FieldRef CDCmdProcess; -typedef Protobuf::FieldRef CDCtrl; -typedef Protobuf::FieldRef CDStat; -typedef Protobuf::FieldRef CDStatP; -typedef Protobuf::FieldPtr, TYPESTRING("transfer"), 8> CDTransfer; -typedef Protobuf::FieldRef CDTransferIndex; -typedef Protobuf::FieldPtr, TYPESTRING("prev"), 10> CDPrev; -typedef Protobuf::FieldPtr, TYPESTRING("param"), 11> CDParam; -typedef Protobuf::FieldPtr, TYPESTRING("result"), 12> CDResult; -typedef Protobuf::FieldRef CDParamC; -// skip id 14 -typedef Protobuf::FieldRef CDResultC; -typedef Protobuf::FieldRef CDResultP; -typedef Protobuf::FieldRef CDResultReady; -typedef Protobuf::FieldRef CDCmd; -typedef Protobuf::FieldRef CDRead; -typedef Protobuf::FieldRef CDSetLocPending; -typedef Protobuf::FieldRef CDReading; -// skip id 22 -// skip id 23 -typedef Protobuf::FieldPtr, TYPESTRING("set_sector_play"), 24> CDSetSectorPlay; -typedef Protobuf::FieldPtr, TYPESTRING("set_sector_end"), 25> CDSetSectorEnd; -typedef Protobuf::FieldPtr, TYPESTRING("set_sector"), 26> CDSetSector; -typedef Protobuf::FieldRef CDTrack; -typedef Protobuf::FieldRef CDPlay; -typedef Protobuf::FieldRef CDMuted; -typedef Protobuf::FieldRef CDCurTrack; -typedef Protobuf::FieldRef CDMode; -typedef Protobuf::FieldRef CDFile; -typedef Protobuf::FieldRef CDChannel; -typedef Protobuf::FieldRef CDSuceeded; -typedef Protobuf::FieldRef CDFirstSector; -typedef Protobuf::FieldRef CDIRQ; -typedef Protobuf::FieldRef CDIrqRepeated; -typedef Protobuf::FieldRef CDECycle; -typedef Protobuf::FieldRef CDSeeked; -typedef Protobuf::FieldRef CDReadRescheduled; -typedef Protobuf::FieldRef CDDriveState; -typedef Protobuf::FieldRef CDFastForward; -typedef Protobuf::FieldRef CDFastBackward; -typedef Protobuf::FieldRef CDAttenuatorLeftToLeft; -typedef Protobuf::FieldRef CDAttenuatorLeftToRight; -typedef Protobuf::FieldRef CDAttenuatorRightToRight; -typedef Protobuf::FieldRef CDAttenuatorRightToLeft; -typedef Protobuf::FieldRef CDAttenuatorLeftToLeftT; -typedef Protobuf::FieldRef CDAttenuatorLeftToRightT; -typedef Protobuf::FieldRef CDAttenuatorRightToRightT; -typedef Protobuf::FieldRef CDAttenuatorRightToLeftT; -typedef Protobuf::FieldRef CDSubQTrack; -typedef Protobuf::FieldRef CDSubQIndex; -typedef Protobuf::FieldPtr, TYPESTRING("subq_relative"), 54> CDSubQRelative; -typedef Protobuf::FieldPtr, TYPESTRING("subq_absolute"), 55> CDSubQAbsolute; -typedef Protobuf::FieldRef CDTrackChanged; -typedef Protobuf::FieldRef CDLocationChanged; - -typedef Protobuf::Message< - TYPESTRING("CDRom"), CDReg1Mode, CDReg2, CDCmdProcess, CDCtrl, CDStat, CDStatP, CDTransfer, CDTransferIndex, CDPrev, - CDParam, CDResult, CDParamC, CDResultC, CDResultP, CDResultReady, CDCmd, CDRead, CDSetLocPending, CDReading, - CDSetSectorPlay, CDSetSectorEnd, CDSetSector, CDTrack, CDPlay, CDMuted, CDCurTrack, CDMode, CDFile, CDChannel, - CDSuceeded, CDFirstSector, CDIRQ, CDIrqRepeated, CDECycle, CDSeeked, CDReadRescheduled, CDDriveState, CDFastForward, - CDFastBackward, CDAttenuatorLeftToLeft, CDAttenuatorLeftToRight, CDAttenuatorRightToRight, CDAttenuatorRightToLeft, - CDAttenuatorLeftToLeftT, CDAttenuatorLeftToRightT, CDAttenuatorRightToRightT, CDAttenuatorRightToLeftT, CDSubQTrack, - CDSubQIndex, CDSubQRelative, CDSubQAbsolute, CDTrackChanged, CDLocationChanged> - CDRom; -typedef Protobuf::MessageField CDRomField; - +// skip id 8 typedef Protobuf::EmptyMessage Hardware; typedef Protobuf::MessageField HardwareField; @@ -296,6 +227,7 @@ typedef Protobuf::Field CallSP; typedef Protobuf::Field CallFP; typedef Protobuf::Field Shadow; typedef Protobuf::Message Call; + typedef Protobuf::RepeatedVariableField Calls; typedef Protobuf::Field LowSP; typedef Protobuf::Field HighSP; @@ -304,14 +236,30 @@ typedef Protobuf::Field PresumedFP; typedef Protobuf::Field CallstackIsCurrent; typedef Protobuf::Message CallStack; + typedef Protobuf::RepeatedVariableField CallStacksMessageField; typedef Protobuf::Field CallStacksCurrentSP; typedef Protobuf::Message CallStacks; typedef Protobuf::MessageField CallStacksField; +typedef Protobuf::FieldPtr, TYPESTRING("dataFIFO"), 1> CDDataFIFO; +typedef Protobuf::FieldPtr, TYPESTRING("paramFIFO"), 2> CDParamFIFO; +typedef Protobuf::FieldPtr, TYPESTRING("responseFIFO"), 3> CDResponseFIFO; +typedef Protobuf::FieldRef CDDataFIFOIndex; +typedef Protobuf::FieldRef CDDataFIFOSize; +typedef Protobuf::FieldRef CDParamFIFOSize; +typedef Protobuf::FieldRef CDResponseFIFOData; +typedef Protobuf::FieldRef CDResponseFIFOSize; +typedef Protobuf::FieldRef CDRegisterIndex; +typedef Protobuf::FieldRef CDBusy; +typedef Protobuf::FieldRef CDState; +typedef Protobuf::FieldRef CDCommand; +typedef Protobuf::Message CDRom; +typedef Protobuf::MessageField CDRomField; + typedef Protobuf::Message + GPUField, SPUField, SIOField, HardwareField, CountersField, MDECField, PCdrvFilesField, + CallStacksField, CDRomField> SaveState; typedef Protobuf::ProtoFilem_cdrom->setIso(new CDRIso(new FailedFile)); - PCSX::g_emulator->m_cdrom->check(); + PCSX::g_emulator->m_cdrom->parseIso(); } if (ImGui::MenuItem(_("Load binary"))) { showOpenBinaryDialog = true; @@ -1192,16 +1192,13 @@ void PCSX::GUI::endFrame() { ImGui::Separator(); if (ImGui::MenuItem(_("Open LID"))) { - PCSX::g_emulator->m_cdrom->setLidOpenTime(-1); - PCSX::g_emulator->m_cdrom->lidInterrupt(); + PCSX::g_emulator->m_cdrom->openLid(); } if (ImGui::MenuItem(_("Close LID"))) { - PCSX::g_emulator->m_cdrom->setLidOpenTime(0); - PCSX::g_emulator->m_cdrom->lidInterrupt(); + PCSX::g_emulator->m_cdrom->closeLid(); } if (ImGui::MenuItem(_("Open and close LID"))) { - PCSX::g_emulator->m_cdrom->setLidOpenTime((int64_t)time(nullptr) + 2); - PCSX::g_emulator->m_cdrom->lidInterrupt(); + PCSX::g_emulator->m_cdrom->scheduleCloseLid(); } ImGui::Separator(); if (ImGui::MenuItem(_("Reboot"))) { @@ -1429,7 +1426,7 @@ in Configuration->Emulation, restart PCSX-Redux, then try again.)")); std::vector fileToOpen = m_openIsoFileDialog.selected(); if (!fileToOpen.empty()) { PCSX::g_emulator->m_cdrom->setIso(new CDRIso(reinterpret_cast(fileToOpen[0].c_str()))); - PCSX::g_emulator->m_cdrom->check(); + PCSX::g_emulator->m_cdrom->parseIso(); } } @@ -2252,18 +2249,17 @@ of the emulator to take effect.)"); void PCSX::GUI::interruptsScaler() { static const char* names[] = { - "SIO", "SIO1", "CDR", "CDR Read", "GPU DMA", "MDEC Out DMA", "SPU DMA", - "GPU Busy", "MDEC In DMA", "GPU OTC DMA", "CDR DMA", "SPU", "CDR Decoded Buffer", "CDR Lid Seek", - "CDR Play", + "SIO", "SIO1", "CDR FIFO", "CDR Command", "CDR Reads", "GPU DMA", + "MDEC Out DMA", "SPU DMA", "MDEC In DMA", "GPU OTC DMA", "CDR DMA", }; - if (ImGui::Begin(_("Interrupt Scaler"), &m_showInterruptsScaler)) { + if (ImGui::Begin(_("Scheduler Scaler"), &m_showInterruptsScaler)) { if (ImGui::Button(_("Reset all"))) { - for (auto& scale : g_emulator->m_cpu->m_interruptScales) { + for (auto& scale : g_emulator->m_cpu->m_scheduleScales) { scale = 1.0f; } } unsigned counter = 0; - for (auto& scale : g_emulator->m_cpu->m_interruptScales) { + for (auto& scale : g_emulator->m_cpu->m_scheduleScales) { ImGui::SliderFloat(names[counter], &scale, 0.0f, 100.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp); counter++; } @@ -2581,7 +2577,7 @@ void PCSX::GUI::magicOpen(const char* pathStr) { // Iso loader is last because its detection is the most broken at the moment. g_emulator->m_cdrom->setIso(new CDRIso(path)); - g_emulator->m_cdrom->check(); + g_emulator->m_cdrom->parseIso(); } bool PCSX::GUI::getSaveStateExists(uint32_t slot) { diff --git a/src/gui/widgets/isobrowser.cc b/src/gui/widgets/isobrowser.cc index 49d7a9fba..2ba372e9c 100644 --- a/src/gui/widgets/isobrowser.cc +++ b/src/gui/widgets/isobrowser.cc @@ -67,7 +67,7 @@ void PCSX::Widgets::IsoBrowser::draw(CDRom* cdrom, const char* title) { showOpenIsoFileDialog = ImGui::MenuItem(_("Open Disk Image")); if (ImGui::MenuItem(_("Close Disk Image"))) { g_emulator->m_cdrom->setIso(new CDRIso(new FailedFile)); - g_emulator->m_cdrom->check(); + g_emulator->m_cdrom->parseIso(); } ImGui::EndMenu(); } @@ -87,7 +87,7 @@ void PCSX::Widgets::IsoBrowser::draw(CDRom* cdrom, const char* title) { std::vector fileToOpen = m_openIsoFileDialog.selected(); if (!fileToOpen.empty()) { g_emulator->m_cdrom->setIso(new CDRIso(reinterpret_cast(fileToOpen[0].c_str()))); - g_emulator->m_cdrom->check(); + g_emulator->m_cdrom->parseIso(); } } auto iso = cdrom->m_iso.get(); diff --git a/src/gui/widgets/log.cc b/src/gui/widgets/log.cc index 17bd38d36..bcc80972b 100644 --- a/src/gui/widgets/log.cc +++ b/src/gui/widgets/log.cc @@ -107,6 +107,8 @@ bool PCSX::Widgets::Log::draw(GUI* gui, const char* title) { auto& debugSettings = g_emulator->settings.get(); changed |= ImGui::MenuItem(_("Log CD-ROM commands"), nullptr, &debugSettings.get().value); + changed |= ImGui::MenuItem(_("Log CD-ROM hardware access"), nullptr, + &debugSettings.get().value); changed |= ImGui::MenuItem(_("CPU trace"), nullptr, &debugSettings.get().value); changed |= ImGui::MenuItem(_("Skip ISR during CPU traces"), nullptr, diff --git a/src/gui/widgets/registers.cc b/src/gui/widgets/registers.cc index c8eadeb67..08c19428b 100644 --- a/src/gui/widgets/registers.cc +++ b/src/gui/widgets/registers.cc @@ -339,7 +339,7 @@ void PCSX::Widgets::Registers::draw(PCSX::GUI* gui, PCSX::psxRegisters* register ImGui::Text("pc : %08x", registers->pc); makeEditableRegister("pc", registers->pc); ImGui::Text("cycle: %08x", registers->cycle); - ImGui::Text("int : %08x", registers->interrupt); + ImGui::Text("sched: %08x", registers->scheduleMask); ImGui::Separator(); uint32_t istat = memory->readHardwareRegister(); uint32_t imask = memory->readHardwareRegister(); diff --git a/src/lua/fileffimeta.lua b/src/lua/fileffimeta.lua index a15046bdb..79ee646c0 100644 --- a/src/lua/fileffimeta.lua +++ b/src/lua/fileffimeta.lua @@ -55,6 +55,10 @@ local bufferMeta = { end elseif index == 'pbSlice' then return Support._internal.createPBSliceFromBuffer(buffer) + elseif index == 'cast' then + return function(buffer, ctype) + return ffi.cast(ctype, buffer.data) + end end error('Unknown index `' .. index .. '` for LuaBuffer') end, diff --git a/src/main/main.cc b/src/main/main.cc index 36591940d..c0f7b579d 100644 --- a/src/main/main.cc +++ b/src/main/main.cc @@ -329,7 +329,7 @@ int pcsxMain(int argc, char **argv) { if (isoToOpen.empty()) isoToOpen = args.get("loadiso", ""); if (isoToOpen.empty()) isoToOpen = args.get("disk", ""); if (!isoToOpen.empty()) emulator->m_cdrom->setIso(new PCSX::CDRIso(isoToOpen)); - emulator->m_cdrom->check(); + emulator->m_cdrom->parseIso(); // After settings are loaded, we're fine setting the SPU part of the emulation. emulator->m_spu->init(); diff --git a/src/mips/common.mk b/src/mips/common.mk index 3fb400c30..87945b649 100644 --- a/src/mips/common.mk +++ b/src/mips/common.mk @@ -47,9 +47,15 @@ LDFLAGS += $(ARCHFLAGS) -Wl,--oformat=$(FORMAT) CPPFLAGS_Release += -Os LDFLAGS_Release += -Os +CPPFLAGS_ReleaseFast += -O3 +LDFLAGS_ReleaseFast += -O3 + CPPFLAGS_LTO += -Os -flto -ffat-lto-objects LDFLAGS_LTO += -Os -flto -ffat-lto-objects +CPPFLAGS_LTOFast += -O3 -flto -ffat-lto-objects +LDFLAGS_LTOFast += -O3 -flto -ffat-lto-objects + CPPFLAGS_Debug += -O0 CPPFLAGS_SmallDebug += -Og CPPFLAGS_Coverage += -Og diff --git a/src/mips/tests/Makefile b/src/mips/tests/Makefile index eb86927a2..44e6e4da6 100644 --- a/src/mips/tests/Makefile +++ b/src/mips/tests/Makefile @@ -1,5 +1,6 @@ all: $(MAKE) -C basic all + $(MAKE) -C cdrom all $(MAKE) -C cpu all $(MAKE) -C cop0 all $(MAKE) -C dma all @@ -10,6 +11,7 @@ all: clean: $(MAKE) -C basic clean + $(MAKE) -C cdrom clean $(MAKE) -C cpu clean $(MAKE) -C cop0 clean $(MAKE) -C dma clean diff --git a/src/mips/tests/basic/Makefile b/src/mips/tests/basic/Makefile index 4d2f670bf..4c70dbe84 100644 --- a/src/mips/tests/basic/Makefile +++ b/src/mips/tests/basic/Makefile @@ -1,46 +1,8 @@ TARGET = basic -USE_FUNCTION_SECTIONS = false -TYPE = ps-exe -SRCS = \ -../uC-sdk-glue/BoardConsole.c \ -../uC-sdk-glue/BoardInit.c \ -../uC-sdk-glue/init.c \ -\ -../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ -../../../../third_party/uC-sdk/libc/src/errno.c \ -../../../../third_party/uC-sdk/libc/src/initfini.c \ -../../../../third_party/uC-sdk/libc/src/malloc.c \ -../../../../third_party/uC-sdk/libc/src/qsort.c \ -../../../../third_party/uC-sdk/libc/src/rand.c \ -../../../../third_party/uC-sdk/libc/src/reent.c \ -../../../../third_party/uC-sdk/libc/src/stdio.c \ -../../../../third_party/uC-sdk/libc/src/string.c \ -../../../../third_party/uC-sdk/libc/src/strto.c \ -../../../../third_party/uC-sdk/libc/src/unistd.c \ -../../../../third_party/uC-sdk/libc/src/xprintf.c \ -../../../../third_party/uC-sdk/libc/src/xscanf.c \ -../../../../third_party/uC-sdk/libc/src/yscanf.c \ -../../../../third_party/uC-sdk/os/src/devfs.c \ -../../../../third_party/uC-sdk/os/src/filesystem.c \ -../../../../third_party/uC-sdk/os/src/fio.c \ -../../../../third_party/uC-sdk/os/src/hash-djb2.c \ -../../../../third_party/uC-sdk/os/src/init.c \ -../../../../third_party/uC-sdk/os/src/osdebug.c \ -../../../../third_party/uC-sdk/os/src/romfs.c \ -../../../../third_party/uC-sdk/os/src/sbrk.c \ - - -CPPFLAGS = -DNOFLOATINGPOINT -CPPFLAGS += -I. -CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include -CPPFLAGS += -I../../../../third_party/uC-sdk/os/include -CPPFLAGS += -I../../../../third_party/libcester/include -CPPFLAGS += -I../../openbios/uC-sdk-glue +include ../common.mk SRCS += \ -../../common/syscalls/printf.s \ -../../common/crt0/uC-sdk-crt0.s \ basic.c \ include ../../common.mk diff --git a/src/mips/tests/cdrom/Makefile b/src/mips/tests/cdrom/Makefile new file mode 100644 index 000000000..eb20bdf34 --- /dev/null +++ b/src/mips/tests/cdrom/Makefile @@ -0,0 +1,8 @@ +TARGET = cdrom + +include ../common.mk + +SRCS += \ +cdrom.c \ + +include ../../common.mk diff --git a/src/mips/tests/cdrom/README.md b/src/mips/tests/cdrom/README.md new file mode 100644 index 000000000..680aac38b --- /dev/null +++ b/src/mips/tests/cdrom/README.md @@ -0,0 +1,25 @@ +# CDRom tests + +This directory contains tests for the CDRom controller of the PS1. They are assuming that there is a CD inserted in the drive, and the lid is closed. The tests require a somewhat specific iso to be mounted. The iso can be created using the `create-test-iso.lua` script. PCSX-Redux itself is the interpreter for this script. It also requires a copy of the [Unirom iso](https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1/releases/) to be present. + +The script can be run using the following command: + +```bash +pcsx-redux -cli -iso UNIROM_BOOTDISC.bin -dofile create-test-iso.lua +``` + +This will emit a `test.cue` file, and multiple corresponding tracks. The data track will contain Unirom itself, for potentially booting on a retail machine and be able to upload the tests to the machine using [`nops`](https://github.com/JonathanDotCel/NOTPSXSerial). + +The tests are written in C, and are compiled using the [MIPS GCC toolchain](../../psyqo/GETTING_STARTED.md#the-toolchain). The tests are compiled using the `make` command, and the resulting binary needs to be run on systems that have an ANSI console connected. + +One important caveat of the test framework is it'll allocate memory for failed tests, and the memory allocator is very rudimentary. This means that if too many tests fail, the system will run out of memory and crash. Check the file `cdrom.c` to disable portions of tests and work incrementally towards a working emulator. In order to further disable some tests, replace the corresponding `CESTER_TEST` keyword with `CESTER_SKIP_TEST`. + +The tests are checking two things: proper results from the CDRom controller, and approximate timings. The former are exact value checks, and will always reproduce properly on the real hardware. The latter are approximate value checks, and will usually only reproduce properly on the real hardware if the CD is inserted in the drive, the lid is closed, and the drive has been settled for a few seconds, but may still be flaky on the real hardware. + +The tests are currently being run on a 9001 machine for its real hardware routine checks, with occasional tests being run on a wider range of hardware. + +The code is commented to explain what is being tested, and why. The tests are also written to be as self-contained as possible, so that they can be easily copied and modified to test other things. + +All in all, these tests are overdoing it in terms of state and feature tests, and over aggressive. PS1 games most definitely do not need this level of accuracy, but this controller is so finicky that it is better to be safe than sorry. An emulator able to pass these tests ought to be able to run anything, as long as the main CPU timings are somewhat accurate. If any of these tests fail, it is likely that the emulator is not emulating the controller properly. If games are still failing despite the tests passing, this would mean there's a behavior not covered by the tests, and they shall be updated accordingly. + +A word on timings: the timings are measured using the hblank timer, which is usually pretty easy to get right in terms of accuracy. However, many games will have busy wait loop when talking to the controller, or race conditions between sending commands. This means that if the CPU runs too slow or too quickly, bugs will surface. If the emulator is passing the timing tests, but games are failing, it is likely that the CPU emulation speed accuracy is at fault here, not the controller's. diff --git a/src/mips/tests/cdrom/cdlgetlocl.c b/src/mips/tests/cdrom/cdlgetlocl.c new file mode 100644 index 000000000..8b4f2894a --- /dev/null +++ b/src/mips/tests/cdrom/cdlgetlocl.c @@ -0,0 +1,146 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlGetLocL, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCL; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Nothing of value is actually being read right after initialization, + // so there's no proper data in this response. There's somewhat of a + // bug in the controller, where doing a GetLocL right after a reset + // will actually work, whereas it shouldn't, as GetLocL is only supposed + // to work during a data transfer. + cester_assert_uint_eq(8, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocL, ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetLocLafterSeekP, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x50, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCL; + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Since we've done a seekP, without a read, we will actually + // error out here, unlike the previous test. + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x80, response[1]); + // Typical value seems to be around 750us, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic getlocL after seekP, errored in %ius\n", errorTime); +) + + +CESTER_TEST(cdlGetLocLafterSeekL, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x50, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCL; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Just like with the seekP, we will error out here, as + // there was no valid read performed. + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x80, response[1]); + // Typical value seems to be around 750us, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocL after seekL, ack in %ius\n", ackTime); +) diff --git a/src/mips/tests/cdrom/cdlgetlocp.c b/src/mips/tests/cdrom/cdlgetlocp.c new file mode 100644 index 000000000..21d2d3419 --- /dev/null +++ b/src/mips/tests/cdrom/cdlgetlocp.c @@ -0,0 +1,336 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlGetLocP, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + struct LocPResult response; + uint8_t responseSize = readResponse((uint8_t*)&response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Right after reset, the drive will over close to 00:02:00, so + // the GetLocP will return values around that. Track will always + // be 01, and the oscillation will be of roughly 4 frames. Index + // will be 00 or 01 depending if the head is over the pregap, + // before or equal to absolute 00:01:74. The absolute time will + // be around 00:02:00, and the relative time will be around + // 00:00:00. The pregap relative time counts down to 0, so + // for an absolute time of 00:01:74, the relative time will be + // 00:00:01. For an absolute time of 00:01:73, the relative time + // will be 00:00:02, and so on. + int inPregap = response.index == 0; + uint32_t relative = MSF2LBA(btoi(response.m), btoi(response.s), btoi(response.f)); + uint32_t absolute = MSF2LBA(btoi(response.am), btoi(response.as), btoi(response.af)); + uint32_t onefifty = absolute; + if (inPregap) { + onefifty = absolute + relative; + } else { + onefifty = absolute - relative; + } + cester_assert_uint_eq(1, response.track); + cester_assert_uint_ge(onefifty, 150); + cester_assert_uint_ge(response.index, 0); + cester_assert_uint_le(response.index, 1); + cester_assert_uint_ge(absolute, 145); + cester_assert_uint_lt(absolute, 155); + cester_assert_uint_lt(relative, 5); + cester_assert_uint_eq(8, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocP, ack in %ius\n", ackTime); + ramsyscall_printf("Full response: track: %02x, index: %02x, relative: %02x:%02x:%02x(%2i), absolute: %02x:%02x:%02x(%2i)\n", + response.track, response.index, + response.m, response.s, response.f, relative, + response.am, response.as, response.af, absolute + ); +) + +CESTER_TEST(cdlGetLocPafterSeekP, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x50, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + struct LocPResult response; + uint8_t responseSize = readResponse((uint8_t*)&response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // After a simple seekP, the head will hover around the seeked + // position, so the GetLocP will return values before that. Distance + // from the seeked position will be up to 50 frames around this location. + int inPregap = response.index == 0; + uint32_t relative = MSF2LBA(btoi(response.m), btoi(response.s), btoi(response.f)); + uint32_t absolute = MSF2LBA(btoi(response.am), btoi(response.as), btoi(response.af)); + uint32_t onefifty = absolute; + if (inPregap) { + onefifty = absolute + relative; + } else { + onefifty = absolute - relative; + } + cester_assert_uint_eq(1, response.track); + cester_assert_uint_ge(onefifty, 150); + cester_assert_uint_eq(0, inPregap); + cester_assert_uint_eq(1, response.index); + cester_assert_uint_ge(absolute, 224950); + cester_assert_uint_lt(absolute, 225000); + cester_assert_uint_ge(relative, 224800); + cester_assert_uint_lt(relative, 224850); + cester_assert_uint_eq(8, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocP after seekP, ack in %ius\n", ackTime); + ramsyscall_printf("Full response: track: %02x, index: %02x, relative: %02x:%02x:%02x(%2i), absolute: %02x:%02x:%02x(%2i)\n", + response.track, response.index, + response.m, response.s, response.f, relative, + response.am, response.as, response.af, absolute + ); +) + +CESTER_TEST(cdlGetLocPafterSeekL, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekLTo(0x50, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + struct LocPResult response; + uint8_t responseSize = readResponse((uint8_t*)&response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // After a simple seekL, the head will hover around the seeked + // position, so the GetLocP will return values around that. Distance + // from the seeked position will be up to 10 frames around this location, + // and sometimes a little bit after it too. It will be much more precise + // than seekP however, and most of the time, will be exactly at the + // seeked position. + int inPregap = response.index == 0; + uint32_t relative = MSF2LBA(btoi(response.m), btoi(response.s), btoi(response.f)); + uint32_t absolute = MSF2LBA(btoi(response.am), btoi(response.as), btoi(response.af)); + uint32_t onefifty = absolute; + if (inPregap) { + onefifty = absolute + relative; + } else { + onefifty = absolute - relative; + } + cester_assert_uint_eq(1, response.track); + cester_assert_uint_ge(onefifty, 150); + cester_assert_uint_eq(0, inPregap); + cester_assert_uint_eq(1, response.index); + cester_assert_uint_ge(absolute, 224990); + cester_assert_uint_le(absolute, 225005); + cester_assert_uint_ge(relative, 224840); + cester_assert_uint_le(relative, 224855); + cester_assert_uint_eq(8, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocP after seekL, ack in %ius\n", ackTime); + ramsyscall_printf("Full response: track: %02x, index: %02x, relative: %02x:%02x:%02x(%2i), absolute: %02x:%02x:%02x(%2i)\n", + response.track, response.index, + response.m, response.s, response.f, relative, + response.am, response.as, response.af, absolute + ); +) + + +CESTER_TEST(cdlGetLocPinT5, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x21, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + struct LocPResult response; + uint8_t responseSize = readResponse((uint8_t*)&response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Let's go inside of track 5, and see what we get. + int inPregap = response.index == 0; + uint32_t relative = MSF2LBA(btoi(response.m), btoi(response.s), btoi(response.f)); + uint32_t absolute = MSF2LBA(btoi(response.am), btoi(response.as), btoi(response.af)); + uint32_t start = absolute; + if (inPregap) { + start = absolute + relative; + } else { + start = absolute - relative; + } + cester_assert_uint_eq(5, response.track); + cester_assert_uint_eq(316500, start); + cester_assert_uint_eq(0, inPregap); + cester_assert_uint_eq(1, response.index); + cester_assert_uint_ge(absolute, 316535); + cester_assert_uint_lt(absolute, 316575); + cester_assert_uint_ge(relative, 35); + cester_assert_uint_lt(relative, 75); + cester_assert_uint_eq(8, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocP after seekP in track5's, ack in %ius\n", ackTime); + ramsyscall_printf("Full response: track: %02x, index: %02x, relative: %02x:%02x:%02x(%2i), absolute: %02x:%02x:%02x(%2i)\n", + response.track, response.index, + response.m, response.s, response.f, relative, + response.am, response.as, response.af, absolute + ); +) + +CESTER_TEST(cdlGetLocPinT5Pregap, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + struct LocPResult response; + uint8_t responseSize = readResponse((uint8_t*)&response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // This time, the head is in the pregap of track 5. + int inPregap = response.index == 0; + uint32_t relative = MSF2LBA(btoi(response.m), btoi(response.s), btoi(response.f)); + uint32_t absolute = MSF2LBA(btoi(response.am), btoi(response.as), btoi(response.af)); + uint32_t start = absolute; + if (inPregap) { + start = absolute + relative; + } else { + start = absolute - relative; + } + cester_assert_uint_eq(5, response.track); + cester_assert_uint_eq(316500, start); + cester_assert_uint_eq(1, inPregap); + cester_assert_uint_eq(0, response.index); + cester_assert_uint_ge(absolute, 316460); + cester_assert_uint_lt(absolute, 316500); + cester_assert_uint_lt(relative, 40); + cester_assert_uint_eq(8, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getlocP after seekP in track5's pregap, ack in %ius\n", ackTime); + ramsyscall_printf("Full response: track: %02x, index: %02x, relative: %02x:%02x:%02x(%2i), absolute: %02x:%02x:%02x(%2i)\n", + response.track, response.index, + response.m, response.s, response.f, relative, + response.am, response.as, response.af, absolute + ); +) diff --git a/src/mips/tests/cdrom/cdlgettd.c b/src/mips/tests/cdrom/cdlgettd.c new file mode 100644 index 000000000..3bf0bfdd2 --- /dev/null +++ b/src/mips/tests/cdrom/cdlgettd.c @@ -0,0 +1,583 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlGetTD0, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x71, response1[1]); + cester_assert_uint_eq(0x28, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 0: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD1, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 1; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0, response1[1]); + cester_assert_uint_eq(2, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 1: ack in %ius\n", ackTime); +) + + +CESTER_TEST(cdlGetTD2, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 2; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x70, response1[1]); + cester_assert_uint_eq(2, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 2: ack in %ius\n", ackTime); +) + + +CESTER_TEST(cdlGetTD3, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 3; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x70, response1[1]); + cester_assert_uint_eq(7, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 3: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD4, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 4; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x70, response1[1]); + cester_assert_uint_eq(0x14, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 4: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD5, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 5; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x70, response1[1]); + cester_assert_uint_eq(0x20, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 5: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD6, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 6; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x70, response1[1]); + cester_assert_uint_eq(0x24, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 6: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD12, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x12; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x70, response1[1]); + cester_assert_uint_eq(0x43, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 12: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD25, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x25; + CDROM_REG1 = CDL_GETTD; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0x71, response1[1]); + cester_assert_uint_eq(0x25, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTD 25: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTD26, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x26; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x10, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic getTD 26: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlGetTD99, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x99; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x10, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic getTD 99: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlGetTDaa, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0xaa; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x10, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic getTD aa: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlGetTD0a, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x0a; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x10, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic getTD 0a: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlGetTD1a, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x1a; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x10, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic getTD 1a: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlGetTDNoArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("No args getTD: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlGetTDTooManyArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_GETTD; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Too many args getTD: errored in %ius\n", errorTime); +) + diff --git a/src/mips/tests/cdrom/cdlgettn.c b/src/mips/tests/cdrom/cdlgettn.c new file mode 100644 index 000000000..d876f37b1 --- /dev/null +++ b/src/mips/tests/cdrom/cdlgettn.c @@ -0,0 +1,100 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlGetTN, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETTN; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, response1[1]); + cester_assert_uint_eq(0x25, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Typical value seems to be around 2ms, but varies a lot. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic getTN: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlGetTNWithArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_GETTN; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Typical value seems to be around 750us. + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Too many args getTN: errored in %ius\n", errorTime); +) diff --git a/src/mips/tests/cdrom/cdlid.c b/src/mips/tests/cdrom/cdlid.c new file mode 100644 index 000000000..f91fb8716 --- /dev/null +++ b/src/mips/tests/cdrom/cdlid.c @@ -0,0 +1,339 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlId, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETID; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(8, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic cdlId, ack in %ius, complete in %ius\n", ackTime, completeTime); + ramsyscall_printf("Full response: %02x %02x %02x %02x %02x %02x %02x %02x\n", + response2[0], response2[1], response2[2], response2[3], + response2[4], response2[5], response2[6], response2[7]); +) + +CESTER_TEST(cdlIdTooManyArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_GETID; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic cdlId with too many args, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlIdReads12BytesThenGetTN, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETID; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[12]; + for (unsigned i = 0; i < 12; i++) { + response2[i] = CDROM_REG1; + } + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETTN; + + waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl5 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x38, ctrl4); + cester_assert_uint_eq(0x18, ctrl5); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_eq(2, response3[0]); + cester_assert_uint_eq(1, response3[1]); + cester_assert_uint_eq(0x25, response3[2]); + cester_assert_uint_eq(3, responseSize3); + ramsyscall_printf("cdlId reading 12 bytes then cdlGetTN, ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlIdReads17BytesThenGetTN, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETID; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[17]; + for (unsigned i = 0; i < 17; i++) { + response2[i] = CDROM_REG1; + } + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETTN; + + waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl5 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x38, ctrl4); + cester_assert_uint_eq(0x18, ctrl5); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_eq(2, response3[0]); + cester_assert_uint_eq(1, response3[1]); + cester_assert_uint_eq(0x25, response3[2]); + cester_assert_uint_eq(3, responseSize3); + ramsyscall_printf("cdlId reading 17 bytes then cdlGetTN, ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlIdReadsWayTooMuchThencdlGetTN, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETID; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[32]; + for (unsigned i = 0; i < 32; i++) { + response2[i] = CDROM_REG1; + } + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETTN; + + waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t response3[32]; + for (unsigned i = 0; i < 32; i++) { + response3[i] = CDROM_REG1; + } + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x38, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + for (unsigned i = 0; i < 16; i++) { + cester_assert_uint_eq(response2[i], response2[i + 16]); + } + for (unsigned i = 8; i < 16; i++) { + cester_assert_uint_eq(0, response2[i]); + } + for (unsigned i = 0; i < 16; i++) { + cester_assert_uint_eq(response3[i], response3[i + 16]); + } + cester_assert_uint_eq(2, response3[0]); + cester_assert_uint_eq(1, response3[1]); + cester_assert_uint_eq(0x25, response3[2]); + for (unsigned i = 3; i < 16; i++) { + cester_assert_uint_eq(0, response3[i]); + } + ramsyscall_printf("cdlId reading way too much data, ack in %ius, complete in %ius\n", ackTime, completeTime); + ramsyscall_printf("Full response:\n%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + response2[0], response2[1], response2[2], response2[3], + response2[4], response2[5], response2[6], response2[7], + response2[8], response2[9], response2[10], response2[11], + response2[12], response2[13], response2[14], response2[15], + response2[16], response2[17], response2[18], response2[19], + response2[20], response2[21], response2[22], response2[23], + response2[24], response2[25], response2[26], response2[27], + response2[28], response2[29], response2[30], response2[31]); + ramsyscall_printf("cdlGetTN reading too much data, full response:\n%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + response3[0], response3[1], response3[2], response3[3], + response3[4], response3[5], response3[6], response3[7], + response3[8], response3[9], response3[10], response3[11], + response3[12], response3[13], response3[14], response3[15], + response3[16], response3[17], response3[18], response3[19], + response3[20], response3[21], response3[22], response3[23], + response3[24], response3[25], response3[26], response3[27], + response3[28], response3[29], response3[30], response3[31]); +) diff --git a/src/mips/tests/cdrom/cdlinit.c b/src/mips/tests/cdrom/cdlinit.c new file mode 100644 index 000000000..1d3a3daad --- /dev/null +++ b/src/mips/tests/cdrom/cdlinit.c @@ -0,0 +1,190 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlInit, test_instance, + initializeTime(); + + CDROM_REG0 = 1; + CDROM_REG3 = 0x1f; + CDROM_REG0 = 1; + CDROM_REG2 = 0x1f; + CDROM_REG0 = 0; + CDROM_REG1 = CDL_INIT; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + // Typical value seems to be around 2ms. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // These may be a bit flaky on real hardware, depending on the motors status when starting. + // Typical value seems to be around 120ms. + cester_assert_uint_ge(completeTime, 50000); + cester_assert_uint_lt(completeTime, 150000); + ramsyscall_printf("Basic initialization: CD-Rom controller initialized, ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlInitDelayed, test_instance, + initializeTime(); + + CDROM_REG0 = 1; + CDROM_REG3 = 0x1f; + CDROM_REG0 = 1; + CDROM_REG2 = 0x1f; + CDROM_REG0 = 0; + CDROM_REG1 = CDL_INIT; + + uint32_t ackTime = waitCDRomIRQ(); + + // We shouldn't get another IRQ until we acknowledge the previous one + // directly to the controller. But the initialization will continue + // in the background nonetheless. Wait 500ms, since the controller + // finishes its initialization in roughly 120ms. + uint32_t delayedTime; + do { + delayedTime = updateTime(); + } while (((IREG & IRQ_CDROM) == 0) && (delayedTime <= 500000)); + int gotIRQ = (IREG & IRQ_CDROM) != 0; + if (gotIRQ) IREG &= ~IRQ_CDROM; + + uint8_t cause1 = ackCDRomCause(); + CDROM_REG1; + uint8_t ctrl1 = CDROM_REG0 & ~3; + + + initializeTime(); + + uint32_t completeTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + CDROM_REG1; + uint8_t ctrl2 = CDROM_REG0 & ~3; + + + cester_assert_false(gotIRQ); + cester_assert_uint_ge(delayedTime, 500000); + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0x18, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // This still takes about 2ms. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // Since the initialization completes in the background of the controller + // waiting its ack, we're really only measuring the roundtrip of the + // communication between the CPU and the mechacon. It typically takes 350us + // to do this roundtrip. + cester_assert_uint_ge(completeTime, 100); + cester_assert_uint_lt(completeTime, 1000); + ramsyscall_printf("Delayed initialization: CD-Rom controller initialized, ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlInitWithArgs, test_instance, + initializeTime(); + + CDROM_REG0 = 1; + CDROM_REG3 = 0x1f; + CDROM_REG0 = 1; + CDROM_REG2 = 0x1f; + CDROM_REG0 = 0; + CDROM_REG2 = 0xff; + CDROM_REG2 = 0xff; + CDROM_REG2 = 0xff; + CDROM_REG2 = 0xff; + CDROM_REG1 = CDL_INIT; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + // Typical value seems to be around 1ms. + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + // Typical value seems to be around 1.5ms. + cester_assert_uint_ge(ackTime, 1000); + cester_assert_uint_lt(ackTime, 3500); + ramsyscall_printf("Initialization with args: CD-Rom controller errored, error in %ius\n", errorTime); + ramsyscall_printf("Initialization with args: requested status, ack in %ius\n", ackTime); +) + + diff --git a/src/mips/tests/cdrom/cdlnop.c b/src/mips/tests/cdrom/cdlnop.c new file mode 100644 index 000000000..21bb84c35 --- /dev/null +++ b/src/mips/tests/cdrom/cdlnop.c @@ -0,0 +1,129 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlNop, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic cdlNop, ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlNopTooManyArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_NOP; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic cdlNop with too many args, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlNopBusyTime, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + while (CDROM_REG0 & 0x80); + uint32_t busyTime = updateTime(); + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic cdlNop, ack in %ius, busy flag was set for %ius, actual processing time %ius\n", ackTime, busyTime, ackTime - busyTime); +) + diff --git a/src/mips/tests/cdrom/cdlreadn.c b/src/mips/tests/cdrom/cdlreadn.c new file mode 100644 index 000000000..bf6a0fa74 --- /dev/null +++ b/src/mips/tests/cdrom/cdlreadn.c @@ -0,0 +1,863 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlReadN1x, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + uint32_t size = 0; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + while ((CDROM_REG0 & 0x40) == 0); + while (((CDROM_REG0 & 0x40) != 0) && (size < 6)) { + sector[size++] = CDROM_REG2; + } + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Pausing at 1x is ~70ms + cester_assert_uint_ge(completeTime, 65000); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + cester_assert_uint_eq(6, size); + ramsyscall_printf("Basic single 6 bytes readN @1x at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadN1xwithDMA, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2048 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Pausing at 1x is ~70ms + cester_assert_uint_ge(completeTime, 65000); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + ramsyscall_printf("Basic single full sector readN @1x with DMA at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadN2x, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + uint32_t size = 0; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + while ((CDROM_REG0 & 0x40) == 0); + while (((CDROM_REG0 & 0x40) != 0) && (size < 6)) { + sector[size++] = CDROM_REG2; + } + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + cester_assert_uint_eq(6, size); + ramsyscall_printf("Basic single 6 bytes readN @2x at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadN2xwithDMA, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2048 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + ramsyscall_printf("Basic single full sector readN @2x with DMA at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadN2xwithDMAMode02, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0xa0); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2340]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2340 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(0x00, sector[0]); + cester_assert_uint_eq(0x02, sector[1]); + cester_assert_uint_eq(0x16, sector[2]); + cester_assert_uint_eq(0x02, sector[3]); + cester_assert_uint_eq(0x00, sector[4]); + cester_assert_uint_eq(0x00, sector[5]); + cester_assert_uint_eq(0x08, sector[6]); + cester_assert_uint_eq(0x00, sector[7]); + cester_assert_uint_eq(0x00, sector[8]); + cester_assert_uint_eq(0x00, sector[9]); + cester_assert_uint_eq(0x08, sector[10]); + cester_assert_uint_eq(0x00, sector[11]); + cester_assert_uint_eq(1, sector[12]); + cester_assert_uint_eq('C', sector[13]); + cester_assert_uint_eq('D', sector[14]); + cester_assert_uint_eq('0', sector[15]); + cester_assert_uint_eq('0', sector[16]); + cester_assert_uint_eq('1', sector[17]); + ramsyscall_printf("Basic single full sector readN @2x in 2340 mode with DMA at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadN2xRunaway, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + uint32_t size = 0; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + while ((CDROM_REG0 & 0x40) == 0); + while (((CDROM_REG0 & 0x40) != 0) && (size < 6)) { + sector[size++] = CDROM_REG2; + } + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + CDROM_REG0 = 0; + CDROM_REG3 = 0; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x78, ctrl5); + cester_assert_uint_eq(0x58, ctrl6); + cester_assert_uint_eq(0x78, ctrl7); + cester_assert_uint_eq(0x58, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + cester_assert_uint_eq(6, size); + ramsyscall_printf("Basic single 6 bytes readN @2x at 00:02:16, runaway read, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadNInAudio, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0x70, 0x21, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(4, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_ge(errorTime, 4000000); + ramsyscall_printf("Basic readN in audio track, ack in %ius, errored in %ius\n", ackTime, errorTime); +) + +CESTER_TEST(cdlReadNTooFar, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0x80, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(0x10, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_ge(errorTime, 600000); + ramsyscall_printf("Basic readN too far, ack in %ius, errored in %ius\n", ackTime, errorTime); +) diff --git a/src/mips/tests/cdrom/cdlreads.c b/src/mips/tests/cdrom/cdlreads.c new file mode 100644 index 000000000..cc7b3a8e5 --- /dev/null +++ b/src/mips/tests/cdrom/cdlreads.c @@ -0,0 +1,842 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlReadS1x, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + uint32_t size = 0; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + while ((CDROM_REG0 & 0x40) == 0); + while (((CDROM_REG0 & 0x40) != 0) && (size < 6)) { + sector[size++] = CDROM_REG2; + } + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Pausing at 1x is ~70ms + cester_assert_uint_ge(completeTime, 65000); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + cester_assert_uint_eq(6, size); + ramsyscall_printf("Basic single 6 bytes readS @1x at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadS1xwithDMA, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2048 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Pausing at 1x is ~70ms + cester_assert_uint_ge(completeTime, 65000); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + ramsyscall_printf("Basic single full sector readS @1x with DMA at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadS2x, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + uint32_t size = 0; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + while ((CDROM_REG0 & 0x40) == 0); + while (((CDROM_REG0 & 0x40) != 0) && (size < 6)) { + sector[size++] = CDROM_REG2; + } + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + cester_assert_uint_eq(6, size); + ramsyscall_printf("Basic single 6 bytes readS @2x at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadS2xwithDMA, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2048 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + ramsyscall_printf("Basic single full sector readS @2x with DMA at 00:02:16, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadS2xRunaway, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0, 2, 0x16); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t readyTime = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint8_t sector[2048]; + uint32_t size = 0; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + while ((CDROM_REG0 & 0x40) == 0); + while (((CDROM_REG0 & 0x40) != 0) && (size < 6)) { + sector[size++] = CDROM_REG2; + } + uint32_t readTime = updateTime(); + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime2; + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + CDROM_REG0 = 0; + CDROM_REG3 = 0; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x78, ctrl5); + cester_assert_uint_eq(0x58, ctrl6); + cester_assert_uint_eq(0x78, ctrl7); + cester_assert_uint_eq(0x58, ctrl8); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + // Switching speed is roughly 650ms for the speed up, + // and then this also contains the seeking time, and + // the time to read the first sector, which is 6.66ms + cester_assert_uint_ge(readyTime, 500000); + // Pausing at 2x is ~35ms + cester_assert_uint_ge(completeTime, 32500); + cester_assert_uint_eq(1, sector[0]); + cester_assert_uint_eq('C', sector[1]); + cester_assert_uint_eq('D', sector[2]); + cester_assert_uint_eq('0', sector[3]); + cester_assert_uint_eq('0', sector[4]); + cester_assert_uint_eq('1', sector[5]); + cester_assert_uint_eq(6, size); + ramsyscall_printf("Basic single 6 bytes readS @2x at 00:02:16, runaway read, ack1 in %ius, ready in %ius, read in %ius, ack2 in %ius, complete in %ius\n", ackTime1, readyTime, readTime, ackTime2, completeTime); +) + +CESTER_TEST(cdlReadSInAudio, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0x70, 0x21, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(4, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_ge(errorTime, 4000000); + ramsyscall_printf("Basic readS in audio track, ack in %ius, errored in %ius\n", ackTime, errorTime); +) + +CESTER_TEST(cdlReadSTooFar, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0x80, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(0x10, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_ge(errorTime, 600000); + ramsyscall_printf("Basic readS too far, ack in %ius, errored in %ius\n", ackTime, errorTime); +) + +CESTER_TEST(cdlReadS2xWithNop, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int setLocDone = setLoc(0x70, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READS; + uint32_t ackTime1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + + uint8_t ctrl2b = CDROM_REG0 & ~3; + + uint32_t ackTime2 = waitCDRomIRQ() - ackTime1; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint32_t readyTime = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + uint32_t ackTime3 = waitCDRomIRQ(); + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime3; + uint8_t cause5 = ackCDRomCause(); + uint8_t ctrl9 = CDROM_REG0 & ~3; + uint8_t response5[16]; + uint8_t responseSize5 = readResponse(response5); + uint8_t ctrl10 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause5b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(1, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(3, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(2, cause5); + cester_assert_uint_eq(0xe0, cause5b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x98, ctrl2b); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(0x38, ctrl9); + cester_assert_uint_eq(0x18, ctrl10); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(0x22, response4[0]); + cester_assert_uint_eq(1, responseSize4); + cester_assert_uint_eq(2, response5[0]); + cester_assert_uint_eq(1, responseSize5); + cester_assert_uint_ge(ackTime1, 500); + cester_assert_uint_lt(ackTime1, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + cester_assert_uint_ge(ackTime3, 500); + cester_assert_uint_lt(ackTime3, 7000); + cester_assert_uint_ge(readyTime, 500000); + cester_assert_uint_ge(completeTime, 32500); + ramsyscall_printf("Basic single full sector readS @2x with DMA at 70:00:00 with nop interleaved, ack1 in %ius, ack2 in %ius, ready in %ius, ack3 in %ius, complete in %ius\n", ackTime1, ackTime2, readyTime, ackTime3, completeTime); +) diff --git a/src/mips/tests/cdrom/cdlseekl.c b/src/mips/tests/cdrom/cdlseekl.c new file mode 100644 index 000000000..1315bf411 --- /dev/null +++ b/src/mips/tests/cdrom/cdlseekl.c @@ -0,0 +1,332 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlSeekL, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKL; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // Timings here won't really be relevant, as the CDRom's head will be hovering + // already around the right place, so it's mostly a no-op to seek there, but + // can vary a lot between 2ms and 500ms as the head is moving and re-aligning. + ramsyscall_printf("Basic seekL to 00:02:00: ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlSeekLwithArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SEEKL; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Too many args seekL, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSeekL2to4, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0, 4, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKL; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // It'd be difficult to put an interval check here, as the seek time will vary + // a lot depending on the status of the drive, but it should probably be + // at least 120ms, and more likely 170ms. + ramsyscall_printf("Basic seekL from 00:02:00 to 00:04:00: ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlSeekL2to71, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x71, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKL; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(4, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // The seekL won't be successful when targeting a sector past the end of the + // data track, but it should still take a while to complete, because it'll + // keep retrying reading data where there's none. Roughly 4s. + cester_assert_uint_ge(errorTime, 4000000); + ramsyscall_printf("Basic seekL from 00:02:00 to 71:00:00: ack in %ius, errored in %ius\n", ackTime, errorTime); +) + +CESTER_TEST(cdlSeekL2to85, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x85, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKL; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(16, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // The seekL won't be successful when targeting a sector past the end of the + // disc. The failure can be faster than the previous test, because it won't + // retry reading where there's clearly no information whatsoever. Will sometimes + // fail in roughly 650ms, which is the seek time plus some minor retry. + cester_assert_uint_ge(errorTime, 500000); + ramsyscall_printf("Basic seekL from 00:02:00 to 85:00:00: ack in %ius, errored in %ius\n", ackTime, errorTime); +) diff --git a/src/mips/tests/cdrom/cdlseekp.c b/src/mips/tests/cdrom/cdlseekp.c new file mode 100644 index 000000000..411607425 --- /dev/null +++ b/src/mips/tests/cdrom/cdlseekp.c @@ -0,0 +1,481 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlSeekP, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // Timings here won't really be relevant, as the CDRom's head will be hovering + // already around the right place, so it's mostly a no-op to seek there, but + // can vary a lot between 2ms and 500ms as the head is moving and re-aligning. + ramsyscall_printf("Basic seekP to 00:02:00: ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlSeekPwithArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0, 2, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SEEKP; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Too many args seekP, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSeekP2to4, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0, 4, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // It'd be difficult to put an interval check here, as the seek time will vary + // a lot depending on the status of the drive, but it should probably be + // at least 100ms, and more likely 150ms. + ramsyscall_printf("Basic seekP from 00:02:00 to 00:04:00: ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlSeekP2to71, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x71, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t completeTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // This is a pretty long distance seek, which will take at least a full second. + cester_assert_uint_ge(completeTime, 1000000); + ramsyscall_printf("Basic seekP from 00:02:00 to 71:00:00: ack in %ius, complete in %ius\n", ackTime, completeTime); +) + +CESTER_TEST(cdlSeekP2to85, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x85, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + uint32_t errorTime = waitCDRomIRQ() - ackTime; + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(16, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + // The seekP won't be successful when targeting a sector past the end of the + // disc. The failure can be faster than the previous test, because it won't + // retry reading where there's clearly no information whatsoever. Will sometimes + // fail in roughly 650ms, which is the seek time plus some minor retry. + cester_assert_uint_ge(errorTime, 600000); + ramsyscall_printf("Basic seekP from 00:02:00 to 85:00:00: ack in %ius, errored in %ius\n", ackTime, errorTime); +) + +CESTER_TEST(cdlSeekP2to85AndNop, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x85, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + + while (CDROM_REG0 & 0x80); + CDROM_REG1 = CDL_NOP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause1b = ackCDRomCause(); + uint8_t ctrl1b = CDROM_REG0 & ~3; + uint8_t response1b[16]; + uint8_t responseSize1b = readResponse(response1b); + uint8_t ctrl2b = CDROM_REG0 & ~3; + + initializeTime(); + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause1b); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response1b[0]); + cester_assert_uint_eq(1, responseSize1b); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(0x10, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_eq(0xb8, ctrl1); + cester_assert_uint_eq(0x38, ctrl1b); + cester_assert_uint_eq(0x98, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + cester_assert_uint_ge(errorTime, 600000); + ramsyscall_printf("Basic seekP with Nop from 00:02:00 to 85:00:00: ack in %ius, errored in %ius\n", ackTime, errorTime); +) + +CESTER_TEST(cdlSeekP2to85AndDelayedNop, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x85, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + + while (updateTime() < 50000); + CDROM_REG1 = CDL_NOP; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause1b = ackCDRomCause(); + uint8_t ctrl1b = CDROM_REG0 & ~3; + uint8_t response1b[16]; + uint8_t responseSize1b = readResponse(response1b); + uint8_t ctrl2b = CDROM_REG0 & ~3; + + initializeTime(); + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause1b); + cester_assert_uint_eq(5, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x42, response1b[0]); + cester_assert_uint_eq(1, responseSize1b); + cester_assert_uint_eq(6, response2[0]); + cester_assert_uint_eq(16, response2[1]); + cester_assert_uint_eq(2, responseSize2); + cester_assert_uint_eq(0xb8, ctrl1); + cester_assert_uint_eq(0x38, ctrl1b); + cester_assert_uint_eq(0x98, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_ge(errorTime, 600000); + ramsyscall_printf("Basic seekP with delayed Nop from 00:02:00 to 85:00:00: ack in %ius, errored in %ius\n", ackTime, errorTime); +) diff --git a/src/mips/tests/cdrom/cdlsetloc.c b/src/mips/tests/cdrom/cdlsetloc.c new file mode 100644 index 000000000..5e57c18c0 --- /dev/null +++ b/src/mips/tests/cdrom/cdlsetloc.c @@ -0,0 +1,295 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlSetLoc, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 2; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SETLOC; + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(2, response[0]); + cester_assert_uint_eq(1, responseSize); + // Typical value seems to be around 1ms, but has + // been seen to spike high from time to time. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic setloc to 00:02:00, ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlSetLocNoArgs, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SETLOC; + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x20, response[1]); + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Invalid setloc with no args, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSetLocMultiple, test_instances, + int resetDone = resetCDRom(); + uint8_t cause; + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 2; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SETLOC; + uint32_t time1 = waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) { + cester_assert_uint_eq(3, cause); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0x99; + CDROM_REG2 = 0x59; + CDROM_REG2 = 0x74; + CDROM_REG1 = CDL_SETLOC; + uint32_t time2 = waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) { + cester_assert_uint_eq(3, cause); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0x50; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SETLOC; + uint32_t time3 = waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) { + cester_assert_uint_eq(3, cause); + return; + } + + // Setloc is only changing an internal state. + // Its response time is very fast, and won't + // vary regardless of the location, but can + // still spike to 6ms from time to time. + cester_assert_uint_ge(time1, 500); + cester_assert_uint_lt(time1, 7000); + cester_assert_uint_ge(time2, 500); + cester_assert_uint_lt(time2, 7000); + cester_assert_uint_ge(time3, 500); + cester_assert_uint_lt(time3, 7000); + ramsyscall_printf("Multiple setloc to 00:02:00, complete in %ius, %ius, %ius\n", time1, time2, time3); +) + +CESTER_TEST(cdlSetLocInvalid1, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 2; + CDROM_REG2 = 0x2a; + CDROM_REG1 = CDL_SETLOC; + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x10, response[1]); + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Invalid setloc to 00:02:2a, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSetLocInvalid2, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 2; + CDROM_REG2 = 0x79; + CDROM_REG1 = CDL_SETLOC; + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x10, response[1]); + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Invalid setloc to 00:02:79, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSetLocTooManyArgs, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 2; + CDROM_REG2 = 0; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SETLOC; + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x20, response[1]); + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Invalid setloc with too many args, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSetLocTooManyArgsAndInvalid, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG2 = 0; + CDROM_REG2 = 2; + CDROM_REG2 = 0x79; + CDROM_REG2 = 0; + CDROM_REG1 = CDL_SETLOC; + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(3, response[0]); + cester_assert_uint_eq(0x20, response[1]); + cester_assert_uint_eq(2, responseSize); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Invalid setloc with too many invalid args, errored in %ius\n", errorTime); +) + diff --git a/src/mips/tests/cdrom/cdlsetmode.c b/src/mips/tests/cdrom/cdlsetmode.c new file mode 100644 index 000000000..399c7b042 --- /dev/null +++ b/src/mips/tests/cdrom/cdlsetmode.c @@ -0,0 +1,133 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlSetMode, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x80; + CDROM_REG1 = CDL_SETMODE; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Typical value seems to be around 2ms, but varies a lot. + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic setMode: ack in %ius\n", ackTime); +) + +CESTER_TEST(cdlSetModeWithoutArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SETMODE; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Typical value seems to be around 750us. + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("No args setMode: errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlSetModeWithTooManyArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x80; + CDROM_REG2 = 0x80; + CDROM_REG2 = 0x80; + CDROM_REG2 = 0x80; + CDROM_REG1 = CDL_SETMODE; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Typical value seems to be around 750us. + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Too many args setMode: errored in %ius\n", errorTime); +) diff --git a/src/mips/tests/cdrom/cdltest.c b/src/mips/tests/cdrom/cdltest.c new file mode 100644 index 000000000..7d0b7e080 --- /dev/null +++ b/src/mips/tests/cdrom/cdltest.c @@ -0,0 +1,161 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(cdlTestNoArg, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_TEST; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic cdlTest with no args, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlTest20, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x20; + CDROM_REG1 = CDL_TEST; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(4, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic cdlTest with arg = 0x20, ack in %ius, response = %02x %02x %02x %02x\n", ackTime, response1[0], response1[1], response1[2], response1[3]); +) + +CESTER_TEST(cdlTest20ExtraArgs, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0x20; + CDROM_REG2 = 0x00; + CDROM_REG1 = CDL_TEST; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Basic cdlTest with arg = 0x20, 0x00, errored in %ius\n", errorTime); +) + +CESTER_TEST(cdlTestff, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 0xff; + CDROM_REG1 = CDL_TEST; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x10, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Basic cdlTest with arg = 0xff, errored in %ius\n", ackTime); +) diff --git a/src/mips/tests/cdrom/cdrom.c b/src/mips/tests/cdrom/cdrom.c new file mode 100644 index 000000000..c7d994c71 --- /dev/null +++ b/src/mips/tests/cdrom/cdrom.c @@ -0,0 +1,65 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#include "common/hardware/cdrom.h" +#include "common/hardware/dma.h" +#include "common/hardware/hwregs.h" +#include "common/hardware/irq.h" +#include "common/hardware/pcsxhw.h" +#include "common/syscalls/syscalls.h" + +#undef unix +#define CESTER_NO_SIGNAL +#define CESTER_NO_TIME +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +#include "exotic/cester.h" + +#include "cester-hw.c" + +#if 1 +#include "cdlgetlocl.c" +#include "cdlgetlocp.c" +#include "cdlgettd.c" +#include "cdlgettn.c" +#include "cdlid.c" +#include "cdlinit.c" +#include "cdlnop.c" +#include "cdlreadn.c" +#include "cdlreads.c" +#include "cdlseekl.c" +#include "cdlseekp.c" +#include "cdlsetloc.c" +#include "cdlsetmode.c" +#include "cdltest.c" +#include "invalid.c" +#include "misc.c" +#include "playing.c" +#include "race.c" +#include "reading.c" +#else +#include "misc.c" +#endif diff --git a/src/mips/tests/cdrom/cester-hw.c b/src/mips/tests/cdrom/cester-hw.c new file mode 100644 index 000000000..4c5736597 --- /dev/null +++ b/src/mips/tests/cdrom/cester-hw.c @@ -0,0 +1,294 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// This file isn't to be compiled directly. It's to be included in every +// sub test .c file that wants to do hardware measurements. + +// clang-format off + +#include + +CESTER_BODY( + static void hexdump(const void* data_, unsigned size) { + const uint8_t* data = (const uint8_t*)data_; + char ascii[17]; + ascii[16] = 0; + for (unsigned i = 0; i < size; i++) { + if (i % 16 == 0) ramsyscall_printf("%08x |", i); + ramsyscall_printf("%02X ", data[i]); + ascii[i % 16] = data[i] >= ' ' && data[i] <= '~' ? data[i] : '.'; + unsigned j = i + 1; + if ((j % 8 == 0) || (j == size)) { + ramsyscall_printf(" "); + if (j % 16 == 0) { + ramsyscall_printf("| %s \n", ascii); + } else if (j == size) { + ascii[j % 16] = 0; + if (j % 16 <= 8) ramsyscall_printf(" "); + for (j %= 16; j < 16; j++) ramsyscall_printf(" "); + ramsyscall_printf("| %s \n", ascii); + } + } + } + } + + static int s_interruptsWereEnabled = 0; + static uint16_t s_oldMode = 0; + static uint32_t s_lastHSyncCounter = 0; + static uint32_t s_currentTime = 0; + static uint32_t s_oldIMASK = 0; + static const unsigned US_PER_HBLANK = 64; + + struct LocPResult { + uint8_t track, index, m, s, f, am, as, af; + uint8_t padding[8]; + }; + + static uint8_t btoi(uint8_t b) { + return (b >> 4) * 10 + (b & 0xf); + } + + static uint8_t itob(uint8_t i) { + return (i / 10) * 16 + (i % 10); + } + + static int isValidBCD(uint8_t b) { + return (b & 0xf) < 10 && (b >> 4) < 10; + } + + static uint32_t MSF2LBA(uint8_t m, uint8_t s, uint8_t f) { + return (m * 60 + s) * 75 + f; + } + + static uint8_t readResponse(uint8_t response[16]) { + uint8_t responseSize = 0; + while ((CDROM_REG0 & 0x20) && (responseSize < 16)) { + response[responseSize++] = CDROM_REG1; + } + return responseSize; + } + + static uint8_t discardResponse() { + uint8_t response[16]; + return readResponse(response); + } + + static inline void initializeTime() { + while (1) { + uint32_t init = COUNTERS[1].value; + uint32_t counter; + while ((counter = COUNTERS[1].value) == init); + if (counter != COUNTERS[1].value) continue; + s_lastHSyncCounter = counter; + break; + } + s_currentTime = 0; + } + + static inline uint32_t updateTime() { + uint32_t lastHSyncCounter = s_lastHSyncCounter; + uint32_t hsyncCounter; + while (1) { + hsyncCounter = COUNTERS[1].value; + if (hsyncCounter != COUNTERS[1].value) continue; + break; + } + if (hsyncCounter < lastHSyncCounter) { + hsyncCounter += 0x10000; + } + uint32_t currentTime = s_currentTime = s_currentTime + (hsyncCounter - lastHSyncCounter) * US_PER_HBLANK; + s_lastHSyncCounter = hsyncCounter; + return currentTime; + } + + static inline uint32_t waitCDRomIRQ() { + uint32_t time; + do { + time = updateTime(); + } while ((IREG & IRQ_CDROM) == 0); + IREG &= ~IRQ_CDROM; + return time; + } + + static inline int waitCDRomIRQWithTimeout(uint32_t* timeoutp) { + uint32_t time = updateTime(); + uint32_t timeout = *timeoutp + time; + do { + time = updateTime(); + } while (((IREG & IRQ_CDROM) == 0) && (time <= timeout)); + int ret = (IREG & IRQ_CDROM) != 0; + *timeoutp = time; + IREG &= ~IRQ_CDROM; + return ret; + } + + static inline uint8_t ackCDRomCause() { + CDROM_REG0 = 1; + uint8_t cause = CDROM_REG3_UC; + if (cause & 7) { + CDROM_REG0 = 1; + CDROM_REG3 = 7; + } + if (cause & 0x18) { + CDROM_REG0 = 1; + CDROM_REG3 = cause & 0x18; + } + return cause & 7; + } + + int setMode(uint8_t mode) { + uint8_t cause; + CDROM_REG0 = 0; + CDROM_REG2 = mode; + CDROM_REG1 = CDL_SETMODE; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + return 1; + } + + static inline int resetCDRom() { + uint8_t cause; + + CDROM_REG0 = 1; + CDROM_REG3 = 0x1f; + CDROM_REG0 = 1; + CDROM_REG2 = 0x1f; + CDROM_REG0 = 0; + CDROM_REG1 = CDL_INIT; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 2) return 0; + + initializeTime(); + // wait 10ms for things to settle + while (updateTime() < 10000); + return setMode(0); + } + + static int setLoc(uint8_t minute, uint8_t second, uint8_t frame) { + uint8_t cause; + + CDROM_REG0 = 0; + CDROM_REG2 = minute; + CDROM_REG2 = second; + CDROM_REG2 = frame; + CDROM_REG1 = CDL_SETLOC; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + + return 1; + } + + static int seekPTo(uint8_t minute, uint8_t second, uint8_t frame) { + uint8_t cause; + + CDROM_REG0 = 0; + CDROM_REG2 = minute; + CDROM_REG2 = second; + CDROM_REG2 = frame; + CDROM_REG1 = CDL_SETLOC; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 2) return 0; + return 1; + } + + static int seekLTo(uint8_t minute, uint8_t second, uint8_t frame) { + uint8_t cause; + + CDROM_REG0 = 0; + CDROM_REG2 = minute; + CDROM_REG2 = second; + CDROM_REG2 = frame; + CDROM_REG1 = CDL_SETLOC; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKL; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 3) return 0; + waitCDRomIRQ(); + cause = ackCDRomCause(); + CDROM_REG1; + if (cause != 2) return 0; + + return 1; + } + + uint8_t getCtrl() { + uint8_t cause; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t ctrl = CDROM_REG1; + + return ctrl; + } +) + +CESTER_BEFORE_ALL(cpu_tests, + s_interruptsWereEnabled = enterCriticalSection(); + s_oldMode = COUNTERS[1].mode; + COUNTERS[1].mode = 0x0100; + SBUS_DEV5_CTRL = 0x20943; + SBUS_COM_CTRL = 0x132c; + s_oldIMASK = IMASK; + IMASK = IRQ_CDROM; +) + +CESTER_AFTER_ALL(cpu_tests, + IMASK = s_oldIMASK; + COUNTERS[1].mode = s_oldMode; + if (s_interruptsWereEnabled) leaveCriticalSection(); +) diff --git a/src/mips/tests/cdrom/create-test-iso.lua b/src/mips/tests/cdrom/create-test-iso.lua new file mode 100644 index 000000000..9b5864fc9 --- /dev/null +++ b/src/mips/tests/cdrom/create-test-iso.lua @@ -0,0 +1,183 @@ +-- Copyright (C) 2022 PCSX-Redux authors +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the +-- Free Software Foundation, Inc., +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +-- This script creates a test ISO image for the CDROM unit tests. + +local ffi = require 'ffi' +local bit = require 'bit' +local uniromDisc = PCSX.getCurrentIso() +local uniromDiscReader = uniromDisc:createReader() +local uniromFile = uniromDiscReader:open 'UNIROM_B.EXE;1' +local licenseFile = uniromDisc:open(0, 2352 * 16, 'RAW') +local iso = PCSX.isoBuilder(Support.File.open('test.bin', 'TRUNCATE')) +iso:writeLicense(licenseFile) + +local b = Support.NewLuaBuffer(2352) +ffi.fill(b.data, 2352) +b:resize(2048) + +local pvd = Support.File.buffer() +pvd:writeAt(b, 0) +pvd:writeU8At(1, 0) +pvd:writeAt('CD001', 1) +pvd:writeU32At(1, 132) +pvd:writeU32At(17, 140) +pvd:writeU32At(18, 158) + +local pt = Support.File.buffer() +pt:writeAt(b, 0) +pt:writeU8At(1, 0) +pt:writeU8At(18, 2) +pt:writeU8At(1, 6) + +local root = Support.File.buffer() +root:writeAt(b, 0) +root:writeU8At(42, 0) +root:writeU32At(19, 2) +root:writeU32At(uniromFile:size(), 10) +root:writeU8At(9, 32) +root:writeAt('PSX.EXE;1', 33) + +pvd:read(b) +iso:writeSector(b) +pt:read(b) +iso:writeSector(b) +root:read(b) +iso:writeSector(b) + +local count = 19 +while not uniromFile:eof() do + uniromFile:read(b) + iso:writeSector(b) + count = count + 1 +end + +ffi.fill(b.data, 2048) +for i = count, 30 * 60 * 75 - 1 do + b[0] = bit.band(i, 0xff) + b[1] = bit.band(bit.rshift(i, 8), 0xff) + b[2] = bit.band(bit.rshift(i, 16), 0xff) + iso:writeSector(b) +end + +local function generateToneSample(frequency, sampleRate, t) + return math.sin(2 * math.pi * frequency * t / sampleRate) +end + +local xa = Support.NewLuaBuffer(2336) +ffi.fill(xa.data, 2336) +local e = PCSX.Adpcm.NewEncoder() +e:reset 'XA' +local samples = ffi.new('int16_t[?]', 224 * 18) +for t = 0, 224 * 18 - 1 do + samples[t] = 25000 * generateToneSample(504, 37800, t) +end +for i = 0, 18 - 1 do + e:processXABlock(samples + 224 * i, xa:cast 'uint8_t *' + 8 + 128 * i, 'XAFourBits', 1) +end +xa[0] = 0x01 +xa[1] = 0x00 +xa[2] = 0x64 +xa[3] = 0x00 +xa[4] = 0x01 +xa[5] = 0x00 +xa[6] = 0x64 +xa[7] = 0x00 +for i = 30 * 60 * 75, 40 * 60 * 75 - 1 do + if i % 16 == 0 then + iso:writeSector(xa, 'M2_RAW') + else + b[0] = bit.band(i, 0xff) + b[1] = bit.band(bit.rshift(i, 8), 0xff) + b[2] = bit.band(bit.rshift(i, 16), 0xff) + iso:writeSector(b) + end +end + +for i = 40 * 60 * 75, 70 * 60 * 75 - 1 do + b[0] = bit.band(i, 0xff) + b[1] = bit.band(bit.rshift(i, 8), 0xff) + b[2] = bit.band(bit.rshift(i, 16), 0xff) + iso:writeSector(b) +end + +b:resize(2352) +for i = 0, 588 - 1 do + local s = 25000 * generateToneSample(450, 44100, i) + b[i * 4 + 0] = bit.band(s, 0xff) + b[i * 4 + 1] = bit.band(bit.rshift(s, 8), 0xff) + b[i * 4 + 2] = b[i * 4 + 0] + b[i * 4 + 3] = b[i * 4 + 1] +end +local audioTrack +audioTrack = Support.File.open('test-t2.bin', 'TRUNCATE') +for i = 1, 75 * 5 do + audioTrack:write(b) +end +audioTrack = Support.File.open('test-t3.bin', 'TRUNCATE') +for i = 1, 75 * 5 + 15 do + audioTrack:write(b) +end +audioTrack = Support.File.open('test-t4.bin', 'TRUNCATE') +for i = 1, 75 * 5 + 15 do + audioTrack:write(b) +end +audioTrack = Support.File.open('test-t5.bin', 'TRUNCATE') +for i = 1, 75 * 5 + 15 do + audioTrack:write(b) +end +audioTrack = Support.File.open('test-t6.bin', 'TRUNCATE') +for i = 1, 75 * 5 + 15 do + audioTrack:write(b) +end + +local cue = Support.File.open('test.cue', 'TRUNCATE') +cue:write [[ +FILE "test.bin" BINARY + TRACK 01 MODE2/2352 + INDEX 01 00:00:00 +FILE "test-t2.bin" BINARY + TRACK 02 AUDIO + INDEX 01 00:00:00 +FILE "test-t3.bin" BINARY + TRACK 03 AUDIO + INDEX 01 00:00:00 +FILE "test-t4.bin" BINARY + TRACK 04 AUDIO + INDEX 00 00:00:00 + INDEX 01 00:02:00 +FILE "test-t5.bin" BINARY + TRACK 05 AUDIO + INDEX 00 00:00:00 + INDEX 01 00:02:45 +FILE "test-t6.bin" BINARY + TRACK 06 AUDIO + INDEX 00 00:00:00 + INDEX 01 00:02:00 +]] + +for i = 7, 25 do + audioTrack = Support.File.open(string.format('test-t%d.bin', i), 'TRUNCATE') + for j = 1, 75 * 5 + 15 do + audioTrack:write(b) + end + cue:write(string.format('FILE "test-t%d.bin" BINARY\n', i)) + cue:write(string.format(' TRACK %02d AUDIO\n', i)) + cue:write ' INDEX 01 00:02:00\n' +end + +PCSX.quit() diff --git a/src/mips/tests/cdrom/invalid.c b/src/mips/tests/cdrom/invalid.c new file mode 100644 index 000000000..aa8362396 --- /dev/null +++ b/src/mips/tests/cdrom/invalid.c @@ -0,0 +1,61 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(invalid0, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SYNC; + + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x40, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + // Typical value seems to be around 750us. + cester_assert_uint_ge(errorTime, 500); + cester_assert_uint_lt(errorTime, 7000); + ramsyscall_printf("Invalid command 0: errored in %ius\n", errorTime); +) diff --git a/src/mips/tests/cdrom/misc.c b/src/mips/tests/cdrom/misc.c new file mode 100644 index 000000000..11fbd7e80 --- /dev/null +++ b/src/mips/tests/cdrom/misc.c @@ -0,0 +1,90 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +// These tests cannot be easily automated. + +CESTER_SKIP_TEST(waitForDoor, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + ramsyscall_printf("Waiting for door to open...\n"); + + initializeTime(); + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x16, response[0]); + cester_assert_uint_eq(8, response[1]); + cester_assert_uint_eq(2, responseSize); +) + +CESTER_SKIP_TEST(doorOpenedOrMissingDisc, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + + initializeTime(); + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x11, response[0]); + cester_assert_uint_eq(0x80, response[1]); + cester_assert_uint_eq(2, responseSize); +) diff --git a/src/mips/tests/cdrom/playing.c b/src/mips/tests/cdrom/playing.c new file mode 100644 index 000000000..60384c5a7 --- /dev/null +++ b/src/mips/tests/cdrom/playing.c @@ -0,0 +1,225 @@ +/* + +MIT License + +Copyright (c) 2024 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(simplePlayingUntilEnd, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x00); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekPTo(0x71, 0x27, 0); + + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PLAY; + uint8_t response1[16]; + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t responseSize1 = readResponse(response1); + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(4, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + // Should finish in 1640ms to 1960ms. + cester_assert_uint_ge(time1, 1640000); + cester_assert_uint_lt(time1, 1960000); + ramsyscall_printf("Simple Playing until end, finished in %ius\n", time1); +) + +CESTER_TEST(simplePlayingUntilEndDoubleTime, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekPTo(0x71, 0x27, 0); + + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PLAY; + uint8_t response1[16]; + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t responseSize1 = readResponse(response1); + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(4, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + // Should finish in 820ms to 980ms. + cester_assert_uint_ge(time1, 820000); + cester_assert_uint_lt(time1, 980000); + ramsyscall_printf("Simple Playing until end, Double Time, finished in %ius\n", time1); +) + +CESTER_TEST(simplePlayingUntilEndWithReport, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x04); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekPTo(0x71, 0x26, 0); + + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PLAY; + uint8_t response[16]; + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + readResponse(response); + + uint8_t causes[32]; + uint8_t responseSizes[32]; + uint8_t responses[32 * 16]; + + __builtin_memset(causes, 0, sizeof(causes)); + __builtin_memset(responseSizes, 0, sizeof(responseSizes)); + + unsigned count; + + for (count = 0; count < 32; count++) { + waitCDRomIRQ(); + uint8_t cause = ackCDRomCause(); + causes[count] = cause; + responseSizes[count] = readResponse(responses + count * 16); + if (cause != 1) break; + } + + for (unsigned i = 0; i < count; i++) { + cester_assert_uint_eq(1, causes[i]); + cester_assert_uint_eq(8, responseSizes[i]); + } + cester_assert_uint_eq(4, causes[count]); + cester_assert_uint_eq(1, responseSizes[count]); + cester_assert_uint_eq(3, cause1); + cester_assert_uint_ge(count, 15); + cester_assert_uint_lt(count, 17); + if (count >= 15) { + unsigned offset = count - 15; + unsigned upCount = 0; + for (unsigned i = offset; i < count; i++) { + // stat + cester_assert_uint_eq(0x82, responses[i * 16 + 0]); + // track + cester_assert_uint_eq(0x25, responses[i * 16 + 1]); + // index + cester_assert_uint_eq(0x01, responses[i * 16 + 2]); + unsigned index = i - offset; + if ((index & 1) == 0) { + // absolute minute time + cester_assert_uint_eq(0x71, responses[i * 16 + 3]); + } else { + // relative minute time + cester_assert_uint_eq(0, responses[i * 16 + 3]); + } + static uint8_t seconds[15] = { 0x26, 0x81, 0x27, 0x81, 0x27, 0x82, 0x27, 0x82, 0x27, 0x82, 0x28, 0x82, 0x28, 0x83, 0x28 }; + cester_assert_uint_eq(seconds[index], responses[i * 16 + 4]); + static uint8_t frames[15] = { 0x60, 0x40, 0x00, 0x55, 0x20, 0x00, 0x40, 0x20, 0x60, 0x40, 0x00, 0x55, 0x20, 0x00, 0x40 }; + // This report might wobble a bit, but the above values ought to be close enough to our most likely values. + cester_assert_uint_ge(responses[i * 16 + 5], frames[index]); + cester_assert_uint_le(responses[i * 16 + 5], frames[index] + 1); + uint16_t peak = (responses[i * 16 + 7] << 8) | responses[i * 16 + 6]; + uint16_t flag = peak & 0x8000; + peak &= 0x7fff; + cester_assert_uint_eq(0x619b, peak); + if (flag == 0x8000) upCount++; + } + cester_assert_uint_ge(upCount, 6); + cester_assert_uint_le(upCount, 8); + } + ramsyscall_printf("Simple Playing until end with reports:\n"); + for (unsigned i = 0; i < count; i++) { + ramsyscall_printf(" - Response[%i] = %02x %02x %02x %02x %02x %02x %02x %02x\n", i, + responses[i * 16 + 0], responses[i * 16 + 1], responses[i * 16 + 2], responses[i * 16 + 3], + responses[i * 16 + 4], responses[i * 16 + 5], responses[i * 16 + 6], responses[i * 16 + 7]); + } +) diff --git a/src/mips/tests/cdrom/race.c b/src/mips/tests/cdrom/race.c new file mode 100644 index 000000000..63e384c07 --- /dev/null +++ b/src/mips/tests/cdrom/race.c @@ -0,0 +1,670 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(raceGetLocPAndNop, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + CDROM_REG1 = CDL_NOP; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t timeout = 150000; + int gotIRQ = waitCDRomIRQWithTimeout(&timeout); + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x98, ctrl0); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_false(gotIRQ); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("GetLocP followed by Nop, ack in %ius\n", ackTime); +) + +CESTER_TEST(raceNopAndGetLocP, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + CDROM_REG1 = CDL_GETLOCP; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t timeout = 150000; + int gotIRQ = waitCDRomIRQWithTimeout(&timeout); + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x98, ctrl0); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(5, response1[0]); + cester_assert_uint_eq(0, response1[1]); + cester_assert_uint_eq(8, responseSize1); + cester_assert_false(gotIRQ); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Nop followed by GetLocP, ack in %ius\n", ackTime); +) + +CESTER_TEST(raceNopAndGetTD1, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 1; + CDROM_REG1 = CDL_NOP; + CDROM_REG1 = CDL_GETTD; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t timeout = 150000; + int gotIRQ = waitCDRomIRQWithTimeout(&timeout); + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(0, response1[1]); + cester_assert_uint_eq(2, response1[2]); + cester_assert_uint_eq(3, responseSize1); + cester_assert_uint_eq(0x90, ctrl0); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_false(gotIRQ); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("Nop followed by GetTD1: ack in %ius\n", ackTime); +) + +CESTER_TEST(raceGetTD1AndNop, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG2 = 1; + CDROM_REG1 = CDL_GETTD; + CDROM_REG1 = CDL_NOP; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t timeout = 150000; + int gotIRQ = waitCDRomIRQWithTimeout(&timeout); + + cester_assert_uint_eq(5, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(3, response1[0]); + cester_assert_uint_eq(0x20, response1[1]); + cester_assert_uint_eq(2, responseSize1); + cester_assert_uint_eq(0x90, ctrl0); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_false(gotIRQ); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + ramsyscall_printf("GetTD1 followed by Nop: ack in %ius\n", ackTime); +) + +CESTER_TEST(raceSeekP2to85AckWaitAndNop, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x85, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKP; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + uint8_t ctrl2b = CDROM_REG0 & ~3; + + initializeTime(); + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + initializeTime(); + uint32_t errorTime = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(5, cause3); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(6, response3[0]); + cester_assert_uint_eq(0x10, response3[1]); + cester_assert_uint_eq(2, responseSize3); + cester_assert_uint_eq(0x98, ctrl0); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x98, ctrl2b); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_ge(ackTime, 500); + cester_assert_uint_lt(ackTime, 7000); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + ramsyscall_printf("SeekP from 00:02:00 to 85:00:00 with Nop in between: ack in %ius, ack2 in %ius, errored in %ius\n", ackTime, ackTime2, errorTime); +) + +CESTER_TEST(raceSeekL2to71WaitAckAndNop, test_instance, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0, 2, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + int setLocDone = setLoc(0x71, 0, 0); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + // wait 50ms for things to settle + while (updateTime() < 50000); + initializeTime(); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_SEEKL; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + while (CDROM_REG0 & 0x80); + + CDROM_REG1 = CDL_NOP; + uint8_t ctrl0b = CDROM_REG0 & ~3; + + initializeTime(); + // Wait 2s... + ramsyscall_printf("Waiting 2s...\n"); + while (updateTime() < 2000000); + ramsyscall_printf("Done...\n"); + + initializeTime(); + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + uint32_t timeout = 1500000; + int gotIRQ = waitCDRomIRQWithTimeout(&timeout); + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x42, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x98, ctrl0); + cester_assert_uint_eq(0xb8, ctrl0b); + cester_assert_uint_eq(0xb8, ctrl1); + cester_assert_uint_eq(0x98, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_false(gotIRQ); + // This one really should be 0, but just in case. + cester_assert_uint_lt(ackTime, 150); + cester_assert_uint_ge(ackTime2, 500); + cester_assert_uint_lt(ackTime2, 7000); + ramsyscall_printf("SeekL from 00:02:00 to 71:00:00 then stall with Nop: ack in %ius, ack2 in %ius\n", ackTime, ackTime2); +) + +CESTER_TEST(raceNopWaitAndNop, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + uint8_t ctrl0 = CDROM_REG0 & ~3; + + while (updateTime() < 50000); + uint8_t ctrl0b = CDROM_REG0 & ~3; + CDROM_REG1 = CDL_NOP; + + initializeTime(); + uint32_t ackTime = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t ackTime2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(0x98, ctrl0); + cester_assert_uint_eq(0x38, ctrl0b); + cester_assert_uint_eq(0xb8, ctrl1); + cester_assert_uint_eq(0x98, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(2, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + // This one really ought to be 0, but let's give some slack anyway. + cester_assert_uint_lt(ackTime, 150); + // The second ack's timing will be affected by the first ack's timing, + // so we can't really test it. + ramsyscall_printf("Nop followed by Nop, ack in %ius, ack2 in %ius\n", ackTime, ackTime2); +) + +CESTER_TEST(nopQueue, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + CDROM_REG0 = 0; + for (unsigned i = 0; i < 4; i++) { + initializeTime(); + CDROM_REG1 = CDL_NOP; + while (updateTime() < 50000); + } + + int gotIRQ = 0; + unsigned count = 0; + do { + initializeTime(); + uint32_t timeout = 50000; + gotIRQ = waitCDRomIRQWithTimeout(&timeout); + ackCDRomCause(); + uint8_t response[16]; + uint8_t responseSize = readResponse(response); + } while (gotIRQ && ++count); + + cester_assert_uint_eq(2, count); + ramsyscall_printf("Nop command queue of %i\n", count); +) + +CESTER_TEST(nopQueueBusy, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + uint8_t ctrl[2]; + CDROM_REG0 = 0; + for (unsigned i = 0; i < 2; i++) { + initializeTime(); + CDROM_REG1 = CDL_NOP; + while (updateTime() < 50000); + ctrl[i] = CDROM_REG0 & ~3; + } + + for (unsigned i = 0; i < 2; i++) { + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + } + + cester_assert_uint_eq(0x38, ctrl[0]); + cester_assert_uint_eq(0xb8, ctrl[1]); +) + +CESTER_TEST(readResponseBeforeAck, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + + while (CDROM_REG0 & 0x80); + CDROM_REG1 = CDL_NOP; + + while (updateTime() < 50000); + + waitCDRomIRQ(); + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t cause1 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + waitCDRomIRQ(); + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t cause2 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, response1[0]); + cester_assert_uint_eq(8, responseSize1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + +) + +CESTER_TEST(ackBeforeReadResponse, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + + while (CDROM_REG0 & 0x80); + CDROM_REG1 = CDL_NOP; + + while (updateTime() < 50000); + + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, response1[0]); + cester_assert_uint_eq(8, responseSize1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + +) + +CESTER_TEST(mixedReadAndAck1, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + + while (CDROM_REG0 & 0x80); + CDROM_REG1 = CDL_NOP; + + while (updateTime() < 50000); + + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + waitCDRomIRQ(); + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t cause2 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, response1[0]); + cester_assert_uint_eq(8, responseSize1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + +) + +CESTER_TEST(mixedReadAndAck2, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int seekDone = seekPTo(0x70, 0x19, 0x73); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_GETLOCP; + + while (CDROM_REG0 & 0x80); + CDROM_REG1 = CDL_NOP; + + while (updateTime() < 50000); + + waitCDRomIRQ(); + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t cause1 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(5, response1[0]); + cester_assert_uint_eq(8, responseSize1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); +) diff --git a/src/mips/tests/cdrom/reading.c b/src/mips/tests/cdrom/reading.c new file mode 100644 index 000000000..63eee8056 --- /dev/null +++ b/src/mips/tests/cdrom/reading.c @@ -0,0 +1,869 @@ +/* + +MIT License + +Copyright (c) 2022 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +// clang-format off + +CESTER_TEST(simpleReading, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 2, 0); + + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint8_t response[16]; + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + readResponse(response); + + uint32_t sectorData[100]; + uint8_t causes[100]; + + __builtin_memset(sectorData, 0, sizeof(sectorData)); + __builtin_memset(causes, 0, sizeof(causes)); + + for (unsigned i = 0; i < 100; i++) { + waitCDRomIRQ(); + uint8_t cause = ackCDRomCause(); + causes[i] = cause; + readResponse(response); + if (cause != 1) break; + + uint8_t sector[2048]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2048 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + + uint32_t *sector32 = (uint32_t *)sector; + sectorData[i] = sector32[0]; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + readResponse(response); + waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + readResponse(response); + + uint32_t start = 20 * 60 * 75; + for (unsigned i = 0; i < 100; i++) { + cester_assert_uint_eq(start + i, sectorData[i]); + cester_assert_uint_eq(1, causes[i]); + } + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(2, cause3); +) + +CESTER_TEST(setLocDuringSimpleReading, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 2, 0); + int setLocDone = 0; + + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint8_t response[16]; + waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + readResponse(response); + + uint32_t sectorData[100]; + uint8_t causes[100]; + + __builtin_memset(sectorData, 0, sizeof(sectorData)); + __builtin_memset(causes, 0, sizeof(causes)); + + for (unsigned i = 0; i < 100; i++) { + waitCDRomIRQ(); + uint8_t cause = ackCDRomCause(); + causes[i] = cause; + readResponse(response); + if (cause != 1) break; + + uint8_t sector[2048]; + + CDROM_REG0 = 0; + CDROM_REG3 = 0x80; + + uint32_t dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x00880000; + DICR = dicr; + DPCR |= 0x8000; + DMA_CTRL[DMA_CDROM].MADR = (uintptr_t)sector; + DMA_CTRL[DMA_CDROM].BCR = (2048 >> 2) | 0x10000; + DMA_CTRL[DMA_CDROM].CHCR = 0x11000000; + + while ((DMA_CTRL[DMA_CDROM].CHCR & 0x01000000) != 0); + dicr = DICR; + dicr &= 0x00ffffff; + dicr |= 0x88000000; + DICR = dicr; + CDROM_REG3 = 0; + + uint32_t *sector32 = (uint32_t *)sector; + sectorData[i] = sector32[0]; + + if (i == 50) setLocDone = setLoc(0x20, 2, 0); + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + readResponse(response); + waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + readResponse(response); + + uint32_t start = 20 * 60 * 75; + for (unsigned i = 0; i < 100; i++) { + cester_assert_uint_eq(start + i, sectorData[i]); + cester_assert_uint_eq(1, causes[i]); + } + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(3, cause2); + cester_assert_uint_eq(2, cause3); + cester_assert_true(seekDone); +) + +CESTER_TEST(simpleReadingWithoutAck, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + + initializeTime(); + while (updateTime() <= 500000); + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + + initializeTime(); + uint32_t time3 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time4 = waitCDRomIRQ(); + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(1, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(0x22, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + ramsyscall_printf("Long read, ack then pause, ready1 in %ius, ready2 in %ius, ack in %ius, complete in %ius\n", time1, time2, time3, time4); +) + +CESTER_TEST(simpleReadingWithoutAckNorResponseReadThenInit, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + waitCDRomIRQ(); + ackCDRomCause(); + + initializeTime(); + while (updateTime() <= 500000); + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_INIT; + + initializeTime(); + uint32_t time3 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time4 = waitCDRomIRQ(); + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(1, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(0x22, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + ramsyscall_printf("Long read, no response read, ack then init, ready1 in %ius, ready2 in %ius, ack in %ius, complete in %ius\n", time1, time2, time3, time4); +) + +CESTER_TEST(simpleReadingWithoutAckThenInit, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + + initializeTime(); + while (updateTime() <= 500000); + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_INIT; + + initializeTime(); + uint32_t time3 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time4 = waitCDRomIRQ(); + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(1, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(0x22, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + ramsyscall_printf("Long read, ack then init, ready1 in %ius, ready2 in %ius, ack in %ius, complete in %ius\n", time1, time2, time3, time4); +) + +CESTER_TEST(simpleReadingPauseWithoutAck, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + + initializeTime(); + while (updateTime() <= 500000); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + + unsigned readyCount = 0; + uint32_t time1; + uint8_t cause1; + uint8_t ctrl1, ctrl2; + uint8_t response1[16]; + uint8_t responseSize1; + while (1) { + initializeTime(); + time1 = waitCDRomIRQ(); + cause1 = ackCDRomCause(); + ctrl1 = CDROM_REG0 & ~3; + responseSize1 = readResponse(response1); + ctrl2 = CDROM_REG0 & ~3; + if (cause1 == 1) { + readyCount++; + } else { + break; + } + } + + initializeTime(); + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(2, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x22, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(2, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_ge(readyCount, 1); + cester_assert_uint_le(readyCount, 2); + ramsyscall_printf("Long read, pause then ack, ack in %ius, complete in %ius\n", time1, time2); +) + +CESTER_TEST(simpleReadingNopQuery, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + + initializeTime(); + uint32_t time3 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time4 = waitCDRomIRQ(); + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(0x02, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(2, response4[0]); + cester_assert_uint_eq(1, responseSize4); + ramsyscall_printf("Long read, nop then pause, ack in %ius, ready in %ius, ack in %ius, complete in %ius\n", time1, time2, time3, time4); +) + +CESTER_TEST(simpleReadingNopSeriesQuery, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setModeDone = setMode(0x80); + if (!setModeDone) { + cester_assert_true(setModeDone); + return; + } + + int seekDone = seekLTo(0x20, 0, 0); + if (!seekDone) { + cester_assert_true(seekDone); + return; + } + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + + initializeTime(); + + unsigned countToRead = 0; + unsigned gotInt1 = 0; + + do { + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + uint8_t cause = 0; + do { + waitCDRomIRQ(); + cause = ackCDRomCause(); + readResponse(response); + if (cause == 1) { + gotInt1 = 1; + } + } while (cause == 1); + countToRead++; + } while (!gotInt1); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + + initializeTime(); + uint32_t time1 = waitCDRomIRQ(); + uint8_t cause1 = ackCDRomCause(); + uint8_t ctrl1 = CDROM_REG0 & ~3; + uint8_t response1[16]; + uint8_t responseSize1 = readResponse(response1); + uint8_t ctrl2 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause1b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause2 = ackCDRomCause(); + uint8_t ctrl3 = CDROM_REG0 & ~3; + uint8_t response2[16]; + uint8_t responseSize2 = readResponse(response2); + uint8_t ctrl4 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause2b = CDROM_REG3_UC; + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + + initializeTime(); + uint32_t time3 = waitCDRomIRQ(); + uint8_t cause3 = ackCDRomCause(); + uint8_t ctrl5 = CDROM_REG0 & ~3; + uint8_t response3[16]; + uint8_t responseSize3 = readResponse(response3); + uint8_t ctrl6 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause3b = CDROM_REG3_UC; + + initializeTime(); + uint32_t time4 = waitCDRomIRQ(); + uint8_t cause4 = ackCDRomCause(); + uint8_t ctrl7 = CDROM_REG0 & ~3; + uint8_t response4[16]; + uint8_t responseSize4 = readResponse(response4); + uint8_t ctrl8 = CDROM_REG0 & ~3; + CDROM_REG0 = 1; + uint8_t cause4b = CDROM_REG3_UC; + + cester_assert_uint_lt(countToRead, 80); + cester_assert_uint_eq(3, cause1); + cester_assert_uint_eq(0xe0, cause1b); + cester_assert_uint_eq(1, cause2); + cester_assert_uint_eq(0xe0, cause2b); + cester_assert_uint_eq(3, cause3); + cester_assert_uint_eq(0xe0, cause3b); + cester_assert_uint_eq(2, cause4); + cester_assert_uint_eq(0xe0, cause4b); + cester_assert_uint_eq(0x38, ctrl1); + cester_assert_uint_eq(0x18, ctrl2); + cester_assert_uint_eq(0x38, ctrl3); + cester_assert_uint_eq(0x18, ctrl4); + cester_assert_uint_eq(0x38, ctrl5); + cester_assert_uint_eq(0x18, ctrl6); + cester_assert_uint_eq(0x38, ctrl7); + cester_assert_uint_eq(0x18, ctrl8); + cester_assert_uint_eq(0x22, response1[0]); + cester_assert_uint_eq(1, responseSize1); + cester_assert_uint_eq(0x22, response2[0]); + cester_assert_uint_eq(1, responseSize2); + cester_assert_uint_eq(0x22, response3[0]); + cester_assert_uint_eq(1, responseSize3); + cester_assert_uint_eq(0x02, response4[0]); + cester_assert_uint_eq(1, responseSize4); + ramsyscall_printf("Long read, nop series of %i then pause, ack in %ius, ready in %ius, ack in %ius, complete in %ius\n", countToRead, time1, time2, time3, time4); +) + +CESTER_TEST(simpleReadingNoSeekNopQueries, test_instances, + int resetDone = resetCDRom(); + if (!resetDone) { + cester_assert_true(resetDone); + return; + } + + int setLocDone = setLoc(0x60, 0x02, 0x00); + if (!setLocDone) { + cester_assert_true(setLocDone); + return; + } + + initializeTime(); + CDROM_REG0 = 0; + CDROM_REG1 = CDL_READN; + uint32_t time1 = waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response[16]; + readResponse(response); + + uint8_t runningCause; + uint8_t responses[32]; + uint32_t times[32]; + int32_t lastResponse = -1; + unsigned responseCount = 0; + + do { + CDROM_REG0 = 0; + CDROM_REG1 = CDL_NOP; + uint32_t time = waitCDRomIRQ(); + runningCause = ackCDRomCause(); + uint8_t runningResponse[16]; + readResponse(runningResponse); + uint8_t r = runningResponse[0]; + if (r != lastResponse) { + responses[responseCount] = lastResponse = r; + times[responseCount] = time; + responseCount++; + } + } while(runningCause == 3); + + uint32_t time2 = waitCDRomIRQ(); + uint8_t cause = ackCDRomCause(); + uint8_t response2[16]; + readResponse(response2); + + CDROM_REG0 = 0; + CDROM_REG1 = CDL_PAUSE; + + uint32_t time3 = waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response3[16]; + readResponse(response3); + + uint32_t time4 = waitCDRomIRQ(); + ackCDRomCause(); + uint8_t response4[16]; + readResponse(response4); + + uint32_t dtime1 = times[0]; + uint32_t dtime2 = times[1] - times[0]; + uint32_t dtime3 = times[2] - times[1]; + + cester_assert_uint_eq(3, cause); + cester_assert_uint_eq(3, responseCount); + cester_assert_uint_eq(0x02, responses[0]); + cester_assert_uint_eq(0x42, responses[1]); + cester_assert_uint_eq(0x22, responses[2]); + cester_assert_uint_ge(dtime1, 1500); + cester_assert_uint_le(dtime1, 4000); + cester_assert_uint_ge(dtime2, 15000); + cester_assert_uint_le(dtime2, 50000); + cester_assert_uint_ge(dtime3, 700000); + cester_assert_uint_le(dtime3, 2000000); + ramsyscall_printf("Reading without seeking first, different nop count = %i, response1 = 0x%02x, dtime1 = %ius, response2 = 0x%02x, dtime2 = %ius, response3 = 0x%02x, dtime3 = %ius, time1 = %ius, time2 = %ius, time3 = %ius, time4 = %ius\n", responseCount, responses[0], dtime1, responses[1], dtime2, responses[2], dtime3, time1, time2, time3, time4); +) diff --git a/src/mips/tests/common.mk b/src/mips/tests/common.mk new file mode 100644 index 000000000..24c28cda9 --- /dev/null +++ b/src/mips/tests/common.mk @@ -0,0 +1,43 @@ +USE_FUNCTION_SECTIONS = false +TYPE = ps-exe + +SRCS = \ +../uC-sdk-glue/BoardConsole.c \ +../uC-sdk-glue/BoardInit.c \ +../uC-sdk-glue/init.c \ +\ +../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ +../../../../third_party/uC-sdk/libc/src/errno.c \ +../../../../third_party/uC-sdk/libc/src/initfini.c \ +../../../../third_party/uC-sdk/libc/src/malloc.c \ +../../../../third_party/uC-sdk/libc/src/qsort.c \ +../../../../third_party/uC-sdk/libc/src/rand.c \ +../../../../third_party/uC-sdk/libc/src/reent.c \ +../../../../third_party/uC-sdk/libc/src/stdio.c \ +../../../../third_party/uC-sdk/libc/src/string.c \ +../../../../third_party/uC-sdk/libc/src/strto.c \ +../../../../third_party/uC-sdk/libc/src/unistd.c \ +../../../../third_party/uC-sdk/libc/src/xprintf.c \ +../../../../third_party/uC-sdk/libc/src/xscanf.c \ +../../../../third_party/uC-sdk/libc/src/yscanf.c \ +../../../../third_party/uC-sdk/os/src/devfs.c \ +../../../../third_party/uC-sdk/os/src/filesystem.c \ +../../../../third_party/uC-sdk/os/src/fio.c \ +../../../../third_party/uC-sdk/os/src/hash-djb2.c \ +../../../../third_party/uC-sdk/os/src/init.c \ +../../../../third_party/uC-sdk/os/src/osdebug.c \ +../../../../third_party/uC-sdk/os/src/romfs.c \ +../../../../third_party/uC-sdk/os/src/sbrk.c \ +../../common/syscalls/printf.s \ +../../common/crt0/uC-sdk-crt0.s \ + +CPPFLAGS = -DNOFLOATINGPOINT +CPPFLAGS += -I. +CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include +CPPFLAGS += -I../../../../third_party/uC-sdk/os/include +CPPFLAGS += -I../../../../third_party/libcester/include +CPPFLAGS += -I../../openbios/uC-sdk-glue + +ifeq ($(PCSX_TESTS),true) +CPPFLAGS += -DPCSX_TESTS=1 +endif diff --git a/src/mips/tests/cop0/Makefile b/src/mips/tests/cop0/Makefile index 81dccab5c..8291f3558 100644 --- a/src/mips/tests/cop0/Makefile +++ b/src/mips/tests/cop0/Makefile @@ -1,50 +1,8 @@ TARGET = cop0 -USE_FUNCTION_SECTIONS = false -TYPE = ps-exe -SRCS = \ -../uC-sdk-glue/BoardConsole.c \ -../uC-sdk-glue/BoardInit.c \ -../uC-sdk-glue/init.c \ -\ -../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ -../../../../third_party/uC-sdk/libc/src/errno.c \ -../../../../third_party/uC-sdk/libc/src/initfini.c \ -../../../../third_party/uC-sdk/libc/src/malloc.c \ -../../../../third_party/uC-sdk/libc/src/qsort.c \ -../../../../third_party/uC-sdk/libc/src/rand.c \ -../../../../third_party/uC-sdk/libc/src/reent.c \ -../../../../third_party/uC-sdk/libc/src/stdio.c \ -../../../../third_party/uC-sdk/libc/src/string.c \ -../../../../third_party/uC-sdk/libc/src/strto.c \ -../../../../third_party/uC-sdk/libc/src/unistd.c \ -../../../../third_party/uC-sdk/libc/src/xprintf.c \ -../../../../third_party/uC-sdk/libc/src/xscanf.c \ -../../../../third_party/uC-sdk/libc/src/yscanf.c \ -../../../../third_party/uC-sdk/os/src/devfs.c \ -../../../../third_party/uC-sdk/os/src/filesystem.c \ -../../../../third_party/uC-sdk/os/src/fio.c \ -../../../../third_party/uC-sdk/os/src/hash-djb2.c \ -../../../../third_party/uC-sdk/os/src/init.c \ -../../../../third_party/uC-sdk/os/src/osdebug.c \ -../../../../third_party/uC-sdk/os/src/romfs.c \ -../../../../third_party/uC-sdk/os/src/sbrk.c \ - - -CPPFLAGS = -DNOFLOATINGPOINT -CPPFLAGS += -I. -CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include -CPPFLAGS += -I../../../../third_party/uC-sdk/os/include -CPPFLAGS += -I../../../../third_party/libcester/include -CPPFLAGS += -I../../openbios/uC-sdk-glue - -ifeq ($(PCSX_TESTS),true) -CPPFLAGS += -DPCSX_TESTS=1 -endif +include ../common.mk SRCS += \ -../../common/syscalls/printf.s \ -../../common/crt0/uC-sdk-crt0.s \ cop0.c \ exceptions.cpp \ diff --git a/src/mips/tests/cpu/Makefile b/src/mips/tests/cpu/Makefile index 7de3ac958..d37aaccc5 100644 --- a/src/mips/tests/cpu/Makefile +++ b/src/mips/tests/cpu/Makefile @@ -1,50 +1,8 @@ TARGET = cpu -USE_FUNCTION_SECTIONS = false -TYPE = ps-exe -SRCS = \ -../uC-sdk-glue/BoardConsole.c \ -../uC-sdk-glue/BoardInit.c \ -../uC-sdk-glue/init.c \ -\ -../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ -../../../../third_party/uC-sdk/libc/src/errno.c \ -../../../../third_party/uC-sdk/libc/src/initfini.c \ -../../../../third_party/uC-sdk/libc/src/malloc.c \ -../../../../third_party/uC-sdk/libc/src/qsort.c \ -../../../../third_party/uC-sdk/libc/src/rand.c \ -../../../../third_party/uC-sdk/libc/src/reent.c \ -../../../../third_party/uC-sdk/libc/src/stdio.c \ -../../../../third_party/uC-sdk/libc/src/string.c \ -../../../../third_party/uC-sdk/libc/src/strto.c \ -../../../../third_party/uC-sdk/libc/src/unistd.c \ -../../../../third_party/uC-sdk/libc/src/xprintf.c \ -../../../../third_party/uC-sdk/libc/src/xscanf.c \ -../../../../third_party/uC-sdk/libc/src/yscanf.c \ -../../../../third_party/uC-sdk/os/src/devfs.c \ -../../../../third_party/uC-sdk/os/src/filesystem.c \ -../../../../third_party/uC-sdk/os/src/fio.c \ -../../../../third_party/uC-sdk/os/src/hash-djb2.c \ -../../../../third_party/uC-sdk/os/src/init.c \ -../../../../third_party/uC-sdk/os/src/osdebug.c \ -../../../../third_party/uC-sdk/os/src/romfs.c \ -../../../../third_party/uC-sdk/os/src/sbrk.c \ - - -CPPFLAGS = -DNOFLOATINGPOINT -CPPFLAGS += -I. -CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include -CPPFLAGS += -I../../../../third_party/uC-sdk/os/include -CPPFLAGS += -I../../../../third_party/libcester/include -CPPFLAGS += -I../../openbios/uC-sdk-glue - -ifeq ($(PCSX_TESTS),true) -CPPFLAGS += -DPCSX_TESTS=1 -endif +include ../common.mk SRCS += \ -../../common/syscalls/printf.s \ -../../common/crt0/uC-sdk-crt0.s \ ../cop0/exceptions.cpp \ branchbranch.s \ cpu.c \ diff --git a/src/mips/tests/libc/Makefile b/src/mips/tests/libc/Makefile index 6eb3517ec..d016e51a1 100644 --- a/src/mips/tests/libc/Makefile +++ b/src/mips/tests/libc/Makefile @@ -1,46 +1,8 @@ TARGET = libc -USE_FUNCTION_SECTIONS = false -TYPE = ps-exe -SRCS = \ -../uC-sdk-glue/BoardConsole.c \ -../uC-sdk-glue/BoardInit.c \ -../uC-sdk-glue/init.c \ -\ -../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ -../../../../third_party/uC-sdk/libc/src/errno.c \ -../../../../third_party/uC-sdk/libc/src/initfini.c \ -../../../../third_party/uC-sdk/libc/src/malloc.c \ -../../../../third_party/uC-sdk/libc/src/qsort.c \ -../../../../third_party/uC-sdk/libc/src/rand.c \ -../../../../third_party/uC-sdk/libc/src/reent.c \ -../../../../third_party/uC-sdk/libc/src/stdio.c \ -../../../../third_party/uC-sdk/libc/src/string.c \ -../../../../third_party/uC-sdk/libc/src/strto.c \ -../../../../third_party/uC-sdk/libc/src/unistd.c \ -../../../../third_party/uC-sdk/libc/src/xprintf.c \ -../../../../third_party/uC-sdk/libc/src/xscanf.c \ -../../../../third_party/uC-sdk/libc/src/yscanf.c \ -../../../../third_party/uC-sdk/os/src/devfs.c \ -../../../../third_party/uC-sdk/os/src/filesystem.c \ -../../../../third_party/uC-sdk/os/src/fio.c \ -../../../../third_party/uC-sdk/os/src/hash-djb2.c \ -../../../../third_party/uC-sdk/os/src/init.c \ -../../../../third_party/uC-sdk/os/src/osdebug.c \ -../../../../third_party/uC-sdk/os/src/romfs.c \ -../../../../third_party/uC-sdk/os/src/sbrk.c \ - - -CPPFLAGS = -DNOFLOATINGPOINT -CPPFLAGS += -I. -CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include -CPPFLAGS += -I../../../../third_party/uC-sdk/os/include -CPPFLAGS += -I../../../../third_party/libcester/include -CPPFLAGS += -I../../openbios/uC-sdk-glue +include ../common.mk SRCS += \ -../../common/syscalls/printf.s \ -../../common/crt0/uC-sdk-crt0.s \ libc.c \ include ../../common.mk diff --git a/src/mips/tests/pcdrv/Makefile b/src/mips/tests/pcdrv/Makefile index e5142a17c..9c5f1d042 100644 --- a/src/mips/tests/pcdrv/Makefile +++ b/src/mips/tests/pcdrv/Makefile @@ -1,46 +1,8 @@ TARGET = pcdrv -USE_FUNCTION_SECTIONS = false -TYPE = ps-exe -SRCS = \ -../uC-sdk-glue/BoardConsole.c \ -../uC-sdk-glue/BoardInit.c \ -../uC-sdk-glue/init.c \ -\ -../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ -../../../../third_party/uC-sdk/libc/src/errno.c \ -../../../../third_party/uC-sdk/libc/src/initfini.c \ -../../../../third_party/uC-sdk/libc/src/malloc.c \ -../../../../third_party/uC-sdk/libc/src/qsort.c \ -../../../../third_party/uC-sdk/libc/src/rand.c \ -../../../../third_party/uC-sdk/libc/src/reent.c \ -../../../../third_party/uC-sdk/libc/src/stdio.c \ -../../../../third_party/uC-sdk/libc/src/string.c \ -../../../../third_party/uC-sdk/libc/src/strto.c \ -../../../../third_party/uC-sdk/libc/src/unistd.c \ -../../../../third_party/uC-sdk/libc/src/xprintf.c \ -../../../../third_party/uC-sdk/libc/src/xscanf.c \ -../../../../third_party/uC-sdk/libc/src/yscanf.c \ -../../../../third_party/uC-sdk/os/src/devfs.c \ -../../../../third_party/uC-sdk/os/src/filesystem.c \ -../../../../third_party/uC-sdk/os/src/fio.c \ -../../../../third_party/uC-sdk/os/src/hash-djb2.c \ -../../../../third_party/uC-sdk/os/src/init.c \ -../../../../third_party/uC-sdk/os/src/osdebug.c \ -../../../../third_party/uC-sdk/os/src/romfs.c \ -../../../../third_party/uC-sdk/os/src/sbrk.c \ - - -CPPFLAGS = -DNOFLOATINGPOINT -CPPFLAGS += -I. -CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include -CPPFLAGS += -I../../../../third_party/uC-sdk/os/include -CPPFLAGS += -I../../../../third_party/libcester/include -CPPFLAGS += -I../../openbios/uC-sdk-glue +include ../common.mk SRCS += \ -../../common/syscalls/printf.s \ -../../common/crt0/uC-sdk-crt0.s \ pcdrv.c \ include ../../common.mk diff --git a/src/supportpsx/iec-60908b.h b/src/supportpsx/iec-60908b.h index e5ba80fa7..1de8f0feb 100644 --- a/src/supportpsx/iec-60908b.h +++ b/src/supportpsx/iec-60908b.h @@ -119,6 +119,49 @@ struct MSF { ++(*this); return tmp; } + MSF &operator--() { + if (f == 0) { + f = 74; + if (s == 0) { + s = 59; + if (m == 0) { + m = 99; + } else { + m--; + } + } else { + s--; + } + } else { + f--; + } + return *this; + } + MSF operator--(int) { + MSF tmp = *this; + --(*this); + return tmp; + } + MSF operator+(const MSF &other) const { + MSF tmp = *this; + tmp += other; + return tmp; + } + MSF operator-(const MSF &other) const { + MSF tmp = *this; + tmp -= other; + return tmp; + } + MSF &operator+=(const MSF &other) { + uint32_t lba = toLBA() + other.toLBA(); + *this = MSF(lba); + return *this; + } + MSF &operator-=(const MSF &other) { + uint32_t lba = toLBA() - other.toLBA(); + *this = MSF(lba); + return *this; + } constexpr uint32_t toLBA() const { return (m * 60 + s) * 75 + f; } constexpr void toBCD(uint8_t *dst) const { dst[0] = itob(m); @@ -148,6 +191,53 @@ void computeEDCECC(uint8_t *sector); // Compute the CRC-16 for the SubQ channel. uint16_t subqCRC(const uint8_t *d, int len = 10); +struct SubHeaders { + uint8_t fileNumber; + uint8_t channelNumber; + uint8_t subMode; + uint8_t codingInfo; + bool fromBuffer(const uint8_t buffer[]) { + fileNumber = buffer[0]; + channelNumber = buffer[1]; + subMode = buffer[2]; + codingInfo = buffer[3]; + return (buffer[0] == buffer[4]) && (buffer[1] == buffer[5]) && (buffer[2] == buffer[6]) && + (buffer[3] == buffer[7]); + } + void toBuffer(uint8_t buffer[]) const { + buffer[0] = buffer[4] = fileNumber; + buffer[1] = buffer[5] = channelNumber; + buffer[2] = buffer[6] = subMode; + buffer[3] = buffer[7] = codingInfo; + } + bool isEndOfRecord() const { return subMode & 0x01; } + bool isVideo() const { return subMode & 0x02; } + bool isAudio() const { return subMode & 0x04; } + bool isData() const { return subMode & 0x08; } + bool isTrigger() const { return subMode & 0x10; } + bool isForm2() const { return subMode & 0x20; } + bool isRealTime() const { return subMode & 0x40; } + bool isEOF() const { return subMode & 0x80; } + + void setEndOfRecord() { subMode |= 0x01; } + void setVideo() { subMode |= 0x02; } + void setAudio() { subMode |= 0x04; } + void setData() { subMode |= 0x08; } + void setTrigger() { subMode |= 0x10; } + void setForm2() { subMode |= 0x20; } + void setRealTime() { subMode |= 0x40; } + void setEOF() { subMode |= 0x80; } + + void clearEndOfRecord() { subMode &= ~0x01; } + void clearVideo() { subMode &= ~0x02; } + void clearAudio() { subMode &= ~0x04; } + void clearData() { subMode &= ~0x08; } + void clearTrigger() { subMode &= ~0x10; } + void clearForm2() { subMode &= ~0x20; } + void clearRealTime() { subMode &= ~0x40; } + void clearEOF() { subMode &= ~0x80; } +}; + } // namespace IEC60908b } // namespace PCSX diff --git a/tests/pcsxrunner/cdrom.cc b/tests/pcsxrunner/cdrom.cc new file mode 100644 index 000000000..989fae0c5 --- /dev/null +++ b/tests/pcsxrunner/cdrom.cc @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2022 PCSX-Redux authors * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#include "gtest/gtest.h" +#include "main/main.h" + +TEST(cdrom, Interpreter) { + MainInvoker invoker("-run", "-stdout", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", + "-iso", "test.cue", "-loadexe", "src/mips/tests/cdrom/cdrom.ps-exe"); + int ret = invoker.invoke(); + EXPECT_EQ(ret, 0); +} + +TEST(cdrom, Dynarec) { + MainInvoker invoker("-run", "-stdout", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", "-iso", + "test.cue", "-loadexe", "src/mips/tests/cdrom/cdrom.ps-exe"); + int ret = invoker.invoke(); + EXPECT_EQ(ret, 0); +} \ No newline at end of file diff --git a/third_party/cueparser/cueparser.c b/third_party/cueparser/cueparser.c index 4632d6b0d..00228d959 100644 --- a/third_party/cueparser/cueparser.c +++ b/third_party/cueparser/cueparser.c @@ -135,6 +135,7 @@ void CueParser_construct(struct CueParser* parser, struct CueDisc* disc) { parser->currentFile = NULL; parser->currentTrack = 0; parser->currentSectorNumber = 0; + parser->cutting = 0; parser->isTrackANewFile = 0; disc->catalog[0] = 0; disc->isrc[0] = 0; @@ -484,17 +485,19 @@ static void parse(struct CueParser* parser, struct CueFile* file, struct CueSche return; } struct CueTrack* track = &parser->disc->tracks[parser->currentTrack]; - track->indices[track->indexCount] = parser->currentSectorNumber + sectorNumber; + if (parser->implicitIndex || (track->indexCount == 0)) { + parser->cutting = sectorNumber; + parser->currentFileSize -= sectorNumber * 2352; + } + track->indices[track->indexCount] = parser->currentSectorNumber + sectorNumber - parser->cutting; if (parser->implicitIndex) { - if (parser->currentTrack == 1) { - track->indices[0] = 0; - } else { - track->indices[0] = track->indices[1]; - track->indices[1] += parser->currentPregap; - track->fileOffset += parser->currentPregap; - parser->currentSectorNumber += parser->currentPregap; - } + track->indices[0] = track->indices[1]; + track->indices[1] += parser->currentPregap; + track->fileOffset += parser->currentPregap + sectorNumber; + parser->currentSectorNumber += parser->currentPregap; parser->implicitIndex = 0; + } else if (track->indexCount == 0) { + track->fileOffset += sectorNumber; } parser->state = CUE_PARSER_START; } break; diff --git a/third_party/cueparser/cueparser.h b/third_party/cueparser/cueparser.h index 175ed4257..39dd3e23a 100644 --- a/third_party/cueparser/cueparser.h +++ b/third_party/cueparser/cueparser.h @@ -88,6 +88,7 @@ struct CueParser { uint64_t currentFileSize; unsigned currentTrack; uint32_t currentSectorNumber; + uint32_t cutting; int implicitIndex; int isTrackANewFile; uint32_t currentPregap; diff --git a/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj b/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj index 0c953369b..5a3431397 100644 --- a/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj +++ b/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj @@ -245,6 +245,7 @@ + diff --git a/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj.filters b/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj.filters index 1b6304fc8..d727a27c8 100644 --- a/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj.filters +++ b/vsprojects/tests/pcsxrunner/pcsxrunner.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + Source Files