diff --git a/parallel-rsp/parallel.cpp b/parallel-rsp/parallel.cpp index b3cf43d..13abf8f 100644 --- a/parallel-rsp/parallel.cpp +++ b/parallel-rsp/parallel.cpp @@ -45,6 +45,8 @@ RSP::JIT::CPU cpu; #endif uint32_t MFC0_count[32]; uint32_t SP_STATUS_TIMEOUT; +uint32_t dma0_timer; +uint32_t dma1_timer; } // namespace RSP extern "C" @@ -86,6 +88,8 @@ extern "C" RSP::cpu.get_state().pc = *RSP::rsp.SP_PC_REG & 0xfff; RSP::cpu.get_state().instruction_count = 0; RSP::cpu.get_state().last_instruction_type = RSP::VU_INSTRUCTION; + RSP::dma0_timer = 0; + RSP::dma1_timer = 0; #ifdef INTENSE_DEBUG fprintf(stderr, "RUN TASK: %u\n", RSP::cpu.get_state().pc); @@ -103,6 +107,9 @@ extern "C" } *RSP::rsp.SP_PC_REG = 0x04001000 | (RSP::cpu.get_state().pc & 0xffc); + *RSP::rsp.SP_DMA_BUSY_REG = 0; + *RSP::rsp.SP_DMA_FULL_REG = 0; + *RSP::rsp.SP_STATUS_REG &= ~(SP_STATUS_DMA_BUSY | SP_STATUS_DMA_FULL); // From CXD4. if (*RSP::rsp.SP_STATUS_REG & SP_STATUS_BROKE) diff --git a/parallel-rsp/rsp/cp0.cpp b/parallel-rsp/rsp/cp0.cpp index 4451399..ad6f784 100644 --- a/parallel-rsp/rsp/cp0.cpp +++ b/parallel-rsp/rsp/cp0.cpp @@ -8,6 +8,8 @@ namespace RSP extern RSP_INFO rsp; extern uint32_t MFC0_count[32]; extern uint32_t SP_STATUS_TIMEOUT; +extern uint32_t dma0_timer; +extern uint32_t dma1_timer; } // namespace RSP #endif @@ -22,6 +24,22 @@ extern "C" int RSP_MFC0(RSP::CPUState *rsp, unsigned rt, unsigned rd) { + if (RSP::dma0_timer && RSP::dma0_timer < rsp->instruction_count) // DMA complete + { + if (RSP::dma1_timer) // Pending DMA + { + RSP::dma0_timer = rsp->instruction_count + RSP::dma1_timer; + RSP::dma1_timer = 0; + *rsp->cp0.cr[CP0_REGISTER_DMA_FULL] = 0; + *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] &= ~SP_STATUS_DMA_FULL; + } + else + { + RSP::dma0_timer = 0; + *rsp->cp0.cr[CP0_REGISTER_DMA_BUSY] = 0; + *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] &= ~SP_STATUS_DMA_BUSY; + } + } rd &= 15; uint32_t res = *rsp->cp0.cr[rd]; if (rt) @@ -35,6 +53,7 @@ extern "C" // WAIT_FOR_CPU_HOST. From CXD4. if (rd == CP0_REGISTER_SP_STATUS) { + --rsp->instruction_count; // Some games check the STATUS reg more than normal since we don't really yield properly RSP::MFC0_count[rt] += 1; if (RSP::MFC0_count[rt] >= RSP::SP_STATUS_TIMEOUT) { @@ -182,6 +201,18 @@ extern "C" #ifdef INTENSE_DEBUG log_rsp_mem_parallel(); #endif + if (*rsp->cp0.cr[CP0_REGISTER_DMA_BUSY] == 0) + { + *rsp->cp0.cr[CP0_REGISTER_DMA_BUSY] = 1; + *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] |= SP_STATUS_DMA_BUSY; + RSP::dma0_timer = rsp->instruction_count + (((length * count) / 4) / 1.5); // see https://hcs64.com/dma.html, divided by 1.5 to convert CPU cycles to RCP cycles + } + else + { + *rsp->cp0.cr[CP0_REGISTER_DMA_FULL] = 1; + *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] |= SP_STATUS_DMA_FULL; + RSP::dma1_timer = ((length * count) / 4) / 1.5; + } return rsp->dirty_blocks ? MODE_CHECK_FLAGS : MODE_CONTINUE; } @@ -234,6 +265,18 @@ extern "C" #ifdef INTENSE_DEBUG log_rsp_mem_parallel(); #endif + if (*rsp->cp0.cr[CP0_REGISTER_DMA_BUSY] == 0) + { + *rsp->cp0.cr[CP0_REGISTER_DMA_BUSY] = 1; + *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] |= SP_STATUS_DMA_BUSY; + RSP::dma0_timer = rsp->instruction_count + (((length * count) / 4) / 1.5); // see https://hcs64.com/dma.html, divided by 1.5 to convert CPU cycles to RCP cycles + } + else + { + *rsp->cp0.cr[CP0_REGISTER_DMA_FULL] = 1; + *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] |= SP_STATUS_DMA_FULL; + RSP::dma1_timer = ((length * count) / 4) / 1.5; + } } #endif