diff --git a/include/dynarmic/coprocessor.h b/include/dynarmic/coprocessor.h index 9a0a33e4..42378a30 100644 --- a/include/dynarmic/coprocessor.h +++ b/include/dynarmic/coprocessor.h @@ -36,6 +36,20 @@ public: boost::optional user_arg; }; + /** + * boost::blank: coprocessor exception will be compiled + * Callback: a call to the Callback will be compiled + * std::uint32_t*: a write/read to that memory address will be compiled + */ + using CallbackOrAccessOneWord = boost::variant; + + /** + * boost::blank: coprocessor exception will be compiled + * Callback: a call to the Callback will be compiled + * std::array: a write/read to those memory addresses will be compiled + */ + using CallbackOrAccessTwoWords = boost::variant>; + /** * Called when compiling CDP or CDP2 for this coprocessor. * A return value of boost::none will cause a coprocessor exception to be compiled. @@ -45,38 +59,38 @@ public: /** * Called when compiling MCR or MCR2 for this coprocessor. - * A return value of boost::none will cause a coprocessor exception to be compiled. + * A return value of boost::blank will cause a coprocessor exception to be compiled. * arg0 of the callback will contain the word sent to the coprocessor. * arg1 and return value of the callback are ignored. */ - virtual boost::optional CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) = 0; + virtual CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) = 0; /** * Called when compiling MCRR or MCRR2 for this coprocessor. - * A return value of boost::none will cause a coprocessor exception to be compiled. + * A return value of boost::blank will cause a coprocessor exception to be compiled. * arg0 and arg1 of the callback will contain the words sent to the coprocessor. * The return value of the callback is ignored. */ - virtual boost::optional CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) = 0; + virtual CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) = 0; /** * Called when compiling MRC or MRC2 for this coprocessor. - * A return value of boost::none will cause a coprocessor exception to be compiled. + * A return value of boost::blank will cause a coprocessor exception to be compiled. * The return value of the callback should contain word from coprocessor. * The low word of the return value will be stored in Rt. * arg0 and arg1 of the callback are ignored. */ - virtual boost::optional CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) = 0; + virtual CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) = 0; /** * Called when compiling MRRC or MRRC2 for this coprocessor. - * A return value of boost::none will cause a coprocessor exception to be compiled. + * A return value of boost::blank will cause a coprocessor exception to be compiled. * The return value of the callback should contain words from coprocessor. * The low word of the return value will be stored in Rt. * The high word of the return value will be stored in Rt2. * arg0 and arg1 of the callback are ignored. */ - virtual boost::optional CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) = 0; + virtual CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) = 0; /** * Called when compiling LDC or LDC2 for this coprocessor. diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 47e1e8ba..b5d68e4f 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -3017,12 +3017,27 @@ void EmitX64::EmitCoprocSendOneWord(IR::Block&, IR::Inst* inst) { } auto action = coproc->CompileSendOneWord(two, opc1, CRn, CRm, opc2); - if (!action) { + switch (action.which()) { + case 0: EmitCoprocessorException(); return; - } + case 1: + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), nullptr, word); + return; + case 2: { + u32* destination_ptr = boost::get(action); - CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), nullptr, word); + Xbyak::Reg32 reg_word = reg_alloc.UseGpr(word).cvt32(); + Xbyak::Reg64 reg_destination_addr = reg_alloc.ScratchGpr(); + + code->mov(reg_destination_addr, reinterpret_cast(destination_ptr)); + code->mov(code->dword[reg_destination_addr], reg_word); + + return; + } + default: + ASSERT_MSG(false, "Unreachable"); + } } void EmitX64::EmitCoprocSendTwoWords(IR::Block&, IR::Inst* inst) { @@ -3043,13 +3058,31 @@ void EmitX64::EmitCoprocSendTwoWords(IR::Block&, IR::Inst* inst) { } auto action = coproc->CompileSendTwoWords(two, opc, CRm); - if (!action) { + switch (action.which()) { + case 0: EmitCoprocessorException(); return; - } + case 1: + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), nullptr, word1, word2); + return; + case 2: { + auto destination_ptrs = boost::get>(action); - CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), nullptr, word1, word2); - } + Xbyak::Reg32 reg_word1 = reg_alloc.UseGpr(word1).cvt32(); + Xbyak::Reg32 reg_word2 = reg_alloc.UseGpr(word2).cvt32(); + Xbyak::Reg64 reg_destination_addr = reg_alloc.ScratchGpr(); + + code->mov(reg_destination_addr, reinterpret_cast(destination_ptrs[0])); + code->mov(code->dword[reg_destination_addr], reg_word1); + code->mov(reg_destination_addr, reinterpret_cast(destination_ptrs[1])); + code->mov(code->dword[reg_destination_addr], reg_word2); + + return; + } + default: + ASSERT_MSG(false, "Unreachable"); + } +} void EmitX64::EmitCoprocGetOneWord(IR::Block&, IR::Inst* inst) { auto coproc_info = inst->GetArg(0).GetCoprocInfo(); @@ -3068,12 +3101,27 @@ void EmitX64::EmitCoprocGetOneWord(IR::Block&, IR::Inst* inst) { } auto action = coproc->CompileGetOneWord(two, opc1, CRn, CRm, opc2); - if (!action) { + switch (action.which()) { + case 0: EmitCoprocessorException(); return; - } + case 1: + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), inst); + return; + case 2: { + u32* source_ptr = boost::get(action); - CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), inst); + Xbyak::Reg32 reg_word = reg_alloc.DefGpr(inst).cvt32(); + Xbyak::Reg64 reg_source_addr = reg_alloc.ScratchGpr(); + + code->mov(reg_source_addr, reinterpret_cast(source_ptr)); + code->mov(reg_word, code->dword[reg_source_addr]); + + return; + } + default: + ASSERT_MSG(false, "Unreachable"); + } } void EmitX64::EmitCoprocGetTwoWords(IR::Block&, IR::Inst* inst) { @@ -3091,12 +3139,32 @@ void EmitX64::EmitCoprocGetTwoWords(IR::Block&, IR::Inst* inst) { } auto action = coproc->CompileGetTwoWords(two, opc, CRm); - if (!action) { + switch (action.which()) { + case 0: EmitCoprocessorException(); return; - } + case 1: + CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), inst); + return; + case 2: { + auto source_ptrs = boost::get>(action); - CallCoprocCallback(code, reg_alloc, jit_interface, boost::get(action), inst); + Xbyak::Reg64 reg_result = reg_alloc.DefGpr(inst); + Xbyak::Reg64 reg_destination_addr = reg_alloc.ScratchGpr(); + Xbyak::Reg64 reg_tmp = reg_alloc.ScratchGpr(); + + code->mov(reg_destination_addr, reinterpret_cast(source_ptrs[1])); + code->mov(reg_result.cvt32(), code->dword[reg_destination_addr]); + code->shl(reg_result, 32); + code->mov(reg_destination_addr, reinterpret_cast(source_ptrs[0])); + code->mov(reg_tmp.cvt32(), code->dword[reg_destination_addr]); + code->or_(reg_result, reg_tmp); + + return; + } + default: + ASSERT_MSG(false, "Unreachable"); + } } void EmitX64::EmitCoprocLoadWords(IR::Block&, IR::Inst* inst) {