From 36b9ab0e48b6893c057a954e1ef3181b452add1c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 24 Jan 2019 23:59:53 -0200 Subject: [PATCH] Add ARM32 support on the translator (#561) * Remove ARM32 interpreter and add ARM32 support on the translator * Nits. * Rename Cond -> Condition * Align code again * Rename Data to Alu * Enable ARM32 support and handle undefined instructions * Use the IsThumb method to check if its a thumb opcode * Remove another 32-bits check --- ChocolArm64/CpuThread.cs | 2 - ChocolArm64/{ => Decoders}/BitUtils.cs | 12 +- .../Decoders/{Cond.cs => Condition.cs} | 2 +- ChocolArm64/Decoders/Decoder.cs | 67 +++- ChocolArm64/Decoders/DecoderHelper.cs | 5 + ChocolArm64/Decoders/IOpCode32.cs | 9 + ChocolArm64/Decoders/IOpCodeAlu32.cs | 10 + ChocolArm64/Decoders/IOpCodeBImm.cs | 7 + ChocolArm64/Decoders/IOpCodeBImm32.cs | 4 + ChocolArm64/Decoders/IOpCodeBReg32.cs | 7 + ChocolArm64/Decoders/IOpCodeCond64.cs | 2 +- ChocolArm64/Decoders/OpCode32.cs | 24 ++ ChocolArm64/Decoders/OpCode64.cs | 10 +- ChocolArm64/Decoders/OpCodeAlu32.cs | 20 ++ ChocolArm64/Decoders/OpCodeAluImm32.cs | 21 ++ ChocolArm64/Decoders/OpCodeAluImm8T16.cs | 22 ++ ChocolArm64/Decoders/OpCodeAluRsImm32.cs | 20 ++ ChocolArm64/Decoders/OpCodeBImm32.cs | 29 ++ ChocolArm64/Decoders/OpCodeBImm64.cs | 2 +- ChocolArm64/Decoders/OpCodeBImmCond64.cs | 4 +- ChocolArm64/Decoders/OpCodeBReg32.cs | 14 + ChocolArm64/Decoders/OpCodeBRegT16.cs | 14 + ChocolArm64/Decoders/OpCodeCcmp64.cs | 10 +- ChocolArm64/Decoders/OpCodeCsel64.cs | 6 +- ChocolArm64/Decoders/OpCodeSimdFcond64.cs | 6 +- ChocolArm64/Decoders/OpCodeT16.cs | 14 + ChocolArm64/Decoders32/A32OpCode.cs | 15 - ChocolArm64/Decoders32/A32OpCodeBImmAl.cs | 16 - ChocolArm64/Instructions/Inst.cs | 14 +- ChocolArm64/Instructions/InstEmit32Helper.cs | 99 ++++++ ChocolArm64/Instructions/InstEmitAlu.cs | 107 +++--- ChocolArm64/Instructions/InstEmitAlu32.cs | 123 +++++++ ChocolArm64/Instructions/InstEmitAluHelper.cs | 329 +++++++++++++++--- ChocolArm64/Instructions/InstEmitCcmp.cs | 2 +- ChocolArm64/Instructions/InstEmitFlow.cs | 36 +- ChocolArm64/Instructions/InstEmitFlow32.cs | 99 ++++++ .../Instructions/InstEmitFlowHelper.cs | 39 +++ .../Instructions/InstEmitSimdMemory.cs | 2 +- ChocolArm64/Instructions/InstInterpreter.cs | 8 - .../Instructions32/A32InstInterpretAlu.cs | 7 - .../Instructions32/A32InstInterpretFlow.cs | 70 ---- .../Instructions32/A32InstInterpretHelper.cs | 65 ---- ChocolArm64/OpCodeTable.cs | 152 ++++---- ChocolArm64/State/Aarch32Mode.cs | 15 + ChocolArm64/State/CpuThreadState.cs | 28 +- ChocolArm64/State/ExecutionMode.cs | 5 +- ChocolArm64/State/PState.cs | 4 + ChocolArm64/State/Register.cs | 2 + ChocolArm64/State/RegisterAlias.cs | 41 +++ ChocolArm64/Translation/ILEmitterCtx.cs | 73 +++- ChocolArm64/Translator.cs | 41 +-- ChocolArm64/TranslatorCache.cs | 1 - Ryujinx.HLE/HOS/Horizon.cs | 15 - .../HOS/Kernel/Process/HleProcessDebugger.cs | 2 + Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 7 + Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 2 + Ryujinx.HLE/HOS/ProgramLoader.cs | 7 +- 57 files changed, 1274 insertions(+), 495 deletions(-) rename ChocolArm64/{ => Decoders}/BitUtils.cs (78%) rename ChocolArm64/Decoders/{Cond.cs => Condition.cs} (94%) create mode 100644 ChocolArm64/Decoders/IOpCode32.cs create mode 100644 ChocolArm64/Decoders/IOpCodeAlu32.cs create mode 100644 ChocolArm64/Decoders/IOpCodeBImm.cs create mode 100644 ChocolArm64/Decoders/IOpCodeBImm32.cs create mode 100644 ChocolArm64/Decoders/IOpCodeBReg32.cs create mode 100644 ChocolArm64/Decoders/OpCode32.cs create mode 100644 ChocolArm64/Decoders/OpCodeAlu32.cs create mode 100644 ChocolArm64/Decoders/OpCodeAluImm32.cs create mode 100644 ChocolArm64/Decoders/OpCodeAluImm8T16.cs create mode 100644 ChocolArm64/Decoders/OpCodeAluRsImm32.cs create mode 100644 ChocolArm64/Decoders/OpCodeBImm32.cs create mode 100644 ChocolArm64/Decoders/OpCodeBReg32.cs create mode 100644 ChocolArm64/Decoders/OpCodeBRegT16.cs create mode 100644 ChocolArm64/Decoders/OpCodeT16.cs delete mode 100644 ChocolArm64/Decoders32/A32OpCode.cs delete mode 100644 ChocolArm64/Decoders32/A32OpCodeBImmAl.cs create mode 100644 ChocolArm64/Instructions/InstEmit32Helper.cs create mode 100644 ChocolArm64/Instructions/InstEmitAlu32.cs create mode 100644 ChocolArm64/Instructions/InstEmitFlow32.cs create mode 100644 ChocolArm64/Instructions/InstEmitFlowHelper.cs delete mode 100644 ChocolArm64/Instructions/InstInterpreter.cs delete mode 100644 ChocolArm64/Instructions32/A32InstInterpretAlu.cs delete mode 100644 ChocolArm64/Instructions32/A32InstInterpretFlow.cs delete mode 100644 ChocolArm64/Instructions32/A32InstInterpretHelper.cs create mode 100644 ChocolArm64/State/Aarch32Mode.cs create mode 100644 ChocolArm64/State/RegisterAlias.cs diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs index dac376a1d..87b213957 100644 --- a/ChocolArm64/CpuThread.cs +++ b/ChocolArm64/CpuThread.cs @@ -25,8 +25,6 @@ namespace ChocolArm64 ThreadState = new CpuThreadState(); - ThreadState.ExecutionMode = ExecutionMode.AArch64; - ThreadState.Running = true; Work = new Thread(delegate() diff --git a/ChocolArm64/BitUtils.cs b/ChocolArm64/Decoders/BitUtils.cs similarity index 78% rename from ChocolArm64/BitUtils.cs rename to ChocolArm64/Decoders/BitUtils.cs index 881ee2484..8b9fb5e3c 100644 --- a/ChocolArm64/BitUtils.cs +++ b/ChocolArm64/Decoders/BitUtils.cs @@ -1,4 +1,4 @@ -namespace ChocolArm64 +namespace ChocolArm64.Decoders { static class BitUtils { @@ -36,6 +36,16 @@ namespace ChocolArm64 return bits == 64 ? -1L : (1L << bits) - 1; } + public static int RotateRight(int bits, int shift, int size) + { + return (int)RotateRight((uint)bits, shift, size); + } + + public static uint RotateRight(uint bits, int shift, int size) + { + return (bits >> shift) | (bits << (size - shift)); + } + public static long RotateRight(long bits, int shift, int size) { return (long)RotateRight((ulong)bits, shift, size); diff --git a/ChocolArm64/Decoders/Cond.cs b/ChocolArm64/Decoders/Condition.cs similarity index 94% rename from ChocolArm64/Decoders/Cond.cs rename to ChocolArm64/Decoders/Condition.cs index 57e12cd60..d1aa57729 100644 --- a/ChocolArm64/Decoders/Cond.cs +++ b/ChocolArm64/Decoders/Condition.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.Decoders { - enum Cond + enum Condition { Eq = 0, Ne = 1, diff --git a/ChocolArm64/Decoders/Decoder.cs b/ChocolArm64/Decoders/Decoder.cs index 1d4f397ac..6c60e1fe5 100644 --- a/ChocolArm64/Decoders/Decoder.cs +++ b/ChocolArm64/Decoders/Decoder.cs @@ -19,20 +19,20 @@ namespace ChocolArm64.Decoders _opActivators = new ConcurrentDictionary(); } - public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start) + public static Block DecodeBasicBlock(MemoryManager memory, long start, ExecutionMode mode) { Block block = new Block(start); - FillBlock(state, memory, block); + FillBlock(memory, mode, block); return block; } public static Block DecodeSubroutine( TranslatorCache cache, - CpuThreadState state, MemoryManager memory, - long start) + long start, + ExecutionMode mode) { Dictionary visited = new Dictionary(); Dictionary visitedEnd = new Dictionary(); @@ -59,7 +59,7 @@ namespace ChocolArm64.Decoders { Block current = blocks.Dequeue(); - FillBlock(state, memory, current); + FillBlock(memory, mode, current); //Set child blocks. "Branch" is the block the branch instruction //points to (when taken), "Next" is the block at the next address, @@ -71,7 +71,7 @@ namespace ChocolArm64.Decoders OpCode64 lastOp = current.GetLastOp(); - if (lastOp is OpCodeBImm64 op) + if (lastOp is IOpCodeBImm op) { if (op.Emitter == InstEmit.Bl) { @@ -83,8 +83,7 @@ namespace ChocolArm64.Decoders } } - if (!((lastOp is OpCodeBImmAl64) || - (lastOp is OpCodeBReg64)) || hasCachedSub) + if (!IsUnconditionalBranch(lastOp) || hasCachedSub) { current.Next = Enqueue(current.EndPosition); } @@ -121,7 +120,7 @@ namespace ChocolArm64.Decoders return entry; } - private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block) + private static void FillBlock(MemoryManager memory, ExecutionMode mode, Block block) { long position = block.Position; @@ -129,13 +128,11 @@ namespace ChocolArm64.Decoders do { - //TODO: This needs to be changed to support both AArch32 and AArch64, - //once JIT support is introduced on AArch32 aswell. - opCode = DecodeOpCode(state, memory, position); + opCode = DecodeOpCode(memory, position, mode); block.OpCodes.Add(opCode); - position += 4; + position += opCode.OpCodeSizeInBytes; } while (!(IsBranch(opCode) || IsException(opCode))); @@ -145,7 +142,35 @@ namespace ChocolArm64.Decoders private static bool IsBranch(OpCode64 opCode) { return opCode is OpCodeBImm64 || - opCode is OpCodeBReg64; + opCode is OpCodeBReg64 || IsAarch32Branch(opCode); + } + + private static bool IsUnconditionalBranch(OpCode64 opCode) + { + return opCode is OpCodeBImmAl64 || + opCode is OpCodeBReg64 || IsAarch32UnconditionalBranch(opCode); + } + + private static bool IsAarch32UnconditionalBranch(OpCode64 opCode) + { + if (!(opCode is OpCode32 op)) + { + return false; + } + + //Note: On ARM32, most instructions have conditional execution, + //so there's no "Always" (unconditional) branch like on ARM64. + //We need to check if the condition is "Always" instead. + return IsAarch32Branch(op) && op.Cond >= Condition.Al; + } + + private static bool IsAarch32Branch(OpCode64 opCode) + { + //Note: On ARM32, most ALU operations can write to R15 (PC), + //so we must consider such operations as a branch in potential aswell. + return opCode is IOpCodeBImm32 || + opCode is IOpCodeBReg32 || + (opCode is IOpCodeAlu32 op && op.Rd == RegisterAlias.Aarch32Pc); } private static bool IsException(OpCode64 opCode) @@ -155,20 +180,26 @@ namespace ChocolArm64.Decoders opCode.Emitter == InstEmit.Und; } - public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position) + public static OpCode64 DecodeOpCode(MemoryManager memory, long position, ExecutionMode mode) { int opCode = memory.ReadInt32(position); Inst inst; - if (state.ExecutionMode == ExecutionMode.AArch64) + if (mode == ExecutionMode.Aarch64) { inst = OpCodeTable.GetInstA64(opCode); } else { - //TODO: Thumb support. - inst = OpCodeTable.GetInstA32(opCode); + if (mode == ExecutionMode.Aarch32Arm) + { + inst = OpCodeTable.GetInstA32(opCode); + } + else /* if (mode == ExecutionMode.Aarch32Thumb) */ + { + inst = OpCodeTable.GetInstT32(opCode); + } } OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode); diff --git a/ChocolArm64/Decoders/DecoderHelper.cs b/ChocolArm64/Decoders/DecoderHelper.cs index 6ee279d7d..2209472b7 100644 --- a/ChocolArm64/Decoders/DecoderHelper.cs +++ b/ChocolArm64/Decoders/DecoderHelper.cs @@ -89,6 +89,11 @@ namespace ChocolArm64.Decoders return value; } + public static long DecodeImm24_2(int opCode) + { + return ((long)opCode << 40) >> 38; + } + public static long DecodeImm26_2(int opCode) { return ((long)opCode << 38) >> 36; diff --git a/ChocolArm64/Decoders/IOpCode32.cs b/ChocolArm64/Decoders/IOpCode32.cs new file mode 100644 index 000000000..3353ffe8d --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode32.cs @@ -0,0 +1,9 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCode32 : IOpCode64 + { + Condition Cond { get; } + + uint GetPc(); + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeAlu32.cs b/ChocolArm64/Decoders/IOpCodeAlu32.cs new file mode 100644 index 000000000..9a4648865 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeAlu32.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAlu32 : IOpCode32 + { + int Rd { get; } + int Rn { get; } + + bool SetFlags { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeBImm.cs b/ChocolArm64/Decoders/IOpCodeBImm.cs new file mode 100644 index 000000000..f0c6a8325 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeBImm.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeBImm : IOpCode64 + { + long Imm { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeBImm32.cs b/ChocolArm64/Decoders/IOpCodeBImm32.cs new file mode 100644 index 000000000..cc8248f27 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeBImm32.cs @@ -0,0 +1,4 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeBImm32 : IOpCode32, IOpCodeBImm { } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeBReg32.cs b/ChocolArm64/Decoders/IOpCodeBReg32.cs new file mode 100644 index 000000000..fb9d94eaf --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeBReg32.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeBReg32 : IOpCode32 + { + int Rm { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeCond64.cs b/ChocolArm64/Decoders/IOpCodeCond64.cs index 9c39d6332..2c465406b 100644 --- a/ChocolArm64/Decoders/IOpCodeCond64.cs +++ b/ChocolArm64/Decoders/IOpCodeCond64.cs @@ -2,6 +2,6 @@ namespace ChocolArm64.Decoders { interface IOpCodeCond64 : IOpCode64 { - Cond Cond { get; } + Condition Cond { get; } } } \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode32.cs b/ChocolArm64/Decoders/OpCode32.cs new file mode 100644 index 000000000..8534b78fc --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCode32 : OpCode64 + { + public Condition Cond { get; protected set; } + + public OpCode32(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + RegisterSize = RegisterSize.Int32; + + Cond = (Condition)((uint)opCode >> 28); + } + + public uint GetPc() + { + //Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline, + //the PC actually points 2 instructions ahead. + return (uint)Position + (uint)OpCodeSizeInBytes * 2; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode64.cs b/ChocolArm64/Decoders/OpCode64.cs index b2dc363b8..1072228e1 100644 --- a/ChocolArm64/Decoders/OpCode64.cs +++ b/ChocolArm64/Decoders/OpCode64.cs @@ -9,9 +9,10 @@ namespace ChocolArm64.Decoders public long Position { get; private set; } public int RawOpCode { get; private set; } - public InstEmitter Emitter { get; protected set; } - public InstInterpreter Interpreter { get; protected set; } - public RegisterSize RegisterSize { get; protected set; } + public int OpCodeSizeInBytes { get; protected set; } = 4; + + public InstEmitter Emitter { get; protected set; } + public RegisterSize RegisterSize { get; protected set; } public OpCode64(Inst inst, long position, int opCode) { @@ -20,8 +21,7 @@ namespace ChocolArm64.Decoders RegisterSize = RegisterSize.Int64; - Emitter = inst.Emitter; - Interpreter = inst.Interpreter; + Emitter = inst.Emitter; } public int GetBitsCount() diff --git a/ChocolArm64/Decoders/OpCodeAlu32.cs b/ChocolArm64/Decoders/OpCodeAlu32.cs new file mode 100644 index 000000000..9612d9c29 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAlu32.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAlu32 : OpCode32, IOpCodeAlu32 + { + public int Rd { get; private set; } + public int Rn { get; private set; } + + public bool SetFlags { get; private set; } + + public OpCodeAlu32(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = (opCode >> 12) & 0xf; + Rn = (opCode >> 16) & 0xf; + + SetFlags = ((opCode >> 20) & 1) != 0; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAluImm32.cs b/ChocolArm64/Decoders/OpCodeAluImm32.cs new file mode 100644 index 000000000..22436709c --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAluImm32.cs @@ -0,0 +1,21 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluImm32 : OpCodeAlu32 + { + public int Imm { get; private set; } + + public bool IsRotated { get; private set; } + + public OpCodeAluImm32(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int value = (opCode >> 0) & 0xff; + int shift = (opCode >> 8) & 0xf; + + Imm = BitUtils.RotateRight(value, shift * 2, 32); + + IsRotated = shift != 0; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAluImm8T16.cs b/ChocolArm64/Decoders/OpCodeAluImm8T16.cs new file mode 100644 index 000000000..beb6dcaa5 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAluImm8T16.cs @@ -0,0 +1,22 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluImm8T16 : OpCodeT16, IOpCodeAlu32 + { + private int _rdn; + + public int Rd => _rdn; + public int Rn => _rdn; + + public bool SetFlags => false; + + public int Imm { get; private set; } + + public OpCodeAluImm8T16(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = (opCode >> 0) & 0xff; + _rdn = (opCode >> 8) & 0x7; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAluRsImm32.cs b/ChocolArm64/Decoders/OpCodeAluRsImm32.cs new file mode 100644 index 000000000..7b8604486 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAluRsImm32.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluRsImm32 : OpCodeAlu32 + { + public int Rm { get; private set; } + public int Imm { get; private set; } + + public ShiftType ShiftType { get; private set; } + + public OpCodeAluRsImm32(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = (opCode >> 0) & 0xf; + Imm = (opCode >> 7) & 0x1f; + + ShiftType = (ShiftType)((opCode >> 5) & 3); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImm32.cs b/ChocolArm64/Decoders/OpCodeBImm32.cs new file mode 100644 index 000000000..127ac1747 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBImm32.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImm32 : OpCode32, IOpCodeBImm32 + { + public long Imm { get; private set; } + + public OpCodeBImm32(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + uint pc = GetPc(); + + //When the codition is never, the instruction is BLX to Thumb mode. + if (Cond != Condition.Nv) + { + pc &= ~3u; + } + + Imm = pc + DecoderHelper.DecodeImm24_2(opCode); + + if (Cond == Condition.Nv) + { + long H = (opCode >> 23) & 2; + + Imm |= H; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImm64.cs b/ChocolArm64/Decoders/OpCodeBImm64.cs index 71c61bab3..bb5326ce6 100644 --- a/ChocolArm64/Decoders/OpCodeBImm64.cs +++ b/ChocolArm64/Decoders/OpCodeBImm64.cs @@ -2,7 +2,7 @@ using ChocolArm64.Instructions; namespace ChocolArm64.Decoders { - class OpCodeBImm64 : OpCode64 + class OpCodeBImm64 : OpCode64, IOpCodeBImm { public long Imm { get; protected set; } diff --git a/ChocolArm64/Decoders/OpCodeBImmCond64.cs b/ChocolArm64/Decoders/OpCodeBImmCond64.cs index 227023092..ca7df5542 100644 --- a/ChocolArm64/Decoders/OpCodeBImmCond64.cs +++ b/ChocolArm64/Decoders/OpCodeBImmCond64.cs @@ -4,7 +4,7 @@ namespace ChocolArm64.Decoders { class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64 { - public Cond Cond { get; private set; } + public Condition Cond { get; private set; } public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode) { @@ -17,7 +17,7 @@ namespace ChocolArm64.Decoders return; } - Cond = (Cond)(opCode & 0xf); + Cond = (Condition)(opCode & 0xf); Imm = position + DecoderHelper.DecodeImmS19_2(opCode); } diff --git a/ChocolArm64/Decoders/OpCodeBReg32.cs b/ChocolArm64/Decoders/OpCodeBReg32.cs new file mode 100644 index 000000000..f89b1ae12 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBReg32.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBReg32 : OpCode32, IOpCodeBReg32 + { + public int Rm { get; private set; } + + public OpCodeBReg32(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = opCode & 0xf; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBRegT16.cs b/ChocolArm64/Decoders/OpCodeBRegT16.cs new file mode 100644 index 000000000..c6c251308 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBRegT16.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBRegT16 : OpCodeT16, IOpCodeBReg32 + { + public int Rm { get; private set; } + + public OpCodeBRegT16(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = (opCode >> 3) & 0xf; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeCcmp64.cs b/ChocolArm64/Decoders/OpCodeCcmp64.cs index 8e91f15ae..d65a24a47 100644 --- a/ChocolArm64/Decoders/OpCodeCcmp64.cs +++ b/ChocolArm64/Decoders/OpCodeCcmp64.cs @@ -8,7 +8,7 @@ namespace ChocolArm64.Decoders public int Nzcv { get; private set; } protected int RmImm; - public Cond Cond { get; private set; } + public Condition Cond { get; private set; } public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode) { @@ -21,11 +21,11 @@ namespace ChocolArm64.Decoders return; } - Nzcv = (opCode >> 0) & 0xf; - Cond = (Cond)((opCode >> 12) & 0xf); - RmImm = (opCode >> 16) & 0x1f; + Nzcv = (opCode >> 0) & 0xf; + Cond = (Condition)((opCode >> 12) & 0xf); + RmImm = (opCode >> 16) & 0x1f; - Rd = CpuThreadState.ZrIndex; + Rd = RegisterAlias.Zr; } } } \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeCsel64.cs b/ChocolArm64/Decoders/OpCodeCsel64.cs index d1a5a2dbe..108a27983 100644 --- a/ChocolArm64/Decoders/OpCodeCsel64.cs +++ b/ChocolArm64/Decoders/OpCodeCsel64.cs @@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders { public int Rm { get; private set; } - public Cond Cond { get; private set; } + public Condition Cond { get; private set; } public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - Rm = (opCode >> 16) & 0x1f; - Cond = (Cond)((opCode >> 12) & 0xf); + Rm = (opCode >> 16) & 0x1f; + Cond = (Condition)((opCode >> 12) & 0xf); } } } \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdFcond64.cs b/ChocolArm64/Decoders/OpCodeSimdFcond64.cs index f805b3c12..47de231c2 100644 --- a/ChocolArm64/Decoders/OpCodeSimdFcond64.cs +++ b/ChocolArm64/Decoders/OpCodeSimdFcond64.cs @@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders { public int Nzcv { get; private set; } - public Cond Cond { get; private set; } + public Condition Cond { get; private set; } public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - Nzcv = (opCode >> 0) & 0xf; - Cond = (Cond)((opCode >> 12) & 0xf); + Nzcv = (opCode >> 0) & 0xf; + Cond = (Condition)((opCode >> 12) & 0xf); } } } diff --git a/ChocolArm64/Decoders/OpCodeT16.cs b/ChocolArm64/Decoders/OpCodeT16.cs new file mode 100644 index 000000000..005c470d1 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeT16.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeT16 : OpCode32 + { + public OpCodeT16(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Cond = Condition.Al; + + OpCodeSizeInBytes = 2; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders32/A32OpCode.cs b/ChocolArm64/Decoders32/A32OpCode.cs deleted file mode 100644 index f0177a438..000000000 --- a/ChocolArm64/Decoders32/A32OpCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ChocolArm64.Decoders; -using ChocolArm64.Instructions; - -namespace ChocolArm64.Decoders32 -{ - class A32OpCode : OpCode64 - { - public Cond Cond { get; private set; } - - public A32OpCode(Inst inst, long position, int opCode) : base(inst, position, opCode) - { - Cond = (Cond)((uint)opCode >> 28); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoders32/A32OpCodeBImmAl.cs b/ChocolArm64/Decoders32/A32OpCodeBImmAl.cs deleted file mode 100644 index c4a196b63..000000000 --- a/ChocolArm64/Decoders32/A32OpCodeBImmAl.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instructions; - -namespace ChocolArm64.Decoders32 -{ - class A32OpCodeBImmAl : A32OpCode - { - public int Imm; - public int H; - - public A32OpCodeBImmAl(Inst inst, long position, int opCode) : base(inst, position, opCode) - { - Imm = (opCode << 8) >> 6; - H = (opCode >> 23) & 2; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instructions/Inst.cs b/ChocolArm64/Instructions/Inst.cs index 5f6740cae..de9ec18b7 100644 --- a/ChocolArm64/Instructions/Inst.cs +++ b/ChocolArm64/Instructions/Inst.cs @@ -4,17 +4,15 @@ namespace ChocolArm64.Instructions { struct Inst { - public InstInterpreter Interpreter { get; private set; } - public InstEmitter Emitter { get; private set; } - public Type Type { get; private set; } + public InstEmitter Emitter { get; } + public Type Type { get; } - public static Inst Undefined => new Inst(null, InstEmit.Und, null); + public static Inst Undefined => new Inst(InstEmit.Und, null); - public Inst(InstInterpreter interpreter, InstEmitter emitter, Type type) + public Inst(InstEmitter emitter, Type type) { - Interpreter = interpreter; - Emitter = emitter; - Type = type; + Emitter = emitter; + Type = type; } } } \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmit32Helper.cs b/ChocolArm64/Instructions/InstEmit32Helper.cs new file mode 100644 index 000000000..d3ff81387 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmit32Helper.cs @@ -0,0 +1,99 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; + +namespace ChocolArm64.Instructions +{ + static class InstEmit32Helper + { + public static bool IsThumb(OpCode64 op) + { + return op is OpCodeT16; + } + + public static void EmitLoadFromRegister(ILEmitterCtx context, int register) + { + if (register == RegisterAlias.Aarch32Pc) + { + OpCode32 op = (OpCode32)context.CurrOp; + + context.EmitLdc_I4((int)op.GetPc()); + } + else + { + context.EmitLdint(InstEmit32Helper.GetRegisterAlias(context.Mode, register)); + } + } + + public static int GetRegisterAlias(Aarch32Mode mode, int register) + { + //Only registers >= 8 are banked, with registers in the range [8, 12] being + //banked for the FIQ mode, and registers 13 and 14 being banked for all modes. + if ((uint)register < 8) + { + return register; + } + + return GetBankedRegisterAlias(mode, register); + } + + public static int GetBankedRegisterAlias(Aarch32Mode mode, int register) + { + switch (register) + { + case 8: return mode == Aarch32Mode.Fiq + ? RegisterAlias.R8Fiq + : RegisterAlias.R8Usr; + + case 9: return mode == Aarch32Mode.Fiq + ? RegisterAlias.R9Fiq + : RegisterAlias.R9Usr; + + case 10: return mode == Aarch32Mode.Fiq + ? RegisterAlias.R10Fiq + : RegisterAlias.R10Usr; + + case 11: return mode == Aarch32Mode.Fiq + ? RegisterAlias.R11Fiq + : RegisterAlias.R11Usr; + + case 12: return mode == Aarch32Mode.Fiq + ? RegisterAlias.R12Fiq + : RegisterAlias.R12Usr; + + case 13: + switch (mode) + { + case Aarch32Mode.User: + case Aarch32Mode.System: return RegisterAlias.SpUsr; + case Aarch32Mode.Fiq: return RegisterAlias.SpFiq; + case Aarch32Mode.Irq: return RegisterAlias.SpIrq; + case Aarch32Mode.Supervisor: return RegisterAlias.SpSvc; + case Aarch32Mode.Abort: return RegisterAlias.SpAbt; + case Aarch32Mode.Hypervisor: return RegisterAlias.SpHyp; + case Aarch32Mode.Undefined: return RegisterAlias.SpUnd; + + default: throw new ArgumentException(nameof(mode)); + } + + case 14: + switch (mode) + { + case Aarch32Mode.User: + case Aarch32Mode.Hypervisor: + case Aarch32Mode.System: return RegisterAlias.LrUsr; + case Aarch32Mode.Fiq: return RegisterAlias.LrFiq; + case Aarch32Mode.Irq: return RegisterAlias.LrIrq; + case Aarch32Mode.Supervisor: return RegisterAlias.LrSvc; + case Aarch32Mode.Abort: return RegisterAlias.LrAbt; + case Aarch32Mode.Undefined: return RegisterAlias.LrUnd; + + default: throw new ArgumentException(nameof(mode)); + } + + default: throw new ArgumentOutOfRangeException(nameof(register)); + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitAlu.cs b/ChocolArm64/Instructions/InstEmitAlu.cs index c0258ed2b..d5d9cd654 100644 --- a/ChocolArm64/Instructions/InstEmitAlu.cs +++ b/ChocolArm64/Instructions/InstEmitAlu.cs @@ -17,7 +17,7 @@ namespace ChocolArm64.Instructions private static void EmitAdc(ILEmitterCtx context, bool setFlags) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Add); @@ -44,14 +44,14 @@ namespace ChocolArm64.Instructions EmitAddsVCheck(context); } - EmitDataStore(context); + EmitAluStore(context); } - public static void Add(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Add); + public static void Add(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Add); public static void Adds(ILEmitterCtx context) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Add); @@ -59,14 +59,14 @@ namespace ChocolArm64.Instructions EmitAddsCCheck(context); EmitAddsVCheck(context); - EmitDataStoreS(context); + EmitAluStoreS(context); } - public static void And(ILEmitterCtx context) => EmitDataOp(context, OpCodes.And); + public static void And(ILEmitterCtx context) => EmitAluOp(context, OpCodes.And); public static void Ands(ILEmitterCtx context) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.And); @@ -74,17 +74,17 @@ namespace ChocolArm64.Instructions context.EmitZnFlagCheck(); - EmitDataStoreS(context); + EmitAluStoreS(context); } - public static void Asrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr); + public static void Asrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr); public static void Bic(ILEmitterCtx context) => EmitBic(context, false); public static void Bics(ILEmitterCtx context) => EmitBic(context, true); private static void EmitBic(ILEmitterCtx context, bool setFlags) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Not); context.Emit(OpCodes.And); @@ -96,7 +96,7 @@ namespace ChocolArm64.Instructions context.EmitZnFlagCheck(); } - EmitDataStore(context, setFlags); + EmitAluStore(context, setFlags); } public static void Cls(ILEmitterCtx context) @@ -136,15 +136,15 @@ namespace ChocolArm64.Instructions public static void Eon(ILEmitterCtx context) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Not); context.Emit(OpCodes.Xor); - EmitDataStore(context); + EmitAluStore(context); } - public static void Eor(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Xor); + public static void Eor(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Xor); public static void Extr(ILEmitterCtx context) { @@ -166,18 +166,18 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.Or); } - EmitDataStore(context); + EmitAluStore(context); } - public static void Lslv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl); - public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un); + public static void Lslv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shl); + public static void Lsrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr_Un); public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false); public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true); private static void EmitSbc(ILEmitterCtx context, bool setFlags) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Sub); @@ -208,16 +208,16 @@ namespace ChocolArm64.Instructions EmitSubsVCheck(context); } - EmitDataStore(context); + EmitAluStore(context); } - public static void Sub(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Sub); + public static void Sub(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Sub); public static void Subs(ILEmitterCtx context) { context.TryOptMarkCondWithoutCmp(); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Sub); @@ -225,20 +225,20 @@ namespace ChocolArm64.Instructions EmitSubsCCheck(context); EmitSubsVCheck(context); - EmitDataStoreS(context); + EmitAluStoreS(context); } public static void Orn(ILEmitterCtx context) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Not); context.Emit(OpCodes.Or); - EmitDataStore(context); + EmitAluStore(context); } - public static void Orr(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Or); + public static void Orr(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Or); public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context, nameof(SoftFallback.ReverseBits32), @@ -283,22 +283,22 @@ namespace ChocolArm64.Instructions public static void Rorv(ILEmitterCtx context) { - EmitDataLoadRn(context); - EmitDataLoadShift(context); + EmitAluLoadRn(context); + EmitAluLoadShift(context); context.Emit(OpCodes.Shr_Un); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.EmitLdc_I4(context.CurrOp.GetBitsCount()); - EmitDataLoadShift(context); + EmitAluLoadShift(context); context.Emit(OpCodes.Sub); context.Emit(OpCodes.Shl); context.Emit(OpCodes.Or); - EmitDataStore(context); + EmitAluStore(context); } public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div); @@ -309,7 +309,7 @@ namespace ChocolArm64.Instructions //If Rm == 0, Rd = 0 (division by zero). context.EmitLdc_I(0); - EmitDataLoadRm(context); + EmitAluLoadRm(context); context.EmitLdc_I(0); @@ -325,13 +325,13 @@ namespace ChocolArm64.Instructions context.EmitLdc_I(intMin); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.EmitLdc_I(intMin); context.Emit(OpCodes.Ceq); - EmitDataLoadRm(context); + EmitAluLoadRm(context); context.EmitLdc_I(-1); @@ -341,38 +341,38 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.Pop); } - EmitDataLoadRn(context); - EmitDataLoadRm(context); + EmitAluLoadRn(context); + EmitAluLoadRm(context); context.Emit(ilOp); context.MarkLabel(badDiv); - EmitDataStore(context); + EmitAluStore(context); } - private static void EmitDataOp(ILEmitterCtx context, OpCode ilOp) + private static void EmitAluOp(ILEmitterCtx context, OpCode ilOp) { - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(ilOp); - EmitDataStore(context); + EmitAluStore(context); } - private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp) + private static void EmitAluOpShift(ILEmitterCtx context, OpCode ilOp) { - EmitDataLoadRn(context); - EmitDataLoadShift(context); + EmitAluLoadRn(context); + EmitAluLoadShift(context); context.Emit(ilOp); - EmitDataStore(context); + EmitAluStore(context); } - private static void EmitDataLoadShift(ILEmitterCtx context) + private static void EmitAluLoadShift(ILEmitterCtx context) { - EmitDataLoadRm(context); + EmitAluLoadRm(context); context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1); @@ -398,5 +398,22 @@ namespace ChocolArm64.Instructions context.EmitStflg((int)PState.CBit); } + + public static void EmitAluStore(ILEmitterCtx context) => EmitAluStore(context, false); + public static void EmitAluStoreS(ILEmitterCtx context) => EmitAluStore(context, true); + + public static void EmitAluStore(ILEmitterCtx context, bool setFlags) + { + IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; + + if (setFlags || op is IOpCodeAluRs64) + { + context.EmitStintzr(op.Rd); + } + else + { + context.EmitStint(op.Rd); + } + } } } diff --git a/ChocolArm64/Instructions/InstEmitAlu32.cs b/ChocolArm64/Instructions/InstEmitAlu32.cs new file mode 100644 index 000000000..2ebb80738 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitAlu32.cs @@ -0,0 +1,123 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmit32Helper; +using static ChocolArm64.Instructions.InstEmitAluHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit32 + { + public static void Add(ILEmitterCtx context) + { + IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; + + EmitAluLoadOpers(context, setCarry: false); + + context.Emit(OpCodes.Add); + + if (op.SetFlags) + { + context.EmitZnFlagCheck(); + + EmitAddsCCheck(context); + EmitAddsVCheck(context); + } + + EmitAluStore(context); + } + + public static void Mov(ILEmitterCtx context) + { + IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; + + EmitAluLoadOper2(context); + + if (op.SetFlags) + { + context.EmitZnFlagCheck(); + } + + EmitAluStore(context); + } + + public static void Sub(ILEmitterCtx context) + { + IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; + + EmitAluLoadOpers(context, setCarry: false); + + context.Emit(OpCodes.Sub); + + if (op.SetFlags) + { + context.EmitZnFlagCheck(); + + EmitSubsCCheck(context); + EmitSubsVCheck(context); + } + + EmitAluStore(context); + } + + private static void EmitAluStore(ILEmitterCtx context) + { + IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; + + if (op.Rd == RegisterAlias.Aarch32Pc) + { + if (op.SetFlags) + { + //TODO: Load SPSR etc. + + context.EmitLdflg((int)PState.TBit); + + ILLabel lblThumb = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblThumb); + + context.EmitLdc_I4(~3); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblThumb); + + context.EmitLdc_I4(~1); + + context.MarkLabel(lblEnd); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Conv_U8); + context.Emit(OpCodes.Ret); + } + else + { + EmitAluWritePc(context); + } + } + else + { + context.EmitStint(GetRegisterAlias(context.Mode, op.Rd)); + } + } + + private static void EmitAluWritePc(ILEmitterCtx context) + { + if (IsThumb(context.CurrOp)) + { + context.EmitLdc_I4(~1); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Conv_U8); + context.Emit(OpCodes.Ret); + } + else + { + EmitBxWritePc(context); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitAluHelper.cs b/ChocolArm64/Instructions/InstEmitAluHelper.cs index 97c505640..db8fd0e57 100644 --- a/ChocolArm64/Instructions/InstEmitAluHelper.cs +++ b/ChocolArm64/Instructions/InstEmitAluHelper.cs @@ -1,6 +1,7 @@ using ChocolArm64.Decoders; using ChocolArm64.State; using ChocolArm64.Translation; +using System; using System.Reflection.Emit; namespace ChocolArm64.Instructions @@ -14,7 +15,7 @@ namespace ChocolArm64.Instructions context.EmitLdtmp(); context.EmitLdtmp(); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Ceq); @@ -24,7 +25,7 @@ namespace ChocolArm64.Instructions context.EmitLdtmp(); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Or); @@ -37,7 +38,7 @@ namespace ChocolArm64.Instructions //C = Rd < Rn context.Emit(OpCodes.Dup); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); @@ -49,11 +50,11 @@ namespace ChocolArm64.Instructions //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 context.Emit(OpCodes.Dup); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Xor); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Xor); context.Emit(OpCodes.Not); @@ -69,7 +70,7 @@ namespace ChocolArm64.Instructions public static void EmitSbcsCCheck(ILEmitterCtx context) { //C = (Rn == Rm && CIn) || Rn > Rm - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Ceq); @@ -77,7 +78,7 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.And); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Cgt_Un); context.Emit(OpCodes.Or); @@ -88,7 +89,7 @@ namespace ChocolArm64.Instructions public static void EmitSubsCCheck(ILEmitterCtx context) { //C = Rn == Rm || Rn > Rm = !(Rn < Rm) - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Clt_Un); @@ -104,11 +105,11 @@ namespace ChocolArm64.Instructions //V = (Rd ^ Rn) & (Rn ^ Rm) < 0 context.Emit(OpCodes.Dup); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Xor); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Xor); context.Emit(OpCodes.And); @@ -120,35 +121,76 @@ namespace ChocolArm64.Instructions context.EmitStflg((int)PState.VBit); } - public static void EmitDataLoadRm(ILEmitterCtx context) + public static void EmitAluLoadRm(ILEmitterCtx context) { - context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm); - } - - public static void EmitDataLoadOpers(ILEmitterCtx context) - { - EmitDataLoadRn(context); - EmitDataLoadOper2(context); - } - - public static void EmitDataLoadRn(ILEmitterCtx context) - { - IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; - - if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) + if (context.CurrOp is IOpCodeAluRs64 op) { - context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + } + else if (context.CurrOp is OpCodeAluRsImm32 op32) + { + InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm); } else { - context.EmitLdint(op.Rn); + throw new InvalidOperationException(); } } - public static void EmitDataLoadOper2(ILEmitterCtx context) + public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true) + { + EmitAluLoadRn(context); + EmitAluLoadOper2(context, setCarry); + } + + public static void EmitAluLoadRn(ILEmitterCtx context) + { + if (context.CurrOp is IOpCodeAlu64 op) + { + if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) + { + context.EmitLdintzr(op.Rn); + } + else + { + context.EmitLdint(op.Rn); + } + } + else if (context.CurrOp is IOpCodeAlu32 op32) + { + InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn); + } + else + { + throw new InvalidOperationException(); + } + } + + public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true) { switch (context.CurrOp) { + //ARM32. + case OpCodeAluImm32 op: + context.EmitLdc_I4(op.Imm); + + if (op.SetFlags && op.IsRotated) + { + context.EmitLdc_I4((int)((uint)op.Imm >> 31)); + + context.EmitStflg((int)PState.CBit); + } + break; + + case OpCodeAluRsImm32 op: + EmitLoadRmShiftedByImmediate(context, op, setCarry); + break; + + case OpCodeAluImm8T16 op: + context.EmitLdc_I4(op.Imm); + break; + + //ARM64. case IOpCodeAluImm64 op: context.EmitLdc_I(op.Imm); break; @@ -170,23 +212,8 @@ namespace ChocolArm64.Instructions context.EmitCast(op.IntType); context.EmitLsl(op.Shift); break; - } - } - public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false); - public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true); - - public static void EmitDataStore(ILEmitterCtx context, bool setFlags) - { - IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; - - if (setFlags || op is IOpCodeAluRs64) - { - context.EmitStintzr(op.Rd); - } - else - { - context.EmitStint(op.Rd); + default: throw new InvalidOperationException(); } } @@ -217,5 +244,219 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.And); context.EmitStflg((int)PState.NBit); } + + //ARM32 helpers. + private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCodeAluRsImm32 op, bool setCarry) + { + int shift = op.Imm; + + if (shift == 0) + { + switch (op.ShiftType) + { + case ShiftType.Lsr: shift = 32; break; + case ShiftType.Asr: shift = 32; break; + case ShiftType.Ror: shift = 1; break; + } + } + + context.EmitLdint(op.Rm); + + if (shift != 0) + { + setCarry &= op.SetFlags; + + switch (op.ShiftType) + { + case ShiftType.Lsl: EmitLslC(context, setCarry, shift); break; + case ShiftType.Lsr: EmitLsrC(context, setCarry, shift); break; + case ShiftType.Asr: EmitAsrC(context, setCarry, shift); break; + case ShiftType.Ror: + if (op.Imm != 0) + { + EmitRorC(context, setCarry, shift); + } + else + { + EmitRrxC(context, setCarry); + } + break; + } + } + } + + private static void EmitLslC(ILEmitterCtx context, bool setCarry, int shift) + { + if ((uint)shift > 32) + { + EmitShiftByMoreThan32(context, setCarry); + } + else if (shift == 32) + { + if (setCarry) + { + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + else + { + context.Emit(OpCodes.Pop); + } + + context.EmitLdc_I4(0); + } + else + { + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(32 - shift); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + + context.EmitLsl(shift); + } + } + + private static void EmitLsrC(ILEmitterCtx context, bool setCarry, int shift) + { + if ((uint)shift > 32) + { + EmitShiftByMoreThan32(context, setCarry); + } + else if (shift == 32) + { + if (setCarry) + { + context.EmitLsr(31); + + context.EmitStflg((int)PState.CBit); + } + else + { + context.Emit(OpCodes.Pop); + } + + context.EmitLdc_I4(0); + } + else + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(shift - 1); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + + context.EmitLsr(shift); + } + } + + private static void EmitShiftByMoreThan32(ILEmitterCtx context, bool setCarry) + { + context.Emit(OpCodes.Pop); + + context.EmitLdc_I4(0); + + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitStflg((int)PState.CBit); + } + } + + private static void EmitAsrC(ILEmitterCtx context, bool setCarry, int shift) + { + if ((uint)shift >= 32) + { + context.EmitAsr(31); + + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + } + else + { + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(shift - 1); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + + context.EmitAsr(shift); + } + } + + private static void EmitRorC(ILEmitterCtx context, bool setCarry, int shift) + { + shift &= 0x1f; + + context.EmitRor(shift); + + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(31); + + context.EmitStflg((int)PState.CBit); + } + } + + private static void EmitRrxC(ILEmitterCtx context, bool setCarry) + { + //Rotate right by 1 with carry. + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitSttmp(); + } + + context.EmitLsr(1); + + context.EmitLdflg((int)PState.CBit); + + context.EmitLsl(31); + + context.Emit(OpCodes.Or); + + if (setCarry) + { + context.EmitLdtmp(); + context.EmitStflg((int)PState.CBit); + } + } } } diff --git a/ChocolArm64/Instructions/InstEmitCcmp.cs b/ChocolArm64/Instructions/InstEmitCcmp.cs index b91104c93..714ae06a0 100644 --- a/ChocolArm64/Instructions/InstEmitCcmp.cs +++ b/ChocolArm64/Instructions/InstEmitCcmp.cs @@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions context.MarkLabel(lblTrue); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); if (cmpOp == CcmpOp.Cmp) { diff --git a/ChocolArm64/Instructions/InstEmitFlow.cs b/ChocolArm64/Instructions/InstEmitFlow.cs index 758bf2120..181c6a04f 100644 --- a/ChocolArm64/Instructions/InstEmitFlow.cs +++ b/ChocolArm64/Instructions/InstEmitFlow.cs @@ -36,36 +36,10 @@ namespace ChocolArm64.Instructions OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp; context.EmitLdc_I(op.Position + 4); - context.EmitStint(CpuThreadState.LrIndex); + context.EmitStint(RegisterAlias.Lr); context.EmitStoreState(); - if (context.TryOptEmitSubroutineCall()) - { - //Note: the return value of the called method will be placed - //at the Stack, the return value is always a Int64 with the - //return address of the function. We check if the address is - //correct, if it isn't we keep returning until we reach the dispatcher. - context.Emit(OpCodes.Dup); - - context.EmitLdc_I8(op.Position + 4); - - ILLabel lblContinue = new ILLabel(); - - context.Emit(OpCodes.Beq_S, lblContinue); - context.Emit(OpCodes.Ret); - - context.MarkLabel(lblContinue); - - context.Emit(OpCodes.Pop); - - context.EmitLoadState(); - } - else - { - context.EmitLdc_I8(op.Imm); - - context.Emit(OpCodes.Ret); - } + InstEmitFlowHelper.EmitCall(context, op.Imm); } public static void Blr(ILEmitterCtx context) @@ -74,7 +48,7 @@ namespace ChocolArm64.Instructions context.EmitLdintzr(op.Rn); context.EmitLdc_I(op.Position + 4); - context.EmitStint(CpuThreadState.LrIndex); + context.EmitStint(RegisterAlias.Lr); context.EmitStoreState(); context.Emit(OpCodes.Ret); @@ -106,7 +80,7 @@ namespace ChocolArm64.Instructions public static void Ret(ILEmitterCtx context) { context.EmitStoreState(); - context.EmitLdint(CpuThreadState.LrIndex); + context.EmitLdint(RegisterAlias.Lr); context.Emit(OpCodes.Ret); } @@ -128,7 +102,7 @@ namespace ChocolArm64.Instructions EmitBranch(context, ilOp); } - private static void EmitBranch(ILEmitterCtx context, Cond cond) + private static void EmitBranch(ILEmitterCtx context, Condition cond) { OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; diff --git a/ChocolArm64/Instructions/InstEmitFlow32.cs b/ChocolArm64/Instructions/InstEmitFlow32.cs new file mode 100644 index 000000000..03b399364 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitFlow32.cs @@ -0,0 +1,99 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmit32Helper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit32 + { + public static void B(ILEmitterCtx context) + { + IOpCodeBImm32 op = (IOpCodeBImm32)context.CurrOp; + + if (context.CurrBlock.Branch != null) + { + context.Emit(OpCodes.Br, context.GetLabel(op.Imm)); + } + else + { + context.EmitStoreState(); + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + public static void Bl(ILEmitterCtx context) + { + Blx(context, x: false); + } + + public static void Blx(ILEmitterCtx context) + { + Blx(context, x: true); + } + + public static void Bx(ILEmitterCtx context) + { + IOpCodeBReg32 op = (IOpCodeBReg32)context.CurrOp; + + context.EmitStoreState(); + + EmitLoadFromRegister(context, op.Rm); + + EmitBxWritePc(context); + } + + private static void Blx(ILEmitterCtx context, bool x) + { + IOpCodeBImm32 op = (IOpCodeBImm32)context.CurrOp; + + uint pc = op.GetPc(); + + bool isThumb = IsThumb(context.CurrOp); + + if (!isThumb) + { + context.EmitLdc_I(op.GetPc() - 4); + } + else + { + context.EmitLdc_I(op.GetPc() | 1); + } + + context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr)); + context.EmitStoreState(); + + //If x is true, then this is a branch with link and exchange. + //In this case we need to swap the mode between Arm <-> Thumb. + if (x) + { + context.EmitLdc_I4(isThumb ? 0 : 1); + + context.EmitStflg((int)PState.TBit); + } + + InstEmitFlowHelper.EmitCall(context, op.Imm); + } + + private static void EmitBxWritePc(ILEmitterCtx context) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.TBit); + + context.EmitLdc_I4(~1); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Conv_U8); + context.Emit(OpCodes.Ret); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitFlowHelper.cs b/ChocolArm64/Instructions/InstEmitFlowHelper.cs new file mode 100644 index 000000000..cf093bb34 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitFlowHelper.cs @@ -0,0 +1,39 @@ +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static class InstEmitFlowHelper + { + public static void EmitCall(ILEmitterCtx context, long imm) + { + if (context.TryOptEmitSubroutineCall()) + { + //Note: the return value of the called method will be placed + //at the Stack, the return value is always a Int64 with the + //return address of the function. We check if the address is + //correct, if it isn't we keep returning until we reach the dispatcher. + context.Emit(OpCodes.Dup); + + context.EmitLdc_I8(context.CurrOp.Position + 4); + + ILLabel lblContinue = new ILLabel(); + + context.Emit(OpCodes.Beq_S, lblContinue); + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblContinue); + + context.Emit(OpCodes.Pop); + + context.EmitLoadState(); + } + else + { + context.EmitLdc_I8(imm); + + context.Emit(OpCodes.Ret); + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdMemory.cs b/ChocolArm64/Instructions/InstEmitSimdMemory.cs index eb0532578..9b84eb868 100644 --- a/ChocolArm64/Instructions/InstEmitSimdMemory.cs +++ b/ChocolArm64/Instructions/InstEmitSimdMemory.cs @@ -168,7 +168,7 @@ namespace ChocolArm64.Instructions context.EmitLdint(op.Rn); - if (op.Rm != CpuThreadState.ZrIndex) + if (op.Rm != RegisterAlias.Zr) { context.EmitLdint(op.Rm); } diff --git a/ChocolArm64/Instructions/InstInterpreter.cs b/ChocolArm64/Instructions/InstInterpreter.cs deleted file mode 100644 index e6354fd5a..000000000 --- a/ChocolArm64/Instructions/InstInterpreter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ChocolArm64.Decoders; -using ChocolArm64.Memory; -using ChocolArm64.State; - -namespace ChocolArm64.Instructions -{ - delegate void InstInterpreter(CpuThreadState state, MemoryManager memory, OpCode64 opCode); -} \ No newline at end of file diff --git a/ChocolArm64/Instructions32/A32InstInterpretAlu.cs b/ChocolArm64/Instructions32/A32InstInterpretAlu.cs deleted file mode 100644 index f3be823ff..000000000 --- a/ChocolArm64/Instructions32/A32InstInterpretAlu.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Instructions32 -{ - static partial class A32InstInterpret - { - - } -} \ No newline at end of file diff --git a/ChocolArm64/Instructions32/A32InstInterpretFlow.cs b/ChocolArm64/Instructions32/A32InstInterpretFlow.cs deleted file mode 100644 index cdf7e4c6a..000000000 --- a/ChocolArm64/Instructions32/A32InstInterpretFlow.cs +++ /dev/null @@ -1,70 +0,0 @@ -using ChocolArm64.Decoders; -using ChocolArm64.Decoders32; -using ChocolArm64.Memory; -using ChocolArm64.State; - -using static ChocolArm64.Instructions32.A32InstInterpretHelper; - -namespace ChocolArm64.Instructions32 -{ - static partial class A32InstInterpret - { - public static void B(CpuThreadState state, MemoryManager memory, OpCode64 opCode) - { - A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode; - - if (IsConditionTrue(state, op.Cond)) - { - BranchWritePc(state, GetPc(state) + (uint)op.Imm); - } - } - - public static void Bl(CpuThreadState state, MemoryManager memory, OpCode64 opCode) - { - Blx(state, memory, opCode, false); - } - - public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode) - { - Blx(state, memory, opCode, true); - } - - public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode, bool x) - { - A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode; - - if (IsConditionTrue(state, op.Cond)) - { - uint pc = GetPc(state); - - if (state.Thumb) - { - state.R14 = pc | 1; - } - else - { - state.R14 = pc - 4U; - } - - if (x) - { - state.Thumb = !state.Thumb; - } - - if (!state.Thumb) - { - pc &= ~3U; - } - - BranchWritePc(state, pc + (uint)op.Imm); - } - } - - private static void BranchWritePc(CpuThreadState state, uint pc) - { - state.R15 = state.Thumb - ? pc & ~1U - : pc & ~3U; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instructions32/A32InstInterpretHelper.cs b/ChocolArm64/Instructions32/A32InstInterpretHelper.cs deleted file mode 100644 index b08e12984..000000000 --- a/ChocolArm64/Instructions32/A32InstInterpretHelper.cs +++ /dev/null @@ -1,65 +0,0 @@ -using ChocolArm64.Decoders; -using ChocolArm64.State; -using System; - -namespace ChocolArm64.Instructions32 -{ - static class A32InstInterpretHelper - { - public static bool IsConditionTrue(CpuThreadState state, Cond cond) - { - switch (cond) - { - case Cond.Eq: return state.Zero; - case Cond.Ne: return !state.Zero; - case Cond.GeUn: return state.Carry; - case Cond.LtUn: return !state.Carry; - case Cond.Mi: return state.Negative; - case Cond.Pl: return !state.Negative; - case Cond.Vs: return state.Overflow; - case Cond.Vc: return !state.Overflow; - case Cond.GtUn: return state.Carry && !state.Zero; - case Cond.LeUn: return !state.Carry && state.Zero; - case Cond.Ge: return state.Negative == state.Overflow; - case Cond.Lt: return state.Negative != state.Overflow; - case Cond.Gt: return state.Negative == state.Overflow && !state.Zero; - case Cond.Le: return state.Negative != state.Overflow && state.Zero; - } - - return true; - } - - public unsafe static uint GetReg(CpuThreadState state, int reg) - { - if ((uint)reg > 15) - { - throw new ArgumentOutOfRangeException(nameof(reg)); - } - - fixed (uint* ptr = &state.R0) - { - return *(ptr + reg); - } - } - - public unsafe static void SetReg(CpuThreadState state, int reg, uint value) - { - if ((uint)reg > 15) - { - throw new ArgumentOutOfRangeException(nameof(reg)); - } - - fixed (uint* ptr = &state.R0) - { - *(ptr + reg) = value; - } - } - - public static uint GetPc(CpuThreadState state) - { - //Due to the old fetch-decode-execute pipeline of old ARM CPUs, - //the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction. - return state.R15 + (state.Thumb ? 2U : 4U); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/OpCodeTable.cs b/ChocolArm64/OpCodeTable.cs index adb71ae7f..34b420fba 100644 --- a/ChocolArm64/OpCodeTable.cs +++ b/ChocolArm64/OpCodeTable.cs @@ -1,7 +1,5 @@ using ChocolArm64.Decoders; -using ChocolArm64.Decoders32; using ChocolArm64.Instructions; -using ChocolArm64.Instructions32; using ChocolArm64.State; using System; using System.Collections.Generic; @@ -10,13 +8,47 @@ namespace ChocolArm64 { static class OpCodeTable { + private const int FastLookupSize = 0x1000; + + private class InstInfo + { + public int Mask; + public int Value; + + public Inst Inst; + + public InstInfo(int mask, int value, Inst inst) + { + Mask = mask; + Value = value; + Inst = inst; + } + } + + private static List _allInstA32 = new List(); + private static List _allInstT32 = new List(); + private static List _allInstA64 = new List(); + + private static InstInfo[][] _instA32FastLookup = new InstInfo[FastLookupSize][]; + private static InstInfo[][] _instT32FastLookup = new InstInfo[FastLookupSize][]; + private static InstInfo[][] _instA64FastLookup = new InstInfo[FastLookupSize][]; + static OpCodeTable() { #region "OpCode Table (AArch32)" //Integer - SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl)); - SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); - SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); + SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCodeAluImm32)); + SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCodeAluRsImm32)); + SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCodeBImm32)); + SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCodeBImm32)); + SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCodeBImm32)); + SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCodeBReg32)); + SetT32( "010001110xxxx000", InstEmit32.Bx, typeof(OpCodeBRegT16)); + SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm32)); + SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCodeAluRsImm32)); + SetT32( "00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm8T16)); + SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCodeAluImm32)); + SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCodeAluRsImm32)); #endregion #region "OpCode Table (AArch64)" @@ -544,63 +576,29 @@ namespace ChocolArm64 SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V, typeof(OpCodeSimdReg64)); #endregion -#region "Generate InstA64FastLookup Table (AArch64)" - var tmp = new List[_fastLookupSize]; - for (int i = 0; i < _fastLookupSize; i++) - { - tmp[i] = new List(); - } - - foreach (var inst in _allInstA64) - { - int mask = ToFastLookupIndex(inst.Mask); - int value = ToFastLookupIndex(inst.Value); - - for (int i = 0; i < _fastLookupSize; i++) - { - if ((i & mask) == value) - { - tmp[i].Add(inst); - } - } - } - - for (int i = 0; i < _fastLookupSize; i++) - { - _instA64FastLookup[i] = tmp[i].ToArray(); - } -#endregion + FillFastLookupTable(_instA32FastLookup, _allInstA32); + FillFastLookupTable(_instT32FastLookup, _allInstT32); + FillFastLookupTable(_instA64FastLookup, _allInstA64); } - private class InstInfo + private static void SetA32(string encoding, InstEmitter emitter, Type type) { - public int Mask; - public int Value; - - public Inst Inst; - - public InstInfo(int mask, int value, Inst inst) - { - Mask = mask; - Value = value; - Inst = inst; - } + Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Arm); } - private static List _allInstA32 = new List(); - private static List _allInstA64 = new List(); - - private static int _fastLookupSize = 0x1000; - private static InstInfo[][] _instA64FastLookup = new InstInfo[_fastLookupSize][]; - - private static void SetA32(string encoding, InstInterpreter interpreter, Type type) + private static void SetT32(string encoding, InstEmitter emitter, Type type) { - Set(encoding, new Inst(interpreter, null, type), ExecutionMode.AArch32); + if (encoding.Length == 16) + { + encoding = "xxxxxxxxxxxxxxxx" + encoding; + } + + Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Thumb); } private static void SetA64(string encoding, InstEmitter emitter, Type type) { - Set(encoding, new Inst(null, emitter, type), ExecutionMode.AArch64); + Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch64); } private static void Set(string encoding, Inst inst, ExecutionMode mode) @@ -673,27 +671,55 @@ namespace ChocolArm64 } } - private static void InsertInst( - int xMask, - int value, - Inst inst, - ExecutionMode mode) + private static void InsertInst(int xMask, int value, Inst inst, ExecutionMode mode) { InstInfo info = new InstInfo(xMask, value, inst); - if (mode == ExecutionMode.AArch64) + switch (mode) { - _allInstA64.Add(info); + case ExecutionMode.Aarch32Arm: _allInstA32.Add(info); break; + case ExecutionMode.Aarch32Thumb: _allInstT32.Add(info); break; + case ExecutionMode.Aarch64: _allInstA64.Add(info); break; } - else + } + + private static void FillFastLookupTable(InstInfo[][] table, List allInsts) + { + List[] tmp = new List[FastLookupSize]; + + for (int i = 0; i < FastLookupSize; i++) { - _allInstA32.Add(info); + tmp[i] = new List(); + } + + foreach (InstInfo inst in allInsts) + { + int mask = ToFastLookupIndex(inst.Mask); + int value = ToFastLookupIndex(inst.Value); + + for (int i = 0; i < FastLookupSize; i++) + { + if ((i & mask) == value) + { + tmp[i].Add(inst); + } + } + } + + for (int i = 0; i < FastLookupSize; i++) + { + table[i] = tmp[i].ToArray(); } } public static Inst GetInstA32(int opCode) { - return GetInstFromList(_allInstA32, opCode); + return GetInstFromList(_instA32FastLookup[ToFastLookupIndex(opCode)], opCode); + } + + public static Inst GetInstT32(int opCode) + { + return GetInstFromList(_instT32FastLookup[ToFastLookupIndex(opCode)], opCode); } public static Inst GetInstA64(int opCode) @@ -708,7 +734,7 @@ namespace ChocolArm64 private static Inst GetInstFromList(IEnumerable instList, int opCode) { - foreach (var node in instList) + foreach (InstInfo node in instList) { if ((opCode & node.Mask) == node.Value) { diff --git a/ChocolArm64/State/Aarch32Mode.cs b/ChocolArm64/State/Aarch32Mode.cs new file mode 100644 index 000000000..bc4e4b645 --- /dev/null +++ b/ChocolArm64/State/Aarch32Mode.cs @@ -0,0 +1,15 @@ +namespace ChocolArm64.State +{ + enum Aarch32Mode + { + User = 0b10000, + Fiq = 0b10001, + Irq = 0b10010, + Supervisor = 0b10011, + Monitor = 0b10110, + Abort = 0b10111, + Hypervisor = 0b11010, + Undefined = 0b11011, + System = 0b11111 + } +} \ No newline at end of file diff --git a/ChocolArm64/State/CpuThreadState.cs b/ChocolArm64/State/CpuThreadState.cs index a4ee5d073..6c00bf482 100644 --- a/ChocolArm64/State/CpuThreadState.cs +++ b/ChocolArm64/State/CpuThreadState.cs @@ -8,25 +8,13 @@ namespace ChocolArm64.State { public class CpuThreadState { - internal const int LrIndex = 30; - internal const int ZrIndex = 31; - internal const int ErgSizeLog2 = 4; internal const int DczSizeLog2 = 4; private const int MinInstForCheck = 4000000; - internal ExecutionMode ExecutionMode; - - //AArch32 state. - public uint R0, R1, R2, R3, - R4, R5, R6, R7, - R8, R9, R10, R11, - R12, R13, R14, R15; - public bool Thumb; - //AArch64 state. public ulong X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, @@ -42,6 +30,10 @@ namespace ChocolArm64.State public bool Zero; public bool Negative; + public bool IsAarch32; + + public int ElrHyp; + public bool Running { get; set; } public int Core { get; set; } @@ -146,6 +138,18 @@ namespace ChocolArm64.State Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode)); } + internal ExecutionMode GetExecutionMode() + { + if (!IsAarch32) + { + return ExecutionMode.Aarch64; + } + else + { + return Thumb ? ExecutionMode.Aarch32Thumb : ExecutionMode.Aarch32Arm; + } + } + internal bool GetFpcrFlag(Fpcr flag) { return (Fpcr & (1 << (int)flag)) != 0; diff --git a/ChocolArm64/State/ExecutionMode.cs b/ChocolArm64/State/ExecutionMode.cs index 4b8c17cec..b735fd5f1 100644 --- a/ChocolArm64/State/ExecutionMode.cs +++ b/ChocolArm64/State/ExecutionMode.cs @@ -2,7 +2,8 @@ namespace ChocolArm64.State { enum ExecutionMode { - AArch32, - AArch64 + Aarch64, + Aarch32Arm, + Aarch32Thumb } } \ No newline at end of file diff --git a/ChocolArm64/State/PState.cs b/ChocolArm64/State/PState.cs index 40636c870..aef5f53b1 100644 --- a/ChocolArm64/State/PState.cs +++ b/ChocolArm64/State/PState.cs @@ -5,11 +5,15 @@ namespace ChocolArm64.State [Flags] enum PState { + TBit = 5, + VBit = 28, CBit = 29, ZBit = 30, NBit = 31, + T = 1 << TBit, + V = 1 << VBit, C = 1 << CBit, Z = 1 << ZBit, diff --git a/ChocolArm64/State/Register.cs b/ChocolArm64/State/Register.cs index ea29e7b6b..34588231a 100644 --- a/ChocolArm64/State/Register.cs +++ b/ChocolArm64/State/Register.cs @@ -43,6 +43,8 @@ namespace ChocolArm64.State { switch ((PState)Index) { + case PState.TBit: return GetField(nameof(CpuThreadState.Thumb)); + case PState.VBit: return GetField(nameof(CpuThreadState.Overflow)); case PState.CBit: return GetField(nameof(CpuThreadState.Carry)); case PState.ZBit: return GetField(nameof(CpuThreadState.Zero)); diff --git a/ChocolArm64/State/RegisterAlias.cs b/ChocolArm64/State/RegisterAlias.cs new file mode 100644 index 000000000..8c2b95d73 --- /dev/null +++ b/ChocolArm64/State/RegisterAlias.cs @@ -0,0 +1,41 @@ +namespace ChocolArm64.State +{ + static class RegisterAlias + { + public const int R8Usr = 8; + public const int R9Usr = 9; + public const int R10Usr = 10; + public const int R11Usr = 11; + public const int R12Usr = 12; + public const int SpUsr = 13; + public const int LrUsr = 14; + + public const int SpHyp = 15; + + public const int LrIrq = 16; + public const int SpIrq = 17; + + public const int LrSvc = 18; + public const int SpSvc = 19; + + public const int LrAbt = 20; + public const int SpAbt = 21; + + public const int LrUnd = 22; + public const int SpUnd = 23; + + public const int R8Fiq = 24; + public const int R9Fiq = 25; + public const int R10Fiq = 26; + public const int R11Fiq = 27; + public const int R12Fiq = 28; + public const int SpFiq = 29; + public const int LrFiq = 30; + + public const int Aarch32Lr = 14; + public const int Aarch32Pc = 15; + + public const int Lr = 30; + public const int Zr = 31; + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs index 778b29ade..d4bd93abc 100644 --- a/ChocolArm64/Translation/ILEmitterCtx.cs +++ b/ChocolArm64/Translation/ILEmitterCtx.cs @@ -23,6 +23,8 @@ namespace ChocolArm64.Translation public Block CurrBlock => _currBlock; public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex]; + public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO + private Dictionary _visitedBlocks; private Queue _branchTargets; @@ -97,11 +99,52 @@ namespace ChocolArm64.Translation EmitSynchronization(); } + //On AARCH32 mode, (almost) all instruction can be conditionally + //executed, and the required condition is encoded on the opcode. + //We handle that here, skipping the instruction if the condition + //is not met. We can just ignore it when the condition is "Always", + //because in this case the instruction is always going to be executed. + //Condition "Never" is also ignored because this is a special encoding + //used by some unconditional instructions. + ILLabel lblSkip = null; + + if (CurrOp is OpCode32 op && op.Cond < Condition.Al) + { + lblSkip = new ILLabel(); + + EmitCondBranch(lblSkip, GetInverseCond(op.Cond)); + } + CurrOp.Emitter(this); + if (lblSkip != null) + { + MarkLabel(lblSkip); + + //If this is the last op on the block, and there's no "next" block + //after this one, then we have to return right now, with the address + //of the next instruction to be executed (in the case that the condition + //is false, and the branch was not taken, as all basic blocks should end with + //some kind of branch). + if (CurrOp == CurrBlock.GetLastOp() && CurrBlock.Next == null) + { + EmitStoreState(); + EmitLdc_I8(CurrOp.Position + CurrOp.OpCodeSizeInBytes); + + Emit(OpCodes.Ret); + } + } + _ilBlock.Add(new ILBarrier()); } + private Condition GetInverseCond(Condition cond) + { + //Bit 0 of all conditions is basically a negation bit, so + //inverting this bit has the effect of inverting the condition. + return (Condition)((int)cond ^ 1); + } + private void EmitSynchronization() { EmitLdarg(TranslatedSub.StateArgIdx); @@ -243,27 +286,27 @@ namespace ChocolArm64.Translation { _optOpLastCompare = CurrOp; - InstEmitAluHelper.EmitDataLoadOpers(this); + InstEmitAluHelper.EmitAluLoadOpers(this); Stloc(CmpOptTmp2Index, IoType.Int); Stloc(CmpOptTmp1Index, IoType.Int); } - private Dictionary _branchOps = new Dictionary() + private Dictionary _branchOps = new Dictionary() { - { Cond.Eq, OpCodes.Beq }, - { Cond.Ne, OpCodes.Bne_Un }, - { Cond.GeUn, OpCodes.Bge_Un }, - { Cond.LtUn, OpCodes.Blt_Un }, - { Cond.GtUn, OpCodes.Bgt_Un }, - { Cond.LeUn, OpCodes.Ble_Un }, - { Cond.Ge, OpCodes.Bge }, - { Cond.Lt, OpCodes.Blt }, - { Cond.Gt, OpCodes.Bgt }, - { Cond.Le, OpCodes.Ble } + { Condition.Eq, OpCodes.Beq }, + { Condition.Ne, OpCodes.Bne_Un }, + { Condition.GeUn, OpCodes.Bge_Un }, + { Condition.LtUn, OpCodes.Blt_Un }, + { Condition.GtUn, OpCodes.Bgt_Un }, + { Condition.LeUn, OpCodes.Ble_Un }, + { Condition.Ge, OpCodes.Bge }, + { Condition.Lt, OpCodes.Blt }, + { Condition.Gt, OpCodes.Bgt }, + { Condition.Le, OpCodes.Ble } }; - public void EmitCondBranch(ILLabel target, Cond cond) + public void EmitCondBranch(ILLabel target, Condition cond) { OpCode ilOp; @@ -432,7 +475,7 @@ namespace ChocolArm64.Translation public void EmitLdintzr(int index) { - if (index != CpuThreadState.ZrIndex) + if (index != RegisterAlias.Zr) { EmitLdint(index); } @@ -444,7 +487,7 @@ namespace ChocolArm64.Translation public void EmitStintzr(int index) { - if (index != CpuThreadState.ZrIndex) + if (index != RegisterAlias.Zr) { EmitStint(index); } diff --git a/ChocolArm64/Translator.cs b/ChocolArm64/Translator.cs index 47a05ba2c..af2586f43 100644 --- a/ChocolArm64/Translator.cs +++ b/ChocolArm64/Translator.cs @@ -4,7 +4,6 @@ using ChocolArm64.Memory; using ChocolArm64.State; using ChocolArm64.Translation; using System; -using System.Reflection.Emit; namespace ChocolArm64 { @@ -23,34 +22,10 @@ namespace ChocolArm64 internal void ExecuteSubroutine(CpuThread thread, long position) { - //TODO: Both the execute A32/A64 methods should be merged on the future, - //when both ISAs are implemented with the interpreter and JIT. - //As of now, A32 only has a interpreter and A64 a JIT. - CpuThreadState state = thread.ThreadState; - MemoryManager memory = thread.Memory; - - if (state.ExecutionMode == ExecutionMode.AArch32) - { - ExecuteSubroutineA32(state, memory); - } - else - { - ExecuteSubroutineA64(state, memory, position); - } + ExecuteSubroutine(thread.ThreadState, thread.Memory, position); } - private void ExecuteSubroutineA32(CpuThreadState state, MemoryManager memory) - { - do - { - OpCode64 opCode = Decoder.DecodeOpCode(state, memory, state.R15); - - opCode.Interpreter(state, memory, opCode); - } - while (state.R15 != 0 && state.Running); - } - - private void ExecuteSubroutineA64(CpuThreadState state, MemoryManager memory, long position) + private void ExecuteSubroutine(CpuThreadState state, MemoryManager memory, long position) { do { @@ -61,12 +36,12 @@ namespace ChocolArm64 if (!_cache.TryGetSubroutine(position, out TranslatedSub sub)) { - sub = TranslateTier0(state, memory, position); + sub = TranslateTier0(memory, position, state.GetExecutionMode()); } if (sub.ShouldReJit()) { - TranslateTier1(state, memory, position); + TranslateTier1(memory, position, state.GetExecutionMode()); } position = sub.Execute(state, memory); @@ -79,9 +54,9 @@ namespace ChocolArm64 return _cache.HasSubroutine(position); } - private TranslatedSub TranslateTier0(CpuThreadState state, MemoryManager memory, long position) + private TranslatedSub TranslateTier0(MemoryManager memory, long position, ExecutionMode mode) { - Block block = Decoder.DecodeBasicBlock(state, memory, position); + Block block = Decoder.DecodeBasicBlock(memory, position, mode); ILEmitterCtx context = new ILEmitterCtx(_cache, block); @@ -98,9 +73,9 @@ namespace ChocolArm64 return subroutine; } - private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position) + private void TranslateTier1(MemoryManager memory, long position, ExecutionMode mode) { - Block graph = Decoder.DecodeSubroutine(_cache, state, memory, position); + Block graph = Decoder.DecodeSubroutine(_cache, memory, position, mode); ILEmitterCtx context = new ILEmitterCtx(_cache, graph); diff --git a/ChocolArm64/TranslatorCache.cs b/ChocolArm64/TranslatorCache.cs index 93da555ec..9903ccaa1 100644 --- a/ChocolArm64/TranslatorCache.cs +++ b/ChocolArm64/TranslatorCache.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 840624bed..8a419af34 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -225,11 +225,6 @@ namespace Ryujinx.HLE.HOS } } - if (!metaData.Is64Bits) - { - throw new NotImplementedException("32-bit titles are unsupported!"); - } - CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); LoadNso("rtld"); @@ -428,11 +423,6 @@ namespace Ryujinx.HLE.HOS } } - if (!metaData.Is64Bits) - { - throw new NotImplementedException("32-bit titles are unsupported!"); - } - CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); LoadNso("rtld"); @@ -543,11 +533,6 @@ namespace Ryujinx.HLE.HOS CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); } - if (!metaData.Is64Bits) - { - throw new NotImplementedException("32-bit titles are not supported!"); - } - LoadNso("rtld"); LoadNso("main"); LoadNso("subsdk"); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 30fa4a5f4..0268de7d0 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -78,6 +78,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } + //TODO: ARM32. long framePointer = (long)threadState.X29; while (framePointer != 0) @@ -245,6 +246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset; long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset; + //TODO: Elf32. while (true) { long tagVal = memory.ReadInt64(dynamicOffset + 0); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 855f3a189..fd4730142 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -3,6 +3,7 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.SupervisorCall; @@ -797,6 +798,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { context.ThreadState.Interrupt += InterruptHandler; context.ThreadState.SvcCall += _svcHandler.SvcCall; + context.ThreadState.Undefined += UndefinedInstructionHandler; } private void InterruptHandler(object sender, EventArgs e) @@ -1021,5 +1023,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}."); } + + private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e) + { + throw new UndefinedInstructionException(e.Position, e.RawOpCode); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 302e8f415..c29b0fbc1 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -152,6 +152,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint); + Context.ThreadState.IsAarch32 = (Owner.MmuFlags & 1) == 0; + Context.ThreadState.X0 = argsPtr; Context.ThreadState.X31 = stackTop; diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 568c56efa..a41df557f 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -125,9 +125,14 @@ namespace Ryujinx.HLE.HOS IExecutable[] staticObjects, byte[] arguments = null) { + if (!metaData.Is64Bits) + { + Logger.PrintWarning(LogClass.Loader, "32-bits application detected!"); + } + ulong argsStart = 0; int argsSize = 0; - ulong codeStart = 0x8000000; + ulong codeStart = metaData.Is64Bits ? 0x8000000UL : 0x200000UL; int codeSize = 0; ulong[] nsoBase = new ulong[staticObjects.Length];