From 6a3aa6cd88cfb01436f036297d08cfe815b52469 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 9 Feb 2018 00:26:20 -0300 Subject: [PATCH] Add FVCTZS (fixed point variant) and LD1 (single structure variant) instructions --- Ryujinx/Cpu/AOpCodeTable.cs | 11 +- Ryujinx/Cpu/AOptimizations.cs | 4 + ...CodeSimdMemMult.cs => AOpCodeSimdMemMs.cs} | 4 +- Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs | 104 ++++++++++++++++++ Ryujinx/Cpu/Instruction/AInstEmitSimd.cs | 98 ++++++++++++++++- Ryujinx/Cpu/Instruction/ASoftFallback.cs | 14 +-- Ryujinx/Cpu/Memory/AMemory.cs | 53 ++++++++- Ryujinx/Cpu/Memory/AMemoryMgr.cs | 9 +- Ryujinx/Loaders/Executable.cs | 4 +- 9 files changed, 279 insertions(+), 22 deletions(-) create mode 100644 Ryujinx/Cpu/AOptimizations.cs rename Ryujinx/Cpu/Decoder/{AOpCodeSimdMemMult.cs => AOpCodeSimdMemMs.cs} (91%) create mode 100644 Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs diff --git a/Ryujinx/Cpu/AOpCodeTable.cs b/Ryujinx/Cpu/AOpCodeTable.cs index a7deb2c7..bf1ceec7 100644 --- a/Ryujinx/Cpu/AOpCodeTable.cs +++ b/Ryujinx/Cpu/AOpCodeTable.cs @@ -153,6 +153,7 @@ namespace ChocolArm64 Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_S, typeof(AOpCodeSimdCvt)); Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimdCvt)); Set("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); + Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V_Fix, typeof(AOpCodeSimdShImm)); Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimdCvt)); Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V_Fix, typeof(AOpCodeSimdShImm)); @@ -186,8 +187,10 @@ namespace ChocolArm64 Set("0x0011101x1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg)); Set("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns)); Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns)); - Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__V, typeof(AOpCodeSimdMemMult)); - Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__V, typeof(AOpCodeSimdMemMult)); + Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); + Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); + Set("0x00110101000000xx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs)); + Set("0x001101110xxxxxxx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs)); Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair)); Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); @@ -218,8 +221,8 @@ namespace ChocolArm64 Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); - Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMult)); - Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMult)); + Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMs)); + Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMs)); Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair)); Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); diff --git a/Ryujinx/Cpu/AOptimizations.cs b/Ryujinx/Cpu/AOptimizations.cs new file mode 100644 index 00000000..cbfd1ce5 --- /dev/null +++ b/Ryujinx/Cpu/AOptimizations.cs @@ -0,0 +1,4 @@ +public static class AOptimizations +{ + +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs similarity index 91% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs rename to Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs index 9731c7e7..0e8480b0 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMult.cs +++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs @@ -3,7 +3,7 @@ using ChocolArm64.State; namespace ChocolArm64.Decoder { - class AOpCodeSimdMemMult : AOpCode, IAOpCodeSimd + class AOpCodeSimdMemMs : AOpCode, IAOpCodeSimd { public int Rt { get; private set; } public int Rn { get; private set; } @@ -14,7 +14,7 @@ namespace ChocolArm64.Decoder public int Elems { get; private set; } public bool WBack { get; private set; } - public AOpCodeSimdMemMult(AInst Inst, long Position, int OpCode) : base(Inst, Position) + public AOpCodeSimdMemMs(AInst Inst, long Position, int OpCode) : base(Inst, Position) { switch ((OpCode >> 12) & 0xf) { diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs new file mode 100644 index 00000000..c2917dfc --- /dev/null +++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs @@ -0,0 +1,104 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemSs : AOpCode, IAOpCodeSimd + { + public int Rt { get; private set; } + public int Rn { get; private set; } + public int Size { get; private set; } + public int Rm { get; private set; } + public int SElems { get; private set; } + public int Index { get; private set; } + public bool Replicate { get; private set; } + public bool WBack { get; private set; } + + public AOpCodeSimdMemSs(AInst Inst, long Position, int OpCode) : base(Inst, Position) + { + int Size = (OpCode >> 10) & 3; + int S = (OpCode >> 12) & 1; + int SElems = (OpCode >> 12) & 2; + int Scale = (OpCode >> 14) & 3; + int L = (OpCode >> 22) & 1; + int Q = (OpCode >> 30) & 1; + + SElems |= (OpCode >> 21) & 1; + + SElems++; + + int Index = (Q << 3) | (S << 2) | Size; + + switch (Scale) + { + case 0: Index >>= 0; break; + + case 1: + { + if ((Index & 1) != 0) + { + Inst = AInst.Undefined; + + return; + } + + Index >>= 1; + + break; + } + + case 2: + { + if ((Index & 2) != 0 || + ((Index & 1) != 0 && S != 0)) + { + Inst = AInst.Undefined; + + return; + } + + if ((Index & 1) != 0) + { + Index >>= 3; + } + else + { + Index >>= 2; + + Scale = 3; + } + + break; + } + + case 3: + { + if (L == 0 || S != 0) + { + Inst = AInst.Undefined; + + return; + } + + Scale = Size; + + Replicate = true; + + break; + } + } + + this.SElems = SElems; + this.Size = Scale; + + Rt = (OpCode >> 0) & 0x1f; + Rn = (OpCode >> 5) & 0x1f; + Rm = (OpCode >> 16) & 0x1f; + WBack = ((OpCode >> 23) & 0x1) != 0; + + RegisterSize = Q != 0 + ? ARegisterSize.SIMD128 + : ARegisterSize.SIMD64; + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs index 8d4ade3a..9ba75bc8 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs @@ -122,6 +122,7 @@ namespace ChocolArm64.Instruction AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; Context.EmitLdvec(Op.Rn); + Context.EmitLdc_I4(0); Context.EmitLdc_I4(Op.SizeF); ASoftFallback.EmitCall(Context, @@ -131,6 +132,21 @@ namespace ChocolArm64.Instruction Context.EmitStvec(Op.Rd); } + public static void Fcvtzs_V_Fix(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + Context.EmitLdvec(Op.Rn); + Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm); + Context.EmitLdc_I4(Op.Size - 2); + + ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Fcvtzs_V64), + nameof(ASoftFallback.Fcvtzs_V128)); + + Context.EmitStvec(Op.Rd); + } + public static void Fcvtzu_V(AILEmitterCtx Context) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; @@ -283,7 +299,8 @@ namespace ChocolArm64.Instruction Context.EmitStvec(Op.Rd); } - public static void Ld__V(AILEmitterCtx Context) => EmitSimdMultLdSt(Context, IsLoad: true); + public static void Ld__Vms(AILEmitterCtx Context) => EmitSimdMemMs(Context, IsLoad: true); + public static void Ld__Vss(AILEmitterCtx Context) => EmitSimdMemSs(Context, IsLoad: true); public static void Mla_V(AILEmitterCtx Context) => EmitVectorMla(Context); @@ -391,7 +408,7 @@ namespace ChocolArm64.Instruction EmitVectorImmBinarySx(Context, OpCodes.Shr, (8 << (Op.Size + 1)) - Op.Imm); } - public static void St__V(AILEmitterCtx Context) => EmitSimdMultLdSt(Context, IsLoad: false); + public static void St__V(AILEmitterCtx Context) => EmitSimdMemMs(Context, IsLoad: false); public static void Sub_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Sub); @@ -571,9 +588,9 @@ namespace ChocolArm64.Instruction Context.EmitStvec(Op.Rd); } - private static void EmitSimdMultLdSt(AILEmitterCtx Context, bool IsLoad) + private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad) { - AOpCodeSimdMemMult Op = (AOpCodeSimdMemMult)Context.CurrOp; + AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp; int Offset = 0; @@ -644,6 +661,79 @@ namespace ChocolArm64.Instruction } } + private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; + + //TODO: Replicate mode. + + int Offset = 0; + + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + if (IsLoad) + { + Context.EmitLdvec(Rt); + Context.EmitLdc_I4(Op.Index); + Context.EmitLdc_I4(Op.Size); + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitReadZxCall(Context, Op.Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); + + Context.EmitStvec(Rt); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Rt); + } + } + else + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + Context.EmitLdvec(Rt); + Context.EmitLdc_I4(Op.Index); + Context.EmitLdc_I4(Op.Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec)); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + + if (Op.WBack) + { + Context.EmitLdint(Op.Rn); + + if (Op.Rm != ARegisters.ZRIndex) + { + Context.EmitLdint(Op.Rm); + } + else + { + Context.EmitLdc_I8(Offset); + } + + Context.Emit(OpCodes.Add); + + Context.EmitStint(Op.Rn); + } + } + private static void EmitVectorAddv(AILEmitterCtx Context) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; diff --git a/Ryujinx/Cpu/Instruction/ASoftFallback.cs b/Ryujinx/Cpu/Instruction/ASoftFallback.cs index 0d527132..caf3cfca 100644 --- a/Ryujinx/Cpu/Instruction/ASoftFallback.cs +++ b/Ryujinx/Cpu/Instruction/ASoftFallback.cs @@ -405,17 +405,17 @@ namespace ChocolArm64.Instruction return Res; } - public static AVec Fcvtzs_V64(AVec Vector, int Size) + public static AVec Fcvtzs_V64(AVec Vector, int FBits, int Size) { - return Fcvtzs_V(Vector, Size, 2); + return Fcvtzs_V(Vector, FBits, Size, 2); } - public static AVec Fcvtzs_V128(AVec Vector, int Size) + public static AVec Fcvtzs_V128(AVec Vector, int FBits, int Size) { - return Fcvtzs_V(Vector, Size, 4); + return Fcvtzs_V(Vector, FBits, Size, 4); } - private static AVec Fcvtzs_V(AVec Vector, int Size, int Bytes) + private static AVec Fcvtzs_V(AVec Vector, int FBits, int Size, int Bytes) { AVec Res = new AVec(); @@ -427,7 +427,7 @@ namespace ChocolArm64.Instruction { float Value = Vector.ExtractSingle(Index); - Res = InsertSVec(Res, Index, Size + 2, SatSingleToInt32(Value)); + Res = InsertSVec(Res, Index, Size + 2, SatSingleToInt32(Value, FBits)); } } else @@ -436,7 +436,7 @@ namespace ChocolArm64.Instruction { double Value = Vector.ExtractDouble(Index); - Res = InsertSVec(Res, Index, Size + 2, SatDoubleToInt64(Value)); + Res = InsertSVec(Res, Index, Size + 2, SatDoubleToInt64(Value, FBits)); } } diff --git a/Ryujinx/Cpu/Memory/AMemory.cs b/Ryujinx/Cpu/Memory/AMemory.cs index 8159b341..7952186d 100644 --- a/Ryujinx/Cpu/Memory/AMemory.cs +++ b/Ryujinx/Cpu/Memory/AMemory.cs @@ -1,3 +1,4 @@ +using ChocolArm64.Exceptions; using ChocolArm64.State; using System; using System.Collections.Generic; @@ -119,26 +120,46 @@ namespace ChocolArm64.Memory public byte ReadByte(long Position) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + return *((byte*)(RamPtr + (uint)Position)); } public ushort ReadUInt16(long Position) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + return *((ushort*)(RamPtr + (uint)Position)); } public uint ReadUInt32(long Position) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + return *((uint*)(RamPtr + (uint)Position)); } public ulong ReadUInt64(long Position) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + return *((ulong*)(RamPtr + (uint)Position)); } public AVec ReadVector128(long Position) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + return new AVec() { X0 = ReadUInt64(Position + 0), @@ -153,33 +174,61 @@ namespace ChocolArm64.Memory public void WriteByte(long Position, byte Value) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + *((byte*)(RamPtr + (uint)Position)) = Value; } public void WriteUInt16(long Position, ushort Value) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + *((ushort*)(RamPtr + (uint)Position)) = Value; } public void WriteUInt32(long Position, uint Value) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + *((uint*)(RamPtr + (uint)Position)) = Value; } public void WriteUInt64(long Position, ulong Value) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + *((ulong*)(RamPtr + (uint)Position)) = Value; } public void WriteVector128(long Position, AVec Value) { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + WriteUInt64(Position + 0, Value.X0); WriteUInt64(Position + 8, Value.X1); } - private bool IsPageCrossed(long Position, int Size) + private void EnsureAccessIsValid(long Position, AMemoryPerm Perm) { - return (Position & AMemoryMgr.PageMask) + Size > AMemoryMgr.PageSize; + if (!Manager.IsMapped(Position)) + { + throw new VmmPageFaultException(Position); + } + + if (!Manager.HasPermission(Position, Perm)) + { + throw new VmmAccessViolationException(Position, Perm); + } } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Memory/AMemoryMgr.cs b/Ryujinx/Cpu/Memory/AMemoryMgr.cs index 0c2c5a50..cd983e78 100644 --- a/Ryujinx/Cpu/Memory/AMemoryMgr.cs +++ b/Ryujinx/Cpu/Memory/AMemoryMgr.cs @@ -163,7 +163,7 @@ namespace ChocolArm64.Memory { while (Size > 0) { - if (!HasPTEntry(Position)) + if (!IsMapped(Position)) { long PhysPos = Allocator.Alloc(PageSize); @@ -254,8 +254,13 @@ namespace ChocolArm64.Memory return new AMemoryMapInfo(Start, Size, BaseEntry.Type, BaseEntry.Perm); } + public bool HasPermission(long Position, AMemoryPerm Perm) + { + return GetPTEntry(Position).Perm.HasFlag(Perm); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool HasPTEntry(long Position) + public bool IsMapped(long Position) { if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) { diff --git a/Ryujinx/Loaders/Executable.cs b/Ryujinx/Loaders/Executable.cs index 785a8c72..65043658 100644 --- a/Ryujinx/Loaders/Executable.cs +++ b/Ryujinx/Loaders/Executable.cs @@ -73,12 +73,14 @@ namespace Ryujinx.Loaders MemoryType Type, AMemoryPerm Perm) { - Memory.Manager.MapPhys(Position, Data.Count, (int)Type, Perm); + Memory.Manager.MapPhys(Position, Data.Count, (int)Type, AMemoryPerm.Write); for (int Index = 0; Index < Data.Count; Index++) { Memory.WriteByte(Position + Index, Data[Index]); } + + Memory.Manager.Reprotect(Position, Data.Count, Perm); } private void MapBss(long Position, long Size)