diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 2299f079..9d648636 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -250,6 +250,15 @@ void EmitX64::EmitBXWritePC(IR::Value* value_) { #endif } +void EmitX64::EmitCallSupervisor(IR::Value* value_) { + auto value = reinterpret_cast(value_); + + auto imm32 = value->GetArg(0).get(); + reg_alloc.HostCall(nullptr, imm32); + + code->ABI_CallFunction(reinterpret_cast(cb.CallSVC)); +} + 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 17711da4..f61fe513 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -49,6 +49,7 @@ private: void EmitGetVFlag(IR::Value* value); void EmitSetVFlag(IR::Value* value); void EmitBXWritePC(IR::Value* value); + void EmitCallSupervisor(IR::Value* value); void EmitGetCarryFromOp(IR::Value* value); void EmitGetOverflowFromOp(IR::Value* value); void EmitLeastSignificantHalf(IR::Value* value); diff --git a/src/frontend/decoder/arm.h b/src/frontend/decoder/arm.h index 6f5d8256..85e83ac0 100644 --- a/src/frontend/decoder/arm.h +++ b/src/frontend/decoder/arm.h @@ -59,7 +59,7 @@ private: }; template -static const std::array, 1> g_arm_instruction_table = { +static const std::array, 2> g_arm_instruction_table = { #define INST(fn, name, bitstring) detail::detail::GetMatcher(name, bitstring) @@ -139,7 +139,7 @@ static const std::array, 1> g_arm_instruction_table = { // Exception Generating instructions //INST(&V::arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv"), // v5 - //INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv"), // all + INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv"), // all INST(&V::arm_UDF, "UDF", "111001111111------------1111----"), // all // Extension instructions diff --git a/src/frontend/disassembler_arm.cpp b/src/frontend/disassembler_arm.cpp index 56f54795..1f8ce1d7 100644 --- a/src/frontend/disassembler_arm.cpp +++ b/src/frontend/disassembler_arm.cpp @@ -326,8 +326,12 @@ public: // Exception generation instructions std::string arm_BKPT(Cond cond, Imm12 imm12, Imm4 imm4) { return "ice"; } - std::string arm_SVC(Cond cond, Imm24 imm24) { return "ice"; } - std::string arm_UDF() { return "ice"; } + std::string arm_SVC(Cond cond, Imm24 imm24) { + return Common::StringFromFormat("svc%s #%u", CondStr(cond), imm24); + } + std::string arm_UDF() { + return Common::StringFromFormat("udf"); + } // Extension functions std::string arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; } diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index d1bc3fdc..db787890 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -18,6 +18,7 @@ OPCODE(SetCFlag, T::Void, T::U1 OPCODE(GetVFlag, T::U1, ) OPCODE(SetVFlag, T::Void, T::U1 ) OPCODE(BXWritePC, T::Void, T::U32 ) +OPCODE(CallSupervisor, 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 5e99a19d..b94399e4 100644 --- a/src/frontend/ir_emitter.cpp +++ b/src/frontend/ir_emitter.cpp @@ -72,6 +72,10 @@ void IREmitter::LoadWritePC(IR::ValuePtr value) { Inst(IR::Opcode::BXWritePC, {value}); } +void IREmitter::CallSupervisor(IR::ValuePtr value) { + Inst(IR::Opcode::CallSupervisor, {value}); +} + IR::ValuePtr IREmitter::GetCFlag() { return Inst(IR::Opcode::GetCFlag, {}); } diff --git a/src/frontend/ir_emitter.h b/src/frontend/ir_emitter.h index 1c4b9770..df1f1070 100644 --- a/src/frontend/ir_emitter.h +++ b/src/frontend/ir_emitter.h @@ -44,6 +44,7 @@ public: void ALUWritePC(IR::ValuePtr value); void LoadWritePC(IR::ValuePtr value); + void CallSupervisor(IR::ValuePtr value); IR::ValuePtr GetCFlag(); void SetNFlag(IR::ValuePtr value); diff --git a/src/frontend/translate_arm.cpp b/src/frontend/translate_arm.cpp index 691e61c8..e05ca230 100644 --- a/src/frontend/translate_arm.cpp +++ b/src/frontend/translate_arm.cpp @@ -16,12 +16,22 @@ namespace Arm { namespace { +enum class ConditionalState { + /// We haven't met any conditional instructions yet. + None, + /// Current instruction is a conditional. This marks the end of this basic block. + Break, + /// This basic block is made up solely of conditional instructions. + Translating, +}; + struct ArmTranslatorVisitor final { explicit ArmTranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) { ASSERT_MSG(!descriptor.TFlag, "The processor must be in Arm mode"); } IREmitter ir; + ConditionalState cond_state = ConditionalState::None; bool TranslateThisInstruction() { ir.SetTerm(IR::Term::Interpret(ir.current_location)); @@ -33,6 +43,53 @@ struct ArmTranslatorVisitor final { return false; } + bool LinkToNextInstruction() { + auto next_location = ir.current_location; + next_location.arm_pc += 4; + ir.SetTerm(IR::Term::LinkBlock{next_location}); + return false; + } + + bool ConditionPassed(Cond cond) { + ASSERT_MSG(cond_state != ConditionalState::Translating, + "In the current impl, ConditionPassed should never be called again once a non-AL cond is hit. " + "(i.e.: one and only one conditional instruction per block)"); + ASSERT_MSG(cond_state != ConditionalState::Break, + "This should never happen. We requested a break but that wasn't honored."); + ASSERT_MSG(cond != Cond::NV, "NV conditional is obsolete"); + + if (cond == Cond::AL) { + // Everything is fine with the world + return true; + } + + // non-AL cond + + if (!ir.block.instructions.empty()) { + // We've already emitted instructions. Quit for now, we'll make a new block here later. + cond_state = ConditionalState::Break; + ir.SetTerm(IR::Term::LinkBlock{ir.current_location}); + return false; + } + + // We've not emitted instructions yet. + // We'll emit one instruction, and set the block-entry conditional appropriately. + + cond_state = ConditionalState::Translating; + ir.block.cond = cond; + return true; + } + + bool arm_SVC(Cond cond, Imm24 imm24) { + u32 imm32 = imm24; + // SVC # + if (ConditionPassed(cond)) { + ir.CallSupervisor(ir.Imm32(imm32)); + return LinkToNextInstruction(); + } + return true; + } + bool arm_UDF() { return TranslateThisInstruction(); } @@ -44,7 +101,7 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor ArmTranslatorVisitor visitor{descriptor}; bool should_continue = true; - while (should_continue) { + while (should_continue && visitor.cond_state == ConditionalState::None) { const u32 arm_pc = visitor.ir.current_location.arm_pc; const u32 arm_instruction = (*memory_read_32)(arm_pc); @@ -55,10 +112,21 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor should_continue = visitor.arm_UDF(); } + if (visitor.cond_state == ConditionalState::Break) { + break; + } + visitor.ir.current_location.arm_pc += 4; visitor.ir.block.cycle_count++; } + if (visitor.cond_state == ConditionalState::Translating) { + if (should_continue) { + visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location}); + } + visitor.ir.block.cond_failed = { visitor.ir.current_location }; + } + return visitor.ir.block; }