diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs index dac376a1db..87b213957d 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 881ee24842..8b9fb5e3cb 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 57e12cd609..d1aa577295 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 1d4f397ac0..2b1954129e 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,85 @@ 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. + if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc) + { + return true; + } + + //Same thing for memory operations. We have the cases where PC is a target + //register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is + //a write back to PC (wback == true && Rn == 15), however the later may + //be "undefined" depending on the CPU, so compilers should not produce that. + if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult) + { + int rt, rn; + + bool wBack, isLoad; + + if (opCode is IOpCode32Mem opMem) + { + rt = opMem.Rt; + rn = opMem.Rn; + wBack = opMem.WBack; + isLoad = opMem.IsLoad; + + //For the dual load, we also need to take into account the + //case were Rt2 == 15 (PC). + if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd) + { + rt = RegisterAlias.Aarch32Pc; + } + } + else if (opCode is IOpCode32MemMult opMemMult) + { + const int pcMask = 1 << RegisterAlias.Aarch32Pc; + + rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0; + rn = opMemMult.Rn; + wBack = opMemMult.PostOffset != 0; + isLoad = opMemMult.IsLoad; + } + else + { + throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder."); + } + + if ((rt == RegisterAlias.Aarch32Pc && isLoad) || + (rn == RegisterAlias.Aarch32Pc && wBack)) + { + return true; + } + } + + //Explicit branch instructions. + return opCode is IOpCode32BImm || + opCode is IOpCode32BReg; } private static bool IsException(OpCode64 opCode) @@ -155,20 +230,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 6ee279d7d6..2209472b7d 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 0000000000..3353ffe8d8 --- /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/IOpCode32Alu.cs b/ChocolArm64/Decoders/IOpCode32Alu.cs new file mode 100644 index 0000000000..d6f6d82a4f --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode32Alu.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCode32Alu : IOpCode32 + { + int Rd { get; } + int Rn { get; } + + bool SetFlags { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCode32BImm.cs b/ChocolArm64/Decoders/IOpCode32BImm.cs new file mode 100644 index 0000000000..b69c1e369f --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode32BImm.cs @@ -0,0 +1,4 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCode32BImm : IOpCode32, IOpCodeBImm { } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCode32BReg.cs b/ChocolArm64/Decoders/IOpCode32BReg.cs new file mode 100644 index 0000000000..a498b02d7c --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode32BReg.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCode32BReg : IOpCode32 + { + int Rm { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCode32Mem.cs b/ChocolArm64/Decoders/IOpCode32Mem.cs new file mode 100644 index 0000000000..8ed25add88 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode32Mem.cs @@ -0,0 +1,12 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCode32Mem : IOpCode32 + { + int Rt { get; } + int Rn { get; } + + bool WBack { get; } + + bool IsLoad { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCode32MemMult.cs b/ChocolArm64/Decoders/IOpCode32MemMult.cs new file mode 100644 index 0000000000..d611c53bff --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode32MemMult.cs @@ -0,0 +1,13 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCode32MemMult : IOpCode32 + { + int Rn { get; } + + int RegisterMask { get; } + + int PostOffset { get; } + + bool IsLoad { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeBImm.cs b/ChocolArm64/Decoders/IOpCodeBImm.cs new file mode 100644 index 0000000000..f0c6a83256 --- /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/IOpCodeCond64.cs b/ChocolArm64/Decoders/IOpCodeCond64.cs index 9c39d63329..2c465406bd 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 0000000000..8534b78fcc --- /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/OpCode32Alu.cs b/ChocolArm64/Decoders/OpCode32Alu.cs new file mode 100644 index 0000000000..0cf066966b --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32Alu.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32Alu : OpCode32, IOpCode32Alu + { + public int Rd { get; private set; } + public int Rn { get; private set; } + + public bool SetFlags { get; private set; } + + public OpCode32Alu(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/OpCode32AluImm.cs b/ChocolArm64/Decoders/OpCode32AluImm.cs new file mode 100644 index 0000000000..4302f117e4 --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32AluImm.cs @@ -0,0 +1,21 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32AluImm : OpCode32Alu + { + public int Imm { get; private set; } + + public bool IsRotated { get; private set; } + + public OpCode32AluImm(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/OpCode32AluRsImm.cs b/ChocolArm64/Decoders/OpCode32AluRsImm.cs new file mode 100644 index 0000000000..f23916be55 --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32AluRsImm.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32AluRsImm : OpCode32Alu + { + public int Rm { get; private set; } + public int Imm { get; private set; } + + public ShiftType ShiftType { get; private set; } + + public OpCode32AluRsImm(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/OpCode32BImm.cs b/ChocolArm64/Decoders/OpCode32BImm.cs new file mode 100644 index 0000000000..43f191eb4d --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32BImm.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32BImm : OpCode32, IOpCode32BImm + { + public long Imm { get; private set; } + + public OpCode32BImm(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/OpCode32BReg.cs b/ChocolArm64/Decoders/OpCode32BReg.cs new file mode 100644 index 0000000000..4a0fa5376b --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32BReg.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32BReg : OpCode32, IOpCode32BReg + { + public int Rm { get; private set; } + + public OpCode32BReg(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = opCode & 0xf; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode32Mem.cs b/ChocolArm64/Decoders/OpCode32Mem.cs new file mode 100644 index 0000000000..ed648a5f73 --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32Mem.cs @@ -0,0 +1,37 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32Mem : OpCode32, IOpCode32Mem + { + public int Rt { get; private set; } + public int Rn { get; private set; } + + public int Imm { get; protected set; } + + public bool Index { get; private set; } + public bool Add { get; private set; } + public bool WBack { get; private set; } + public bool Unprivileged { get; private set; } + + public bool IsLoad { get; private set; } + + public OpCode32Mem(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = (opCode >> 12) & 0xf; + Rn = (opCode >> 16) & 0xf; + + bool isLoad = (opCode & (1 << 20)) != 0; + bool w = (opCode & (1 << 21)) != 0; + bool u = (opCode & (1 << 23)) != 0; + bool p = (opCode & (1 << 24)) != 0; + + Index = p; + Add = u; + WBack = !p || w; + Unprivileged = !p && w; + + IsLoad = isLoad || inst.Emitter == InstEmit32.Ldrd; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode32MemImm.cs b/ChocolArm64/Decoders/OpCode32MemImm.cs new file mode 100644 index 0000000000..ca46e08f87 --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32MemImm.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32MemImm : OpCode32Mem + { + public OpCode32MemImm(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = opCode & 0xfff; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode32MemImm8.cs b/ChocolArm64/Decoders/OpCode32MemImm8.cs new file mode 100644 index 0000000000..02e446e8f1 --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32MemImm8.cs @@ -0,0 +1,15 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32MemImm8 : OpCode32Mem + { + public OpCode32MemImm8(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int imm4L = (opCode >> 0) & 0xf; + int imm4H = (opCode >> 8) & 0xf; + + Imm = imm4L | (imm4H << 4); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode32MemMult.cs b/ChocolArm64/Decoders/OpCode32MemMult.cs new file mode 100644 index 0000000000..652da8a552 --- /dev/null +++ b/ChocolArm64/Decoders/OpCode32MemMult.cs @@ -0,0 +1,57 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCode32MemMult : OpCode32, IOpCode32MemMult + { + public int Rn { get; private set; } + + public int RegisterMask { get; private set; } + public int Offset { get; private set; } + public int PostOffset { get; private set; } + + public bool IsLoad { get; private set; } + + public OpCode32MemMult(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rn = (opCode >> 16) & 0xf; + + bool isLoad = (opCode & (1 << 20)) != 0; + bool w = (opCode & (1 << 21)) != 0; + bool u = (opCode & (1 << 23)) != 0; + bool p = (opCode & (1 << 24)) != 0; + + RegisterMask = opCode & 0xffff; + + int regsSize = 0; + + for (int index = 0; index < 16; index++) + { + regsSize += (RegisterMask >> index) & 1; + } + + regsSize *= 4; + + if (!u) + { + Offset -= regsSize; + } + + if (u == p) + { + Offset += 4; + } + + if (w) + { + PostOffset = u ? regsSize : -regsSize; + } + else + { + PostOffset = 0; + } + + IsLoad = isLoad; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCode64.cs b/ChocolArm64/Decoders/OpCode64.cs index b2dc363b8b..1072228e1c 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/OpCodeBImm64.cs b/ChocolArm64/Decoders/OpCodeBImm64.cs index 71c61bab3b..bb5326ce6b 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 227023092e..ca7df5542c 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/OpCodeCcmp64.cs b/ChocolArm64/Decoders/OpCodeCcmp64.cs index 8e91f15ae7..d65a24a47e 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 d1a5a2dbee..108a279836 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 f805b3c120..47de231c2e 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 0000000000..005c470d1a --- /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/Decoders/OpCodeT16AluImm8.cs b/ChocolArm64/Decoders/OpCodeT16AluImm8.cs new file mode 100644 index 0000000000..52c059f40e --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeT16AluImm8.cs @@ -0,0 +1,22 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu + { + private int _rdn; + + public int Rd => _rdn; + public int Rn => _rdn; + + public bool SetFlags => false; + + public int Imm { get; private set; } + + public OpCodeT16AluImm8(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/OpCodeT16BReg.cs b/ChocolArm64/Decoders/OpCodeT16BReg.cs new file mode 100644 index 0000000000..2951470055 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeT16BReg.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeT16BReg : OpCodeT16, IOpCode32BReg + { + public int Rm { get; private set; } + + public OpCodeT16BReg(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = (opCode >> 3) & 0xf; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders32/A32OpCode.cs b/ChocolArm64/Decoders32/A32OpCode.cs deleted file mode 100644 index f0177a4384..0000000000 --- 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 c4a196b63e..0000000000 --- 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/CryptoHelper.cs b/ChocolArm64/Instructions/CryptoHelper.cs index b38d79a8c7..e9b6ed5fb2 100644 --- a/ChocolArm64/Instructions/CryptoHelper.cs +++ b/ChocolArm64/Instructions/CryptoHelper.cs @@ -9,7 +9,7 @@ namespace ChocolArm64.Instructions static class CryptoHelper { #region "LookUp Tables" - private static byte[] _sBox = + private static readonly byte[] _sBox = new byte[] { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, @@ -29,7 +29,7 @@ namespace ChocolArm64.Instructions 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; - private static byte[] _invSBox = + private static readonly byte[] _invSBox = new byte[] { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, @@ -49,7 +49,7 @@ namespace ChocolArm64.Instructions 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; - private static byte[] _gfMul02 = + private static readonly byte[] _gfMul02 = new byte[] { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, @@ -69,7 +69,7 @@ namespace ChocolArm64.Instructions 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }; - private static byte[] _gfMul03 = + private static readonly byte[] _gfMul03 = new byte[] { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, @@ -89,7 +89,7 @@ namespace ChocolArm64.Instructions 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }; - private static byte[] _gfMul09 = + private static readonly byte[] _gfMul09 = new byte[] { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, @@ -109,7 +109,7 @@ namespace ChocolArm64.Instructions 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }; - private static byte[] _gfMul0B = + private static readonly byte[] _gfMul0B = new byte[] { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, @@ -129,7 +129,7 @@ namespace ChocolArm64.Instructions 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }; - private static byte[] _gfMul0D = + private static readonly byte[] _gfMul0D = new byte[] { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, @@ -149,7 +149,7 @@ namespace ChocolArm64.Instructions 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }; - private static byte[] _gfMul0E = + private static readonly byte[] _gfMul0E = new byte[] { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, @@ -169,9 +169,15 @@ namespace ChocolArm64.Instructions 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }; - private static byte[] _srPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }; + private static readonly byte[] _srPerm = new byte[] + { + 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 + }; - private static byte[] _isrPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }; + private static readonly byte[] _isrPerm = new byte[] + { + 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 + }; #endregion public static Vector128 AesInvMixColumns(Vector128 op) @@ -179,7 +185,7 @@ namespace ChocolArm64.Instructions byte[] inState = new byte[16]; byte[] outState = new byte[16]; - FromVectorToByteArray(inState, ref op); + FromVectorToByteArray(op, inState); for (int columns = 0; columns <= 3; columns++) { @@ -206,7 +212,7 @@ namespace ChocolArm64.Instructions byte[] inState = new byte[16]; byte[] outState = new byte[16]; - FromVectorToByteArray(inState, ref op); + FromVectorToByteArray(op, inState); for (int idx = 0; idx <= 15; idx++) { @@ -223,7 +229,7 @@ namespace ChocolArm64.Instructions byte[] inState = new byte[16]; byte[] outState = new byte[16]; - FromVectorToByteArray(inState, ref op); + FromVectorToByteArray(op, inState); for (int idx = 0; idx <= 15; idx++) { @@ -240,7 +246,7 @@ namespace ChocolArm64.Instructions byte[] inState = new byte[16]; byte[] outState = new byte[16]; - FromVectorToByteArray(inState, ref op); + FromVectorToByteArray(op, inState); for (int columns = 0; columns <= 3; columns++) { @@ -267,7 +273,7 @@ namespace ChocolArm64.Instructions byte[] inState = new byte[16]; byte[] outState = new byte[16]; - FromVectorToByteArray(inState, ref op); + FromVectorToByteArray(op, inState); for (int idx = 0; idx <= 15; idx++) { @@ -284,7 +290,7 @@ namespace ChocolArm64.Instructions byte[] inState = new byte[16]; byte[] outState = new byte[16]; - FromVectorToByteArray(inState, ref op); + FromVectorToByteArray(op, inState); for (int idx = 0; idx <= 15; idx++) { @@ -296,33 +302,30 @@ namespace ChocolArm64.Instructions return op; } - private static void FromVectorToByteArray(byte[] state, ref Vector128 op) - { - ulong uLongLow = VectorHelper.VectorExtractIntZx((op), (byte)0, 3); - ulong uLongHigh = VectorHelper.VectorExtractIntZx((op), (byte)1, 3); - - for (int idx = 0; idx <= 7; idx++) - { - state[idx + 0] = (byte)(uLongLow & 0xFFUL); - state[idx + 8] = (byte)(uLongHigh & 0xFFUL); - - uLongLow >>= 8; - uLongHigh >>= 8; - } - } - - private static void FromByteArrayToVector(byte[] state, ref Vector128 op) + private unsafe static void FromVectorToByteArray(Vector128 op, byte[] state) { if (!Sse2.IsSupported) { throw new PlatformNotSupportedException(); } - op = Sse.StaticCast(Sse2.SetVector128( - state[15], state[14], state[13], state[12], - state[11], state[10], state[9], state[8], - state[7], state[6], state[5], state[4], - state[3], state[2], state[1], state[0])); + fixed (byte* ptr = &state[0]) + { + Sse2.Store(ptr, Sse.StaticCast(op)); + } + } + + private unsafe static void FromByteArrayToVector(byte[] state, ref Vector128 op) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + fixed (byte* ptr = &state[0]) + { + op = Sse.StaticCast(Sse2.LoadVector128(ptr)); + } } } } diff --git a/ChocolArm64/Instructions/Inst.cs b/ChocolArm64/Instructions/Inst.cs index 5f6740caec..de9ec18b7a 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 0000000000..792e96f5a8 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmit32Helper.cs @@ -0,0 +1,145 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +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 void EmitStoreToRegister(ILEmitterCtx context, int register) + { + if (register == RegisterAlias.Aarch32Pc) + { + context.EmitStoreState(); + + EmitBxWritePc(context); + } + else + { + context.EmitStint(GetRegisterAlias(context.Mode, register)); + } + } + + public static void EmitBxWritePc(ILEmitterCtx context) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Dup); + + context.EmitStflg((int)PState.TBit); + + ILLabel lblArmMode = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblArmMode); + + context.EmitLdc_I4(~1); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblArmMode); + + context.EmitLdc_I4(~3); + + context.MarkLabel(lblEnd); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Conv_U8); + context.Emit(OpCodes.Ret); + } + + 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 c0258ed2b7..d5d9cd6541 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 0000000000..539e7c4366 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitAlu32.cs @@ -0,0 +1,141 @@ +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) + { + IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; + + EmitAluLoadOpers(context, setCarry: false); + + context.Emit(OpCodes.Add); + + if (op.SetFlags) + { + context.EmitZnFlagCheck(); + + EmitAddsCCheck(context); + EmitAddsVCheck(context); + } + + EmitAluStore(context); + } + + public static void Cmp(ILEmitterCtx context) + { + IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; + + EmitAluLoadOpers(context, setCarry: false); + + context.Emit(OpCodes.Sub); + + context.EmitZnFlagCheck(); + + EmitSubsCCheck(context); + EmitSubsVCheck(context); + + context.Emit(OpCodes.Pop); + } + + public static void Mov(ILEmitterCtx context) + { + IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; + + EmitAluLoadOper2(context); + + if (op.SetFlags) + { + context.EmitZnFlagCheck(); + } + + EmitAluStore(context); + } + + public static void Sub(ILEmitterCtx context) + { + IOpCode32Alu op = (IOpCode32Alu)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) + { + IOpCode32Alu op = (IOpCode32Alu)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) + { + context.EmitStoreState(); + + 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 97c5056407..181f645ab9 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 OpCode32AluRsImm 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 IOpCode32Alu op32) + { + InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn); + } + else + { + throw new InvalidOperationException(); + } + } + + public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true) { switch (context.CurrOp) { + //ARM32. + case OpCode32AluImm 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 OpCode32AluRsImm op: + EmitLoadRmShiftedByImmediate(context, op, setCarry); + break; + + case OpCodeT16AluImm8 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, OpCode32AluRsImm 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 b91104c938..714ae06a06 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 758bf2120c..181c6a04f7 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 0000000000..61f1d34c53 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitFlow32.cs @@ -0,0 +1,82 @@ +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) + { + IOpCode32BImm op = (IOpCode32BImm)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) + { + IOpCode32BReg op = (IOpCode32BReg)context.CurrOp; + + context.EmitStoreState(); + + EmitLoadFromRegister(context, op.Rm); + + EmitBxWritePc(context); + } + + private static void Blx(ILEmitterCtx context, bool x) + { + IOpCode32BImm op = (IOpCode32BImm)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); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitFlowHelper.cs b/ChocolArm64/Instructions/InstEmitFlowHelper.cs new file mode 100644 index 0000000000..cf093bb34f --- /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/InstEmitMemory32.cs b/ChocolArm64/Instructions/InstEmitMemory32.cs new file mode 100644 index 0000000000..4d6a57a472 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitMemory32.cs @@ -0,0 +1,325 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmit32Helper; +using static ChocolArm64.Instructions.InstEmitMemoryHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit32 + { + private const int ByteSizeLog2 = 0; + private const int HWordSizeLog2 = 1; + private const int WordSizeLog2 = 2; + private const int DWordSizeLog2 = 3; + + [Flags] + enum AccessType + { + Store = 0, + Signed = 1, + Load = 2, + + LoadZx = Load, + LoadSx = Load | Signed, + } + + public static void Ldm(ILEmitterCtx context) + { + OpCode32MemMult op = (OpCode32MemMult)context.CurrOp; + + EmitLoadFromRegister(context, op.Rn); + + bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0; + + bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc); + + if (writeBack) + { + context.Emit(OpCodes.Dup); + } + + context.EmitLdc_I4(op.Offset); + + context.Emit(OpCodes.Add); + + context.EmitSttmp(); + + if (writeBack) + { + context.EmitLdc_I4(op.PostOffset); + + context.Emit(OpCodes.Add); + + EmitStoreToRegister(context, op.Rn); + } + + int mask = op.RegisterMask; + int offset = 0; + + for (int register = 0; mask != 0; mask >>= 1, register++) + { + if ((mask & 1) != 0) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + + context.EmitLdc_I4(offset); + + context.Emit(OpCodes.Add); + + EmitReadZxCall(context, WordSizeLog2); + + EmitStoreToRegister(context, register); + + offset += 4; + } + } + } + + public static void Ldr(ILEmitterCtx context) + { + EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx); + } + + public static void Ldrb(ILEmitterCtx context) + { + EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx); + } + + public static void Ldrd(ILEmitterCtx context) + { + EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx); + } + + public static void Ldrh(ILEmitterCtx context) + { + EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx); + } + + public static void Ldrsb(ILEmitterCtx context) + { + EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx); + } + + public static void Ldrsh(ILEmitterCtx context) + { + EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx); + } + + public static void Stm(ILEmitterCtx context) + { + OpCode32MemMult op = (OpCode32MemMult)context.CurrOp; + + EmitLoadFromRegister(context, op.Rn); + + context.EmitLdc_I4(op.Offset); + + context.Emit(OpCodes.Add); + + context.EmitSttmp(); + + int mask = op.RegisterMask; + int offset = 0; + + for (int register = 0; mask != 0; mask >>= 1, register++) + { + if ((mask & 1) != 0) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + + context.EmitLdc_I4(offset); + + context.Emit(OpCodes.Add); + + EmitLoadFromRegister(context, register); + + EmitWriteCall(context, WordSizeLog2); + + //Note: If Rn is also specified on the register list, + //and Rn is the first register on this list, then the + //value that is written to memory is the unmodified value, + //before the write back. If it is on the list, but it's + //not the first one, then the value written to memory + //varies between CPUs. + if (offset == 0 && op.PostOffset != 0) + { + //Emit write back after the first write. + EmitLoadFromRegister(context, op.Rn); + + context.EmitLdc_I4(op.PostOffset); + + context.Emit(OpCodes.Add); + + EmitStoreToRegister(context, op.Rn); + } + + offset += 4; + } + } + } + + public static void Str(ILEmitterCtx context) + { + EmitLoadOrStore(context, WordSizeLog2, AccessType.Store); + } + + public static void Strb(ILEmitterCtx context) + { + EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store); + } + + public static void Strd(ILEmitterCtx context) + { + EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store); + } + + public static void Strh(ILEmitterCtx context) + { + EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store); + } + + private static void EmitLoadOrStore(ILEmitterCtx context, int size, AccessType accType) + { + OpCode32Mem op = (OpCode32Mem)context.CurrOp; + + if (op.Index || op.WBack) + { + EmitLoadFromRegister(context, op.Rn); + + context.EmitLdc_I4(op.Imm); + + context.Emit(op.Add ? OpCodes.Add : OpCodes.Sub); + + context.EmitSttmp(); + } + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + if (op.Index) + { + context.EmitLdtmp(); + } + else + { + EmitLoadFromRegister(context, op.Rn); + } + + if ((accType & AccessType.Load) != 0) + { + if ((accType & AccessType.Signed) != 0) + { + EmitReadSx32Call(context, size); + } + else + { + EmitReadZxCall(context, size); + } + + if (op.WBack) + { + context.EmitLdtmp(); + + EmitStoreToRegister(context, op.Rn); + } + + if (size == DWordSizeLog2) + { + context.Emit(OpCodes.Dup); + + context.EmitLdflg((int)PState.EBit); + + ILLabel lblBigEndian = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblBigEndian); + + //Little endian mode. + context.Emit(OpCodes.Conv_U4); + + EmitStoreToRegister(context, op.Rt); + + context.EmitLsr(32); + + context.Emit(OpCodes.Conv_U4); + + EmitStoreToRegister(context, op.Rt | 1); + + context.Emit(OpCodes.Br_S, lblEnd); + + //Big endian mode. + context.MarkLabel(lblBigEndian); + + context.EmitLsr(32); + + context.Emit(OpCodes.Conv_U4); + + EmitStoreToRegister(context, op.Rt); + + context.Emit(OpCodes.Conv_U4); + + EmitStoreToRegister(context, op.Rt | 1); + + context.MarkLabel(lblEnd); + } + else + { + EmitStoreToRegister(context, op.Rt); + } + } + else + { + if (op.WBack) + { + context.EmitLdtmp(); + + EmitStoreToRegister(context, op.Rn); + } + + EmitLoadFromRegister(context, op.Rt); + + if (size == DWordSizeLog2) + { + context.Emit(OpCodes.Conv_U8); + + context.EmitLdflg((int)PState.EBit); + + ILLabel lblBigEndian = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblBigEndian); + + //Little endian mode. + EmitLoadFromRegister(context, op.Rt | 1); + + context.Emit(OpCodes.Conv_U8); + + context.EmitLsl(32); + + context.Emit(OpCodes.Or); + + context.Emit(OpCodes.Br_S, lblEnd); + + //Big endian mode. + context.MarkLabel(lblBigEndian); + + context.EmitLsl(32); + + EmitLoadFromRegister(context, op.Rt | 1); + + context.Emit(OpCodes.Conv_U8); + + context.Emit(OpCodes.Or); + + context.MarkLabel(lblEnd); + } + + EmitWriteCall(context, size); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs b/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs index d1e71ecbf4..acb9f7f093 100644 --- a/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs @@ -392,8 +392,7 @@ namespace ChocolArm64.Instructions public static void Fadd_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.AddScalar)); } @@ -408,8 +407,7 @@ namespace ChocolArm64.Instructions public static void Fadd_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Add)); } @@ -470,8 +468,7 @@ namespace ChocolArm64.Instructions public static void Faddp_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Add)); } @@ -486,8 +483,7 @@ namespace ChocolArm64.Instructions public static void Fdiv_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.DivideScalar)); } @@ -502,8 +498,7 @@ namespace ChocolArm64.Instructions public static void Fdiv_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Divide)); } @@ -564,8 +559,7 @@ namespace ChocolArm64.Instructions public static void Fmax_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.MaxScalar)); } @@ -580,8 +574,7 @@ namespace ChocolArm64.Instructions public static void Fmax_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Max)); } @@ -612,8 +605,7 @@ namespace ChocolArm64.Instructions public static void Fmaxp_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Max)); } @@ -628,8 +620,7 @@ namespace ChocolArm64.Instructions public static void Fmin_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.MinScalar)); } @@ -644,8 +635,7 @@ namespace ChocolArm64.Instructions public static void Fmin_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Min)); } @@ -676,8 +666,7 @@ namespace ChocolArm64.Instructions public static void Fminp_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Min)); } @@ -984,8 +973,7 @@ namespace ChocolArm64.Instructions public static void Fmul_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.MultiplyScalar)); } @@ -1005,8 +993,7 @@ namespace ChocolArm64.Instructions public static void Fmul_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Multiply)); } @@ -1753,8 +1740,7 @@ namespace ChocolArm64.Instructions public static void Fsqrt_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.SqrtScalar)); } @@ -1769,8 +1755,7 @@ namespace ChocolArm64.Instructions public static void Fsqrt_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Sqrt)); } @@ -1785,8 +1770,7 @@ namespace ChocolArm64.Instructions public static void Fsub_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarSseOrSse2OpF(context, nameof(Sse.SubtractScalar)); } @@ -1801,8 +1785,7 @@ namespace ChocolArm64.Instructions public static void Fsub_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Subtract)); } @@ -2268,6 +2251,15 @@ namespace ChocolArm64.Instructions } } + public static void Smlal_Ve(ILEmitterCtx context) + { + EmitVectorWidenTernaryOpByElemSx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + public static void Smlsl_V(ILEmitterCtx context) { OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; @@ -2319,11 +2311,25 @@ namespace ChocolArm64.Instructions } } + public static void Smlsl_Ve(ILEmitterCtx context) + { + EmitVectorWidenTernaryOpByElemSx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + public static void Smull_V(ILEmitterCtx context) { EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Mul)); } + public static void Smull_Ve(ILEmitterCtx context) + { + EmitVectorWidenBinaryOpByElemSx(context, () => context.Emit(OpCodes.Mul)); + } + public static void Sqabs_S(ILEmitterCtx context) { EmitScalarSaturatingUnaryOpSx(context, () => EmitAbs(context)); @@ -2929,6 +2935,15 @@ namespace ChocolArm64.Instructions } } + public static void Umlal_Ve(ILEmitterCtx context) + { + EmitVectorWidenTernaryOpByElemZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + public static void Umlsl_V(ILEmitterCtx context) { OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; @@ -2980,11 +2995,25 @@ namespace ChocolArm64.Instructions } } + public static void Umlsl_Ve(ILEmitterCtx context) + { + EmitVectorWidenTernaryOpByElemZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + public static void Umull_V(ILEmitterCtx context) { EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); } + public static void Umull_Ve(ILEmitterCtx context) + { + EmitVectorWidenBinaryOpByElemZx(context, () => context.Emit(OpCodes.Mul)); + } + public static void Uqadd_S(ILEmitterCtx context) { EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add); diff --git a/ChocolArm64/Instructions/InstEmitSimdCmp.cs b/ChocolArm64/Instructions/InstEmitSimdCmp.cs index e118437557..fdf3951e64 100644 --- a/ChocolArm64/Instructions/InstEmitSimdCmp.cs +++ b/ChocolArm64/Instructions/InstEmitSimdCmp.cs @@ -173,8 +173,7 @@ namespace ChocolArm64.Instructions public static void Fcmeq_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar), scalar: true); } @@ -186,8 +185,7 @@ namespace ChocolArm64.Instructions public static void Fcmeq_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqual), scalar: false); } @@ -199,8 +197,7 @@ namespace ChocolArm64.Instructions public static void Fcmge_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true); } @@ -212,8 +209,7 @@ namespace ChocolArm64.Instructions public static void Fcmge_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false); } @@ -225,8 +221,7 @@ namespace ChocolArm64.Instructions public static void Fcmgt_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true); } @@ -238,8 +233,7 @@ namespace ChocolArm64.Instructions public static void Fcmgt_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false); } @@ -251,8 +245,7 @@ namespace ChocolArm64.Instructions public static void Fcmle_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true, isLeOrLt: true); } @@ -264,8 +257,7 @@ namespace ChocolArm64.Instructions public static void Fcmle_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false, isLeOrLt: true); } @@ -277,8 +269,7 @@ namespace ChocolArm64.Instructions public static void Fcmlt_S(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true, isLeOrLt: true); } @@ -290,8 +281,7 @@ namespace ChocolArm64.Instructions public static void Fcmlt_V(ILEmitterCtx context) { - if (Optimizations.FastFP && Optimizations.UseSse - && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseSse2) { EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false, isLeOrLt: true); } diff --git a/ChocolArm64/Instructions/InstEmitSimdCvt.cs b/ChocolArm64/Instructions/InstEmitSimdCvt.cs index fd6146b376..2eac3194d6 100644 --- a/ChocolArm64/Instructions/InstEmitSimdCvt.cs +++ b/ChocolArm64/Instructions/InstEmitSimdCvt.cs @@ -78,7 +78,6 @@ namespace ChocolArm64.Instructions if (Optimizations.UseSse2 && sizeF == 1) { - Type[] typesMov = new Type[] { typeof(Vector128), typeof(Vector128) }; Type[] typesCvt = new Type[] { typeof(Vector128) }; string nameMov = op.RegisterSize == RegisterSize.Simd128 @@ -88,7 +87,7 @@ namespace ChocolArm64.Instructions context.EmitLdvec(op.Rn); context.Emit(OpCodes.Dup); - context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); + context.EmitCall(typeof(Sse).GetMethod(nameMov)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Double), typesCvt)); @@ -144,7 +143,6 @@ namespace ChocolArm64.Instructions if (Optimizations.UseSse2 && sizeF == 1) { - Type[] typesMov = new Type[] { typeof(Vector128), typeof(Vector128) }; Type[] typesCvt = new Type[] { typeof(Vector128) }; string nameMov = op.RegisterSize == RegisterSize.Simd128 @@ -154,15 +152,15 @@ namespace ChocolArm64.Instructions context.EmitLdvec(op.Rd); VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); EmitLdvecWithCastToDouble(context, op.Rn); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); context.Emit(OpCodes.Dup); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); - context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); + context.EmitCall(typeof(Sse).GetMethod(nameMov)); context.EmitStvec(op.Rd); } diff --git a/ChocolArm64/Instructions/InstEmitSimdHelper.cs b/ChocolArm64/Instructions/InstEmitSimdHelper.cs index cea481a697..5a44e1a148 100644 --- a/ChocolArm64/Instructions/InstEmitSimdHelper.cs +++ b/ChocolArm64/Instructions/InstEmitSimdHelper.cs @@ -642,21 +642,21 @@ namespace ChocolArm64.Instructions { OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; - EmitVectorOpByElem(context, emit, op.Index, false, true); + EmitVectorOpByElem(context, emit, op.Index, ternary: false, signed: true); } public static void EmitVectorBinaryOpByElemZx(ILEmitterCtx context, Action emit) { OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; - EmitVectorOpByElem(context, emit, op.Index, false, false); + EmitVectorOpByElem(context, emit, op.Index, ternary: false, signed: false); } public static void EmitVectorTernaryOpByElemZx(ILEmitterCtx context, Action emit) { OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; - EmitVectorOpByElem(context, emit, op.Index, true, false); + EmitVectorOpByElem(context, emit, op.Index, ternary: true, signed: false); } public static void EmitVectorOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed) @@ -809,6 +809,64 @@ namespace ChocolArm64.Instructions context.EmitStvec(op.Rd); } + public static void EmitVectorWidenBinaryOpByElemSx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorWidenOpByElem(context, emit, op.Index, ternary: false, signed: true); + } + + public static void EmitVectorWidenBinaryOpByElemZx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorWidenOpByElem(context, emit, op.Index, ternary: false, signed: false); + } + + public static void EmitVectorWidenTernaryOpByElemSx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorWidenOpByElem(context, emit, op.Index, ternary: true, signed: true); + } + + public static void EmitVectorWidenTernaryOpByElemZx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorWidenOpByElem(context, emit, op.Index, ternary: true, signed: false); + } + + public static void EmitVectorWidenOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + EmitVectorExtract(context, op.Rm, elem, op.Size, signed); + context.EmitSttmp(); + + for (int index = 0; index < elems; index++) + { + if (ternary) + { + EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + } + + EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); + context.EmitLdtmp(); + + emit(); + + EmitVectorInsertTmp(context, index, op.Size + 1); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } + public static void EmitVectorPairwiseOpSx(ILEmitterCtx context, Action emit) { EmitVectorPairwiseOp(context, emit, true); @@ -1416,7 +1474,7 @@ namespace ChocolArm64.Instructions if (Optimizations.UseSse) { //TODO: Use Sse2.MoveScalar once it is fixed, - //as of the time of writing it just crashes the JIT (SDK 2.1.500). + //as of the time of writing it just crashes the JIT (SDK 2.1.503). /*Type[] typesMov = new Type[] { typeof(Vector128) }; diff --git a/ChocolArm64/Instructions/InstEmitSimdMemory.cs b/ChocolArm64/Instructions/InstEmitSimdMemory.cs index eb05325786..9b84eb8681 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/InstEmitSimdMove.cs b/ChocolArm64/Instructions/InstEmitSimdMove.cs index d40ccff915..2844dfdf4c 100644 --- a/ChocolArm64/Instructions/InstEmitSimdMove.cs +++ b/ChocolArm64/Instructions/InstEmitSimdMove.cs @@ -12,6 +12,34 @@ namespace ChocolArm64.Instructions { static partial class InstEmit { +#region "Masks" + private static readonly long[] _masksE0_TrnUzpXtn = new long[] + { + 14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0, + 13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0, + 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0 + }; + + private static readonly long[] _masksE1_TrnUzp = new long[] + { + 15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0, + 15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0, + 15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0 + }; + + private static readonly long[] _masksE0_Uzp = new long[] + { + 13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0, + 11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0 + }; + + private static readonly long[] _masksE1_Uzp = new long[] + { + 15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0, + 15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0 + }; +#endregion + public static void Dup_Gp(ILEmitterCtx context) { OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; @@ -379,15 +407,6 @@ namespace ChocolArm64.Instructions if (Optimizations.UseSsse3) { - long[] masks = new long[] - { - 14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0, - 13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0, - 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0 - }; - - Type[] typesMov = new Type[] { typeof(Vector128), typeof(Vector128) }; - Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128) }; Type[] typesSve = new Type[] { typeof(long), typeof(long) }; string nameMov = op.RegisterSize == RegisterSize.Simd128 @@ -397,18 +416,18 @@ namespace ChocolArm64.Instructions context.EmitLdvec(op.Rd); VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); - EmitLdvecWithSignedCast(context, op.Rn, 0); + EmitLdvecWithSignedCast(context, op.Rn, 0); // value - context.EmitLdc_I8(masks[op.Size]); - context.Emit(OpCodes.Dup); + context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // mask + context.Emit(OpCodes.Dup); // mask context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); - context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl)); + context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0))); - context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); + context.EmitCall(typeof(Sse).GetMethod(nameMov)); context.EmitStvec(op.Rd); } @@ -465,22 +484,61 @@ namespace ChocolArm64.Instructions { OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; - int words = op.GetBitsCount() >> 4; - int pairs = words >> op.Size; - - for (int index = 0; index < pairs; index++) + if (Optimizations.UseSsse3) { - int idx = index << 1; + Type[] typesSve = new Type[] { typeof(long), typeof(long) }; - EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); - EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + string nameUpk = part == 0 + ? nameof(Sse2.UnpackLow) + : nameof(Sse2.UnpackHigh); - EmitVectorInsertTmp(context, idx + 1, op.Size); - EmitVectorInsertTmp(context, idx, op.Size); + EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value + + if (op.Size < 3) + { + context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1 + context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0 + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); + + context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0))); + } + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value + + if (op.Size < 3) + { + context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1 + context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0 + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); + + context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0))); + } + + context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size))); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); } + else + { + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; - context.EmitLdvectmp(); - context.EmitStvec(op.Rd); + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); + EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + + EmitVectorInsertTmp(context, idx + 1, op.Size); + EmitVectorInsertTmp(context, idx, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } if (op.RegisterSize == RegisterSize.Simd64) { @@ -492,26 +550,91 @@ namespace ChocolArm64.Instructions { OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; - int words = op.GetBitsCount() >> 4; - int pairs = words >> op.Size; - - for (int index = 0; index < pairs; index++) + if (Optimizations.UseSsse3) { - int idx = index << 1; + Type[] typesSve = new Type[] { typeof(long), typeof(long) }; - EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); - EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + string nameUpk = part == 0 + ? nameof(Sse2.UnpackLow) + : nameof(Sse2.UnpackHigh); - EmitVectorInsertTmp(context, pairs + index, op.Size); - EmitVectorInsertTmp(context, index, op.Size); + if (op.RegisterSize == RegisterSize.Simd128) + { + EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value + + if (op.Size < 3) + { + context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1 + context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0 + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); + + context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0))); + } + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value + + if (op.Size < 3) + { + context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1 + context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0 + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); + + context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0))); + } + + context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3))); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + } + else + { + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size))); // value + + if (op.Size < 2) + { + context.EmitLdc_I8(_masksE1_Uzp[op.Size]); // maskE1 + context.EmitLdc_I8(_masksE0_Uzp[op.Size]); // maskE0 + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); + + context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0))); + } + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero)); + + context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3))); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + } } - - context.EmitLdvectmp(); - context.EmitStvec(op.Rd); - - if (op.RegisterSize == RegisterSize.Simd64) + else { - EmitVectorZeroUpper(context, op.Rd); + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); + EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + + EmitVectorInsertTmp(context, pairs + index, op.Size); + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } } } @@ -521,36 +644,26 @@ namespace ChocolArm64.Instructions if (Optimizations.UseSse2) { - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); - - Type[] types = new Type[] - { - VectorUIntTypesPerSizeLog2[op.Size], - VectorUIntTypesPerSizeLog2[op.Size] - }; - - string name = part == 0 || (part != 0 && op.RegisterSize == RegisterSize.Simd64) + string nameUpk = part == 0 ? nameof(Sse2.UnpackLow) : nameof(Sse2.UnpackHigh); - context.EmitCall(typeof(Sse2).GetMethod(name, types)); + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + EmitLdvecWithSignedCast(context, op.Rm, op.Size); - if (op.RegisterSize == RegisterSize.Simd64 && part != 0) + if (op.RegisterSize == RegisterSize.Simd128) { - context.EmitLdc_I4(8); + context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size))); + } + else + { + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size))); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero)); - Type[] shTypes = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; - - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), shTypes)); + context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3))); } - EmitStvecWithUnsignedCast(context, op.Rd, op.Size); - - if (op.RegisterSize == RegisterSize.Simd64 && part == 0) - { - EmitVectorZeroUpper(context, op.Rd); - } + EmitStvecWithSignedCast(context, op.Rd, op.Size); } else { @@ -579,5 +692,10 @@ namespace ChocolArm64.Instructions } } } + + private static Type[] GetTypesSflUpk(int size) + { + return new Type[] { VectorIntTypesPerSizeLog2[size], VectorIntTypesPerSizeLog2[size] }; + } } } diff --git a/ChocolArm64/Instructions/InstInterpreter.cs b/ChocolArm64/Instructions/InstInterpreter.cs deleted file mode 100644 index e6354fd5a7..0000000000 --- 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/Instructions/SoftFallback.cs b/ChocolArm64/Instructions/SoftFallback.cs index def953434e..1663889457 100644 --- a/ChocolArm64/Instructions/SoftFallback.cs +++ b/ChocolArm64/Instructions/SoftFallback.cs @@ -664,7 +664,7 @@ namespace ChocolArm64.Instructions for (int bit = highBit; bit >= 0; bit--) { - if (((value >> bit) & 0b1) != 0) + if (((int)(value >> bit) & 0b1) != 0) { return (ulong)(highBit - bit); } @@ -688,7 +688,7 @@ namespace ChocolArm64.Instructions do { nibbleIdx -= 4; - preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111]; + preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111]; count += preCount; } while (preCount == 4); @@ -698,11 +698,6 @@ namespace ChocolArm64.Instructions public static ulong CountSetBits8(ulong value) // "size" is 8 (SIMD&FP Inst.). { - if (value == 0xfful) - { - return 8ul; - } - value = ((value >> 1) & 0x55ul) + (value & 0x55ul); value = ((value >> 2) & 0x33ul) + (value & 0x33ul); diff --git a/ChocolArm64/Instructions/SoftFloat.cs b/ChocolArm64/Instructions/SoftFloat.cs index 39d279de19..3521ad152e 100644 --- a/ChocolArm64/Instructions/SoftFloat.cs +++ b/ChocolArm64/Instructions/SoftFloat.cs @@ -1545,9 +1545,9 @@ namespace ChocolArm64.Instructions return -value; } - private static float ZerosOrOnes(bool zeros) + private static float ZerosOrOnes(bool ones) { - return BitConverter.Int32BitsToSingle(!zeros ? 0 : -1); + return BitConverter.Int32BitsToSingle(ones ? -1 : 0); } private static float FPUnpack( @@ -2629,9 +2629,9 @@ namespace ChocolArm64.Instructions return -value; } - private static double ZerosOrOnes(bool zeros) + private static double ZerosOrOnes(bool ones) { - return BitConverter.Int64BitsToDouble(!zeros ? 0L : -1L); + return BitConverter.Int64BitsToDouble(ones ? -1L : 0L); } private static double FPUnpack( diff --git a/ChocolArm64/Instructions32/A32InstInterpretAlu.cs b/ChocolArm64/Instructions32/A32InstInterpretAlu.cs deleted file mode 100644 index f3be823ffc..0000000000 --- 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 cdf7e4c6af..0000000000 --- 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 b08e12984a..0000000000 --- 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 adb71ae7fb..3a8d3948d8 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,61 @@ 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(OpCode32AluImm)); + SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCode32AluRsImm)); + SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCode32BImm)); + SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCode32BImm)); + SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCode32BImm)); + SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCode32BReg)); + SetT32("010001110xxxx000", InstEmit32.Bx, typeof(OpCodeT16BReg)); + SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstEmit32.Cmp, typeof(OpCode32AluImm)); + SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstEmit32.Cmp, typeof(OpCode32AluRsImm)); + SetA32("<<<<100xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldm, typeof(OpCode32MemMult)); + SetA32("<<<<010xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldr, typeof(OpCode32MemImm)); + SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldrb, typeof(OpCode32MemImm)); + SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrd, typeof(OpCode32MemImm8)); + SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstEmit32.Ldrh, typeof(OpCode32MemImm8)); + SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrsb, typeof(OpCode32MemImm8)); + SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstEmit32.Ldrsh, typeof(OpCode32MemImm8)); + SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCode32AluImm)); + SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCode32AluRsImm)); + SetT32("00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeT16AluImm8)); + SetA32("<<<<100xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Stm, typeof(OpCode32MemMult)); + SetA32("<<<<010xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Str, typeof(OpCode32MemImm)); + SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Strb, typeof(OpCode32MemImm)); + SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstEmit32.Strd, typeof(OpCode32MemImm8)); + SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstEmit32.Strh, typeof(OpCode32MemImm8)); + SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCode32AluImm)); + SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCode32AluRsImm)); #endregion #region "OpCode Table (AArch64)" @@ -413,9 +459,12 @@ namespace ChocolArm64 SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", InstEmit.Smin_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", InstEmit.Sminp_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", InstEmit.Smlal_V, typeof(OpCodeSimdReg64)); + SetA64("0x001111xxxxxxxx0010x0xxxxxxxxxx", InstEmit.Smlal_Ve, typeof(OpCodeSimdRegElem64)); SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", InstEmit.Smlsl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001111xxxxxxxx0110x0xxxxxxxxxx", InstEmit.Smlsl_Ve, typeof(OpCodeSimdRegElem64)); SetA64("0x001110000xxxxx001011xxxxxxxxxx", InstEmit.Smov_S, typeof(OpCodeSimdIns64)); SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", InstEmit.Smull_V, typeof(OpCodeSimdReg64)); + SetA64("0x001111xxxxxxxx1010x0xxxxxxxxxx", InstEmit.Smull_Ve, typeof(OpCodeSimdRegElem64)); SetA64("01011110xx100000011110xxxxxxxxxx", InstEmit.Sqabs_S, typeof(OpCodeSimd64)); SetA64("0>001110<<100000011110xxxxxxxxxx", InstEmit.Sqabs_V, typeof(OpCodeSimd64)); SetA64("01011110xx1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_S, typeof(OpCodeSimdReg64)); @@ -502,9 +551,12 @@ namespace ChocolArm64 SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", InstEmit.Umin_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", InstEmit.Uminp_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", InstEmit.Umlal_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx0010x0xxxxxxxxxx", InstEmit.Umlal_Ve, typeof(OpCodeSimdRegElem64)); SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", InstEmit.Umlsl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx0110x0xxxxxxxxxx", InstEmit.Umlsl_Ve, typeof(OpCodeSimdRegElem64)); SetA64("0x001110000xxxxx001111xxxxxxxxxx", InstEmit.Umov_S, typeof(OpCodeSimdIns64)); SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", InstEmit.Umull_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx1010x0xxxxxxxxxx", InstEmit.Umull_Ve, typeof(OpCodeSimdRegElem64)); SetA64("01111110xx1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_S, typeof(OpCodeSimdReg64)); SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_V, typeof(OpCodeSimdReg64)); SetA64("0>101110<<1xxxxx010111xxxxxxxxxx", InstEmit.Uqrshl_V, typeof(OpCodeSimdReg64)); @@ -544,63 +596,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 +691,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 +754,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 0000000000..bc4e4b6455 --- /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 a4ee5d0737..12edc429a0 100644 --- a/ChocolArm64/State/CpuThreadState.cs +++ b/ChocolArm64/State/CpuThreadState.cs @@ -8,25 +8,11 @@ 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, @@ -37,11 +23,18 @@ namespace ChocolArm64.State V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31; + public bool Aarch32; + + public bool Thumb; + public bool BigEndian; + public bool Overflow; public bool Carry; public bool Zero; public bool Negative; + public int ElrHyp; + public bool Running { get; set; } public int Core { get; set; } @@ -59,10 +52,10 @@ namespace ChocolArm64.State { get { - return (Negative ? (int)PState.N : 0) | - (Zero ? (int)PState.Z : 0) | - (Carry ? (int)PState.C : 0) | - (Overflow ? (int)PState.V : 0); + return (Negative ? (int)PState.NMask : 0) | + (Zero ? (int)PState.ZMask : 0) | + (Carry ? (int)PState.CMask : 0) | + (Overflow ? (int)PState.VMask : 0); } } @@ -146,6 +139,18 @@ namespace ChocolArm64.State Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode)); } + internal ExecutionMode GetExecutionMode() + { + if (!Aarch32) + { + 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 4b8c17cec0..b735fd5f17 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 40636c8705..053a5357c1 100644 --- a/ChocolArm64/State/PState.cs +++ b/ChocolArm64/State/PState.cs @@ -5,19 +5,20 @@ namespace ChocolArm64.State [Flags] enum PState { + TBit = 5, + EBit = 9, + VBit = 28, CBit = 29, ZBit = 30, NBit = 31, - V = 1 << VBit, - C = 1 << CBit, - Z = 1 << ZBit, - N = 1 << NBit, + TMask = 1 << TBit, + EMask = 1 << EBit, - Nz = N | Z, - Cv = C | V, - - Nzcv = Nz | Cv + VMask = 1 << VBit, + CMask = 1 << CBit, + ZMask = 1 << ZBit, + NMask = 1 << NBit } } diff --git a/ChocolArm64/State/Register.cs b/ChocolArm64/State/Register.cs index ea29e7b6b3..12c3f5c34c 100644 --- a/ChocolArm64/State/Register.cs +++ b/ChocolArm64/State/Register.cs @@ -43,6 +43,9 @@ namespace ChocolArm64.State { switch ((PState)Index) { + case PState.TBit: return GetField(nameof(CpuThreadState.Thumb)); + case PState.EBit: return GetField(nameof(CpuThreadState.BigEndian)); + 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 0000000000..8c2b95d730 --- /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 778b29ade9..b5ebff75bd 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); } @@ -487,7 +530,15 @@ namespace ChocolArm64.Translation public void EmitLdflg(int index) => Ldloc(index, IoType.Flag); public void EmitStflg(int index) { - _optOpLastFlagSet = CurrOp; + //Set this only if any of the NZCV flag bits were modified. + //This is used to ensure that, when emiting a direct IL branch + //instruction for compare + branch sequences, we're not expecting + //to use comparison values from an old instruction, when in fact + //the flags were already overwritten by another instruction further along. + if (index >= (int)PState.VBit) + { + _optOpLastFlagSet = CurrOp; + } Stloc(index, IoType.Flag); } diff --git a/ChocolArm64/Translator.cs b/ChocolArm64/Translator.cs index 091db4d6b3..01be1b20d8 100644 --- a/ChocolArm64/Translator.cs +++ b/ChocolArm64/Translator.cs @@ -24,34 +24,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) { ProfileConfig tier0 = Profiles.CPU.TranslateTier0; ProfileConfig tier1 = Profiles.CPU.TranslateTier1; @@ -69,14 +45,14 @@ namespace ChocolArm64 if (!_cache.TryGetSubroutine(position, out TranslatedSub sub)) { Profile.Begin(tier0); - sub = TranslateTier0(state, memory, position); + sub = TranslateTier0(memory, position, state.GetExecutionMode()); Profile.End(tier0); } if (sub.ShouldReJit()) { Profile.Begin(tier1); - TranslateTier1(state, memory, position); + TranslateTier1(memory, position, state.GetExecutionMode()); Profile.End(tier1); } @@ -90,9 +66,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); @@ -109,9 +85,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 93da555ec8..9903ccaa15 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.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 8739fbc677..f20347b6b5 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -5,6 +5,7 @@ namespace Ryujinx.Common.Logging Audio, Cpu, Font, + Emulation, Gpu, Hid, Kernel, diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs index 5e58f8064c..35ca416bc9 100644 --- a/Ryujinx.Common/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Common.Logging public static event EventHandler Updated; + public static bool EnableFileLog { get; set; } + static Logger() { m_EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length]; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index e9143c19d5..deccd890f7 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -278,25 +278,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL { forceUpdate = false; - if (New.ScissorTestEnabled[Index] != Old.ScissorTestEnabled[Index]) + if (New.ScissorTestEnabled[Index]) { - if (New.ScissorTestEnabled[Index]) + // If there is only 1 scissor test, geometry shaders are disabled so the scissor test applies to all viewports + if (New.ScissorTestCount == 1) { - // If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports - if (New.ScissorTestCount == 1) - { - GL.Enable(EnableCap.ScissorTest); - } - else - { - GL.Enable(IndexedEnableCap.ScissorTest, Index); - } - forceUpdate = true; + GL.Enable(EnableCap.ScissorTest); } else { - GL.Disable(IndexedEnableCap.ScissorTest, Index); + GL.Enable(IndexedEnableCap.ScissorTest, Index); } + forceUpdate = true; + } + else + { + GL.Disable(IndexedEnableCap.ScissorTest, Index); } if (New.ScissorTestEnabled[Index] && diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index ce5364e154..0d7bb3cd0a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -367,6 +367,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Disable(EnableCap.FramebufferSrgb); + // Will be re-enabled if needed while binding, called before any game GL calls + GL.Disable(EnableCap.ScissorTest); + GL.BlitFramebuffer( SrcX0, SrcY0, diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index b144cef306..43923da742 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.Gal.Shader private Dictionary m_OutAttributes; private Dictionary m_Gprs; + private Dictionary m_GprsHalf; private Dictionary m_Preds; public IReadOnlyDictionary CbTextures => m_CbTextures; @@ -74,8 +75,9 @@ namespace Ryujinx.Graphics.Gal.Shader public IReadOnlyDictionary InAttributes => m_InAttributes; public IReadOnlyDictionary OutAttributes => m_OutAttributes; - public IReadOnlyDictionary Gprs => m_Gprs; - public IReadOnlyDictionary Preds => m_Preds; + public IReadOnlyDictionary Gprs => m_Gprs; + public IReadOnlyDictionary GprsHalf => m_GprsHalf; + public IReadOnlyDictionary Preds => m_Preds; public GalShaderType ShaderType { get; private set; } @@ -92,8 +94,9 @@ namespace Ryujinx.Graphics.Gal.Shader m_InAttributes = new Dictionary(); m_OutAttributes = new Dictionary(); - m_Gprs = new Dictionary(); - m_Preds = new Dictionary(); + m_Gprs = new Dictionary(); + m_GprsHalf = new Dictionary(); + m_Preds = new Dictionary(); } public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType, ShaderHeader Header) : this(ShaderType) @@ -146,8 +149,9 @@ namespace Ryujinx.Graphics.Gal.Shader Merge(Combined.m_Attributes, VpA.m_Attributes, VpB.m_Attributes); Merge(Combined.m_OutAttributes, VpA.m_OutAttributes, VpB.m_OutAttributes); - Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs); - Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds); + Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs); + Merge(Combined.m_GprsHalf, VpA.m_GprsHalf, VpB.m_GprsHalf); + Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds); //Merge input attributes. foreach (KeyValuePair KV in VpA.m_InAttributes) @@ -343,7 +347,20 @@ namespace Ryujinx.Graphics.Gal.Shader { string Name = GetGprName(Gpr.Index); - m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index)); + if (Gpr.RegisterSize == ShaderRegisterSize.Single) + { + m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index)); + } + else if (Gpr.RegisterSize == ShaderRegisterSize.Half) + { + Name += "_h" + Gpr.HalfPart; + + m_GprsHalf.TryAdd((Gpr.Index << 1) | Gpr.HalfPart, new ShaderDeclInfo(Name, Gpr.Index)); + } + else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */ + { + throw new NotImplementedException("Double types are not supported."); + } } break; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index b74ecabfab..c521af2534 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -377,6 +377,7 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclGprs() { PrintDecls(Decl.Gprs); + PrintDecls(Decl.GprsHalf); } private void PrintDeclPreds() @@ -910,7 +911,23 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetName(ShaderIrOperGpr Gpr) { - return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index); + if (Gpr.IsConst) + { + return "0"; + } + + if (Gpr.RegisterSize == ShaderRegisterSize.Single) + { + return GetNameWithSwizzle(Decl.Gprs, Gpr.Index); + } + else if (Gpr.RegisterSize == ShaderRegisterSize.Half) + { + return GetNameWithSwizzle(Decl.GprsHalf, (Gpr.Index << 1) | Gpr.HalfPart); + } + else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */ + { + throw new NotImplementedException("Double types are not supported."); + } } private string GetValue(ShaderIrOperImm Imm) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 6957e30b2a..24c85c8f0e 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -6,6 +6,14 @@ namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { + private enum HalfOutputType + { + PackedFp16, + Fp32, + MergeH0, + MergeH1 + } + public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position) { EmitBfe(Block, OpCode, ShaderOper.CR); @@ -144,6 +152,16 @@ namespace Ryujinx.Graphics.Gal.Shader EmitFsetp(Block, OpCode, ShaderOper.RR); } + public static void Hadd2_R(ShaderIrBlock Block, long OpCode, int Position) + { + EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fadd); + } + + public static void Hmul2_R(ShaderIrBlock Block, long OpCode, int Position) + { + EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fmul); + } + public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd(Block, OpCode, ShaderOper.CR); @@ -1041,6 +1059,47 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); } + private static void EmitBinaryHalfOp(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) + { + bool AbsB = OpCode.Read(30); + bool NegB = OpCode.Read(31); + bool Sat = OpCode.Read(32); + bool AbsA = OpCode.Read(44); + + ShaderIrOperGpr[] VecA = OpCode.GprHalfVec8(); + ShaderIrOperGpr[] VecB = OpCode.GprHalfVec20(); + + HalfOutputType OutputType = (HalfOutputType)OpCode.Read(49, 3); + + int Elems = OutputType == HalfOutputType.PackedFp16 ? 2 : 1; + int First = OutputType == HalfOutputType.MergeH1 ? 1 : 0; + + for (int Index = First; Index < Elems; Index++) + { + ShaderIrNode OperA = GetAluFabs (VecA[Index], AbsA); + ShaderIrNode OperB = GetAluFabsFneg(VecB[Index], AbsB, NegB); + + ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); + + ShaderIrOperGpr Dst = GetHalfDst(OpCode, OutputType, Index); + + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, GetAluFsat(Op, Sat)))); + } + } + + private static ShaderIrOperGpr GetHalfDst(long OpCode, HalfOutputType OutputType, int Index) + { + switch (OutputType) + { + case HalfOutputType.PackedFp16: return OpCode.GprHalf0(Index); + case HalfOutputType.Fp32: return OpCode.Gpr0(); + case HalfOutputType.MergeH0: return OpCode.GprHalf0(0); + case HalfOutputType.MergeH1: return OpCode.GprHalf0(1); + } + + throw new ArgumentException(nameof(OutputType)); + } + private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { int SubOp = OpCode.Read(41, 3); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index cd65599503..adcc47b955 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -6,8 +6,6 @@ namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { - private const int TempRegStart = 0x100; - private const int ____ = 0x0; private const int R___ = 0x1; private const int _G__ = 0x2; @@ -149,14 +147,18 @@ namespace Ryujinx.Graphics.Gal.Shader for (int Index = 0; Index < Coords.Length; Index++) { - Coords[Index] = OpCode.Gpr8(); + ShaderIrOperGpr CoordReg = OpCode.Gpr8(); - Coords[Index].Index += Index; + CoordReg.Index += Index; - if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex) + if (!CoordReg.IsValidRegister) { - Coords[Index].Index = ShaderIrOperGpr.ZRIndex; + CoordReg.Index = ShaderIrOperGpr.ZRIndex; } + + Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index); + + Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg)); } int ChMask = OpCode.Read(31, 0xf); @@ -167,17 +169,6 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; - for (int Ch = 0; Ch < 4; Ch++) - { - ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); - - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - - ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); - } - int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) @@ -187,18 +178,20 @@ namespace Ryujinx.Graphics.Gal.Shader continue; } - ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); - ShaderIrOperGpr Dst = OpCode.Gpr0(); Dst.Index += RegInc++; - if (Dst.Index >= ShaderIrOperGpr.ZRIndex) + if (!Dst.IsValidRegister || Dst.IsConst) { continue; } - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src))); + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); + + ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); + + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } } @@ -215,57 +208,81 @@ namespace Ryujinx.Graphics.Gal.Shader private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. - ShaderIrNode OperA = OpCode.Gpr8(); - ShaderIrNode OperB = OpCode.Gpr20(); - ShaderIrNode OperC = OpCode.Imm13_36(); - int LutIndex; - LutIndex = OpCode.Gpr0 ().Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; - LutIndex |= OpCode.Gpr28().Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; + LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; + LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0; if (LutIndex == 0) { - //Both registers are RZ, color is not written anywhere. - //So, the intruction is basically a no-op. + //Both destination registers are RZ, do nothing. return; } - int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)]; + bool Fp16 = !OpCode.Read(59); - for (int Ch = 0; Ch < 4; Ch++) - { - ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); - - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - - ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); - } - - int RegInc = 0; + int DstIncrement = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr Dst; - switch (LutIndex) + if (Fp16) { - case 1: Dst = OpCode.Gpr0(); break; - case 2: Dst = OpCode.Gpr28(); break; - case 3: Dst = (RegInc >> 1) != 0 - ? OpCode.Gpr28() - : OpCode.Gpr0 (); break; + //FP16 mode, two components are packed on the two + //halfs of a 32-bits register, as two half-float values. + int HalfPart = DstIncrement & 1; - default: throw new InvalidOperationException(); + switch (LutIndex) + { + case 1: Dst = OpCode.GprHalf0(HalfPart); break; + case 2: Dst = OpCode.GprHalf28(HalfPart); break; + case 3: Dst = (DstIncrement >> 1) != 0 + ? OpCode.GprHalf28(HalfPart) + : OpCode.GprHalf0(HalfPart); break; + + default: throw new InvalidOperationException(); + } + } + else + { + //32-bits mode, each component uses one register. + //Two components uses two consecutive registers. + switch (LutIndex) + { + case 1: Dst = OpCode.Gpr0(); break; + case 2: Dst = OpCode.Gpr28(); break; + case 3: Dst = (DstIncrement >> 1) != 0 + ? OpCode.Gpr28() + : OpCode.Gpr0(); break; + + default: throw new InvalidOperationException(); + } + + Dst.Index += DstIncrement & 1; } - Dst.Index += RegInc++ & 1; + DstIncrement++; return Dst; } + int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)]; + + if (ChMask == 0) + { + //All channels are disabled, do nothing. + return; + } + + ShaderIrNode OperC = OpCode.Imm13_36(); + + ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0); + ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1); + + Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8())); + Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20())); + for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) @@ -273,13 +290,15 @@ namespace Ryujinx.Graphics.Gal.Shader continue; } - ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); + + ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); ShaderIrOperGpr Dst = GetDst(); - if (Dst.Index != ShaderIrOperGpr.ZRIndex) + if (Dst.IsValidRegister && !Dst.IsConst) { - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs index 3af17cae83..6531138e7a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs @@ -75,6 +75,49 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperGpr(OpCode.Read(28, 0xff)); } + private static ShaderIrOperGpr[] GprHalfVec8(this long OpCode) + { + return GetGprHalfVec2(OpCode.Read(8, 0xff), OpCode.Read(47, 3)); + } + + private static ShaderIrOperGpr[] GprHalfVec20(this long OpCode) + { + return GetGprHalfVec2(OpCode.Read(20, 0xff), OpCode.Read(28, 3)); + } + + private static ShaderIrOperGpr[] GetGprHalfVec2(int Gpr, int Mask) + { + if (Mask == 1) + { + //This value is used for FP32, the whole 32-bits register + //is used as each element on the vector. + return new ShaderIrOperGpr[] + { + new ShaderIrOperGpr(Gpr), + new ShaderIrOperGpr(Gpr) + }; + } + + ShaderIrOperGpr Low = new ShaderIrOperGpr(Gpr, 0); + ShaderIrOperGpr High = new ShaderIrOperGpr(Gpr, 1); + + return new ShaderIrOperGpr[] + { + (Mask & 1) != 0 ? High : Low, + (Mask & 2) != 0 ? High : Low + }; + } + + private static ShaderIrOperGpr GprHalf0(this long OpCode, int HalfPart) + { + return new ShaderIrOperGpr(OpCode.Read(0, 0xff), HalfPart); + } + + private static ShaderIrOperGpr GprHalf28(this long OpCode, int HalfPart) + { + return new ShaderIrOperGpr(OpCode.Read(28, 0xff), HalfPart); + } + private static ShaderIrOperImm Imm5_39(this long OpCode) { return new ShaderIrOperImm(OpCode.Read(39, 0x1f)); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs index 9dd196e693..b4a5cab4d4 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs @@ -6,13 +6,26 @@ namespace Ryujinx.Graphics.Gal.Shader public bool IsConst => Index == ZRIndex; - public bool IsValidRegister => (Index <= ZRIndex); + public bool IsValidRegister => (uint)Index <= ZRIndex; - public int Index { get; set; } + public int Index { get; set; } + public int HalfPart { get; set; } + + public ShaderRegisterSize RegisterSize { get; private set; } public ShaderIrOperGpr(int Index) { this.Index = Index; + + RegisterSize = ShaderRegisterSize.Single; + } + + public ShaderIrOperGpr(int Index, int HalfPart) + { + this.Index = Index; + this.HalfPart = HalfPart; + + RegisterSize = ShaderRegisterSize.Half; } public static ShaderIrOperGpr MakeTemporary(int Index = 0) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index d44659c755..177e36c3e1 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Gal.Shader Set("010010111011xx", ShaderDecode.Fsetp_C); Set("0011011x1011xx", ShaderDecode.Fsetp_I); Set("010110111011xx", ShaderDecode.Fsetp_R); + Set("0101110100010x", ShaderDecode.Hadd2_R); + Set("0101110100001x", ShaderDecode.Hmul2_R); Set("0100110010111x", ShaderDecode.I2f_C); Set("0011100x10111x", ShaderDecode.I2f_I); Set("0101110010111x", ShaderDecode.I2f_R); @@ -118,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.Shader Set("110000xxxx111x", ShaderDecode.Tex); Set("1101111010111x", ShaderDecode.Tex_B); Set("1101111101001x", ShaderDecode.Texq); - Set("1101100xxxxxxx", ShaderDecode.Texs); + Set("1101x00xxxxxxx", ShaderDecode.Texs); Set("1101101xxxxxxx", ShaderDecode.Tlds); Set("01011111xxxxxx", ShaderDecode.Vmad); Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderRegisterSize.cs b/Ryujinx.Graphics/Gal/Shader/ShaderRegisterSize.cs new file mode 100644 index 0000000000..eb37359bf4 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderRegisterSize.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + enum ShaderRegisterSize + { + Half, + Single, + Double + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics/Gal/ShaderDumper.cs index d3bcbf0d87..21e92491a7 100644 --- a/Ryujinx.Graphics/Gal/ShaderDumper.cs +++ b/Ryujinx.Graphics/Gal/ShaderDumper.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gal ulong Instruction = 0; //Dump until a NOP instruction is found - while ((Instruction >> 52 & 0xfff8) != 0x50b0) + while ((Instruction >> 48 & 0xfff8) != 0x50b0) { uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0); uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4); diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs index c9a77f1c94..f408b65213 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs @@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Graphics3d // Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1 State.ScissorTestCount = 1; - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + for (int Index = 0; Index < State.ScissorTestCount; Index++) { State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4); @@ -473,6 +473,12 @@ namespace Ryujinx.Graphics.Graphics3d if ((int)State.FlipY == -1) { State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index]; + + // Handle negative viewpont coordinate + if (State.ScissorTestY[Index] < 0) + { + State.ScissorTestY[Index] = 0; + } } } } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 6b5f5723c5..8a419af343 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"); @@ -394,7 +389,57 @@ namespace Ryujinx.HLE.HOS return; } - Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); + // This is not a normal NSP, it's actually a ExeFS as a NSP + Npdm metaData = null; + + PfsFileEntry npdmFile = nsp.Files.FirstOrDefault(x => x.Name.Equals("main.npdm")); + + if (npdmFile != null) + { + Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); + + metaData = new Npdm(nsp.OpenFile(npdmFile).AsStream()); + } + else + { + Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + + metaData = GetDefaultNpdm(); + } + + List staticObjects = new List(); + + void LoadNso(string searchPattern) + { + PfsFileEntry entry = nsp.Files.FirstOrDefault(x => x.Name.Equals(searchPattern)); + + if (entry != null) + { + Logger.PrintInfo(LogClass.Loader, $"Loading {entry.Name}..."); + + NxStaticObject staticObject = new NxStaticObject(nsp.OpenFile(entry).AsStream()); + + staticObjects.Add(staticObject); + } + } + + CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); + + LoadNso("rtld"); + LoadNso("main"); + LoadNso("subsdk*"); + LoadNso("sdk"); + + ContentManager.LoadEntries(); + + if (staticObjects.Count == 0) + { + Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); + } + else + { + ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray()); + } } public void LoadNca(Nca mainNca, Nca controlNca) @@ -488,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 30fa4a5f4c..0268de7d07 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 855f3a1896..fd4730142e 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 302e8f4150..7eb27efc12 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -152,8 +152,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint); + bool isAarch32 = (Owner.MmuFlags & 1) == 0; + + Context.ThreadState.Aarch32 = isAarch32; + Context.ThreadState.X0 = argsPtr; - Context.ThreadState.X31 = stackTop; + + if (isAarch32) + { + Context.ThreadState.X13 = (uint)stackTop; + } + else + { + Context.ThreadState.X31 = stackTop; + } Context.ThreadState.CntfrqEl0 = 19200000; Context.ThreadState.Tpidr = (long)_tlsAddress; diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 568c56efa2..a41df557f8 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]; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs index d97bd7b082..7fc593a849 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs @@ -45,6 +45,32 @@ namespace Ryujinx.Tests.Cpu 0x0F808000u // MUL V0.2S, V0.2S, V0.S[0] }; } + + private static uint[] _SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_() + { + return new uint[] + { + 0x0F402000u, // SMLAL V0.4S, V0.4H, V0.H[0] + 0x0F406000u, // SMLSL V0.4S, V0.4H, V0.H[0] + 0x0F40A000u, // SMULL V0.4S, V0.4H, V0.H[0] + 0x2F402000u, // UMLAL V0.4S, V0.4H, V0.H[0] + 0x2F406000u, // UMLSL V0.4S, V0.4H, V0.H[0] + 0x2F40A000u // UMULL V0.4S, V0.4H, V0.H[0] + }; + } + + private static uint[] _SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_() + { + return new uint[] + { + 0x0F802000u, // SMLAL V0.2D, V0.2S, V0.S[0] + 0x0F806000u, // SMLSL V0.2D, V0.2S, V0.S[0] + 0x0F80A000u, // SMULL V0.2D, V0.2S, V0.S[0] + 0x2F802000u, // UMLAL V0.2D, V0.2S, V0.S[0] + 0x2F806000u, // UMLSL V0.2D, V0.2S, V0.S[0] + 0x2F80A000u // UMULL V0.2D, V0.2S, V0.S[0] + }; + } #endregion private const int RndCnt = 2; @@ -103,6 +129,61 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + + [Test, Pairwise] + public void SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H_")] [Random(RndCnt)] ulong b, + [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <4H4S, 8H4S> + { + uint h = (index >> 2) & 1; + uint l = (index >> 1) & 1; + uint m = index & 1; + + opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (l << 21) | (m << 20) | (h << 11); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_2S_")] [Random(RndCnt)] ulong b, + [Values(0u, 1u, 2u, 3u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D> + { + uint h = (index >> 1) & 1; + uint l = index & 1; + + opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (l << 21) | (h << 11); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } #endif } } diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index fdff2e8aa2..629ead4d1b 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -78,6 +78,8 @@ namespace Ryujinx MaxLevel = Convert.ToInt32(parser.Value("Profiling_Max_Level")), }); + Logger.EnableFileLog = Convert.ToBoolean(parser.Value("Enable_File_Log")); + SystemLanguage SetLanguage = Enum.Parse(parser.Value("System_Language")); device.System.State.SetLanguage(SetLanguage); @@ -134,7 +136,6 @@ namespace Ryujinx }); NpadController = new NpadController( - Convert.ToBoolean(parser.Value("GamePad_Enable")), Convert.ToInt32 (parser.Value("GamePad_Index")), (float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index a02a1e401a..16b2d9d346 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -23,7 +23,10 @@ namespace Ryujinx Config.Read(device); - Logger.Updated += ConsoleLog.Log; + Logger.Updated += Log.LogMessage; + + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; if (args.Length == 1) { @@ -62,6 +65,7 @@ namespace Ryujinx device.LoadNca(args[0]); break; case ".nsp": + case ".pfs0": Console.WriteLine("Loading as NSP."); device.LoadNsp(args[0]); break; @@ -89,6 +93,23 @@ namespace Ryujinx audioOut.Dispose(); } + private static void CurrentDomain_ProcessExit(object sender, EventArgs e) + { + Log.Close(); + } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + var exception = e.ExceptionObject as Exception; + + Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}"); + + if (e.IsTerminating) + { + Log.Close(); + } + } + /// /// Picks an audio output renderer supported on this machine /// diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index e3c854cc80..bf936cddff 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -37,6 +37,9 @@ Profiling_History = 5 #Set the maximum profiling level. Higher values may cause a heavy load on your system but will allow you to profile in more detail. Profiling_Max_Level = 0 +#Enable file logging +Enable_File_Log = false + #System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b #Change System Language System_Language = AmericanEnglish diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/Log.cs similarity index 73% rename from Ryujinx/Ui/ConsoleLog.cs rename to Ryujinx/Ui/Log.cs index ac3d41c9e1..5daae14026 100644 --- a/Ryujinx/Ui/ConsoleLog.cs +++ b/Ryujinx/Ui/Log.cs @@ -1,22 +1,27 @@ -using Ryujinx.Common.Logging; +using Ryujinx.Common.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Reflection; using System.Text; using System.Threading; namespace Ryujinx { - static class ConsoleLog + static class Log { + private static readonly string _path; + + private static StreamWriter _logWriter; + private static Thread _messageThread; private static BlockingCollection _messageQueue; private static Dictionary _logColors; - static ConsoleLog() + static Log() { _logColors = new Dictionary() { @@ -47,6 +52,13 @@ namespace Ryujinx } }); + _path = Path.Combine(Environment.CurrentDirectory, "Ryujinx.log"); + + if (Logger.EnableFileLog) + { + _logWriter = new StreamWriter(File.Open(_path,FileMode.Create, FileAccess.Write)); + } + _messageThread.IsBackground = true; _messageThread.Start(); } @@ -82,26 +94,47 @@ namespace Ryujinx } } + string message = sb.ToString(); + if (_logColors.TryGetValue(e.Level, out ConsoleColor color)) { Console.ForegroundColor = color; - Console.WriteLine(sb.ToString()); + Console.WriteLine(message); Console.ResetColor(); } else { - Console.WriteLine(sb.ToString()); + Console.WriteLine(message); + } + + if (Logger.EnableFileLog) + { + _logWriter.WriteLine(message); } } - public static void Log(object sender, LogEventArgs e) + public static void LogMessage(object sender, LogEventArgs e) { if (!_messageQueue.IsAddingCompleted) { _messageQueue.Add(e); } } + + public static void Close() + { + _messageQueue.CompleteAdding(); + + _messageThread.Join(); + + if (Logger.EnableFileLog) + { + _logWriter.Flush(); + _logWriter.Close(); + _logWriter.Dispose(); + } + } } -} \ No newline at end of file +}