diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 58ce9f19..22ed5e30 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -37,12 +37,13 @@ static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) { return iter == uses.end() ? nullptr : reinterpret_cast(iter->get()); } -CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block block) { +CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block block) { inhibit_emission.clear(); reg_alloc.Reset(); code->INT3(); CodePtr code_ptr = code->GetCodePtr(); + basic_blocks[descriptor] = code_ptr; for (const auto& value : block.instructions) { if (inhibit_emission.count(value.get()) != 0) @@ -215,6 +216,38 @@ void EmitX64::EmitSetVFlag(IR::Value* value_) { code->OR(32, MJitStateCpsr(), R(to_store)); } +void EmitX64::EmitBXWritePC(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + X64Reg new_pc = reg_alloc.UseRegister(value->GetArg(0).get()); + X64Reg tmp = reg_alloc.ScratchRegister(); + X64Reg cpsr = reg_alloc.ScratchRegister(); + + // Note: new_pc<1:0> == '10' is UNPREDICTABLE + + // Alternative implementations +#if 0 + code->MOV(32, R(tmp), MJitStateCpsr()); + code->MOV(32, R(cpsr), R(tmp)); + code->OR(32, R(tmp), Imm32(1 << 5)); + code->AND(32, R(cpsr), Imm32(~(1 << 5))); + code->BTR(32, R(new_pc), Imm8(0)); + code->CMOVcc(32, cpsr, R(tmp), CC_C); + code->MOV(32, MJitStateReg(Arm::Reg::PC), R(new_pc)); + code->MOV(32, MJitStateCpsr(), R(cpsr)); +#else + code->MOV(32, R(tmp), R(new_pc)); + code->AND(32, R(tmp), Imm8(1)); + code->AND(32, R(new_pc), Imm32(0xFFFFFFFE)); + code->MOV(32, R(cpsr), MJitStateCpsr()); + code->SHL(32, R(tmp), Imm8(5)); + code->AND(32, R(cpsr), Imm32(~(1 << 5))); + code->OR(32, R(cpsr), R(tmp)); + code->MOV(32, MJitStateReg(Arm::Reg::PC), R(new_pc)); + code->MOV(32, MJitStateCpsr(), R(cpsr)); +#endif +} + void EmitX64::EmitGetCarryFromOp(IR::Value*) { ASSERT_MSG(0, "should never happen"); } diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index 9caccea0..21daa52a 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -23,7 +23,7 @@ public: EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface) : reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {} - CodePtr Emit(Arm::LocationDescriptor descriptor, IR::Block ir); + CodePtr Emit(const Arm::LocationDescriptor descriptor, IR::Block ir); CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) { auto iter = basic_blocks.find(descriptor); @@ -44,6 +44,7 @@ public: void EmitSetCFlag(IR::Value* value); void EmitGetVFlag(IR::Value* value); void EmitSetVFlag(IR::Value* value); + void EmitBXWritePC(IR::Value* value); void EmitGetCarryFromOp(IR::Value* value); void EmitGetOverflowFromOp(IR::Value* value); void EmitLeastSignificantHalf(IR::Value* value); diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index a8151718..d1bc3fdc 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -17,6 +17,7 @@ OPCODE(GetCFlag, T::U1, OPCODE(SetCFlag, T::Void, T::U1 ) OPCODE(GetVFlag, T::U1, ) OPCODE(SetVFlag, T::Void, T::U1 ) +OPCODE(BXWritePC, T::Void, T::U32 ) // Pseudo-operation, handled specially at final emit OPCODE(GetCarryFromOp, T::U1, T::U32 ) diff --git a/src/frontend/ir_emitter.cpp b/src/frontend/ir_emitter.cpp index d88cd82b..5e99a19d 100644 --- a/src/frontend/ir_emitter.cpp +++ b/src/frontend/ir_emitter.cpp @@ -56,7 +56,20 @@ void IREmitter::SetRegister(const Reg reg, IR::ValuePtr value) { void IREmitter::ALUWritePC(IR::ValuePtr value) { // This behaviour is ARM version-dependent. - ASSERT_MSG(false, "Unimplemented"); + // The below implementation is for ARMv6k + if (!current_location.TFlag) { + auto new_pc = And(value, Imm32(0xFFFFFFFC)); + Inst(IR::Opcode::SetRegister, { RegRef(Reg::PC), new_pc }); + } else { + auto new_pc = And(value, Imm32(0xFFFFFFFE)); + Inst(IR::Opcode::SetRegister, { RegRef(Reg::PC), new_pc }); + } +} + +void IREmitter::LoadWritePC(IR::ValuePtr value) { + // This behaviour is ARM version-dependent. + // The below implementation is for ARMv6k + Inst(IR::Opcode::BXWritePC, {value}); } IR::ValuePtr IREmitter::GetCFlag() { diff --git a/src/frontend/ir_emitter.h b/src/frontend/ir_emitter.h index 54ef09e4..1c4b9770 100644 --- a/src/frontend/ir_emitter.h +++ b/src/frontend/ir_emitter.h @@ -43,6 +43,7 @@ public: void SetRegister(const Reg dest_reg, IR::ValuePtr value); void ALUWritePC(IR::ValuePtr value); + void LoadWritePC(IR::ValuePtr value); IR::ValuePtr GetCFlag(); void SetNFlag(IR::ValuePtr value);