From 7601c05662f6279ff27f0a382869e9c4b9bf6136 Mon Sep 17 00:00:00 2001 From: emmauss Date: Tue, 20 Feb 2018 22:09:23 +0200 Subject: [PATCH] Split main project into core,graphics and chocolarm4 subproject (#29) --- ABitUtils.cs | 57 ++ AOpCodeTable.cs | 408 ++++++++++++++ AOptimizations.cs | 4 + AThread.cs | 72 +++ ATranslatedSub.cs | 104 ++++ ATranslator.cs | 106 ++++ ChocolArm64.csproj | 15 + Decoder/ABlock.cs | 35 ++ Decoder/ACond.cs | 22 + Decoder/ADataOp.cs | 10 + Decoder/ADecoder.cs | 207 ++++++++ Decoder/ADecoderHelper.cs | 107 ++++ Decoder/AIntType.cs | 14 + Decoder/AOpCode.cs | 38 ++ Decoder/AOpCodeAdr.cs | 18 + Decoder/AOpCodeAlu.cs | 24 + Decoder/AOpCodeAluImm.cs | 39 ++ Decoder/AOpCodeAluRs.cs | 29 + Decoder/AOpCodeAluRx.cs | 19 + Decoder/AOpCodeBImm.cs | 11 + Decoder/AOpCodeBImmAl.cs | 12 + Decoder/AOpCodeBImmCmp.cs | 16 + Decoder/AOpCodeBImmCond.cs | 25 + Decoder/AOpCodeBImmTest.cs | 20 + Decoder/AOpCodeBReg.cs | 24 + Decoder/AOpCodeBfm.cs | 29 + Decoder/AOpCodeCcmp.cs | 31 ++ Decoder/AOpCodeCcmpImm.cs | 11 + Decoder/AOpCodeCcmpReg.cs | 15 + Decoder/AOpCodeCsel.cs | 17 + Decoder/AOpCodeException.cs | 14 + Decoder/AOpCodeMem.cs | 19 + Decoder/AOpCodeMemEx.cs | 16 + Decoder/AOpCodeMemImm.cs | 53 ++ Decoder/AOpCodeMemLit.cs | 28 + Decoder/AOpCodeMemPair.cs | 25 + Decoder/AOpCodeMemReg.cs | 20 + Decoder/AOpCodeMov.cs | 36 ++ Decoder/AOpCodeMul.cs | 16 + Decoder/AOpCodeSimd.cs | 27 + Decoder/AOpCodeSimdCvt.cs | 31 ++ Decoder/AOpCodeSimdFcond.cs | 17 + Decoder/AOpCodeSimdFmov.cs | 33 ++ Decoder/AOpCodeSimdImm.cs | 94 ++++ Decoder/AOpCodeSimdIns.cs | 36 ++ Decoder/AOpCodeSimdMemImm.cs | 19 + Decoder/AOpCodeSimdMemLit.cs | 31 ++ Decoder/AOpCodeSimdMemMs.cs | 49 ++ Decoder/AOpCodeSimdMemPair.cs | 16 + Decoder/AOpCodeSimdMemReg.cs | 14 + Decoder/AOpCodeSimdMemSs.cs | 97 ++++ Decoder/AOpCodeSimdReg.cs | 18 + Decoder/AOpCodeSimdRegElem.cs | 22 + Decoder/AOpCodeSimdShImm.cs | 16 + Decoder/AOpCodeSimdTbl.cs | 12 + Decoder/AOpCodeSystem.cs | 24 + Decoder/AShiftType.cs | 10 + Decoder/IAOpCode.cs | 13 + Decoder/IAOpCodeAlu.cs | 10 + Decoder/IAOpCodeAluImm.cs | 7 + Decoder/IAOpCodeAluRs.cs | 10 + Decoder/IAOpCodeAluRx.cs | 10 + Decoder/IAOpCodeCond.cs | 7 + Decoder/IAOpCodeLit.cs | 11 + Decoder/IAOpCodeSimd.cs | 7 + Exceptions/VmmAccessViolationException.cs | 14 + Exceptions/VmmOutOfMemoryException.cs | 13 + Exceptions/VmmPageFaultException.cs | 13 + Instruction/AInst.cs | 18 + Instruction/AInstEmitAlu.cs | 364 +++++++++++++ Instruction/AInstEmitAluHelper.cs | 168 ++++++ Instruction/AInstEmitBfm.cs | 217 ++++++++ Instruction/AInstEmitCcmp.cs | 81 +++ Instruction/AInstEmitCsel.cs | 59 +++ Instruction/AInstEmitException.cs | 65 +++ Instruction/AInstEmitFlow.cs | 124 +++++ Instruction/AInstEmitMemory.cs | 252 +++++++++ Instruction/AInstEmitMemoryEx.cs | 180 +++++++ Instruction/AInstEmitMemoryHelper.cs | 138 +++++ Instruction/AInstEmitMove.cs | 41 ++ Instruction/AInstEmitMul.cs | 80 +++ Instruction/AInstEmitSimdArithmetic.cs | 367 +++++++++++++ Instruction/AInstEmitSimdCmp.cs | 233 ++++++++ Instruction/AInstEmitSimdCvt.cs | 444 ++++++++++++++++ Instruction/AInstEmitSimdHelper.cs | 616 ++++++++++++++++++++++ Instruction/AInstEmitSimdLogical.cs | 69 +++ Instruction/AInstEmitSimdMemory.cs | 184 +++++++ Instruction/AInstEmitSimdMove.cs | 333 ++++++++++++ Instruction/AInstEmitSimdShift.cs | 297 +++++++++++ Instruction/AInstEmitSystem.cs | 122 +++++ Instruction/AInstEmitter.cs | 6 + Instruction/ASoftFallback.cs | 316 +++++++++++ Memory/AMemory.cs | 345 ++++++++++++ Memory/AMemoryAlloc.cs | 35 ++ Memory/AMemoryHelper.cs | 73 +++ Memory/AMemoryMapInfo.cs | 21 + Memory/AMemoryMgr.cs | 286 ++++++++++ Memory/AMemoryPerm.cs | 15 + State/AInstExceptEventArgs.cs | 14 + State/AInstUndEventArgs.cs | 16 + State/APState.cs | 23 + State/ARegister.cs | 142 +++++ State/ARegisterSize.cs | 10 + State/ARegisterType.cs | 9 + State/AThreadState.cs | 64 +++ State/AVec.cs | 243 +++++++++ Translation/AILBlock.cs | 65 +++ Translation/AILEmitter.cs | 187 +++++++ Translation/AILEmitterCtx.cs | 487 +++++++++++++++++ Translation/AILLabel.cs | 28 + Translation/AILOpCode.cs | 19 + Translation/AILOpCodeBranch.cs | 21 + Translation/AILOpCodeCall.cs | 20 + Translation/AILOpCodeConst.cs | 81 +++ Translation/AILOpCodeLoad.cs | 77 +++ Translation/AILOpCodeLog.cs | 17 + Translation/AILOpCodeStore.cs | 75 +++ Translation/AIoType.cs | 15 + Translation/ALocalAlloc.cs | 231 ++++++++ Translation/IAILEmit.cs | 7 + Translation/ILGeneratorEx.cs | 129 +++++ 121 files changed, 9976 insertions(+) create mode 100644 ABitUtils.cs create mode 100644 AOpCodeTable.cs create mode 100644 AOptimizations.cs create mode 100644 AThread.cs create mode 100644 ATranslatedSub.cs create mode 100644 ATranslator.cs create mode 100644 ChocolArm64.csproj create mode 100644 Decoder/ABlock.cs create mode 100644 Decoder/ACond.cs create mode 100644 Decoder/ADataOp.cs create mode 100644 Decoder/ADecoder.cs create mode 100644 Decoder/ADecoderHelper.cs create mode 100644 Decoder/AIntType.cs create mode 100644 Decoder/AOpCode.cs create mode 100644 Decoder/AOpCodeAdr.cs create mode 100644 Decoder/AOpCodeAlu.cs create mode 100644 Decoder/AOpCodeAluImm.cs create mode 100644 Decoder/AOpCodeAluRs.cs create mode 100644 Decoder/AOpCodeAluRx.cs create mode 100644 Decoder/AOpCodeBImm.cs create mode 100644 Decoder/AOpCodeBImmAl.cs create mode 100644 Decoder/AOpCodeBImmCmp.cs create mode 100644 Decoder/AOpCodeBImmCond.cs create mode 100644 Decoder/AOpCodeBImmTest.cs create mode 100644 Decoder/AOpCodeBReg.cs create mode 100644 Decoder/AOpCodeBfm.cs create mode 100644 Decoder/AOpCodeCcmp.cs create mode 100644 Decoder/AOpCodeCcmpImm.cs create mode 100644 Decoder/AOpCodeCcmpReg.cs create mode 100644 Decoder/AOpCodeCsel.cs create mode 100644 Decoder/AOpCodeException.cs create mode 100644 Decoder/AOpCodeMem.cs create mode 100644 Decoder/AOpCodeMemEx.cs create mode 100644 Decoder/AOpCodeMemImm.cs create mode 100644 Decoder/AOpCodeMemLit.cs create mode 100644 Decoder/AOpCodeMemPair.cs create mode 100644 Decoder/AOpCodeMemReg.cs create mode 100644 Decoder/AOpCodeMov.cs create mode 100644 Decoder/AOpCodeMul.cs create mode 100644 Decoder/AOpCodeSimd.cs create mode 100644 Decoder/AOpCodeSimdCvt.cs create mode 100644 Decoder/AOpCodeSimdFcond.cs create mode 100644 Decoder/AOpCodeSimdFmov.cs create mode 100644 Decoder/AOpCodeSimdImm.cs create mode 100644 Decoder/AOpCodeSimdIns.cs create mode 100644 Decoder/AOpCodeSimdMemImm.cs create mode 100644 Decoder/AOpCodeSimdMemLit.cs create mode 100644 Decoder/AOpCodeSimdMemMs.cs create mode 100644 Decoder/AOpCodeSimdMemPair.cs create mode 100644 Decoder/AOpCodeSimdMemReg.cs create mode 100644 Decoder/AOpCodeSimdMemSs.cs create mode 100644 Decoder/AOpCodeSimdReg.cs create mode 100644 Decoder/AOpCodeSimdRegElem.cs create mode 100644 Decoder/AOpCodeSimdShImm.cs create mode 100644 Decoder/AOpCodeSimdTbl.cs create mode 100644 Decoder/AOpCodeSystem.cs create mode 100644 Decoder/AShiftType.cs create mode 100644 Decoder/IAOpCode.cs create mode 100644 Decoder/IAOpCodeAlu.cs create mode 100644 Decoder/IAOpCodeAluImm.cs create mode 100644 Decoder/IAOpCodeAluRs.cs create mode 100644 Decoder/IAOpCodeAluRx.cs create mode 100644 Decoder/IAOpCodeCond.cs create mode 100644 Decoder/IAOpCodeLit.cs create mode 100644 Decoder/IAOpCodeSimd.cs create mode 100644 Exceptions/VmmAccessViolationException.cs create mode 100644 Exceptions/VmmOutOfMemoryException.cs create mode 100644 Exceptions/VmmPageFaultException.cs create mode 100644 Instruction/AInst.cs create mode 100644 Instruction/AInstEmitAlu.cs create mode 100644 Instruction/AInstEmitAluHelper.cs create mode 100644 Instruction/AInstEmitBfm.cs create mode 100644 Instruction/AInstEmitCcmp.cs create mode 100644 Instruction/AInstEmitCsel.cs create mode 100644 Instruction/AInstEmitException.cs create mode 100644 Instruction/AInstEmitFlow.cs create mode 100644 Instruction/AInstEmitMemory.cs create mode 100644 Instruction/AInstEmitMemoryEx.cs create mode 100644 Instruction/AInstEmitMemoryHelper.cs create mode 100644 Instruction/AInstEmitMove.cs create mode 100644 Instruction/AInstEmitMul.cs create mode 100644 Instruction/AInstEmitSimdArithmetic.cs create mode 100644 Instruction/AInstEmitSimdCmp.cs create mode 100644 Instruction/AInstEmitSimdCvt.cs create mode 100644 Instruction/AInstEmitSimdHelper.cs create mode 100644 Instruction/AInstEmitSimdLogical.cs create mode 100644 Instruction/AInstEmitSimdMemory.cs create mode 100644 Instruction/AInstEmitSimdMove.cs create mode 100644 Instruction/AInstEmitSimdShift.cs create mode 100644 Instruction/AInstEmitSystem.cs create mode 100644 Instruction/AInstEmitter.cs create mode 100644 Instruction/ASoftFallback.cs create mode 100644 Memory/AMemory.cs create mode 100644 Memory/AMemoryAlloc.cs create mode 100644 Memory/AMemoryHelper.cs create mode 100644 Memory/AMemoryMapInfo.cs create mode 100644 Memory/AMemoryMgr.cs create mode 100644 Memory/AMemoryPerm.cs create mode 100644 State/AInstExceptEventArgs.cs create mode 100644 State/AInstUndEventArgs.cs create mode 100644 State/APState.cs create mode 100644 State/ARegister.cs create mode 100644 State/ARegisterSize.cs create mode 100644 State/ARegisterType.cs create mode 100644 State/AThreadState.cs create mode 100644 State/AVec.cs create mode 100644 Translation/AILBlock.cs create mode 100644 Translation/AILEmitter.cs create mode 100644 Translation/AILEmitterCtx.cs create mode 100644 Translation/AILLabel.cs create mode 100644 Translation/AILOpCode.cs create mode 100644 Translation/AILOpCodeBranch.cs create mode 100644 Translation/AILOpCodeCall.cs create mode 100644 Translation/AILOpCodeConst.cs create mode 100644 Translation/AILOpCodeLoad.cs create mode 100644 Translation/AILOpCodeLog.cs create mode 100644 Translation/AILOpCodeStore.cs create mode 100644 Translation/AIoType.cs create mode 100644 Translation/ALocalAlloc.cs create mode 100644 Translation/IAILEmit.cs create mode 100644 Translation/ILGeneratorEx.cs diff --git a/ABitUtils.cs b/ABitUtils.cs new file mode 100644 index 0000000..357dd45 --- /dev/null +++ b/ABitUtils.cs @@ -0,0 +1,57 @@ +namespace ChocolArm64 +{ + static class ABitUtils + { + public static int CountBitsSet(long Value) + { + int Count = 0; + + for (int Bit = 0; Bit < 64; Bit++) + { + Count += (int)(Value >> Bit) & 1; + } + + return Count; + } + + public static int HighestBitSet32(int Value) + { + for (int Bit = 31; Bit >= 0; Bit--) + { + if (((Value >> Bit) & 1) != 0) + { + return Bit; + } + } + + return -1; + } + + public static long Replicate(long Bits, int Size) + { + long Output = 0; + + for (int Bit = 0; Bit < 64; Bit += Size) + { + Output |= Bits << Bit; + } + + return Output; + } + + public static long FillWithOnes(int Bits) + { + return Bits == 64 ? -1L : (1L << Bits) - 1; + } + + public static long RotateRight(long Bits, int Shift, int Size) + { + return (Bits >> Shift) | (Bits << (Size - Shift)); + } + + public static bool IsPow2(int Value) + { + return Value != 0 && (Value & (Value - 1)) == 0; + } + } +} \ No newline at end of file diff --git a/AOpCodeTable.cs b/AOpCodeTable.cs new file mode 100644 index 0000000..ff2796b --- /dev/null +++ b/AOpCodeTable.cs @@ -0,0 +1,408 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Instruction; +using System; + +namespace ChocolArm64 +{ + static class AOpCodeTable + { + static AOpCodeTable() + { + #region "OpCode Table" + //Integer + Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs)); + Set("x00100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluImm)); + Set("x0001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs)); + Set("x0001011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx)); + Set("x01100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluImm)); + Set("x0101011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRs)); + Set("x0101011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRx)); + Set("0xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adr, typeof(AOpCodeAdr)); + Set("1xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adrp, typeof(AOpCodeAdr)); + Set("x00100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluImm)); + Set("x0001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluRs)); + Set("x11100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluImm)); + Set("x1101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx001010xxxxxxxxxx", AInstEmit.Asrv, typeof(AOpCodeAluRs)); + Set("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B, typeof(AOpCodeBImmAl)); + Set("01010100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B_Cond, typeof(AOpCodeBImmCond)); + Set("x01100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); + Set("x0001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs)); + Set("x1101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); + Set("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bl, typeof(AOpCodeBImmAl)); + Set("11010110001xxxxx000000xxxxxxxxxx", AInstEmit.Blr, typeof(AOpCodeBReg)); + Set("11010110000xxxxx000000xxxxxxxxxx", AInstEmit.Br, typeof(AOpCodeBReg)); + Set("11010100001xxxxxxxxxxxxxxxx00000", AInstEmit.Brk, typeof(AOpCodeException)); + Set("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbnz, typeof(AOpCodeBImmCmp)); + Set("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbz, typeof(AOpCodeBImmCmp)); + Set("x0111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpImm)); + Set("x0111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpReg)); + Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm)); + Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg)); + Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem)); + Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu)); + Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel)); + Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel)); + Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel)); + Set("x1011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csneg, typeof(AOpCodeCsel)); + Set("11010101000000110011xxxx10111111", AInstEmit.Dmb, typeof(AOpCodeSystem)); + Set("11010101000000110011xxxx10011111", AInstEmit.Dsb, typeof(AOpCodeSystem)); + Set("x1001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eon, typeof(AOpCodeAluRs)); + Set("x10100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm)); + Set("x1001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs)); + Set("x00100111x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs)); + Set("xx001000110xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldar, typeof(AOpCodeMemEx)); + Set("1x001000011xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxp, typeof(AOpCodeMemEx)); + Set("xx001000010xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxr, typeof(AOpCodeMemEx)); + Set("<<10100xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeMemPair)); + Set("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); + Set("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); + Set("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg)); + Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit)); + Set("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); + Set("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); + Set("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); + Set("1011100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); + Set("0x1110001x1xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg)); + Set("10111000101xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg)); + Set("xx001000010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxr, typeof(AOpCodeMemEx)); + Set("1x001000011xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxp, typeof(AOpCodeMemEx)); + Set("x0011010110xxxxx001000xxxxxxxxxx", AInstEmit.Lslv, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx001001xxxxxxxxxx", AInstEmit.Lsrv, typeof(AOpCodeAluRs)); + Set("x0011011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Madd, typeof(AOpCodeMul)); + Set("x11100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movk, typeof(AOpCodeMov)); + Set("x00100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movn, typeof(AOpCodeMov)); + Set("x10100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movz, typeof(AOpCodeMov)); + Set("110101010011xxxxxxxxxxxxxxxxxxxx", AInstEmit.Mrs, typeof(AOpCodeSystem)); + Set("110101010001xxxxxxxxxxxxxxxxxxxx", AInstEmit.Msr, typeof(AOpCodeSystem)); + Set("x0011011000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Msub, typeof(AOpCodeMul)); + Set("11010101000000110010000000011111", AInstEmit.Nop, typeof(AOpCodeSystem)); + Set("x0101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orn, typeof(AOpCodeAluRs)); + Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm)); + Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs)); + Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm)); + Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit)); + Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu)); + Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg)); + Set("x101101011000000000001xxxxxxxxxx", AInstEmit.Rev16, typeof(AOpCodeAlu)); + Set("x101101011000000000010xxxxxxxxxx", AInstEmit.Rev32, typeof(AOpCodeAlu)); + Set("1101101011000000000011xxxxxxxxxx", AInstEmit.Rev64, typeof(AOpCodeAlu)); + Set("x0011010110xxxxx001011xxxxxxxxxx", AInstEmit.Rorv, typeof(AOpCodeAluRs)); + Set("x1011010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbc, typeof(AOpCodeAluRs)); + Set("x00100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm)); + Set("x0011010110xxxxx000011xxxxxxxxxx", AInstEmit.Sdiv, typeof(AOpCodeAluRs)); + Set("10011011001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smaddl, typeof(AOpCodeMul)); + Set("10011011001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Smsubl, typeof(AOpCodeMul)); + Set("10011011010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smulh, typeof(AOpCodeMul)); + Set("xx001000100xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlr, typeof(AOpCodeMemEx)); + Set("1x001000001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxp, typeof(AOpCodeMemEx)); + Set("xx001000000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxr, typeof(AOpCodeMemEx)); + Set("x010100xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeMemPair)); + Set("xx111000000xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm)); + Set("xx11100100xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm)); + Set("xx111000001xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemReg)); + Set("1x001000001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxp, typeof(AOpCodeMemEx)); + Set("xx001000000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxr, typeof(AOpCodeMemEx)); + Set("x10100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluImm)); + Set("x1001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRs)); + Set("x1001011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRx)); + Set("x11100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluImm)); + Set("x1101011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRs)); + Set("x1101011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRx)); + Set("11010100000xxxxxxxxxxxxxxxx00001", AInstEmit.Svc, typeof(AOpCodeException)); + Set("1101010100001xxxxxxxxxxxxxxxxxxx", AInstEmit.Sys, typeof(AOpCodeSystem)); + Set("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbnz, typeof(AOpCodeBImmTest)); + Set("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbz, typeof(AOpCodeBImmTest)); + Set("x10100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ubfm, typeof(AOpCodeBfm)); + Set("x0011010110xxxxx000010xxxxxxxxxx", AInstEmit.Udiv, typeof(AOpCodeAluRs)); + Set("10011011101xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umaddl, typeof(AOpCodeMul)); + Set("10011011101xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Umsubl, typeof(AOpCodeMul)); + Set("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul)); + + //Vector + Set("0>001110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V, typeof(AOpCodeSimdReg)); + Set("01011110xx110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd)); + Set("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg)); + Set("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd)); + Set("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd)); + Set("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg)); + Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg)); + Set("0x10111100000xxx<101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg)); + Set("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd)); + Set("0>001110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimdReg)); + Set("0>101110<<100000100010xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimd)); + Set("0>001110<<1xxxxx001101xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimdReg)); + Set("0>001110<<100000100010xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimd)); + Set("0>101110<<1xxxxx001101xxxxxxxxxx", AInstEmit.Cmhi_V, typeof(AOpCodeSimdReg)); + Set("0>101110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_V, typeof(AOpCodeSimdReg)); + Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd)); + Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd)); + Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd)); + Set("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns)); + Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); + Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns)); + Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg)); + Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd)); + Set("00011110xx1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg)); + Set("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond)); + Set("00011110xx1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S, typeof(AOpCodeSimdFcond)); + Set("00011110xx1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond)); + Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); + Set("x0011110xx100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt)); + Set("0>0011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); + Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm)); + Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); + Set("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); + Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm)); + Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg)); + Set("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg)); + Set("00011111xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); + Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); + Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); + Set("0x0011111<1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); + Set("0x0011111<0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg)); + Set("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns)); + Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns)); + 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)); + Set("xx111100x10xxxxxxxxx11xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); + Set("xx111101x1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); + Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg)); + Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit)); + Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg)); + Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg)); + Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); + Set("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); + Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); + Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); + Set("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg)); + Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); + Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); + Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); + Set("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V, typeof(AOpCodeSimdReg)); + Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd)); + Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg)); + Set("0x00111100000xxx<>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm)); + Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); + Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); + Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); + Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); + Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); + Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); + 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__Vms, typeof(AOpCodeSimdMemMs)); + Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); + Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); + Set("0x001101100xxxxxxx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); + Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair)); + Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); + Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); + Set("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); + Set("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); + Set("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg)); + Set("01111110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg)); + Set("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg)); + Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl)); + Set("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); + Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); + Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg)); + Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt)); + Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd)); + Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd)); + Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); + Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); + Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); + Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); + Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); + Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); + Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); + Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); + Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); + Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg)); + Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg)); +#endregion + } + + private class TreeNode + { + public int Mask; + public int Value; + + public TreeNode Next; + public TreeNode Child; + + public AInst Inst; + + public TreeNode(int Mask, int Value, AInst Inst) + { + this.Mask = Mask; + this.Value = Value; + this.Inst = Inst; + } + } + + private static TreeNode Root; + + private static void Set(string Encoding, AInstEmitter Emitter, Type Type) + { + Set(Encoding, new AInst(Emitter, Type)); + } + + private static void Set(string Encoding, AInst Inst) + { + int Bit = Encoding.Length - 1; + int Value = 0; + int XMask = 0; + int ZCount = 0; + int OCount = 0; + + int[] ZPos = new int[Encoding.Length]; + int[] OPos = new int[Encoding.Length]; + + for (int Index = 0; Index < Encoding.Length; Index++, Bit--) + { + //Note: < and > are used on special encodings. + //The < means that we should never have ALL bits with the '<' set. + //So, when the encoding has <<, it means that 00, 01, and 10 are valid, + //but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on... + //For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid, + //but 00 isn't. + switch (Encoding[Index]) + { + case '0': /* Do nothing. */ break; + case '1': Value |= 1 << Bit; break; + case 'x': XMask |= 1 << Bit; break; + + case '<': OPos[OCount++] = Bit; break; + case '>': ZPos[ZCount++] = Bit; break; + + default: throw new ArgumentException(nameof(Encoding)); + } + } + + if (ZCount + OCount == 0) + { + InsertTop(XMask, Value, Inst); + } + else if (ZCount != 0 && OCount != 0) + { + //When both the > and the < are used, then a value is blacklisted, + //with > indicating 0, and < indicating 1. So, for example, ><< + //blacklists the pattern 011, but 000, 001, 010, 100, 101, + //110 and 111 are valid. + for (int OCtr = 0; (uint)OCtr < (1 << OCount); OCtr++) + { + int OVal = Value; + + for (int O = 0; O < OCount; O++) + { + OVal |= ((OCtr >> O) & 1) << OPos[O]; + } + + int ZStart = OCtr == (1 << OCount) ? 1 : 0; + + InsertWithCtr(ZStart, 1 << ZCount, ZCount, ZPos, XMask, OVal, Inst); + } + } + else if (ZCount != 0) + { + InsertWithCtr(1, 1 << ZCount, ZCount, ZPos, XMask, Value, Inst); + } + else if (OCount != 0) + { + InsertWithCtr(0, (1 << OCount) - 1, OCount, OPos, XMask, Value, Inst); + } + } + + private static void InsertWithCtr( + int Start, + int End, + int Cnt, + int[] Pos, + int XMask, + int Value, + AInst Inst) + { + for (int Ctr = Start; (uint)Ctr < End; Ctr++) + { + int Val = Value; + + for (int Index = 0; Index < Cnt; Index++) + { + Val |= ((Ctr >> Index) & 1) << Pos[Index]; + } + + InsertTop(XMask, Val, Inst); + } + } + + private static void InsertTop(int XMask, int Value, AInst Inst) + { + TreeNode Next = Root; + + Root = new TreeNode(~XMask, Value, Inst); + + Root.Next = Next; + } + + public static AInst GetInst(int OpCode) + { + TreeNode Node = Root; + + do + { + if ((OpCode & Node.Mask) == Node.Value) + { + return Node.Inst; + } + } + while ((Node = Node.Next) != null); + + return AInst.Undefined; + } + } +} \ No newline at end of file diff --git a/AOptimizations.cs b/AOptimizations.cs new file mode 100644 index 0000000..cbfd1ce --- /dev/null +++ b/AOptimizations.cs @@ -0,0 +1,4 @@ +public static class AOptimizations +{ + +} \ No newline at end of file diff --git a/AThread.cs b/AThread.cs new file mode 100644 index 0000000..5c03228 --- /dev/null +++ b/AThread.cs @@ -0,0 +1,72 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Threading; + +namespace ChocolArm64 +{ + public class AThread + { + public AThreadState ThreadState { get; private set; } + public AMemory Memory { get; private set; } + + public long EntryPoint { get; private set; } + + private ATranslator Translator; + + private ThreadPriority Priority; + + private Thread Work; + + public event EventHandler WorkFinished; + + public int ThreadId => ThreadState.ThreadId; + + public bool IsAlive => Work.IsAlive; + + private bool IsExecuting; + + private object ExecuteLock; + + public AThread(AMemory Memory, ThreadPriority Priority, long EntryPoint) + { + this.Memory = Memory; + this.Priority = Priority; + this.EntryPoint = EntryPoint; + + ThreadState = new AThreadState(); + Translator = new ATranslator(this); + ExecuteLock = new object(); + } + + public void StopExecution() => Translator.StopExecution(); + + public bool Execute() + { + lock (ExecuteLock) + { + if (IsExecuting) + { + return false; + } + + IsExecuting = true; + } + + Work = new Thread(delegate() + { + Translator.ExecuteSubroutine(EntryPoint); + + Memory.RemoveMonitor(ThreadId); + + WorkFinished?.Invoke(this, EventArgs.Empty); + }); + + Work.Priority = Priority; + + Work.Start(); + + return true; + } + } +} \ No newline at end of file diff --git a/ATranslatedSub.cs b/ATranslatedSub.cs new file mode 100644 index 0000000..71a6793 --- /dev/null +++ b/ATranslatedSub.cs @@ -0,0 +1,104 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + class ATranslatedSub + { + private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); + + private AA64Subroutine ExecDelegate; + + private bool HasDelegate; + + public static Type[] FixedArgTypes { get; private set; } + + public static int StateArgIdx { get; private set; } + public static int MemoryArgIdx { get; private set; } + + public DynamicMethod Method { get; private set; } + + public HashSet SubCalls { get; private set; } + + public List Params { get; private set; } + + public bool NeedsReJit { get; private set; } + + public ATranslatedSub() + { + SubCalls = new HashSet(); + } + + public ATranslatedSub(DynamicMethod Method, List Params) : this() + { + if (Params == null) + { + throw new ArgumentNullException(nameof(Params)); + } + + this.Method = Method; + this.Params = Params; + } + + static ATranslatedSub() + { + MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke"); + + ParameterInfo[] Params = MthdInfo.GetParameters(); + + FixedArgTypes = new Type[Params.Length]; + + for (int Index = 0; Index < Params.Length; Index++) + { + Type ParamType = Params[Index].ParameterType; + + FixedArgTypes[Index] = ParamType; + + if (ParamType == typeof(AThreadState)) + { + StateArgIdx = Index; + } + else if (ParamType == typeof(AMemory)) + { + MemoryArgIdx = Index; + } + } + } + + public long Execute(AThreadState ThreadState, AMemory Memory) + { + if (!HasDelegate) + { + string Name = $"{Method.Name}_Dispatch"; + + DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); + + ILGenerator Generator = Mthd.GetILGenerator(); + + Generator.EmitLdargSeq(FixedArgTypes.Length); + + foreach (ARegister Reg in Params) + { + Generator.EmitLdarg(StateArgIdx); + + Generator.Emit(OpCodes.Ldfld, Reg.GetField()); + } + + Generator.Emit(OpCodes.Call, Method); + Generator.Emit(OpCodes.Ret); + + ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); + + HasDelegate = true; + } + + return ExecDelegate(ThreadState, Memory); + } + + public void MarkForReJit() => NeedsReJit = true; + } +} \ No newline at end of file diff --git a/ATranslator.cs b/ATranslator.cs new file mode 100644 index 0000000..96bbc89 --- /dev/null +++ b/ATranslator.cs @@ -0,0 +1,106 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Instruction; +using ChocolArm64.Translation; +using System.Collections.Generic; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + class ATranslator + { + public AThread Thread { get; private set; } + + private Dictionary CachedSubs; + + private bool KeepRunning; + + public ATranslator(AThread Parent) + { + this.Thread = Parent; + + CachedSubs = new Dictionary(); + + KeepRunning = true; + } + + public void StopExecution() => KeepRunning = false; + + public void ExecuteSubroutine(long Position) + { + do + { + if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit) + { + Position = Sub.Execute(Thread.ThreadState, Thread.Memory); + } + else + { + Position = TranslateSubroutine(Position).Execute(Thread.ThreadState, Thread.Memory); + } + } + while (Position != 0 && KeepRunning); + } + + public bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) + { + if (OpCode.Emitter != AInstEmit.Bl) + { + Sub = null; + + return false; + } + + return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub); + } + + public bool TryGetCachedSub(long Position, out ATranslatedSub Sub) + { + return CachedSubs.TryGetValue(Position, out Sub); + } + + public bool HasCachedSub(long Position) + { + return CachedSubs.ContainsKey(Position); + } + + private ATranslatedSub TranslateSubroutine(long Position) + { + (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(this, Position); + + AILEmitterCtx Context = new AILEmitterCtx( + this, + Cfg.Graph, + Cfg.Root); + + if (Context.CurrBlock.Position != Position) + { + Context.Emit(OpCodes.Br, Context.GetLabel(Position)); + } + + do + { + Context.EmitOpCode(); + } + while (Context.AdvanceOpCode()); + + //Mark all methods that calls this method for ReJiting, + //since we can now call it directly which is faster. + foreach (ATranslatedSub TS in CachedSubs.Values) + { + if (TS.SubCalls.Contains(Position)) + { + TS.MarkForReJit(); + } + } + + ATranslatedSub Subroutine = Context.GetSubroutine(); + + if (!CachedSubs.TryAdd(Position, Subroutine)) + { + CachedSubs[Position] = Subroutine; + } + + return Subroutine; + } + } +} \ No newline at end of file diff --git a/ChocolArm64.csproj b/ChocolArm64.csproj new file mode 100644 index 0000000..47d66cf --- /dev/null +++ b/ChocolArm64.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.0 + + + + true + + + + true + + + diff --git a/Decoder/ABlock.cs b/Decoder/ABlock.cs new file mode 100644 index 0000000..32974c1 --- /dev/null +++ b/Decoder/ABlock.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Decoder +{ + class ABlock + { + public long Position { get; set; } + public long EndPosition { get; set; } + + public ABlock Next { get; set; } + public ABlock Branch { get; set; } + + public List OpCodes { get; private set; } + + public ABlock() + { + OpCodes = new List(); + } + + public ABlock(long Position) : this() + { + this.Position = Position; + } + + public AOpCode GetLastOp() + { + if (OpCodes.Count > 0) + { + return OpCodes[OpCodes.Count - 1]; + } + + return null; + } + } +} \ No newline at end of file diff --git a/Decoder/ACond.cs b/Decoder/ACond.cs new file mode 100644 index 0000000..f2da8bd --- /dev/null +++ b/Decoder/ACond.cs @@ -0,0 +1,22 @@ +namespace ChocolArm64.Decoder +{ + enum ACond + { + Eq = 0, + Ne = 1, + Ge_Un = 2, + Lt_Un = 3, + Mi = 4, + Pl = 5, + Vs = 6, + Vc = 7, + Gt_Un = 8, + Le_Un = 9, + Ge = 10, + Lt = 11, + Gt = 12, + Le = 13, + Al = 14, + Nv = 15 + } +} \ No newline at end of file diff --git a/Decoder/ADataOp.cs b/Decoder/ADataOp.cs new file mode 100644 index 0000000..a5601a3 --- /dev/null +++ b/Decoder/ADataOp.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoder +{ + enum ADataOp + { + Adr = 0, + Arithmetic = 1, + Logical = 2, + BitField = 3 + } +} \ No newline at end of file diff --git a/Decoder/ADecoder.cs b/Decoder/ADecoder.cs new file mode 100644 index 0000000..06a535c --- /dev/null +++ b/Decoder/ADecoder.cs @@ -0,0 +1,207 @@ +using ChocolArm64.Instruction; +using ChocolArm64.Memory; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; + +namespace ChocolArm64.Decoder +{ + static class ADecoder + { + public static (ABlock[] Graph, ABlock Root) DecodeSubroutine(ATranslator Translator, long Start) + { + Dictionary Visited = new Dictionary(); + Dictionary VisitedEnd = new Dictionary(); + + Queue Blocks = new Queue(); + + ABlock Enqueue(long Position) + { + if (!Visited.TryGetValue(Position, out ABlock Output)) + { + Output = new ABlock(Position); + + Blocks.Enqueue(Output); + + Visited.Add(Position, Output); + } + + return Output; + } + + ABlock Root = Enqueue(Start); + + while (Blocks.Count > 0) + { + ABlock Current = Blocks.Dequeue(); + + FillBlock(Translator.Thread.Memory, Current); + + //Set child blocks. "Branch" is the block the branch instruction + //points to (when taken), "Next" is the block at the next address, + //executed when the branch is not taken. For Unconditional Branches + //(except BL/BLR that are sub calls) or end of executable, Next is null. + if (Current.OpCodes.Count > 0) + { + bool HasCachedSub = false; + + AOpCode LastOp = Current.GetLastOp(); + + if (LastOp is AOpCodeBImm Op) + { + if (Op.Emitter == AInstEmit.Bl) + { + HasCachedSub = Translator.HasCachedSub(Op.Imm); + } + else + { + Current.Branch = Enqueue(Op.Imm); + } + } + + if ((!(LastOp is AOpCodeBImmAl) && + !(LastOp is AOpCodeBReg)) || HasCachedSub) + { + Current.Next = Enqueue(Current.EndPosition); + } + } + + //If we have on the tree two blocks with the same end position, + //then we need to split the bigger block and have two small blocks, + //the end position of the bigger "Current" block should then be == to + //the position of the "Smaller" block. + while (VisitedEnd.TryGetValue(Current.EndPosition, out ABlock Smaller)) + { + if (Current.Position > Smaller.Position) + { + ABlock Temp = Smaller; + + Smaller = Current; + Current = Temp; + } + + Current.EndPosition = Smaller.Position; + Current.Next = Smaller; + Current.Branch = null; + + Current.OpCodes.RemoveRange( + Current.OpCodes.Count - Smaller.OpCodes.Count, + Smaller.OpCodes.Count); + + VisitedEnd[Smaller.EndPosition] = Smaller; + } + + VisitedEnd.Add(Current.EndPosition, Current); + } + + //Make and sort Graph blocks array by position. + ABlock[] Graph = new ABlock[Visited.Count]; + + while (Visited.Count > 0) + { + ulong FirstPos = ulong.MaxValue; + + foreach (ABlock Block in Visited.Values) + { + if (FirstPos > (ulong)Block.Position) + FirstPos = (ulong)Block.Position; + } + + ABlock Current = Visited[(long)FirstPos]; + + do + { + Graph[Graph.Length - Visited.Count] = Current; + + Visited.Remove(Current.Position); + + Current = Current.Next; + } + while (Current != null); + } + + return (Graph, Root); + } + + private static void FillBlock(AMemory Memory, ABlock Block) + { + long Position = Block.Position; + + AOpCode OpCode; + + do + { + OpCode = DecodeOpCode(Memory, Position); + + Block.OpCodes.Add(OpCode); + + Position += 4; + } + while (!(IsBranch(OpCode) || IsException(OpCode))); + + Block.EndPosition = Position; + } + + private static bool IsBranch(AOpCode OpCode) + { + return OpCode is AOpCodeBImm || + OpCode is AOpCodeBReg; + } + + private static bool IsException(AOpCode OpCode) + { + return OpCode.Emitter == AInstEmit.Brk || + OpCode.Emitter == AInstEmit.Svc || + OpCode.Emitter == AInstEmit.Und; + } + + public static AOpCode DecodeOpCode(AMemory Memory, long Position) + { + int OpCode = Memory.ReadInt32(Position); + + AInst Inst = AOpCodeTable.GetInst(OpCode); + + AOpCode DecodedOpCode = new AOpCode(AInst.Undefined, Position, OpCode); + + if (Inst.Type != null) + { + DecodedOpCode = CreateOpCode(Inst.Type, Inst, Position, OpCode); + } + + return DecodedOpCode; + } + + private delegate object OpActivator(AInst Inst, long Position, int OpCode); + + private static Dictionary Activators = new Dictionary(); + + private static AOpCode CreateOpCode(Type Type, AInst Inst, long Position, int OpCode) + { + if (Type == null) + { + throw new ArgumentNullException(nameof(Type)); + } + + if (!Activators.TryGetValue(Type, out OpActivator CreateInstance)) + { + Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) }; + + DynamicMethod Mthd = new DynamicMethod($"{Type.Name}_Create", Type, ArgTypes); + + ILGenerator Generator = Mthd.GetILGenerator(); + + Generator.Emit(OpCodes.Ldarg_0); + Generator.Emit(OpCodes.Ldarg_1); + Generator.Emit(OpCodes.Ldarg_2); + Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes)); + Generator.Emit(OpCodes.Ret); + + CreateInstance = (OpActivator)Mthd.CreateDelegate(typeof(OpActivator)); + + Activators.Add(Type, CreateInstance); + } + + return (AOpCode)CreateInstance(Inst, Position, OpCode); + } + } +} \ No newline at end of file diff --git a/Decoder/ADecoderHelper.cs b/Decoder/ADecoderHelper.cs new file mode 100644 index 0000000..a2179f4 --- /dev/null +++ b/Decoder/ADecoderHelper.cs @@ -0,0 +1,107 @@ +using System; + +namespace ChocolArm64.Decoder +{ + static class ADecoderHelper + { + public struct BitMask + { + public long WMask; + public long TMask; + public int Pos; + public int Shift; + public bool IsUndefined; + + public static BitMask Invalid => new BitMask { IsUndefined = true }; + } + + public static BitMask DecodeBitMask(int OpCode, bool Immediate) + { + int ImmS = (OpCode >> 10) & 0x3f; + int ImmR = (OpCode >> 16) & 0x3f; + + int N = (OpCode >> 22) & 1; + int SF = (OpCode >> 31) & 1; + + int Length = ABitUtils.HighestBitSet32((~ImmS & 0x3f) | (N << 6)); + + if (Length < 1 || (SF == 0 && N != 0)) + { + return BitMask.Invalid; + } + + int Size = 1 << Length; + + int Levels = Size - 1; + + int S = ImmS & Levels; + int R = ImmR & Levels; + + if (Immediate && S == Levels) + { + return BitMask.Invalid; + } + + long WMask = ABitUtils.FillWithOnes(S + 1); + long TMask = ABitUtils.FillWithOnes(((S - R) & Levels) + 1); + + if (R > 0) + { + WMask = ABitUtils.RotateRight(WMask, R, Size); + WMask &= ABitUtils.FillWithOnes(Size); + } + + return new BitMask() + { + WMask = ABitUtils.Replicate(WMask, Size), + TMask = ABitUtils.Replicate(TMask, Size), + + Pos = ImmS, + Shift = ImmR + }; + } + + public static long DecodeImm8Float(long Imm, int Size) + { + int E = 0, F = 0; + + switch (Size) + { + case 0: E = 8; F = 23; break; + case 1: E = 11; F = 52; break; + + default: throw new ArgumentOutOfRangeException(nameof(Size)); + } + + long Value = (Imm & 0x3f) << F - 4; + + long EBit = (Imm >> 6) & 1; + long SBit = (Imm >> 7) & 1; + + if (EBit != 0) + { + Value |= (1L << E - 3) - 1 << F + 2; + } + + Value |= (EBit ^ 1) << F + E - 1; + Value |= SBit << F + E; + + return Value; + } + + public static long DecodeImm26_2(int OpCode) + { + return ((long)OpCode << 38) >> 36; + } + + public static long DecodeImmS19_2(int OpCode) + { + return (((long)OpCode << 40) >> 43) & ~3; + } + + public static long DecodeImmS14_2(int OpCode) + { + return (((long)OpCode << 45) >> 48) & ~3; + } + } +} \ No newline at end of file diff --git a/Decoder/AIntType.cs b/Decoder/AIntType.cs new file mode 100644 index 0000000..242fdad --- /dev/null +++ b/Decoder/AIntType.cs @@ -0,0 +1,14 @@ +namespace ChocolArm64.Decoder +{ + enum AIntType + { + UInt8 = 0, + UInt16 = 1, + UInt32 = 2, + UInt64 = 3, + Int8 = 4, + Int16 = 5, + Int32 = 6, + Int64 = 7 + } +} \ No newline at end of file diff --git a/Decoder/AOpCode.cs b/Decoder/AOpCode.cs new file mode 100644 index 0000000..5d12759 --- /dev/null +++ b/Decoder/AOpCode.cs @@ -0,0 +1,38 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; +using System; + +namespace ChocolArm64.Decoder +{ + class AOpCode : IAOpCode + { + public long Position { get; private set; } + public int RawOpCode { get; private set; } + + public AInstEmitter Emitter { get; protected set; } + public ARegisterSize RegisterSize { get; protected set; } + + public AOpCode(AInst Inst, long Position, int OpCode) + { + this.Position = Position; + this.RawOpCode = OpCode; + + RegisterSize = ARegisterSize.Int64; + + Emitter = Inst.Emitter; + } + + public int GetBitsCount() + { + switch (RegisterSize) + { + case ARegisterSize.Int32: return 32; + case ARegisterSize.Int64: return 64; + case ARegisterSize.SIMD64: return 64; + case ARegisterSize.SIMD128: return 128; + } + + throw new InvalidOperationException(); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeAdr.cs b/Decoder/AOpCodeAdr.cs new file mode 100644 index 0000000..3396281 --- /dev/null +++ b/Decoder/AOpCodeAdr.cs @@ -0,0 +1,18 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeAdr : AOpCode + { + public int Rd { get; private set; } + public long Imm { get; private set; } + + public AOpCodeAdr(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rd = OpCode & 0x1f; + + Imm = ADecoderHelper.DecodeImmS19_2(OpCode); + Imm |= ((long)OpCode >> 29) & 3; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeAlu.cs b/Decoder/AOpCodeAlu.cs new file mode 100644 index 0000000..e1a44f0 --- /dev/null +++ b/Decoder/AOpCodeAlu.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeAlu : AOpCode, IAOpCodeAlu + { + public int Rd { get; protected set; } + public int Rn { get; private set; } + + public ADataOp DataOp { get; private set; } + + public AOpCodeAlu(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rd = (OpCode >> 0) & 0x1f; + Rn = (OpCode >> 5) & 0x1f; + DataOp = (ADataOp)((OpCode >> 24) & 0x3); + + RegisterSize = (OpCode >> 31) != 0 + ? ARegisterSize.Int64 + : ARegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeAluImm.cs b/Decoder/AOpCodeAluImm.cs new file mode 100644 index 0000000..e913475 --- /dev/null +++ b/Decoder/AOpCodeAluImm.cs @@ -0,0 +1,39 @@ +using ChocolArm64.Instruction; +using System; + +namespace ChocolArm64.Decoder +{ + class AOpCodeAluImm : AOpCodeAlu, IAOpCodeAluImm + { + public long Imm { get; private set; } + + public AOpCodeAluImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + if (DataOp == ADataOp.Arithmetic) + { + Imm = (OpCode >> 10) & 0xfff; + + int Shift = (OpCode >> 22) & 3; + + Imm <<= Shift * 12; + } + else if (DataOp == ADataOp.Logical) + { + var BM = ADecoderHelper.DecodeBitMask(OpCode, true); + + if (BM.IsUndefined) + { + Emitter = AInstEmit.Und; + + return; + } + + Imm = BM.WMask; + } + else + { + throw new ArgumentException(nameof(OpCode)); + } + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeAluRs.cs b/Decoder/AOpCodeAluRs.cs new file mode 100644 index 0000000..9c215be --- /dev/null +++ b/Decoder/AOpCodeAluRs.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeAluRs : AOpCodeAlu, IAOpCodeAluRs + { + public int Shift { get; private set; } + public int Rm { get; private set; } + + public AShiftType ShiftType { get; private set; } + + public AOpCodeAluRs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int Shift = (OpCode >> 10) & 0x3f; + + if (Shift >= GetBitsCount()) + { + Emitter = AInstEmit.Und; + + return; + } + + this.Shift = Shift; + + Rm = (OpCode >> 16) & 0x1f; + ShiftType = (AShiftType)((OpCode >> 22) & 0x3); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeAluRx.cs b/Decoder/AOpCodeAluRx.cs new file mode 100644 index 0000000..7dd72a6 --- /dev/null +++ b/Decoder/AOpCodeAluRx.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeAluRx : AOpCodeAlu, IAOpCodeAluRx + { + public int Shift { get; private set; } + public int Rm { get; private set; } + + public AIntType IntType { get; private set; } + + public AOpCodeAluRx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Shift = (OpCode >> 10) & 0x7; + IntType = (AIntType)((OpCode >> 13) & 0x7); + Rm = (OpCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBImm.cs b/Decoder/AOpCodeBImm.cs new file mode 100644 index 0000000..6519d28 --- /dev/null +++ b/Decoder/AOpCodeBImm.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBImm : AOpCode + { + public long Imm { get; protected set; } + + public AOpCodeBImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmAl.cs b/Decoder/AOpCodeBImmAl.cs new file mode 100644 index 0000000..a4ff686 --- /dev/null +++ b/Decoder/AOpCodeBImmAl.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBImmAl : AOpCodeBImm + { + public AOpCodeBImmAl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Imm = Position + ADecoderHelper.DecodeImm26_2(OpCode); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmCmp.cs b/Decoder/AOpCodeBImmCmp.cs new file mode 100644 index 0000000..1b6185d --- /dev/null +++ b/Decoder/AOpCodeBImmCmp.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBImmCmp : AOpCodeBImm + { + public int Rt { get; private set; } + + public AOpCodeBImmCmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt = OpCode & 0x1f; + + Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmCond.cs b/Decoder/AOpCodeBImmCond.cs new file mode 100644 index 0000000..1310feb --- /dev/null +++ b/Decoder/AOpCodeBImmCond.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBImmCond : AOpCodeBImm, IAOpCodeCond + { + public ACond Cond { get; private set; } + + public AOpCodeBImmCond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int O0 = (OpCode >> 4) & 1; + + if (O0 != 0) + { + Emitter = AInstEmit.Und; + + return; + } + + Cond = (ACond)(OpCode & 0xf); + + Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBImmTest.cs b/Decoder/AOpCodeBImmTest.cs new file mode 100644 index 0000000..73e57b7 --- /dev/null +++ b/Decoder/AOpCodeBImmTest.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBImmTest : AOpCodeBImm + { + public int Rt { get; private set; } + public int Pos { get; private set; } + + public AOpCodeBImmTest(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt = OpCode & 0x1f; + + Imm = Position + ADecoderHelper.DecodeImmS14_2(OpCode); + + Pos = (OpCode >> 19) & 0x1f; + Pos |= (OpCode >> 26) & 0x20; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBReg.cs b/Decoder/AOpCodeBReg.cs new file mode 100644 index 0000000..c9c600a --- /dev/null +++ b/Decoder/AOpCodeBReg.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBReg : AOpCode + { + public int Rn { get; private set; } + + public AOpCodeBReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int Op4 = (OpCode >> 0) & 0x1f; + int Op2 = (OpCode >> 16) & 0x1f; + + if (Op2 != 0b11111 || Op4 != 0b00000) + { + Emitter = AInstEmit.Und; + + return; + } + + Rn = (OpCode >> 5) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeBfm.cs b/Decoder/AOpCodeBfm.cs new file mode 100644 index 0000000..6498d8e --- /dev/null +++ b/Decoder/AOpCodeBfm.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeBfm : AOpCodeAlu + { + public long WMask { get; private set; } + public long TMask { get; private set; } + public int Pos { get; private set; } + public int Shift { get; private set; } + + public AOpCodeBfm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + var BM = ADecoderHelper.DecodeBitMask(OpCode, false); + + if (BM.IsUndefined) + { + Emitter = AInstEmit.Und; + + return; + } + + WMask = BM.WMask; + TMask = BM.TMask; + Pos = BM.Pos; + Shift = BM.Shift; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeCcmp.cs b/Decoder/AOpCodeCcmp.cs new file mode 100644 index 0000000..d0c7f77 --- /dev/null +++ b/Decoder/AOpCodeCcmp.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeCcmp : AOpCodeAlu, IAOpCodeCond + { + public int NZCV { get; private set; } + protected int RmImm; + + public ACond Cond { get; private set; } + + public AOpCodeCcmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int O3 = (OpCode >> 4) & 1; + + if (O3 != 0) + { + Emitter = AInstEmit.Und; + + return; + } + + NZCV = (OpCode >> 0) & 0xf; + Cond = (ACond)((OpCode >> 12) & 0xf); + RmImm = (OpCode >> 16) & 0x1f; + + Rd = AThreadState.ZRIndex; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeCcmpImm.cs b/Decoder/AOpCodeCcmpImm.cs new file mode 100644 index 0000000..803eefc --- /dev/null +++ b/Decoder/AOpCodeCcmpImm.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeCcmpImm : AOpCodeCcmp, IAOpCodeAluImm + { + public long Imm => RmImm; + + public AOpCodeCcmpImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeCcmpReg.cs b/Decoder/AOpCodeCcmpReg.cs new file mode 100644 index 0000000..c364ae6 --- /dev/null +++ b/Decoder/AOpCodeCcmpReg.cs @@ -0,0 +1,15 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeCcmpReg : AOpCodeCcmp, IAOpCodeAluRs + { + public int Rm => RmImm; + + public int Shift => 0; + + public AShiftType ShiftType => AShiftType.Lsl; + + public AOpCodeCcmpReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeCsel.cs b/Decoder/AOpCodeCsel.cs new file mode 100644 index 0000000..cdef3e7 --- /dev/null +++ b/Decoder/AOpCodeCsel.cs @@ -0,0 +1,17 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeCsel : AOpCodeAlu, IAOpCodeCond + { + public int Rm { get; private set; } + + public ACond Cond { get; private set; } + + public AOpCodeCsel(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rm = (OpCode >> 16) & 0x1f; + Cond = (ACond)((OpCode >> 12) & 0xf); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeException.cs b/Decoder/AOpCodeException.cs new file mode 100644 index 0000000..4579c1a --- /dev/null +++ b/Decoder/AOpCodeException.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeException : AOpCode + { + public int Id { get; private set; } + + public AOpCodeException(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Id = (OpCode >> 5) & 0xffff; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMem.cs b/Decoder/AOpCodeMem.cs new file mode 100644 index 0000000..be5367c --- /dev/null +++ b/Decoder/AOpCodeMem.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMem : AOpCode + { + public int Rt { get; protected set; } + public int Rn { get; protected set; } + public int Size { get; protected set; } + public bool Extend64 { get; protected set; } + + public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt = (OpCode >> 0) & 0x1f; + Rn = (OpCode >> 5) & 0x1f; + Size = (OpCode >> 30) & 0x3; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMemEx.cs b/Decoder/AOpCodeMemEx.cs new file mode 100644 index 0000000..3a28cfd --- /dev/null +++ b/Decoder/AOpCodeMemEx.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMemEx : AOpCodeMem + { + public int Rt2 { get; private set; } + public int Rs { get; private set; } + + public AOpCodeMemEx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt2 = (OpCode >> 10) & 0x1f; + Rs = (OpCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMemImm.cs b/Decoder/AOpCodeMemImm.cs new file mode 100644 index 0000000..14edc51 --- /dev/null +++ b/Decoder/AOpCodeMemImm.cs @@ -0,0 +1,53 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMemImm : AOpCodeMem + { + public long Imm { get; protected set; } + public bool WBack { get; protected set; } + public bool PostIdx { get; protected set; } + protected bool Unscaled { get; private set; } + + private enum MemOp + { + Unscaled = 0, + PostIndexed = 1, + Unprivileged = 2, + PreIndexed = 3, + Unsigned + } + + public AOpCodeMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Extend64 = ((OpCode >> 22) & 3) == 2; + WBack = ((OpCode >> 24) & 1) == 0; + + //The type is not valid for the Unsigned Immediate 12-bits encoding, + //because the bits 11:10 are used for the larger Immediate offset. + MemOp Type = WBack ? (MemOp)((OpCode >> 10) & 3) : MemOp.Unsigned; + + PostIdx = Type == MemOp.PostIndexed; + Unscaled = Type == MemOp.Unscaled || + Type == MemOp.Unprivileged; + + //Unscaled and Unprivileged doesn't write back, + //but they do use the 9-bits Signed Immediate. + if (Unscaled) + { + WBack = false; + } + + if (WBack || Unscaled) + { + //9-bits Signed Immediate. + Imm = (OpCode << 43) >> 55; + } + else + { + //12-bits Unsigned Immediate. + Imm = ((OpCode >> 10) & 0xfff) << Size; + } + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMemLit.cs b/Decoder/AOpCodeMemLit.cs new file mode 100644 index 0000000..ad719a1 --- /dev/null +++ b/Decoder/AOpCodeMemLit.cs @@ -0,0 +1,28 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMemLit : AOpCode, IAOpCodeLit + { + public int Rt { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + public bool Signed { get; private set; } + public bool Prefetch { get; private set; } + + public AOpCodeMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt = OpCode & 0x1f; + + Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); + + switch ((OpCode >> 30) & 3) + { + case 0: Size = 2; Signed = false; Prefetch = false; break; + case 1: Size = 3; Signed = false; Prefetch = false; break; + case 2: Size = 2; Signed = true; Prefetch = false; break; + case 3: Size = 0; Signed = false; Prefetch = true; break; + } + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMemPair.cs b/Decoder/AOpCodeMemPair.cs new file mode 100644 index 0000000..ec866c8 --- /dev/null +++ b/Decoder/AOpCodeMemPair.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMemPair : AOpCodeMemImm + { + public int Rt2 { get; private set; } + + public AOpCodeMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt2 = (OpCode >> 10) & 0x1f; + WBack = ((OpCode >> 23) & 0x1) != 0; + PostIdx = ((OpCode >> 23) & 0x3) == 1; + Extend64 = ((OpCode >> 30) & 0x3) == 1; + Size = ((OpCode >> 31) & 0x1) | 2; + + DecodeImm(OpCode); + } + + protected void DecodeImm(int OpCode) + { + Imm = ((long)(OpCode >> 15) << 57) >> (57 - Size); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMemReg.cs b/Decoder/AOpCodeMemReg.cs new file mode 100644 index 0000000..9892712 --- /dev/null +++ b/Decoder/AOpCodeMemReg.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMemReg : AOpCodeMem + { + public bool Shift { get; private set; } + public int Rm { get; private set; } + + public AIntType IntType { get; private set; } + + public AOpCodeMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Shift = ((OpCode >> 12) & 0x1) != 0; + IntType = (AIntType)((OpCode >> 13) & 0x7); + Rm = (OpCode >> 16) & 0x1f; + Extend64 = ((OpCode >> 22) & 0x3) == 2; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMov.cs b/Decoder/AOpCodeMov.cs new file mode 100644 index 0000000..d539864 --- /dev/null +++ b/Decoder/AOpCodeMov.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMov : AOpCode + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Pos { get; private set; } + + public AOpCodeMov(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int P1 = (OpCode >> 22) & 1; + int SF = (OpCode >> 31) & 1; + + if (SF == 0 && P1 != 0) + { + Emitter = AInstEmit.Und; + + return; + } + + Rd = (OpCode >> 0) & 0x1f; + Imm = (OpCode >> 5) & 0xffff; + Pos = (OpCode >> 21) & 0x3; + + Pos <<= 4; + Imm <<= Pos; + + RegisterSize = (OpCode >> 31) != 0 + ? ARegisterSize.Int64 + : ARegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeMul.cs b/Decoder/AOpCodeMul.cs new file mode 100644 index 0000000..ca2b0cd --- /dev/null +++ b/Decoder/AOpCodeMul.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeMul : AOpCodeAlu + { + public int Rm { get; private set; } + public int Ra { get; private set; } + + public AOpCodeMul(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Ra = (OpCode >> 10) & 0x1f; + Rm = (OpCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimd.cs b/Decoder/AOpCodeSimd.cs new file mode 100644 index 0000000..5f940b2 --- /dev/null +++ b/Decoder/AOpCodeSimd.cs @@ -0,0 +1,27 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimd : AOpCode, IAOpCodeSimd + { + public int Rd { get; private set; } + public int Rn { get; private set; } + public int Opc { get; private set; } + public int Size { get; protected set; } + + public int SizeF => Size & 1; + + public AOpCodeSimd(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rd = (OpCode >> 0) & 0x1f; + Rn = (OpCode >> 5) & 0x1f; + Opc = (OpCode >> 15) & 0x3; + Size = (OpCode >> 22) & 0x3; + + RegisterSize = ((OpCode >> 30) & 1) != 0 + ? ARegisterSize.SIMD128 + : ARegisterSize.SIMD64; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdCvt.cs b/Decoder/AOpCodeSimdCvt.cs new file mode 100644 index 0000000..41f4d3b --- /dev/null +++ b/Decoder/AOpCodeSimdCvt.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdCvt : AOpCodeSimd + { + public int FBits { get; private set; } + + public AOpCodeSimdCvt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + //TODO: + //Und of Fixed Point variants. + int Scale = (OpCode >> 10) & 0x3f; + int SF = (OpCode >> 31) & 0x1; + + /*if (Type != SF && !(Type == 2 && SF == 1)) + { + Emitter = AInstEmit.Und; + + return; + }*/ + + FBits = 64 - Scale; + + RegisterSize = SF != 0 + ? ARegisterSize.Int64 + : ARegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdFcond.cs b/Decoder/AOpCodeSimdFcond.cs new file mode 100644 index 0000000..e38e742 --- /dev/null +++ b/Decoder/AOpCodeSimdFcond.cs @@ -0,0 +1,17 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdFcond : AOpCodeSimdReg, IAOpCodeCond + { + public int NZCV { get; private set; } + + public ACond Cond { get; private set; } + + public AOpCodeSimdFcond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + NZCV = (OpCode >> 0) & 0xf; + Cond = (ACond)((OpCode >> 12) & 0xf); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdFmov.cs b/Decoder/AOpCodeSimdFmov.cs new file mode 100644 index 0000000..3f88895 --- /dev/null +++ b/Decoder/AOpCodeSimdFmov.cs @@ -0,0 +1,33 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdFmov : AOpCode, IAOpCodeSimd + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + + public AOpCodeSimdFmov(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int Imm5 = (OpCode >> 5) & 0x1f; + int Type = (OpCode >> 22) & 0x3; + + if (Imm5 != 0b00000 || Type > 1) + { + Emitter = AInstEmit.Und; + + return; + } + + Size = Type; + + long Imm; + + Rd = (OpCode >> 0) & 0x1f; + Imm = (OpCode >> 13) & 0xff; + + this.Imm = ADecoderHelper.DecodeImm8Float(Imm, Type); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdImm.cs b/Decoder/AOpCodeSimdImm.cs new file mode 100644 index 0000000..2959aee --- /dev/null +++ b/Decoder/AOpCodeSimdImm.cs @@ -0,0 +1,94 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdImm : AOpCode, IAOpCodeSimd + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + + public AOpCodeSimdImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rd = OpCode & 0x1f; + + int CMode = (OpCode >> 12) & 0xf; + int Op = (OpCode >> 29) & 0x1; + + int ModeLow = CMode & 1; + int ModeHigh = CMode >> 1; + + long Imm; + + Imm = ((uint)OpCode >> 5) & 0x1f; + Imm |= ((uint)OpCode >> 11) & 0xe0; + + if (ModeHigh == 0b111) + { + Size = ModeLow != 0 ? Op : 3; + + switch (Op | (ModeLow << 1)) + { + case 0: + //64-bits Immediate. + //Transform abcd efgh into abcd efgh abcd efgh ... + Imm = (long)((ulong)Imm * 0x0101010101010101); + break; + + case 1: + //64-bits Immediate. + //Transform abcd efgh into aaaa aaaa bbbb bbbb ... + Imm = (Imm & 0xf0) >> 4 | (Imm & 0x0f) << 4; + Imm = (Imm & 0xcc) >> 2 | (Imm & 0x33) << 2; + Imm = (Imm & 0xaa) >> 1 | (Imm & 0x55) << 1; + + Imm = (long)((ulong)Imm * 0x8040201008040201); + Imm = (long)((ulong)Imm & 0x8080808080808080); + + Imm |= Imm >> 4; + Imm |= Imm >> 2; + Imm |= Imm >> 1; + break; + + case 2: + case 3: + //Floating point Immediate. + Imm = ADecoderHelper.DecodeImm8Float(Imm, Size); + break; + } + } + else if ((ModeHigh & 0b110) == 0b100) + { + //16-bits shifted Immediate. + Size = 1; Imm <<= (ModeHigh & 1) << 3; + } + else if ((ModeHigh & 0b100) == 0b000) + { + //32-bits shifted Immediate. + Size = 2; Imm <<= ModeHigh << 3; + } + else if ((ModeHigh & 0b111) == 0b110) + { + //32-bits shifted Immediate (fill with ones). + Size = 2; Imm = ShlOnes(Imm, 8 << ModeLow); + } + else + { + //8 bits without shift. + Size = 0; + } + + this.Imm = Imm; + + RegisterSize = ((OpCode >> 30) & 1) != 0 + ? ARegisterSize.SIMD128 + : ARegisterSize.SIMD64; + } + + private static long ShlOnes(long Value, int Shift) + { + return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdIns.cs b/Decoder/AOpCodeSimdIns.cs new file mode 100644 index 0000000..0b60bbe --- /dev/null +++ b/Decoder/AOpCodeSimdIns.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdIns : AOpCodeSimd + { + public int SrcIndex { get; private set; } + public int DstIndex { get; private set; } + + public AOpCodeSimdIns(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int Imm4 = (OpCode >> 11) & 0xf; + int Imm5 = (OpCode >> 16) & 0x1f; + + if (Imm5 == 0b10000) + { + Emitter = AInstEmit.Und; + + return; + } + + Size = Imm5 & -Imm5; + + switch (Size) + { + case 1: Size = 0; break; + case 2: Size = 1; break; + case 4: Size = 2; break; + case 8: Size = 3; break; + } + + SrcIndex = Imm4 >> Size; + DstIndex = Imm5 >> (Size + 1); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemImm.cs b/Decoder/AOpCodeSimdMemImm.cs new file mode 100644 index 0000000..1ef19a5 --- /dev/null +++ b/Decoder/AOpCodeSimdMemImm.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemImm : AOpCodeMemImm, IAOpCodeSimd + { + public AOpCodeSimdMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Size |= (OpCode >> 21) & 4; + + if (!WBack && !Unscaled && Size >= 4) + { + Imm <<= 4; + } + + Extend64 = false; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemLit.cs b/Decoder/AOpCodeSimdMemLit.cs new file mode 100644 index 0000000..ea6fe00 --- /dev/null +++ b/Decoder/AOpCodeSimdMemLit.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemLit : AOpCode, IAOpCodeSimd, IAOpCodeLit + { + public int Rt { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + public bool Signed => false; + public bool Prefetch => false; + + public AOpCodeSimdMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int Opc = (OpCode >> 30) & 3; + + if (Opc == 3) + { + Emitter = AInstEmit.Und; + + return; + } + + Rt = OpCode & 0x1f; + + Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); + + Size = Opc + 2; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemMs.cs b/Decoder/AOpCodeSimdMemMs.cs new file mode 100644 index 0000000..9ea979b --- /dev/null +++ b/Decoder/AOpCodeSimdMemMs.cs @@ -0,0 +1,49 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemMs : AOpCodeMemReg, IAOpCodeSimd + { + public int Reps { get; private set; } + public int SElems { get; private set; } + public int Elems { get; private set; } + public bool WBack { get; private set; } + + public AOpCodeSimdMemMs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + switch ((OpCode >> 12) & 0xf) + { + case 0b0000: Reps = 1; SElems = 4; break; + case 0b0010: Reps = 4; SElems = 1; break; + case 0b0100: Reps = 1; SElems = 3; break; + case 0b0110: Reps = 3; SElems = 1; break; + case 0b0111: Reps = 1; SElems = 1; break; + case 0b1000: Reps = 1; SElems = 2; break; + case 0b1010: Reps = 2; SElems = 1; break; + + default: Inst = AInst.Undefined; return; + } + + Size = (OpCode >> 10) & 0x3; + WBack = ((OpCode >> 23) & 0x1) != 0; + + bool Q = ((OpCode >> 30) & 1) != 0; + + if (!Q && Size == 3 && SElems != 1) + { + Inst = AInst.Undefined; + + return; + } + + Extend64 = false; + + RegisterSize = Q + ? ARegisterSize.SIMD128 + : ARegisterSize.SIMD64; + + Elems = (GetBitsCount() >> 3) >> Size; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemPair.cs b/Decoder/AOpCodeSimdMemPair.cs new file mode 100644 index 0000000..db99e3d --- /dev/null +++ b/Decoder/AOpCodeSimdMemPair.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemPair : AOpCodeMemPair, IAOpCodeSimd + { + public AOpCodeSimdMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Size = ((OpCode >> 30) & 3) + 2; + + Extend64 = false; + + DecodeImm(OpCode); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemReg.cs b/Decoder/AOpCodeSimdMemReg.cs new file mode 100644 index 0000000..aabf484 --- /dev/null +++ b/Decoder/AOpCodeSimdMemReg.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemReg : AOpCodeMemReg, IAOpCodeSimd + { + public AOpCodeSimdMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Size |= (OpCode >> 21) & 4; + + Extend64 = false; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdMemSs.cs b/Decoder/AOpCodeSimdMemSs.cs new file mode 100644 index 0000000..be4a8cd --- /dev/null +++ b/Decoder/AOpCodeSimdMemSs.cs @@ -0,0 +1,97 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdMemSs : AOpCodeMemReg, IAOpCodeSimd + { + 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, OpCode) + { + 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 1: + { + if ((Size & 1) != 0) + { + Inst = AInst.Undefined; + + return; + } + + Index >>= 1; + + break; + } + + case 2: + { + if ((Size & 2) != 0 || + ((Size & 1) != 0 && S != 0)) + { + Inst = AInst.Undefined; + + return; + } + + if ((Size & 1) != 0) + { + Index >>= 3; + + Scale = 3; + } + else + { + Index >>= 2; + } + + break; + } + + case 3: + { + if (L == 0 || S != 0) + { + Inst = AInst.Undefined; + + return; + } + + Scale = Size; + + Replicate = true; + + break; + } + } + + this.SElems = SElems; + this.Size = Scale; + + Extend64 = false; + + WBack = ((OpCode >> 23) & 0x1) != 0; + + RegisterSize = Q != 0 + ? ARegisterSize.SIMD128 + : ARegisterSize.SIMD64; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdReg.cs b/Decoder/AOpCodeSimdReg.cs new file mode 100644 index 0000000..10a4aff --- /dev/null +++ b/Decoder/AOpCodeSimdReg.cs @@ -0,0 +1,18 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdReg : AOpCodeSimd + { + public bool Bit3 { get; private set; } + public int Ra { get; private set; } + public int Rm { get; private set; } + + public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Bit3 = ((OpCode >> 3) & 0x1) != 0; + Ra = (OpCode >> 10) & 0x1f; + Rm = (OpCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdRegElem.cs b/Decoder/AOpCodeSimdRegElem.cs new file mode 100644 index 0000000..d878b12 --- /dev/null +++ b/Decoder/AOpCodeSimdRegElem.cs @@ -0,0 +1,22 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdRegElem : AOpCodeSimdReg + { + public int Index { get; private set; } + + public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + if ((Size & 1) != 0) + { + Index = (OpCode >> 11) & 1; + } + else + { + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2; + } + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdShImm.cs b/Decoder/AOpCodeSimdShImm.cs new file mode 100644 index 0000000..6c83988 --- /dev/null +++ b/Decoder/AOpCodeSimdShImm.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdShImm : AOpCodeSimd + { + public int Imm { get; private set; } + + public AOpCodeSimdShImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Imm = (OpCode >> 16) & 0x7f; + + Size = ABitUtils.HighestBitSet32(Imm >> 3); + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSimdTbl.cs b/Decoder/AOpCodeSimdTbl.cs new file mode 100644 index 0000000..c8ae5ba --- /dev/null +++ b/Decoder/AOpCodeSimdTbl.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdTbl : AOpCodeSimdReg + { + public AOpCodeSimdTbl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Size = ((OpCode >> 13) & 3) + 1; + } + } +} \ No newline at end of file diff --git a/Decoder/AOpCodeSystem.cs b/Decoder/AOpCodeSystem.cs new file mode 100644 index 0000000..3d81a5d --- /dev/null +++ b/Decoder/AOpCodeSystem.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSystem : AOpCode + { + public int Rt { get; private set; } + public int Op2 { get; private set; } + public int CRm { get; private set; } + public int CRn { get; private set; } + public int Op1 { get; private set; } + public int Op0 { get; private set; } + + public AOpCodeSystem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Rt = (OpCode >> 0) & 0x1f; + Op2 = (OpCode >> 5) & 0x7; + CRm = (OpCode >> 8) & 0xf; + CRn = (OpCode >> 12) & 0xf; + Op1 = (OpCode >> 16) & 0x7; + Op0 = ((OpCode >> 19) & 0x1) | 2; + } + } +} \ No newline at end of file diff --git a/Decoder/AShiftType.cs b/Decoder/AShiftType.cs new file mode 100644 index 0000000..34ceea2 --- /dev/null +++ b/Decoder/AShiftType.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoder +{ + enum AShiftType + { + Lsl, + Lsr, + Asr, + Ror + } +} \ No newline at end of file diff --git a/Decoder/IAOpCode.cs b/Decoder/IAOpCode.cs new file mode 100644 index 0000000..44bf9cb --- /dev/null +++ b/Decoder/IAOpCode.cs @@ -0,0 +1,13 @@ +using ChocolArm64.Instruction; +using ChocolArm64.State; + +namespace ChocolArm64.Decoder +{ + interface IAOpCode + { + long Position { get; } + + AInstEmitter Emitter { get; } + ARegisterSize RegisterSize { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeAlu.cs b/Decoder/IAOpCodeAlu.cs new file mode 100644 index 0000000..22af4c8 --- /dev/null +++ b/Decoder/IAOpCodeAlu.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeAlu : IAOpCode + { + int Rd { get; } + int Rn { get; } + + ADataOp DataOp { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeAluImm.cs b/Decoder/IAOpCodeAluImm.cs new file mode 100644 index 0000000..04b5c5f --- /dev/null +++ b/Decoder/IAOpCodeAluImm.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeAluImm : IAOpCodeAlu + { + long Imm { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeAluRs.cs b/Decoder/IAOpCodeAluRs.cs new file mode 100644 index 0000000..5ca9de4 --- /dev/null +++ b/Decoder/IAOpCodeAluRs.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeAluRs : IAOpCodeAlu + { + int Shift { get; } + int Rm { get; } + + AShiftType ShiftType { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeAluRx.cs b/Decoder/IAOpCodeAluRx.cs new file mode 100644 index 0000000..b49d532 --- /dev/null +++ b/Decoder/IAOpCodeAluRx.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeAluRx : IAOpCodeAlu + { + int Shift { get; } + int Rm { get; } + + AIntType IntType { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeCond.cs b/Decoder/IAOpCodeCond.cs new file mode 100644 index 0000000..1655aba --- /dev/null +++ b/Decoder/IAOpCodeCond.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeCond : IAOpCode + { + ACond Cond { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeLit.cs b/Decoder/IAOpCodeLit.cs new file mode 100644 index 0000000..0f5092d --- /dev/null +++ b/Decoder/IAOpCodeLit.cs @@ -0,0 +1,11 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeLit : IAOpCode + { + int Rt { get; } + long Imm { get; } + int Size { get; } + bool Signed { get; } + bool Prefetch { get; } + } +} \ No newline at end of file diff --git a/Decoder/IAOpCodeSimd.cs b/Decoder/IAOpCodeSimd.cs new file mode 100644 index 0000000..19032ad --- /dev/null +++ b/Decoder/IAOpCodeSimd.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoder +{ + interface IAOpCodeSimd : IAOpCode + { + int Size { get; } + } +} \ No newline at end of file diff --git a/Exceptions/VmmAccessViolationException.cs b/Exceptions/VmmAccessViolationException.cs new file mode 100644 index 0000000..a557502 --- /dev/null +++ b/Exceptions/VmmAccessViolationException.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Memory; +using System; + +namespace ChocolArm64.Exceptions +{ + public class VmmAccessViolationException : Exception + { + private const string ExMsg = "Address 0x{0:x16} does not have \"{1}\" permission!"; + + public VmmAccessViolationException() { } + + public VmmAccessViolationException(long Position, AMemoryPerm Perm) : base(string.Format(ExMsg, Position, Perm)) { } + } +} \ No newline at end of file diff --git a/Exceptions/VmmOutOfMemoryException.cs b/Exceptions/VmmOutOfMemoryException.cs new file mode 100644 index 0000000..c11384d --- /dev/null +++ b/Exceptions/VmmOutOfMemoryException.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChocolArm64.Exceptions +{ + public class VmmOutOfMemoryException : Exception + { + private const string ExMsg = "Failed to allocate {0} bytes of memory!"; + + public VmmOutOfMemoryException() { } + + public VmmOutOfMemoryException(long Size) : base(string.Format(ExMsg, Size)) { } + } +} \ No newline at end of file diff --git a/Exceptions/VmmPageFaultException.cs b/Exceptions/VmmPageFaultException.cs new file mode 100644 index 0000000..d55c2c1 --- /dev/null +++ b/Exceptions/VmmPageFaultException.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChocolArm64.Exceptions +{ + public class VmmPageFaultException : Exception + { + private const string ExMsg = "Tried to access unmapped address 0x{0:x16}!"; + + public VmmPageFaultException() { } + + public VmmPageFaultException(long Position) : base(string.Format(ExMsg, Position)) { } + } +} \ No newline at end of file diff --git a/Instruction/AInst.cs b/Instruction/AInst.cs new file mode 100644 index 0000000..cab597d --- /dev/null +++ b/Instruction/AInst.cs @@ -0,0 +1,18 @@ +using System; + +namespace ChocolArm64.Instruction +{ + struct AInst + { + public AInstEmitter Emitter { get; private set; } + public Type Type { get; private set; } + + public static AInst Undefined => new AInst(AInstEmit.Und, null); + + public AInst(AInstEmitter Emitter, Type Type) + { + this.Emitter = Emitter; + this.Type = Type; + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitAlu.cs b/Instruction/AInstEmitAlu.cs new file mode 100644 index 0000000..72903f5 --- /dev/null +++ b/Instruction/AInstEmitAlu.cs @@ -0,0 +1,364 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitAluHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Adc(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Add); + + Context.EmitLdflg((int)APState.CBit); + + Type[] MthdTypes = new Type[] { typeof(bool) }; + + MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes); + + Context.EmitCall(MthdInfo); + + if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_I8); + } + + Context.Emit(OpCodes.Add); + + EmitDataStore(Context); + } + + public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add); + + public static void Adds(AILEmitterCtx Context) + { + Context.TryOptMarkCondWithoutCmp(); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Add); + + Context.EmitZNFlagCheck(); + + EmitAddsCCheck(Context); + EmitAddsVCheck(Context); + EmitDataStoreS(Context); + } + + public static void And(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.And); + + public static void Ands(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.And); + + EmitZeroCVFlags(Context); + + Context.EmitZNFlagCheck(); + + EmitDataStoreS(Context); + } + + public static void Asrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr); + + public static void Bic(AILEmitterCtx Context) => EmitBic(Context, false); + public static void Bics(AILEmitterCtx Context) => EmitBic(Context, true); + + private static void EmitBic(AILEmitterCtx Context, bool SetFlags) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + + if (SetFlags) + { + EmitZeroCVFlags(Context); + + Context.EmitZNFlagCheck(); + } + + EmitDataStore(Context, SetFlags); + } + + public static void Clz(AILEmitterCtx Context) + { + AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Op.RegisterSize == ARegisterSize.Int32) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros32)); + } + else + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros64)); + } + + Context.EmitStintzr(Op.Rd); + } + + public static void Eon(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.Xor); + + EmitDataStore(Context); + } + + public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor); + + public static void Extr(AILEmitterCtx Context) + { + //TODO: Ensure that the Shift is valid for the Is64Bits. + AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; + + Context.EmitLdintzr(Op.Rm); + + if (Op.Shift > 0) + { + Context.EmitLdc_I4(Op.Shift); + + Context.Emit(OpCodes.Shr_Un); + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdc_I4(Op.GetBitsCount() - Op.Shift); + + Context.Emit(OpCodes.Shl); + Context.Emit(OpCodes.Or); + } + + EmitDataStore(Context); + } + + public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl); + public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un); + + public static void Sbc(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Sub); + + Context.EmitLdflg((int)APState.CBit); + + Type[] MthdTypes = new Type[] { typeof(bool) }; + + MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes); + + Context.EmitCall(MthdInfo); + + Context.EmitLdc_I4(1); + + Context.Emit(OpCodes.Xor); + + if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_I8); + } + + Context.Emit(OpCodes.Sub); + + EmitDataStore(Context); + } + + public static void Sub(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Sub); + + public static void Subs(AILEmitterCtx Context) + { + Context.TryOptMarkCondWithoutCmp(); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Sub); + + Context.EmitZNFlagCheck(); + + EmitSubsCCheck(Context); + EmitSubsVCheck(Context); + EmitDataStoreS(Context); + } + + public static void Orn(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.Or); + + EmitDataStore(Context); + } + + public static void Orr(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Or); + + public static void Rbit(AILEmitterCtx Context) => EmitFallback32_64(Context, + nameof(ASoftFallback.ReverseBits32), + nameof(ASoftFallback.ReverseBits64)); + + public static void Rev16(AILEmitterCtx Context) => EmitFallback32_64(Context, + nameof(ASoftFallback.ReverseBytes16_32), + nameof(ASoftFallback.ReverseBytes16_64)); + + public static void Rev32(AILEmitterCtx Context) => EmitFallback32_64(Context, + nameof(ASoftFallback.ReverseBytes32_32), + nameof(ASoftFallback.ReverseBytes32_64)); + + public static void EmitFallback32_64(AILEmitterCtx Context, string Name32, string Name64) + { + AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Op.RegisterSize == ARegisterSize.Int32) + { + ASoftFallback.EmitCall(Context, Name32); + } + else + { + ASoftFallback.EmitCall(Context, Name64); + } + + Context.EmitStintzr(Op.Rd); + } + + public static void Rev64(AILEmitterCtx Context) + { + AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBytes64)); + + Context.EmitStintzr(Op.Rd); + } + + public static void Rorv(AILEmitterCtx Context) + { + EmitDataLoadRn(Context); + EmitDataLoadShift(Context); + + Context.Emit(OpCodes.Shr_Un); + + EmitDataLoadRn(Context); + + Context.EmitLdc_I4(Context.CurrOp.GetBitsCount()); + + EmitDataLoadShift(Context); + + Context.Emit(OpCodes.Sub); + Context.Emit(OpCodes.Shl); + Context.Emit(OpCodes.Or); + + EmitDataStore(Context); + } + + public static void Sdiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div); + public static void Udiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div_Un); + + private static void EmitDiv(AILEmitterCtx Context, OpCode ILOp) + { + //If Rm == 0, Rd = 0 (division by zero). + Context.EmitLdc_I(0); + + EmitDataLoadRm(Context); + + Context.EmitLdc_I(0); + + AILLabel BadDiv = new AILLabel(); + + Context.Emit(OpCodes.Beq_S, BadDiv); + Context.Emit(OpCodes.Pop); + + if (ILOp == OpCodes.Div) + { + //If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow). + long IntMin = 1L << (Context.CurrOp.GetBitsCount() - 1); + + Context.EmitLdc_I(IntMin); + + EmitDataLoadRn(Context); + + Context.EmitLdc_I(IntMin); + + Context.Emit(OpCodes.Ceq); + + EmitDataLoadRm(Context); + + Context.EmitLdc_I(-1); + + Context.Emit(OpCodes.Ceq); + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Brtrue_S, BadDiv); + Context.Emit(OpCodes.Pop); + } + + EmitDataLoadRn(Context); + EmitDataLoadRm(Context); + + Context.Emit(ILOp); + + Context.MarkLabel(BadDiv); + + EmitDataStore(Context); + } + + private static void EmitDataOp(AILEmitterCtx Context, OpCode ILOp) + { + EmitDataLoadOpers(Context); + + Context.Emit(ILOp); + + EmitDataStore(Context); + } + + private static void EmitDataOpShift(AILEmitterCtx Context, OpCode ILOp) + { + EmitDataLoadRn(Context); + EmitDataLoadShift(Context); + + Context.Emit(ILOp); + + EmitDataStore(Context); + } + + private static void EmitDataLoadShift(AILEmitterCtx Context) + { + EmitDataLoadRm(Context); + + Context.EmitLdc_I(Context.CurrOp.GetBitsCount() - 1); + + Context.Emit(OpCodes.And); + + //Note: Only 32-bits shift values are valid, so when the value is 64-bits + //we need to cast it to a 32-bits integer. This is fine because we + //AND the value and only keep the lower 5 or 6 bits anyway -- it + //could very well fit on a byte. + if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_I4); + } + } + + private static void EmitZeroCVFlags(AILEmitterCtx Context) + { + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.CBit); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitAluHelper.cs b/Instruction/AInstEmitAluHelper.cs new file mode 100644 index 0000000..e848742 --- /dev/null +++ b/Instruction/AInstEmitAluHelper.cs @@ -0,0 +1,168 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitAluHelper + { + public static void EmitAddsCCheck(AILEmitterCtx Context) + { + //C = Rd < Rn + Context.Emit(OpCodes.Dup); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Clt_Un); + + Context.EmitStflg((int)APState.CBit); + } + + public static void EmitAddsVCheck(AILEmitterCtx Context) + { + //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 + Context.Emit(OpCodes.Dup); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Xor); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(0); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.VBit); + } + + public static void EmitSubsCCheck(AILEmitterCtx Context) + { + //C = Rn == Rm || Rn > Rm = !(Rn < Rm) + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Clt_Un); + + Context.EmitLdc_I4(1); + + Context.Emit(OpCodes.Xor); + + Context.EmitStflg((int)APState.CBit); + } + + public static void EmitSubsVCheck(AILEmitterCtx Context) + { + //V = (Rd ^ Rn) & (Rn ^ Rm) < 0 + Context.Emit(OpCodes.Dup); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Xor); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(0); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.VBit); + } + + public static void EmitDataLoadRm(AILEmitterCtx Context) + { + Context.EmitLdintzr(((IAOpCodeAluRs)Context.CurrOp).Rm); + } + + public static void EmitDataLoadOpers(AILEmitterCtx Context) + { + EmitDataLoadRn(Context); + EmitDataLoadOper2(Context); + } + + public static void EmitDataLoadRn(AILEmitterCtx Context) + { + IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp; + + if (Op.DataOp == ADataOp.Logical || Op is IAOpCodeAluRs) + { + Context.EmitLdintzr(Op.Rn); + } + else + { + Context.EmitLdint(Op.Rn); + } + } + + public static void EmitDataLoadOper2(AILEmitterCtx Context) + { + switch (Context.CurrOp) + { + case IAOpCodeAluImm Op: + Context.EmitLdc_I(Op.Imm); + break; + + case IAOpCodeAluRs Op: + Context.EmitLdintzr(Op.Rm); + + switch (Op.ShiftType) + { + case AShiftType.Lsl: Context.EmitLsl(Op.Shift); break; + case AShiftType.Lsr: Context.EmitLsr(Op.Shift); break; + case AShiftType.Asr: Context.EmitAsr(Op.Shift); break; + case AShiftType.Ror: Context.EmitRor(Op.Shift); break; + } + break; + + case IAOpCodeAluRx Op: + Context.EmitLdintzr(Op.Rm); + Context.EmitCast(Op.IntType); + Context.EmitLsl(Op.Shift); + break; + } + } + + public static void EmitDataStore(AILEmitterCtx Context) => EmitDataStore(Context, false); + public static void EmitDataStoreS(AILEmitterCtx Context) => EmitDataStore(Context, true); + + public static void EmitDataStore(AILEmitterCtx Context, bool SetFlags) + { + IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp; + + if (SetFlags || Op is IAOpCodeAluRs) + { + Context.EmitStintzr(Op.Rd); + } + else + { + Context.EmitStint(Op.Rd); + } + } + + public static void EmitSetNZCV(AILEmitterCtx Context, int NZCV) + { + Context.EmitLdc_I4((NZCV >> 0) & 1); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4((NZCV >> 1) & 1); + + Context.EmitStflg((int)APState.CBit); + + Context.EmitLdc_I4((NZCV >> 2) & 1); + + Context.EmitStflg((int)APState.ZBit); + + Context.EmitLdc_I4((NZCV >> 3) & 1); + + Context.EmitStflg((int)APState.NBit); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitBfm.cs b/Instruction/AInstEmitBfm.cs new file mode 100644 index 0000000..823af73 --- /dev/null +++ b/Instruction/AInstEmitBfm.cs @@ -0,0 +1,217 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Bfm(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + EmitBfmLoadRn(Context); + + Context.EmitLdintzr(Op.Rd); + Context.EmitLdc_I(~Op.WMask & Op.TMask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + Context.EmitLdintzr(Op.Rd); + Context.EmitLdc_I(~Op.TMask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + Context.EmitStintzr(Op.Rd); + } + + public static void Sbfm(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + int BitsCount = Op.GetBitsCount(); + + if (Op.Pos + 1 == BitsCount) + { + EmitSbfmShift(Context); + } + else if (Op.Pos < Op.Shift) + { + EmitSbfiz(Context); + } + else if (Op.Pos == 7 && Op.Shift == 0) + { + EmitSbfmCast(Context, OpCodes.Conv_I1); + } + else if (Op.Pos == 15 && Op.Shift == 0) + { + EmitSbfmCast(Context, OpCodes.Conv_I2); + } + else if (Op.Pos == 31 && Op.Shift == 0) + { + EmitSbfmCast(Context, OpCodes.Conv_I4); + } + else if (Op.Shift == 0) + { + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(BitsCount - 1 - Op.Pos); + Context.EmitAsr(BitsCount - 1); + + Context.EmitStintzr(Op.Rd); + } + else + { + EmitBfmLoadRn(Context); + + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(BitsCount - 1 - Op.Pos); + Context.EmitAsr(BitsCount - 1); + + Context.EmitLdc_I(~Op.TMask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + Context.EmitStintzr(Op.Rd); + } + } + + public static void Ubfm(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + if (Op.Pos + 1 == Op.GetBitsCount()) + { + EmitUbfmShift(Context); + } + else if (Op.Pos < Op.Shift) + { + EmitUbfiz(Context); + } + else if (Op.Pos + 1 == Op.Shift) + { + EmitBfmLsl(Context); + } + else if (Op.Pos == 7 && Op.Shift == 0) + { + EmitUbfmCast(Context, OpCodes.Conv_U1); + } + else if (Op.Pos == 15 && Op.Shift == 0) + { + EmitUbfmCast(Context, OpCodes.Conv_U2); + } + else + { + EmitBfmLoadRn(Context); + + Context.EmitStintzr(Op.Rd); + } + } + + private static void EmitSbfiz(AILEmitterCtx Context) => EmitBfiz(Context, true); + private static void EmitUbfiz(AILEmitterCtx Context) => EmitBfiz(Context, false); + + private static void EmitBfiz(AILEmitterCtx Context, bool Signed) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + int Width = Op.Pos + 1; + + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(Op.GetBitsCount() - Width); + + if (Signed) + { + Context.EmitAsr(Op.Shift - Width); + } + else + { + Context.EmitLsr(Op.Shift - Width); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitSbfmCast(AILEmitterCtx Context, OpCode ILOp) + { + EmitBfmCast(Context, ILOp, true); + } + + private static void EmitUbfmCast(AILEmitterCtx Context, OpCode ILOp) + { + EmitBfmCast(Context, ILOp, false); + } + + private static void EmitBfmCast(AILEmitterCtx Context, OpCode ILOp, bool Signed) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + Context.Emit(ILOp); + + if (Op.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(Signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitSbfmShift(AILEmitterCtx Context) + { + EmitBfmShift(Context, true); + } + + private static void EmitUbfmShift(AILEmitterCtx Context) + { + EmitBfmShift(Context, false); + } + + private static void EmitBfmShift(AILEmitterCtx Context, bool Signed) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdc_I4(Op.Shift); + + Context.Emit(Signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitBfmLsl(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + Context.EmitLsl(Op.GetBitsCount() - Op.Shift); + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitBfmLoadRn(AILEmitterCtx Context) + { + AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + Context.EmitRor(Op.Shift); + + Context.EmitLdc_I(Op.WMask & Op.TMask); + + Context.Emit(OpCodes.And); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitCcmp.cs b/Instruction/AInstEmitCcmp.cs new file mode 100644 index 0000000..7153a6a --- /dev/null +++ b/Instruction/AInstEmitCcmp.cs @@ -0,0 +1,81 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitAluHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + private enum CcmpOp + { + Cmp, + Cmn + } + + public static void Ccmn(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmn); + public static void Ccmp(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmp); + + private static void EmitCcmp(AILEmitterCtx Context, CcmpOp CmpOp) + { + AOpCodeCcmp Op = (AOpCodeCcmp)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + Context.EmitLdc_I4((Op.NZCV >> 0) & 1); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4((Op.NZCV >> 1) & 1); + + Context.EmitStflg((int)APState.CBit); + + Context.EmitLdc_I4((Op.NZCV >> 2) & 1); + + Context.EmitStflg((int)APState.ZBit); + + Context.EmitLdc_I4((Op.NZCV >> 3) & 1); + + Context.EmitStflg((int)APState.NBit); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitDataLoadOpers(Context); + + if (CmpOp == CcmpOp.Cmp) + { + Context.Emit(OpCodes.Sub); + + Context.EmitZNFlagCheck(); + + EmitSubsCCheck(Context); + EmitSubsVCheck(Context); + } + else if (CmpOp == CcmpOp.Cmn) + { + Context.Emit(OpCodes.Add); + + Context.EmitZNFlagCheck(); + + EmitAddsCCheck(Context); + EmitAddsVCheck(Context); + } + else + { + throw new ArgumentException(nameof(CmpOp)); + } + + Context.Emit(OpCodes.Pop); + + Context.MarkLabel(LblEnd); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitCsel.cs b/Instruction/AInstEmitCsel.cs new file mode 100644 index 0000000..3308098 --- /dev/null +++ b/Instruction/AInstEmitCsel.cs @@ -0,0 +1,59 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + private enum CselOperation + { + None, + Increment, + Invert, + Negate + } + + public static void Csel(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.None); + public static void Csinc(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Increment); + public static void Csinv(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Invert); + public static void Csneg(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Negate); + + private static void EmitCsel(AILEmitterCtx Context, CselOperation CselOp) + { + AOpCodeCsel Op = (AOpCodeCsel)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + Context.EmitLdintzr(Op.Rm); + + if (CselOp == CselOperation.Increment) + { + Context.EmitLdc_I(1); + + Context.Emit(OpCodes.Add); + } + else if (CselOp == CselOperation.Invert) + { + Context.Emit(OpCodes.Not); + } + else if (CselOp == CselOperation.Negate) + { + Context.Emit(OpCodes.Neg); + } + + Context.EmitStintzr(Op.Rd); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + Context.EmitLdintzr(Op.Rn); + Context.EmitStintzr(Op.Rd); + + Context.MarkLabel(LblEnd); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitException.cs b/Instruction/AInstEmitException.cs new file mode 100644 index 0000000..209ba56 --- /dev/null +++ b/Instruction/AInstEmitException.cs @@ -0,0 +1,65 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance; + + public static void Brk(AILEmitterCtx Context) + { + EmitExceptionCall(Context, nameof(AThreadState.OnBreak)); + } + + public static void Svc(AILEmitterCtx Context) + { + EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall)); + } + + private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName) + { + AOpCodeException Op = (AOpCodeException)Context.CurrOp; + + Context.EmitStoreState(); + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + Context.EmitLdc_I4(Op.Id); + + MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding); + + Context.EmitCall(MthdInfo); + + if (Context.CurrBlock.Next != null) + { + Context.EmitLoadState(Context.CurrBlock.Next); + } + } + + public static void Und(AILEmitterCtx Context) + { + AOpCode Op = Context.CurrOp; + + Context.EmitStoreState(); + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + Context.EmitLdc_I8(Op.Position); + Context.EmitLdc_I4(Op.RawOpCode); + + string MthdName = nameof(AThreadState.OnUndefined); + + MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding); + + Context.EmitCall(MthdInfo); + + if (Context.CurrBlock.Next != null) + { + Context.EmitLoadState(Context.CurrBlock.Next); + } + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitFlow.cs b/Instruction/AInstEmitFlow.cs new file mode 100644 index 0000000..be68aa6 --- /dev/null +++ b/Instruction/AInstEmitFlow.cs @@ -0,0 +1,124 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void B(AILEmitterCtx Context) + { + AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; + + Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); + } + + public static void B_Cond(AILEmitterCtx Context) + { + AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp; + + Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond); + } + + public static void Bl(AILEmitterCtx Context) + { + AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; + + Context.EmitLdc_I(Op.Position + 4); + Context.EmitStint(AThreadState.LRIndex); + 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); + + AILLabel LblContinue = new AILLabel(); + + Context.Emit(OpCodes.Beq_S, LblContinue); + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblContinue); + + Context.Emit(OpCodes.Pop); + + if (Context.CurrBlock.Next != null) + { + Context.EmitLoadState(Context.CurrBlock.Next); + } + } + else + { + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } + } + + public static void Blr(AILEmitterCtx Context) + { + AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; + + Context.EmitLdc_I(Op.Position + 4); + Context.EmitStint(AThreadState.LRIndex); + Context.EmitStoreState(); + Context.EmitLdintzr(Op.Rn); + + Context.Emit(OpCodes.Ret); + } + + public static void Br(AILEmitterCtx Context) + { + AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; + + Context.EmitStoreState(); + Context.EmitLdintzr(Op.Rn); + + Context.Emit(OpCodes.Ret); + } + + public static void Cbnz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Bne_Un); + public static void Cbz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Beq); + + private static void EmitCb(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeBImmCmp Op = (AOpCodeBImmCmp)Context.CurrOp; + + Context.EmitLdintzr(Op.Rt); + Context.EmitLdc_I(0); + + Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + } + + public static void Ret(AILEmitterCtx Context) + { + Context.EmitStoreState(); + Context.EmitLdint(AThreadState.LRIndex); + + Context.Emit(OpCodes.Ret); + } + + public static void Tbnz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Bne_Un); + public static void Tbz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Beq); + + private static void EmitTb(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeBImmTest Op = (AOpCodeBImmTest)Context.CurrOp; + + Context.EmitLdintzr(Op.Rt); + Context.EmitLdc_I(1L << Op.Pos); + + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(0); + + Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitMemory.cs b/Instruction/AInstEmitMemory.cs new file mode 100644 index 0000000..af7de3b --- /dev/null +++ b/Instruction/AInstEmitMemory.cs @@ -0,0 +1,252 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Adr(AILEmitterCtx Context) + { + AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; + + Context.EmitLdc_I(Op.Position + Op.Imm); + Context.EmitStintzr(Op.Rd); + } + + public static void Adrp(AILEmitterCtx Context) + { + AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; + + Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12)); + Context.EmitStintzr(Op.Rd); + } + + public static void Ldr(AILEmitterCtx Context) => EmitLdr(Context, false); + public static void Ldrs(AILEmitterCtx Context) => EmitLdr(Context, true); + + public static void EmitLdr(AILEmitterCtx Context, bool Signed) + { + AOpCodeMem Op = (AOpCodeMem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + if (Signed && Op.Extend64) + { + EmitReadSx64Call(Context, Op.Size); + } + else if (Signed) + { + EmitReadSx32Call(Context, Op.Size); + } + else + { + EmitReadZxCall(Context, Op.Size); + } + + if (Op is IAOpCodeSimd) + { + Context.EmitStvec(Op.Rt); + } + else + { + Context.EmitStintzr(Op.Rt); + } + + EmitWBackIfNeeded(Context); + } + + public static void LdrLit(AILEmitterCtx Context) + { + IAOpCodeLit Op = (IAOpCodeLit)Context.CurrOp; + + if (Op.Prefetch) + { + return; + } + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdc_I8(Op.Imm); + + if (Op.Signed) + { + EmitReadSx64Call(Context, Op.Size); + } + else + { + EmitReadZxCall(Context, Op.Size); + } + + if (Op is IAOpCodeSimd) + { + Context.EmitStvec(Op.Rt); + } + else + { + Context.EmitStint(Op.Rt); + } + } + + public static void Ldp(AILEmitterCtx Context) + { + AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp; + + void EmitReadAndStore(int Rt) + { + if (Op.Extend64) + { + EmitReadSx64Call(Context, Op.Size); + } + else + { + EmitReadZxCall(Context, Op.Size); + } + + if (Op is IAOpCodeSimd) + { + Context.EmitStvec(Rt); + } + else + { + Context.EmitStintzr(Rt); + } + } + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + EmitReadAndStore(Op.Rt); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdtmp(); + Context.EmitLdc_I8(1 << Op.Size); + + Context.Emit(OpCodes.Add); + + EmitReadAndStore(Op.Rt2); + + EmitWBackIfNeeded(Context); + } + + public static void Str(AILEmitterCtx Context) + { + AOpCodeMem Op = (AOpCodeMem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + if (Op is IAOpCodeSimd) + { + Context.EmitLdvec(Op.Rt); + } + else + { + Context.EmitLdintzr(Op.Rt); + } + + EmitWriteCall(Context, Op.Size); + + EmitWBackIfNeeded(Context); + } + + public static void Stp(AILEmitterCtx Context) + { + AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + + EmitLoadAddress(Context); + + if (Op is IAOpCodeSimd) + { + Context.EmitLdvec(Op.Rt); + } + else + { + Context.EmitLdintzr(Op.Rt); + } + + EmitWriteCall(Context, Op.Size); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdtmp(); + Context.EmitLdc_I8(1 << Op.Size); + + Context.Emit(OpCodes.Add); + + if (Op is IAOpCodeSimd) + { + Context.EmitLdvec(Op.Rt2); + } + else + { + Context.EmitLdintzr(Op.Rt2); + } + + EmitWriteCall(Context, Op.Size); + + EmitWBackIfNeeded(Context); + } + + private static void EmitLoadAddress(AILEmitterCtx Context) + { + switch (Context.CurrOp) + { + case AOpCodeMemImm Op: + Context.EmitLdint(Op.Rn); + + if (!Op.PostIdx) + { + //Pre-indexing. + Context.EmitLdc_I(Op.Imm); + + Context.Emit(OpCodes.Add); + } + break; + + case AOpCodeMemReg Op: + Context.EmitLdint(Op.Rn); + Context.EmitLdintzr(Op.Rm); + Context.EmitCast(Op.IntType); + + if (Op.Shift) + { + Context.EmitLsl(Op.Size); + } + + Context.Emit(OpCodes.Add); + break; + } + + //Save address to Scratch var since the register value may change. + Context.Emit(OpCodes.Dup); + + Context.EmitSttmp(); + } + + private static void EmitWBackIfNeeded(AILEmitterCtx Context) + { + //Check whenever the current OpCode has post-indexed write back, if so write it. + //Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both. + if (Context.CurrOp is AOpCodeMemImm Op && Op.WBack) + { + Context.EmitLdtmp(); + + if (Op.PostIdx) + { + Context.EmitLdc_I(Op.Imm); + + Context.Emit(OpCodes.Add); + } + + Context.EmitStint(Op.Rn); + } + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitMemoryEx.cs b/Instruction/AInstEmitMemoryEx.cs new file mode 100644 index 0000000..a339bb6 --- /dev/null +++ b/Instruction/AInstEmitMemoryEx.cs @@ -0,0 +1,180 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Threading; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + [Flags] + private enum AccessType + { + None = 0, + Ordered = 1, + Exclusive = 2, + OrderedEx = Ordered | Exclusive + } + + public static void Clrex(AILEmitterCtx Context) + { + EmitMemoryCall(Context, nameof(AMemory.ClearExclusive)); + } + + public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context); + public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context); + + public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered); + public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx); + public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive); + public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive); + public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx); + + private static void EmitLdr(AILEmitterCtx Context, AccessType AccType) + { + EmitLoad(Context, AccType, false); + } + + private static void EmitLdp(AILEmitterCtx Context, AccessType AccType) + { + EmitLoad(Context, AccType, true); + } + + private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair) + { + AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + + EmitReadZxCall(Context, Op.Size); + + Context.EmitStintzr(Op.Rt); + + if (Pair) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I(8 << Op.Size); + + Context.Emit(OpCodes.Add); + + EmitReadZxCall(Context, Op.Size); + + Context.EmitStintzr(Op.Rt2); + } + + if (AccType.HasFlag(AccessType.Exclusive)) + { + EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); + } + + if (AccType.HasFlag(AccessType.Ordered)) + { + EmitBarrier(Context); + } + } + + public static void Pfrm(AILEmitterCtx Context) + { + //Memory Prefetch, execute as no-op. + } + + public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered); + public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx); + public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive); + public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive); + public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx); + + private static void EmitStr(AILEmitterCtx Context, AccessType AccType) + { + EmitStore(Context, AccType, false); + } + + private static void EmitStp(AILEmitterCtx Context, AccessType AccType) + { + EmitStore(Context, AccType, true); + } + + private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair) + { + AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; + + if (AccType.HasFlag(AccessType.Ordered)) + { + EmitBarrier(Context); + } + + AILLabel LblEx = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + if (AccType.HasFlag(AccessType.Exclusive)) + { + EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn); + + Context.Emit(OpCodes.Brtrue_S, LblEx); + + Context.EmitLdc_I8(1); + Context.EmitStintzr(Op.Rs); + + Context.Emit(OpCodes.Br_S, LblEnd); + } + + Context.MarkLabel(LblEx); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdintzr(Op.Rt); + + EmitWriteCall(Context, Op.Size); + + if (Pair) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I(8 << Op.Size); + + Context.Emit(OpCodes.Add); + + Context.EmitLdintzr(Op.Rt2); + + EmitWriteCall(Context, Op.Size); + } + + if (AccType.HasFlag(AccessType.Exclusive)) + { + Context.EmitLdc_I8(0); + Context.EmitStintzr(Op.Rs); + + Clrex(Context); + } + + Context.MarkLabel(LblEnd); + } + + private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + if (Rn != -1) + { + Context.EmitLdint(Rn); + } + + Context.EmitCall(typeof(AMemory), Name); + } + + private static void EmitBarrier(AILEmitterCtx Context) + { + //Note: This barrier is most likely not necessary, and probably + //doesn't make any difference since we need to do a ton of stuff + //(software MMU emulation) to read or write anything anyway. + Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier)); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitMemoryHelper.cs b/Instruction/AInstEmitMemoryHelper.cs new file mode 100644 index 0000000..d5a0051 --- /dev/null +++ b/Instruction/AInstEmitMemoryHelper.cs @@ -0,0 +1,138 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitMemoryHelper + { + private enum Extension + { + Zx, + Sx32, + Sx64 + } + + public static void EmitReadZxCall(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Zx, Size); + } + + public static void EmitReadSx32Call(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Sx32, Size); + } + + public static void EmitReadSx64Call(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Sx64, Size); + } + + private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size) + { + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + if (IsSimd) + { + switch (Size) + { + case 0: Name = nameof(AMemory.ReadVector8); break; + case 1: Name = nameof(AMemory.ReadVector16); break; + case 2: Name = nameof(AMemory.ReadVector32); break; + case 3: Name = nameof(AMemory.ReadVector64); break; + case 4: Name = nameof(AMemory.ReadVector128); break; + } + } + else + { + switch (Size) + { + case 0: Name = nameof(AMemory.ReadByte); break; + case 1: Name = nameof(AMemory.ReadUInt16); break; + case 2: Name = nameof(AMemory.ReadUInt32); break; + case 3: Name = nameof(AMemory.ReadUInt64); break; + } + } + + Context.EmitCall(typeof(AMemory), Name); + + if (!IsSimd) + { + if (Ext == Extension.Sx32 || + Ext == Extension.Sx64) + { + switch (Size) + { + case 0: Context.Emit(OpCodes.Conv_I1); break; + case 1: Context.Emit(OpCodes.Conv_I2); break; + case 2: Context.Emit(OpCodes.Conv_I4); break; + } + } + + if (Size < 3) + { + Context.Emit(Ext == Extension.Sx64 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + } + } + + public static void EmitWriteCall(AILEmitterCtx Context, int Size) + { + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + if (Size < 3 && !IsSimd) + { + Context.Emit(OpCodes.Conv_I4); + } + + if (IsSimd) + { + switch (Size) + { + case 0: Name = nameof(AMemory.WriteVector8); break; + case 1: Name = nameof(AMemory.WriteVector16); break; + case 2: Name = nameof(AMemory.WriteVector32); break; + case 3: Name = nameof(AMemory.WriteVector64); break; + case 4: Name = nameof(AMemory.WriteVector128); break; + } + } + else + { + switch (Size) + { + case 0: Name = nameof(AMemory.WriteByte); break; + case 1: Name = nameof(AMemory.WriteUInt16); break; + case 2: Name = nameof(AMemory.WriteUInt32); break; + case 3: Name = nameof(AMemory.WriteUInt64); break; + } + } + + Context.EmitCall(typeof(AMemory), Name); + } + + private static bool GetIsSimd(AILEmitterCtx Context) + { + return Context.CurrOp is IAOpCodeSimd && + !(Context.CurrOp is AOpCodeSimdMemMs || + Context.CurrOp is AOpCodeSimdMemSs); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitMove.cs b/Instruction/AInstEmitMove.cs new file mode 100644 index 0000000..719b53d --- /dev/null +++ b/Instruction/AInstEmitMove.cs @@ -0,0 +1,41 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Movk(AILEmitterCtx Context) + { + AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; + + Context.EmitLdintzr(Op.Rd); + Context.EmitLdc_I(~(0xffffL << Op.Pos)); + + Context.Emit(OpCodes.And); + + Context.EmitLdc_I(Op.Imm); + + Context.Emit(OpCodes.Or); + + Context.EmitStintzr(Op.Rd); + } + + public static void Movn(AILEmitterCtx Context) + { + AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; + + Context.EmitLdc_I(~Op.Imm); + Context.EmitStintzr(Op.Rd); + } + + public static void Movz(AILEmitterCtx Context) + { + AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; + + Context.EmitLdc_I(Op.Imm); + Context.EmitStintzr(Op.Rd); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitMul.cs b/Instruction/AInstEmitMul.cs new file mode 100644 index 0000000..3713c81 --- /dev/null +++ b/Instruction/AInstEmitMul.cs @@ -0,0 +1,80 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Madd(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Add); + public static void Msub(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Sub); + + private static void EmitMul(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + Context.EmitLdintzr(Op.Ra); + Context.EmitLdintzr(Op.Rn); + Context.EmitLdintzr(Op.Rm); + + Context.Emit(OpCodes.Mul); + Context.Emit(ILOp); + + Context.EmitStintzr(Op.Rd); + } + + public static void Smaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, true); + public static void Smsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, true); + public static void Umaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, false); + public static void Umsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, false); + + private static void EmitMull(AILEmitterCtx Context, OpCode AddSubOp, bool Signed) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + OpCode CastOp = Signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8; + + Context.EmitLdintzr(Op.Ra); + Context.EmitLdintzr(Op.Rn); + + Context.Emit(OpCodes.Conv_I4); + Context.Emit(CastOp); + + Context.EmitLdintzr(Op.Rm); + + Context.Emit(OpCodes.Conv_I4); + Context.Emit(CastOp); + Context.Emit(OpCodes.Mul); + + Context.Emit(AddSubOp); + + Context.EmitStintzr(Op.Rd); + } + + public static void Smulh(AILEmitterCtx Context) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdintzr(Op.Rm); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SMulHi128)); + + Context.EmitStintzr(Op.Rd); + } + + public static void Umulh(AILEmitterCtx Context) + { + AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + Context.EmitLdintzr(Op.Rm); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UMulHi128)); + + Context.EmitStintzr(Op.Rd); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdArithmetic.cs b/Instruction/AInstEmitSimdArithmetic.cs new file mode 100644 index 0000000..21ec11a --- /dev/null +++ b/Instruction/AInstEmitSimdArithmetic.cs @@ -0,0 +1,367 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Add_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Addp_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, 1, Op.Size); + + Context.Emit(OpCodes.Add); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Addp_V(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = (Index & (Half - 1)) << 1; + + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size); + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size); + + Context.Emit(OpCodes.Add); + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Addv_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + for (int Index = 1; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Add); + } + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Cnt_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, 0); + + Context.Emit(OpCodes.Conv_U1); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); + + Context.Emit(OpCodes.Conv_U8); + + EmitVectorInsert(Context, Op.Rd, Index, 0); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Fabs_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Abs)); + }); + } + + public static void Fadd_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Fadd_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Fdiv_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + } + + public static void Fdiv_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + } + + public static void Fmadd_S(AILEmitterCtx Context) + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmax_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinaryMathCall(Context, nameof(Math.Max)); + }); + } + + public static void Fmin_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinaryMathCall(Context, nameof(Math.Min)); + }); + } + + public static void Fmaxnm_S(AILEmitterCtx Context) + { + Fmax_S(Context); + } + + public static void Fminnm_S(AILEmitterCtx Context) + { + Fmin_S(Context); + } + + public static void Fmla_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmla_Ve(AILEmitterCtx Context) + { + EmitVectorTernaryOpByElemF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmsub_S(AILEmitterCtx Context) + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + + public static void Fmul_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fmul_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fmul_Ve(AILEmitterCtx Context) + { + EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fneg_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Fnmul_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Neg); + }); + } + + public static void Fnmsub_S(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + EmitVectorExtractF(Context, Op.Rn, 0, SizeF); + EmitVectorExtractF(Context, Op.Rm, 0, SizeF); + + Context.Emit(OpCodes.Mul); + + EmitVectorExtractF(Context, Op.Ra, 0, SizeF); + + Context.Emit(OpCodes.Sub); + + EmitScalarSetF(Context, Op.Rd, SizeF); + } + + public static void Frinta_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Frintm_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Floor)); + }); + } + + public static void Fsqrt_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Sqrt)); + }); + } + + public static void Fsub_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Fsub_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Mla_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Mls_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + + public static void Mul_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Neg_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Saddw_V(AILEmitterCtx Context) + { + EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Smax_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); + + EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Smin_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); + + EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Smull_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Sub_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Sub_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Uaddlv_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + for (int Index = 1; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Add); + } + + EmitScalarSet(Context, Op.Rd, Op.Size + 1); + } + + public static void Uaddw_V(AILEmitterCtx Context) + { + EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdCmp.cs b/Instruction/AInstEmitSimdCmp.cs new file mode 100644 index 0000000..97ccf0a --- /dev/null +++ b/Instruction/AInstEmitSimdCmp.cs @@ -0,0 +1,233 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitAluHelper; +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Cmeq_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Beq_S); + } + + public static void Cmge_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bge_S); + } + + public static void Cmgt_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bgt_S); + } + + public static void Cmhi_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bgt_Un_S); + } + + public static void Cmhs_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bge_Un_S); + } + + public static void Cmle_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Ble_S); + } + + public static void Cmlt_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Blt_S); + } + + public static void Fccmp_S(AILEmitterCtx Context) + { + AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + EmitSetNZCV(Context, Op.NZCV); + + Context.Emit(OpCodes.Br, LblEnd); + + Context.MarkLabel(LblTrue); + + Fcmp_S(Context); + + Context.MarkLabel(LblEnd); + } + + public static void Fccmpe_S(AILEmitterCtx Context) + { + Fccmp_S(Context); + } + + public static void Fcmp_S(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false; + + //Handle NaN case. If any number is NaN, then NZCV = 0011. + if (CmpWithZero) + { + EmitNaNCheck(Context, Op.Rn); + } + else + { + EmitNaNCheck(Context, Op.Rn); + EmitNaNCheck(Context, Op.Rm); + + Context.Emit(OpCodes.Or); + } + + AILLabel LblNaN = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.Brtrue_S, LblNaN); + + void EmitLoadOpers() + { + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (CmpWithZero) + { + EmitLdcImmF(Context, 0, Op.Size); + } + else + { + EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); + } + } + + //Z = Rn == Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Ceq); + Context.Emit(OpCodes.Dup); + + Context.EmitStflg((int)APState.ZBit); + + //C = Rn >= Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Cgt); + Context.Emit(OpCodes.Or); + + Context.EmitStflg((int)APState.CBit); + + //N = Rn < Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.NBit); + + //V = 0 + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.VBit); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblNaN); + + EmitSetNZCV(Context, 0b0011); + + Context.MarkLabel(LblEnd); + } + + public static void Fcmpe_S(AILEmitterCtx Context) + { + Fcmp_S(Context); + } + + private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size) + { + if (Size == 0) + { + Context.EmitLdc_R4((float)ImmF); + } + else if (Size == 1) + { + Context.EmitLdc_R8(ImmF); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + private static void EmitNaNCheck(AILEmitterCtx Context, int Reg) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Reg, 0, Op.Size); + + if (Op.Size == 0) + { + Context.EmitCall(typeof(float), nameof(float.IsNaN)); + } + else if (Op.Size == 1) + { + Context.EmitCall(typeof(double), nameof(double.IsNaN)); + } + else + { + throw new InvalidOperationException(); + } + } + + private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); + + if (Op is AOpCodeSimdReg BinOp) + { + EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size); + } + else + { + Context.EmitLdc_I8(0); + } + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(ILOp, LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); + + Context.MarkLabel(LblEnd); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdCvt.cs b/Instruction/AInstEmitSimdCvt.cs new file mode 100644 index 0000000..00c2fe9 --- /dev/null +++ b/Instruction/AInstEmitSimdCvt.cs @@ -0,0 +1,444 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Fcvt_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitFloatCast(Context, Op.Opc); + + EmitScalarSetF(Context, Op.Rd, Op.Opc); + } + + public static void Fcvtas_Gp(AILEmitterCtx Context) + { + Fcvta__Gp(Context, Signed: true); + } + + public static void Fcvtau_Gp(AILEmitterCtx Context) + { + Fcvta__Gp(Context, Signed: false); + } + + public static void Fcvtms_Gp(AILEmitterCtx Context) + { + EmitFcvt_s_Gp(Context, nameof(Math.Floor)); + } + + public static void Fcvtps_Gp(AILEmitterCtx Context) + { + EmitFcvt_s_Gp(Context, nameof(Math.Ceiling)); + } + + public static void Fcvtzs_Gp(AILEmitterCtx Context) + { + EmitFcvtz__Gp(Context, Signed: true); + } + + public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context) + { + EmitFcvtz__Gp_Fix(Context, Signed: true); + } + + public static void Fcvtzs_V(AILEmitterCtx Context) + { + EmitVectorFcvt(Context, Signed: true); + } + + public static void Fcvtzu_Gp(AILEmitterCtx Context) + { + EmitFcvtz__Gp(Context, Signed: false); + } + + public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context) + { + EmitFcvtz__Gp_Fix(Context, Signed: false); + } + + public static void Fcvtzu_V(AILEmitterCtx Context) + { + EmitVectorFcvt(Context, Signed: false); + } + + public static void Scvtf_Gp(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Scvtf_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size + 2); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Scvtf_V(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, Signed: true); + } + + public static void Ucvtf_Gp(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Ucvtf_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size + 2); + + Context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Ucvtf_V(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, Signed: false); + } + + private static int GetFBits(AILEmitterCtx Context) + { + if (Context.CurrOp is AOpCodeSimdShImm Op) + { + return GetImmShr(Op); + } + + return 0; + } + + private static void EmitFloatCast(AILEmitterCtx Context, int Size) + { + if (Size == 0) + { + Context.Emit(OpCodes.Conv_R4); + } + else if (Size == 1) + { + Context.Emit(OpCodes.Conv_R8); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + private static void Fcvta__Gp(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, 0); + } + else + { + EmitScalarFcvtu(Context, Op.Size, 0); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitUnaryMathCall(Context, Name); + + EmitScalarFcvts(Context, Op.Size, 0); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, 0); + } + else + { + EmitScalarFcvtu(Context, Op.Size, 0); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, Op.FBits); + } + else + { + EmitScalarFcvtu(Context, Op.Size, Op.FBits); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int FBits = GetFBits(Context); + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeI); Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed); + + if (!Signed) + { + Context.Emit(OpCodes.Conv_R_Un); + } + + Context.Emit(SizeF == 0 + ? OpCodes.Conv_R4 + : OpCodes.Conv_R8); + + EmitI2fFBitsMul(Context, SizeF, FBits); + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int FBits = GetFBits(Context); + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeI); Index++) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + + EmitF2iFBitsMul(Context, SizeF, FBits); + + if (SizeF == 0) + { + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.SatF32ToS32) + : nameof(ASoftFallback.SatF32ToU32)); + } + else /* if (SizeF == 1) */ + { + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.SatF64ToS64) + : nameof(ASoftFallback.SatF64ToU64)); + } + + if (SizeF == 0) + { + Context.Emit(OpCodes.Conv_U8); + } + + EmitVectorInsert(Context, Op.Rd, Index, SizeI); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitScalarFcvts(AILEmitterCtx Context, int Size, int FBits) + { + if (Size < 0 || Size > 1) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + EmitF2iFBitsMul(Context, Size, FBits); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS32)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS32)); + } + } + else + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS64)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS64)); + } + } + } + + private static void EmitScalarFcvtu(AILEmitterCtx Context, int Size, int FBits) + { + if (Size < 0 || Size > 1) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + EmitF2iFBitsMul(Context, Size, FBits); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU32)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU32)); + } + } + else + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU64)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU64)); + } + } + } + + private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits) + { + if (FBits != 0) + { + if (Size == 0) + { + Context.EmitLdc_R4(MathF.Pow(2, FBits)); + } + else if (Size == 1) + { + Context.EmitLdc_R8(Math.Pow(2, FBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.Emit(OpCodes.Mul); + } + } + + private static void EmitI2fFBitsMul(AILEmitterCtx Context, int Size, int FBits) + { + if (FBits != 0) + { + if (Size == 0) + { + Context.EmitLdc_R4(1f / MathF.Pow(2, FBits)); + } + else if (Size == 1) + { + Context.EmitLdc_R8(1 / Math.Pow(2, FBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.Emit(OpCodes.Mul); + } + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdHelper.cs b/Instruction/AInstEmitSimdHelper.cs new file mode 100644 index 0000000..20c8be2 --- /dev/null +++ b/Instruction/AInstEmitSimdHelper.cs @@ -0,0 +1,616 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitSimdHelper + { + [Flags] + public enum OperFlags + { + Rd = 1 << 0, + Rn = 1 << 1, + Rm = 1 << 2, + Ra = 1 << 3, + + RnRm = Rn | Rm, + RdRn = Rd | Rn, + RaRnRm = Ra | Rn | Rm, + RdRnRm = Rd | Rn | Rm + } + + public static int GetImmShl(AOpCodeSimdShImm Op) + { + return Op.Imm - (8 << Op.Size); + } + + public static int GetImmShr(AOpCodeSimdShImm Op) + { + return (8 << (Op.Size + 1)) - Op.Imm; + } + + public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + MethodInfo MthdInfo; + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) }); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) }); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + MethodInfo MthdInfo; + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitRoundMathCall(AILEmitterCtx Context, MidpointRounding RoundMode) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + Context.EmitLdc_I4((int)RoundMode); + + MethodInfo MthdInfo; + + Type[] Types = new Type[] { null, typeof(MidpointRounding) }; + + Types[0] = Op.Size == 0 + ? typeof(float) + : typeof(double); + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.Rn, true); + } + + public static void EmitScalarBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RnRm, true); + } + + public static void EmitScalarUnaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.Rn, false); + } + + public static void EmitScalarBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RnRm, false); + } + + public static void EmitScalarTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RdRnRm, false); + } + + public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed); + } + + Emit(); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void EmitScalarUnaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.Rn); + } + + public static void EmitScalarBinaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.RnRm); + } + + public static void EmitScalarTernaryRaOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.RaRnRm); + } + + public static void EmitScalarOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + if (Opers.HasFlag(OperFlags.Ra)) + { + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtractF(Context, Op.Rn, 0, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF); + } + + Emit(); + + EmitScalarSetF(Context, Op.Rd, SizeF); + } + + public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.RnRm); + } + + public static void EmitVectorTernaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.RdRnRm); + } + + public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); + } + + public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); + } + + public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + { + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtractF(Context, Op.Rm, Index, SizeF); + } + + Emit(); + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + { + if (Ternary) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + } + + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + EmitVectorExtractF(Context, Op.Rm, Elem, SizeF); + + Emit(); + + EmitVectorInsertTmpF(Context, Index, SizeF); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.Rn, true); + } + + public static void EmitVectorBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RnRm, true); + } + + public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.Rn, false); + } + + public static void EmitVectorBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RnRm, false); + } + + public static void EmitVectorTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false); + } + + public static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); + } + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit) + { + EmitVectorImmOp(Context, Emit, false); + } + + public static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit) + { + EmitVectorImmOp(Context, Emit, true); + } + + public static void EmitVectorImmOp(AILEmitterCtx Context, Action Emit, bool Binary) + { + AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Binary) + { + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + } + + Context.EmitLdc_I8(Op.Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorWidenRmBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRmBinaryOp(Context, Emit, true); + } + + public static void EmitVectorWidenRmBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRmBinaryOp(Context, Emit, false); + } + + public static void EmitVectorWidenRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + + public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmBinaryOp(Context, Emit, true); + } + + public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmBinaryOp(Context, Emit, false); + } + + public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + + public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsert(Context, Reg, 0, Size); + } + + public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size) + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsertF(Context, Reg, 0, Size); + } + + public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size) + { + EmitVectorExtract(Context, Reg, Index, Size, true); + } + + public static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size) + { + EmitVectorExtract(Context, Reg, Index, Size, false); + } + + public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.VectorExtractIntSx) + : nameof(ASoftFallback.VectorExtractIntZx)); + } + + public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size) + { + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd) + { + EmitVectorZeroLower(Context, Rd); + EmitVectorZeroUpper(Context, Rd); + } + + public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) + { + EmitVectorInsert(Context, Rd, 0, 3, 0); + } + + public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) + { + EmitVectorInsert(Context, Rd, 1, 3, 0); + } + + public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvectmp(); + } + + public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdc_I8(Value); + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) + { + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size) + { + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvectmp(); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdLogical.cs b/Instruction/AInstEmitSimdLogical.cs new file mode 100644 index 0000000..ea4b17b --- /dev/null +++ b/Instruction/AInstEmitSimdLogical.cs @@ -0,0 +1,69 @@ +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void And_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And)); + } + + public static void Bic_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } + + public static void Bic_Vi(AILEmitterCtx Context) + { + EmitVectorImmBinaryOp(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } + + public static void Bsl_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.EmitSttmp(); + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.And); + + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + }); + } + + public static void Eor_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor)); + } + + public static void Not_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not)); + } + + public static void Orr_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or)); + } + + public static void Orr_Vi(AILEmitterCtx Context) + { + EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdMemory.cs b/Instruction/AInstEmitSimdMemory.cs new file mode 100644 index 0000000..d98ec01 --- /dev/null +++ b/Instruction/AInstEmitSimdMemory.cs @@ -0,0 +1,184 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + 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 St__Vms(AILEmitterCtx Context) + { + EmitSimdMemMs(Context, IsLoad: false); + } + + public static void St__Vss(AILEmitterCtx Context) + { + EmitSimdMemSs(Context, IsLoad: false); + } + + private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp; + + int Offset = 0; + + for (int Rep = 0; Rep < Op.Reps; Rep++) + for (int Elem = 0; Elem < Op.Elems; Elem++) + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rtt = (Op.Rt + Rep + SElem) & 0x1f; + + if (IsLoad) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rtt, Elem, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1) + { + EmitVectorZeroUpper(Context, Rtt); + } + } + else + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitVectorExtractZx(Context, Rtt, Elem, Op.Size); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + + if (Op.WBack) + { + EmitSimdMemWBack(Context, Offset); + } + } + + private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; + + int Offset = 0; + + void EmitMemAddress() + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + } + + if (Op.Replicate) + { + //Only loads uses the replicate mode. + if (!IsLoad) + { + throw new InvalidOperationException(); + } + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitMemAddress(); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rt, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Rt); + } + + Offset += 1 << Op.Size; + } + } + else + { + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + if (IsLoad) + { + EmitMemAddress(); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rt, Op.Index, Op.Size); + } + else + { + EmitMemAddress(); + + EmitVectorExtractZx(Context, Rt, Op.Index, Op.Size); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + } + + if (Op.WBack) + { + EmitSimdMemWBack(Context, Offset); + } + } + + private static void EmitSimdMemWBack(AILEmitterCtx Context, int Offset) + { + AOpCodeMemReg Op = (AOpCodeMemReg)Context.CurrOp; + + Context.EmitLdint(Op.Rn); + + if (Op.Rm != AThreadState.ZRIndex) + { + Context.EmitLdint(Op.Rm); + } + else + { + Context.EmitLdc_I8(Offset); + } + + Context.Emit(OpCodes.Add); + + Context.EmitStint(Op.Rn); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdMove.cs b/Instruction/AInstEmitSimdMove.cs new file mode 100644 index 0000000..aabb8f3 --- /dev/null +++ b/Instruction/AInstEmitSimdMove.cs @@ -0,0 +1,333 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Dup_Gp(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Dup_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Dup_V(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Fcsel_S(AILEmitterCtx Context) + { + AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + Context.MarkLabel(LblEnd); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Fmov_Ftoi(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, 3); + + EmitIntZeroHigherIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + + public static void Fmov_Ftoi1(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 1, 3); + + EmitIntZeroHigherIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + + public static void Fmov_Itof(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitIntZeroHigherIfNeeded(Context); + + EmitScalarSet(Context, Op.Rd, 3); + } + + public static void Fmov_Itof1(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitIntZeroHigherIfNeeded(Context); + + EmitVectorInsert(Context, Op.Rd, 1, 3); + } + + public static void Fmov_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Fmov_Si(AILEmitterCtx Context) + { + AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp; + + Context.EmitLdc_I8(Op.Imm); + + EmitScalarSet(Context, Op.Rd, Op.Size + 2); + } + + public static void Fmov_V(AILEmitterCtx Context) + { + AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 4 : 2; + + for (int Index = 0; Index < (Elems >> Op.Size); Index++) + { + Context.EmitLdc_I8(Op.Imm); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size + 2); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Ins_Gp(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); + } + + public static void Ins_V(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.SrcIndex, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); + } + + public static void Movi_V(AILEmitterCtx Context) + { + EmitVectorImmUnaryOp(Context, () => { }); + } + + public static void Mvni_V(AILEmitterCtx Context) + { + EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not)); + } + + public static void Tbl_V(AILEmitterCtx Context) + { + AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp; + + Context.EmitLdvec(Op.Rm); + + for (int Index = 0; Index < Op.Size; Index++) + { + Context.EmitLdvec((Op.Rn + Index) & 0x1f); + } + + switch (Op.Size) + { + case 1: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl1_V64), + nameof(ASoftFallback.Tbl1_V128)); break; + + case 2: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl2_V64), + nameof(ASoftFallback.Tbl2_V128)); break; + + case 3: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl3_V64), + nameof(ASoftFallback.Tbl3_V128)); break; + + case 4: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl4_V64), + nameof(ASoftFallback.Tbl4_V128)); break; + + default: throw new InvalidOperationException(); + } + + Context.EmitStvec(Op.Rd); + } + + public static void Umov_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + Context.EmitStintzr(Op.Rd); + } + + public static void Uzp1_V(AILEmitterCtx Context) + { + EmitVectorUnzip(Context, Part: 0); + } + + public static void Uzp2_V(AILEmitterCtx Context) + { + EmitVectorUnzip(Context, Part: 1); + } + + public static void Xtn_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Zip1_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 0); + } + + public static void Zip2_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 1); + } + + private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context) + { + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + Context.Emit(OpCodes.Conv_U8); + } + } + + private static void EmitVectorUnzip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part + ((Index & (Half - 1)) << 1); + + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorZip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part * Half + (Index >> 1); + + EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSimdShift.cs b/Instruction/AInstEmitSimdShift.cs new file mode 100644 index 0000000..8740ba4 --- /dev/null +++ b/Instruction/AInstEmitSimdShift.cs @@ -0,0 +1,297 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Shl_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + Context.EmitLdc_I4(GetImmShl(Op)); + + Context.Emit(OpCodes.Shl); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Shl_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Shrn_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift); + } + + public static void Sshl_V(AILEmitterCtx Context) + { + EmitVectorShl(Context, Signed: true); + } + + public static void Sshll_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Sshr_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Sshr_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + } + + public static void Ushl_V(AILEmitterCtx Context) + { + EmitVectorShl(Context, Signed: false); + } + + public static void Ushll_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Ushr_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitScalarUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + }); + } + + public static void Ushr_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + }); + } + + public static void Usra_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + Action Emit = () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + Context.Emit(OpCodes.Add); + }; + + EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false); + } + + private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) + { + //This instruction shifts the value on vector A by the number of bits + //specified on the signed, lower 8 bits of vector B. If the shift value + //is greater or equal to the data size of each lane, then the result is zero. + //Additionally, negative shifts produces right shifts by the negated shift value. + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int MaxShift = 8 << Op.Size; + + Action Emit = () => + { + AILLabel LblShl = new AILLabel(); + AILLabel LblZero = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + void EmitShift(OpCode ILOp) + { + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(MaxShift); + + Context.Emit(OpCodes.Bge_S, LblZero); + Context.Emit(ILOp); + Context.Emit(OpCodes.Br_S, LblEnd); + } + + Context.Emit(OpCodes.Conv_I1); + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(0); + + Context.Emit(OpCodes.Bge_S, LblShl); + Context.Emit(OpCodes.Neg); + + EmitShift(Signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + Context.MarkLabel(LblShl); + + EmitShift(OpCodes.Shl); + + Context.MarkLabel(LblZero); + + Context.Emit(OpCodes.Pop); + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(0); + + Context.MarkLabel(LblEnd); + }; + + if (Signed) + { + EmitVectorBinaryOpSx(Context, Emit); + } + else + { + EmitVectorBinaryOpZx(Context, Emit); + } + } + + private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorShImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorShImmWidenBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmWidenBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitSystem.cs b/Instruction/AInstEmitSystem.cs new file mode 100644 index 0000000..6754be7 --- /dev/null +++ b/Instruction/AInstEmitSystem.cs @@ -0,0 +1,122 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Mrs(AILEmitterCtx Context) + { + AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + string PropName; + + switch (GetPackedId(Op)) + { + case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break; + case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break; + case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; + case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break; + case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break; + + default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); + } + + Context.EmitCallPropGet(typeof(AThreadState), PropName); + + PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); + + if (PropInfo.PropertyType != typeof(long) && + PropInfo.PropertyType != typeof(ulong)) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rt); + } + + public static void Msr(AILEmitterCtx Context) + { + AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.EmitLdintzr(Op.Rt); + + string PropName; + + switch (GetPackedId(Op)) + { + case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; + + default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}"); + } + + PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); + + if (PropInfo.PropertyType != typeof(long) && + PropInfo.PropertyType != typeof(ulong)) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.EmitCallPropSet(typeof(AThreadState), PropName); + } + + public static void Nop(AILEmitterCtx Context) + { + //Do nothing. + } + + public static void Sys(AILEmitterCtx Context) + { + //This instruction is used to do some operations on the CPU like cache invalidation, + //address translation and the like. + //We treat it as no-op here since we don't have any cache being emulated anyway. + AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + + switch (GetPackedId(Op)) + { + case 0b11_011_0111_0100_001: + { + //DC ZVA + for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rt); + Context.EmitLdc_I(Offs); + + Context.Emit(OpCodes.Add); + + Context.EmitLdc_I8(0); + + AInstEmitMemoryHelper.EmitWriteCall(Context, 3); + } + break; + } + } + } + + private static int GetPackedId(AOpCodeSystem Op) + { + int Id; + + Id = Op.Op2 << 0; + Id |= Op.CRm << 3; + Id |= Op.CRn << 7; + Id |= Op.Op1 << 11; + Id |= Op.Op0 << 14; + + return Id; + } + } +} \ No newline at end of file diff --git a/Instruction/AInstEmitter.cs b/Instruction/AInstEmitter.cs new file mode 100644 index 0000000..8712a73 --- /dev/null +++ b/Instruction/AInstEmitter.cs @@ -0,0 +1,6 @@ +using ChocolArm64.Translation; + +namespace ChocolArm64.Instruction +{ + delegate void AInstEmitter(AILEmitterCtx Context); +} \ No newline at end of file diff --git a/Instruction/ASoftFallback.cs b/Instruction/ASoftFallback.cs new file mode 100644 index 0000000..a57966b --- /dev/null +++ b/Instruction/ASoftFallback.cs @@ -0,0 +1,316 @@ +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ChocolArm64.Instruction +{ + static class ASoftFallback + { + public static void EmitCall(AILEmitterCtx Context, string Name64, string Name128) + { + bool IsSimd64 = Context.CurrOp.RegisterSize == ARegisterSize.SIMD64; + + Context.EmitCall(typeof(ASoftFallback), IsSimd64 ? Name64 : Name128); + } + + public static void EmitCall(AILEmitterCtx Context, string MthdName) + { + Context.EmitCall(typeof(ASoftFallback), MthdName); + } + + public static uint CountLeadingZeros32(uint Value) => (uint)CountLeadingZeros(Value, 32); + public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64); + + private static ulong CountLeadingZeros(ulong Value, int Size) + { + int HighBit = Size - 1; + + for (int Bit = HighBit; Bit >= 0; Bit--) + { + if (((Value >> Bit) & 1) != 0) + { + return (ulong)(HighBit - Bit); + } + } + + return (ulong)Size; + } + + public static uint ReverseBits32(uint Value) + { + Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); + Value = ((Value & 0xcccccccc) >> 2) | ((Value & 0x33333333) << 2); + Value = ((Value & 0xf0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f) << 4); + Value = ((Value & 0xff00ff00) >> 8) | ((Value & 0x00ff00ff) << 8); + + return (Value >> 16) | (Value << 16); + } + + public static ulong ReverseBits64(ulong Value) + { + Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1) | ((Value & 0x5555555555555555) << 1); + Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2); + Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4); + Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); + + return (Value >> 32) | (Value << 32); + } + + public static uint ReverseBytes16_32(uint Value) => (uint)ReverseBytes16_64(Value); + public static uint ReverseBytes32_32(uint Value) => (uint)ReverseBytes32_64(Value); + + public static ulong ReverseBytes16_64(ulong Value) => ReverseBytes(Value, RevSize.Rev16); + public static ulong ReverseBytes32_64(ulong Value) => ReverseBytes(Value, RevSize.Rev32); + public static ulong ReverseBytes64(ulong Value) => ReverseBytes(Value, RevSize.Rev64); + + private enum RevSize + { + Rev16, + Rev32, + Rev64 + } + + private static ulong ReverseBytes(ulong Value, RevSize Size) + { + Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + + if (Size == RevSize.Rev16) + { + return Value; + } + + Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); + + if (Size == RevSize.Rev32) + { + return Value; + } + + Value = ((Value & 0xffffffff00000000) >> 32) | ((Value & 0x00000000ffffffff) << 32); + + if (Size == RevSize.Rev64) + { + return Value; + } + + throw new ArgumentException(nameof(Size)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF32ToS32(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > int.MaxValue ? int.MaxValue : + Value < int.MinValue ? int.MinValue : (int)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF32ToS64(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > long.MaxValue ? long.MaxValue : + Value < long.MinValue ? long.MinValue : (long)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF32ToU32(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > uint.MaxValue ? uint.MaxValue : + Value < uint.MinValue ? uint.MinValue : (uint)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF32ToU64(float Value) + { + if (float.IsNaN(Value)) return 0; + + return Value > ulong.MaxValue ? ulong.MaxValue : + Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF64ToS32(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > int.MaxValue ? int.MaxValue : + Value < int.MinValue ? int.MinValue : (int)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF64ToS64(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > long.MaxValue ? long.MaxValue : + Value < long.MinValue ? long.MinValue : (long)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF64ToU32(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > uint.MaxValue ? uint.MaxValue : + Value < uint.MinValue ? uint.MinValue : (uint)Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF64ToU64(double Value) + { + if (double.IsNaN(Value)) return 0; + + return Value > ulong.MaxValue ? ulong.MaxValue : + Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; + } + + public static long SMulHi128(long LHS, long RHS) + { + return (long)(BigInteger.Multiply(LHS, RHS) >> 64); + } + + public static ulong UMulHi128(ulong LHS, ulong RHS) + { + return (ulong)(BigInteger.Multiply(LHS, RHS) >> 64); + } + + public static int CountSetBits8(byte Value) + { + return (Value >> 0) & 1 + (Value >> 1) & 1 + + (Value >> 2) & 1 + (Value >> 3) & 1 + + (Value >> 4) & 1 + (Value >> 5) & 1 + + (Value >> 6) & 1 + (Value >> 7); + } + + public static AVec Tbl1_V64(AVec Vector, AVec Tb0) + { + return Tbl(Vector, 8, Tb0); + } + + public static AVec Tbl1_V128(AVec Vector, AVec Tb0) + { + return Tbl(Vector, 16, Tb0); + } + + public static AVec Tbl2_V64(AVec Vector, AVec Tb0, AVec Tb1) + { + return Tbl(Vector, 8, Tb0, Tb1); + } + + public static AVec Tbl2_V128(AVec Vector, AVec Tb0, AVec Tb1) + { + return Tbl(Vector, 16, Tb0, Tb1); + } + + public static AVec Tbl3_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2) + { + return Tbl(Vector, 8, Tb0, Tb1, Tb2); + } + + public static AVec Tbl3_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2) + { + return Tbl(Vector, 16, Tb0, Tb1, Tb2); + } + + public static AVec Tbl4_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3) + { + return Tbl(Vector, 8, Tb0, Tb1, Tb2, Tb3); + } + + public static AVec Tbl4_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3) + { + return Tbl(Vector, 16, Tb0, Tb1, Tb2, Tb3); + } + + private static AVec Tbl(AVec Vector, int Bytes, params AVec[] Tb) + { + AVec Res = new AVec(); + + byte[] Table = new byte[Tb.Length * 16]; + + for (int Index = 0; Index < Tb.Length; Index++) + for (int Index2 = 0; Index2 < 16; Index2++) + { + Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0); + } + + for (int Index = 0; Index < Bytes; Index++) + { + byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0); + + if (TblIdx < Table.Length) + { + Res = VectorInsertInt(Table[TblIdx], Res, Index, 0); + } + } + + return Res; + } + + public static ulong VectorExtractIntZx(AVec Vector, int Index, int Size) + { + switch (Size) + { + case 0: return Vector.ExtractByte (Index); + case 1: return Vector.ExtractUInt16(Index); + case 2: return Vector.ExtractUInt32(Index); + case 3: return Vector.ExtractUInt64(Index); + } + + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + public static long VectorExtractIntSx(AVec Vector, int Index, int Size) + { + switch (Size) + { + case 0: return (sbyte)Vector.ExtractByte (Index); + case 1: return (short)Vector.ExtractUInt16(Index); + case 2: return (int)Vector.ExtractUInt32(Index); + case 3: return (long)Vector.ExtractUInt64(Index); + } + + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + public static float VectorExtractSingle(AVec Vector, int Index) + { + return Vector.ExtractSingle(Index); + } + + public static double VectorExtractDouble(AVec Vector, int Index) + { + return Vector.ExtractDouble(Index); + } + + public static AVec VectorInsertSingle(float Value, AVec Vector, int Index) + { + return AVec.InsertSingle(Vector, Index, Value); + } + + public static AVec VectorInsertDouble(double Value, AVec Vector, int Index) + { + return AVec.InsertDouble(Vector, Index, Value); + } + + public static AVec VectorInsertInt(ulong Value, AVec Vector, int Index, int Size) + { + switch (Size) + { + case 0: return AVec.InsertByte (Vector, Index, (byte)Value); + case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value); + case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value); + case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value); + } + + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } +} \ No newline at end of file diff --git a/Memory/AMemory.cs b/Memory/AMemory.cs new file mode 100644 index 0000000..f2abffb --- /dev/null +++ b/Memory/AMemory.cs @@ -0,0 +1,345 @@ +using ChocolArm64.Exceptions; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace ChocolArm64.Memory +{ + public unsafe class AMemory + { + private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; + + public AMemoryMgr Manager { get; private set; } + + private struct ExMonitor + { + public long Position { get; private set; } + + private bool ExState; + + public ExMonitor(long Position, bool ExState) + { + this.Position = Position; + this.ExState = ExState; + } + + public bool HasExclusiveAccess(long Position) + { + return this.Position == Position && ExState; + } + + public void Reset() + { + ExState = false; + } + } + + private Dictionary Monitors; + + private HashSet ExAddrs; + + private byte* RamPtr; + + public AMemory(IntPtr Ram, AMemoryAlloc Allocator) + { + Manager = new AMemoryMgr(Allocator); + + Monitors = new Dictionary(); + + ExAddrs = new HashSet(); + + RamPtr = (byte*)Ram; + } + + public void RemoveMonitor(int ThreadId) + { + lock (Monitors) + { + if (Monitors.TryGetValue(ThreadId, out ExMonitor Monitor)) + { + ExAddrs.Remove(Monitor.Position); + } + + Monitors.Remove(ThreadId); + } + } + + public void SetExclusive(AThreadState ThreadState, long Position) + { + Position &= ~ErgMask; + + lock (Monitors) + { + if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) + { + ExAddrs.Remove(Monitor.Position); + } + + bool ExState = ExAddrs.Add(Position); + + Monitor = new ExMonitor(Position, ExState); + + if (!Monitors.TryAdd(ThreadState.ThreadId, Monitor)) + { + Monitors[ThreadState.ThreadId] = Monitor; + } + } + } + + public bool TestExclusive(AThreadState ThreadState, long Position) + { + Position &= ~ErgMask; + + lock (Monitors) + { + if (!Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) + { + return false; + } + + return Monitor.HasExclusiveAccess(Position); + } + } + + public void ClearExclusive(AThreadState ThreadState) + { + lock (Monitors) + { + if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) + { + Monitor.Reset(); + ExAddrs.Remove(Monitor.Position); + } + } + } + + public bool AcquireAddress(long Position) + { + Position &= ~ErgMask; + + lock (Monitors) + { + return ExAddrs.Add(Position); + } + } + + public void ReleaseAddress(long Position) + { + Position &= ~ErgMask; + + lock (Monitors) + { + ExAddrs.Remove(Position); + } + } + + public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position); + public short ReadInt16(long Position) => (short)ReadUInt16(Position); + public int ReadInt32(long Position) => (int)ReadUInt32(Position); + public long ReadInt64(long Position) => (long)ReadUInt64(Position); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return *((byte*)(RamPtr + (uint)Position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUInt16(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return *((ushort*)(RamPtr + (uint)Position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt32(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return *((uint*)(RamPtr + (uint)Position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadUInt64(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return *((ulong*)(RamPtr + (uint)Position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AVec ReadVector8(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return new AVec() { B0 = ReadByte(Position) }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AVec ReadVector16(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return new AVec() { H0 = ReadUInt16(Position) }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AVec ReadVector32(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return new AVec() { W0 = ReadUInt32(Position) }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AVec ReadVector64(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return new AVec() { X0 = ReadUInt64(Position) }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public AVec ReadVector128(long Position) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Read); +#endif + + return new AVec() + { + X0 = ReadUInt64(Position + 0), + X1 = ReadUInt64(Position + 8) + }; + } + + public void WriteSByte(long Position, sbyte Value) => WriteByte (Position, (byte)Value); + public void WriteInt16(long Position, short Value) => WriteUInt16(Position, (ushort)Value); + public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value); + public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteByte(long Position, byte Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + *((byte*)(RamPtr + (uint)Position)) = Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt16(long Position, ushort Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + *((ushort*)(RamPtr + (uint)Position)) = Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt32(long Position, uint Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + *((uint*)(RamPtr + (uint)Position)) = Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt64(long Position, ulong Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + *((ulong*)(RamPtr + (uint)Position)) = Value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector8(long Position, AVec Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + WriteByte(Position, Value.B0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector16(long Position, AVec Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + WriteUInt16(Position, Value.H0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector32(long Position, AVec Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + WriteUInt32(Position, Value.W0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector64(long Position, AVec Value) + { +#if DEBUG + EnsureAccessIsValid(Position, AMemoryPerm.Write); +#endif + + WriteUInt64(Position, Value.X0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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 void EnsureAccessIsValid(long Position, AMemoryPerm Perm) + { + 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/Memory/AMemoryAlloc.cs b/Memory/AMemoryAlloc.cs new file mode 100644 index 0000000..b11e779 --- /dev/null +++ b/Memory/AMemoryAlloc.cs @@ -0,0 +1,35 @@ +using ChocolArm64.Exceptions; + +namespace ChocolArm64.Memory +{ + public class AMemoryAlloc + { + private long PhysPos; + + public long Alloc(long Size) + { + long Position = PhysPos; + + Size = AMemoryHelper.PageRoundUp(Size); + + PhysPos += Size; + + if (PhysPos > AMemoryMgr.RamSize || PhysPos < 0) + { + throw new VmmOutOfMemoryException(Size); + } + + return Position; + } + + public void Free(long Position) + { + //TODO + } + + public long GetFreeMem() + { + return AMemoryMgr.RamSize - PhysPos; + } + } +} \ No newline at end of file diff --git a/Memory/AMemoryHelper.cs b/Memory/AMemoryHelper.cs new file mode 100644 index 0000000..219aeeb --- /dev/null +++ b/Memory/AMemoryHelper.cs @@ -0,0 +1,73 @@ +using System.IO; +using System.Text; + +namespace ChocolArm64.Memory +{ + public static class AMemoryHelper + { + public static void FillWithZeros(AMemory Memory, long Position, int Size) + { + int Size8 = Size & ~(8 - 1); + + for (int Offs = 0; Offs < Size8; Offs += 8) + { + Memory.WriteInt64(Position + Offs, 0); + } + + for (int Offs = Size8; Offs < (Size - Size8); Offs++) + { + Memory.WriteByte(Position + Offs, 0); + } + } + + public static byte[] ReadBytes(AMemory Memory, long Position, int Size) + { + byte[] Data = new byte[Size]; + + for (int Offs = 0; Offs < Size; Offs++) + { + Data[Offs] = (byte)Memory.ReadByte(Position + Offs); + } + + return Data; + } + + public static void WriteBytes(AMemory Memory, long Position, byte[] Data) + { + for (int Offs = 0; Offs < Data.Length; Offs++) + { + Memory.WriteByte(Position + Offs, Data[Offs]); + } + } + + public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1) + { + using (MemoryStream MS = new MemoryStream()) + { + for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++) + { + byte Value = (byte)Memory.ReadByte(Position + Offs); + + if (Value == 0) + { + break; + } + + MS.WriteByte(Value); + } + + return Encoding.ASCII.GetString(MS.ToArray()); + } + } + + public static long PageRoundUp(long Value) + { + return (Value + AMemoryMgr.PageMask) & ~AMemoryMgr.PageMask; + } + + public static long PageRoundDown(long Value) + { + return Value & ~AMemoryMgr.PageMask; + } + } +} \ No newline at end of file diff --git a/Memory/AMemoryMapInfo.cs b/Memory/AMemoryMapInfo.cs new file mode 100644 index 0000000..44b2cc0 --- /dev/null +++ b/Memory/AMemoryMapInfo.cs @@ -0,0 +1,21 @@ +namespace ChocolArm64.Memory +{ + public struct AMemoryMapInfo + { + public long Position { get; private set; } + public long Size { get; private set; } + public int Type { get; private set; } + public int Attr { get; private set; } + + public AMemoryPerm Perm { get; private set; } + + public AMemoryMapInfo(long Position, long Size, int Type, int Attr, AMemoryPerm Perm) + { + this.Position = Position; + this.Size = Size; + this.Type = Type; + this.Attr = Attr; + this.Perm = Perm; + } + } +} \ No newline at end of file diff --git a/Memory/AMemoryMgr.cs b/Memory/AMemoryMgr.cs new file mode 100644 index 0000000..0528405 --- /dev/null +++ b/Memory/AMemoryMgr.cs @@ -0,0 +1,286 @@ +namespace ChocolArm64.Memory +{ + public class AMemoryMgr + { + public const long AddrSize = RamSize; + public const long RamSize = 4L * 1024 * 1024 * 1024; + + private const int PTLvl0Bits = 11; + private const int PTLvl1Bits = 13; + private const int PTPageBits = 12; + + private const int PTLvl0Size = 1 << PTLvl0Bits; + private const int PTLvl1Size = 1 << PTLvl1Bits; + public const int PageSize = 1 << PTPageBits; + + private const int PTLvl0Mask = PTLvl0Size - 1; + private const int PTLvl1Mask = PTLvl1Size - 1; + public const int PageMask = PageSize - 1; + + private const int PTLvl0Bit = PTPageBits + PTLvl0Bits; + private const int PTLvl1Bit = PTPageBits; + + private AMemoryAlloc Allocator; + + private enum PTMap + { + Unmapped, + Mapped + } + + private struct PTEntry + { + public PTMap Map; + public AMemoryPerm Perm; + + public int Type; + public int Attr; + + public PTEntry(PTMap Map, AMemoryPerm Perm, int Type, int Attr) + { + this.Map = Map; + this.Perm = Perm; + this.Type = Type; + this.Attr = Attr; + } + } + + private PTEntry[][] PageTable; + + private bool IsHeapInitialized; + + public long HeapAddr { get; private set; } + public long HeapSize { get; private set; } + + public AMemoryMgr(AMemoryAlloc Allocator) + { + this.Allocator = Allocator; + + PageTable = new PTEntry[PTLvl0Size][]; + } + + public long GetTotalMemorySize() + { + return Allocator.GetFreeMem() + GetUsedMemorySize(); + } + + public long GetUsedMemorySize() + { + long Size = 0; + + for (int L0 = 0; L0 < PageTable.Length; L0++) + { + if (PageTable[L0] == null) + { + continue; + } + + for (int L1 = 0; L1 < PageTable[L0].Length; L1++) + { + Size += PageTable[L0][L1].Map != PTMap.Unmapped ? PageSize : 0; + } + } + + return Size; + } + + public bool SetHeapAddr(long Position) + { + if (!IsHeapInitialized) + { + HeapAddr = Position; + + IsHeapInitialized = true; + + return true; + } + + return false; + } + + public void SetHeapSize(long Size, int Type) + { + //TODO: Return error when theres no enough space to allocate heap. + Size = AMemoryHelper.PageRoundUp(Size); + + long Position = HeapAddr; + + if ((ulong)Size < (ulong)HeapSize) + { + //Try to free now free area if size is smaller than old size. + Position += Size; + + while ((ulong)Size < (ulong)HeapSize) + { + Allocator.Free(Position); + + Position += PageSize; + } + } + else + { + //Allocate extra needed size. + Position += HeapSize; + Size -= HeapSize; + + MapPhys(Position, Size, Type, AMemoryPerm.RW); + } + + HeapSize = Size; + } + + public void MapPhys(long Position, long Size, int Type, AMemoryPerm Perm) + { + while (Size > 0) + { + if (!IsMapped(Position)) + { + SetPTEntry(Position, new PTEntry(PTMap.Mapped, Perm, Type, 0)); + } + + long CPgSize = PageSize - (Position & PageMask); + + Position += CPgSize; + Size -= CPgSize; + } + } + + public void MapMirror(long Src, long Dst, long Size, int Type) + { + Src = AMemoryHelper.PageRoundDown(Src); + Dst = AMemoryHelper.PageRoundDown(Dst); + + Size = AMemoryHelper.PageRoundUp(Size); + + long PagesCount = Size / PageSize; + + while (PagesCount-- > 0) + { + PTEntry SrcEntry = GetPTEntry(Src); + PTEntry DstEntry = GetPTEntry(Dst); + + DstEntry.Map = PTMap.Mapped; + DstEntry.Type = Type; + DstEntry.Perm = SrcEntry.Perm; + + SrcEntry.Perm = AMemoryPerm.None; + + SrcEntry.Attr |= 1; + + SetPTEntry(Src, SrcEntry); + SetPTEntry(Dst, DstEntry); + + Src += PageSize; + Dst += PageSize; + } + } + + public void Reprotect(long Position, long Size, AMemoryPerm Perm) + { + Position = AMemoryHelper.PageRoundDown(Position); + + Size = AMemoryHelper.PageRoundUp(Size); + + long PagesCount = Size / PageSize; + + while (PagesCount-- > 0) + { + PTEntry Entry = GetPTEntry(Position); + + Entry.Perm = Perm; + + SetPTEntry(Position, Entry); + + Position += PageSize; + } + } + + public AMemoryMapInfo GetMapInfo(long Position) + { + Position = AMemoryHelper.PageRoundDown(Position); + + PTEntry BaseEntry = GetPTEntry(Position); + + bool IsSameSegment(long Pos) + { + PTEntry Entry = GetPTEntry(Pos); + + return Entry.Map == BaseEntry.Map && + Entry.Perm == BaseEntry.Perm && + Entry.Type == BaseEntry.Type && + Entry.Attr == BaseEntry.Attr; + } + + long Start = Position; + long End = Position + PageSize; + + while (Start > 0 && IsSameSegment(Start - PageSize)) + { + Start -= PageSize; + } + + while (End < AddrSize && IsSameSegment(End)) + { + End += PageSize; + } + + long Size = End - Start; + + return new AMemoryMapInfo( + Start, + Size, + BaseEntry.Type, + BaseEntry.Attr, + BaseEntry.Perm); + } + + public bool HasPermission(long Position, AMemoryPerm Perm) + { + return GetPTEntry(Position).Perm.HasFlag(Perm); + } + + public bool IsMapped(long Position) + { + if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) + { + return false; + } + + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return false; + } + + return PageTable[L0][L1].Map != PTMap.Unmapped; + } + + private PTEntry GetPTEntry(long Position) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return default(PTEntry); + } + + return PageTable[L0][L1]; + } + + private void SetPTEntry(long Position, PTEntry Entry) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + PageTable[L0] = new PTEntry[PTLvl1Size]; + } + + PageTable[L0][L1] = Entry; + } + } +} \ No newline at end of file diff --git a/Memory/AMemoryPerm.cs b/Memory/AMemoryPerm.cs new file mode 100644 index 0000000..b425eb9 --- /dev/null +++ b/Memory/AMemoryPerm.cs @@ -0,0 +1,15 @@ +using System; + +namespace ChocolArm64.Memory +{ + [Flags] + public enum AMemoryPerm + { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + Execute = 1 << 2, + RW = Read | Write, + RX = Read | Execute + } +} \ No newline at end of file diff --git a/State/AInstExceptEventArgs.cs b/State/AInstExceptEventArgs.cs new file mode 100644 index 0000000..f2ee039 --- /dev/null +++ b/State/AInstExceptEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.State +{ + public class AInstExceptEventArgs : EventArgs + { + public int Id { get; private set; } + + public AInstExceptEventArgs(int Id) + { + this.Id = Id; + } + } +} \ No newline at end of file diff --git a/State/AInstUndEventArgs.cs b/State/AInstUndEventArgs.cs new file mode 100644 index 0000000..53de65a --- /dev/null +++ b/State/AInstUndEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace ChocolArm64.State +{ + public class AInstUndEventArgs : EventArgs + { + public long Position { get; private set; } + public int RawOpCode { get; private set; } + + public AInstUndEventArgs(long Position, int RawOpCode) + { + this.Position = Position; + this.RawOpCode = RawOpCode; + } + } +} \ No newline at end of file diff --git a/State/APState.cs b/State/APState.cs new file mode 100644 index 0000000..f55431a --- /dev/null +++ b/State/APState.cs @@ -0,0 +1,23 @@ +using System; + +namespace ChocolArm64.State +{ + [Flags] + public enum APState + { + VBit = 28, + CBit = 29, + ZBit = 30, + NBit = 31, + + V = 1 << VBit, + C = 1 << CBit, + Z = 1 << ZBit, + N = 1 << NBit, + + NZ = N | Z, + CV = C | V, + + NZCV = NZ | CV + } +} \ No newline at end of file diff --git a/State/ARegister.cs b/State/ARegister.cs new file mode 100644 index 0000000..5861db8 --- /dev/null +++ b/State/ARegister.cs @@ -0,0 +1,142 @@ +using System; +using System.Reflection; + +namespace ChocolArm64.State +{ + struct ARegister + { + public int Index; + + public ARegisterType Type; + + public ARegister(int Index, ARegisterType Type) + { + this.Index = Index; + this.Type = Type; + } + + public override int GetHashCode() + { + return (ushort)Index | ((ushort)Type << 16); + } + + public override bool Equals(object Obj) + { + return Obj is ARegister Reg && + Reg.Index == Index && + Reg.Type == Type; + } + + public FieldInfo GetField() + { + switch (Type) + { + case ARegisterType.Flag: return GetFieldFlag(); + case ARegisterType.Int: return GetFieldInt(); + case ARegisterType.Vector: return GetFieldVector(); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldFlag() + { + switch ((APState)Index) + { + case APState.VBit: return GetField(nameof(AThreadState.Overflow)); + case APState.CBit: return GetField(nameof(AThreadState.Carry)); + case APState.ZBit: return GetField(nameof(AThreadState.Zero)); + case APState.NBit: return GetField(nameof(AThreadState.Negative)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldInt() + { + switch (Index) + { + case 0: return GetField(nameof(AThreadState.X0)); + case 1: return GetField(nameof(AThreadState.X1)); + case 2: return GetField(nameof(AThreadState.X2)); + case 3: return GetField(nameof(AThreadState.X3)); + case 4: return GetField(nameof(AThreadState.X4)); + case 5: return GetField(nameof(AThreadState.X5)); + case 6: return GetField(nameof(AThreadState.X6)); + case 7: return GetField(nameof(AThreadState.X7)); + case 8: return GetField(nameof(AThreadState.X8)); + case 9: return GetField(nameof(AThreadState.X9)); + case 10: return GetField(nameof(AThreadState.X10)); + case 11: return GetField(nameof(AThreadState.X11)); + case 12: return GetField(nameof(AThreadState.X12)); + case 13: return GetField(nameof(AThreadState.X13)); + case 14: return GetField(nameof(AThreadState.X14)); + case 15: return GetField(nameof(AThreadState.X15)); + case 16: return GetField(nameof(AThreadState.X16)); + case 17: return GetField(nameof(AThreadState.X17)); + case 18: return GetField(nameof(AThreadState.X18)); + case 19: return GetField(nameof(AThreadState.X19)); + case 20: return GetField(nameof(AThreadState.X20)); + case 21: return GetField(nameof(AThreadState.X21)); + case 22: return GetField(nameof(AThreadState.X22)); + case 23: return GetField(nameof(AThreadState.X23)); + case 24: return GetField(nameof(AThreadState.X24)); + case 25: return GetField(nameof(AThreadState.X25)); + case 26: return GetField(nameof(AThreadState.X26)); + case 27: return GetField(nameof(AThreadState.X27)); + case 28: return GetField(nameof(AThreadState.X28)); + case 29: return GetField(nameof(AThreadState.X29)); + case 30: return GetField(nameof(AThreadState.X30)); + case 31: return GetField(nameof(AThreadState.X31)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldVector() + { + switch (Index) + { + case 0: return GetField(nameof(AThreadState.V0)); + case 1: return GetField(nameof(AThreadState.V1)); + case 2: return GetField(nameof(AThreadState.V2)); + case 3: return GetField(nameof(AThreadState.V3)); + case 4: return GetField(nameof(AThreadState.V4)); + case 5: return GetField(nameof(AThreadState.V5)); + case 6: return GetField(nameof(AThreadState.V6)); + case 7: return GetField(nameof(AThreadState.V7)); + case 8: return GetField(nameof(AThreadState.V8)); + case 9: return GetField(nameof(AThreadState.V9)); + case 10: return GetField(nameof(AThreadState.V10)); + case 11: return GetField(nameof(AThreadState.V11)); + case 12: return GetField(nameof(AThreadState.V12)); + case 13: return GetField(nameof(AThreadState.V13)); + case 14: return GetField(nameof(AThreadState.V14)); + case 15: return GetField(nameof(AThreadState.V15)); + case 16: return GetField(nameof(AThreadState.V16)); + case 17: return GetField(nameof(AThreadState.V17)); + case 18: return GetField(nameof(AThreadState.V18)); + case 19: return GetField(nameof(AThreadState.V19)); + case 20: return GetField(nameof(AThreadState.V20)); + case 21: return GetField(nameof(AThreadState.V21)); + case 22: return GetField(nameof(AThreadState.V22)); + case 23: return GetField(nameof(AThreadState.V23)); + case 24: return GetField(nameof(AThreadState.V24)); + case 25: return GetField(nameof(AThreadState.V25)); + case 26: return GetField(nameof(AThreadState.V26)); + case 27: return GetField(nameof(AThreadState.V27)); + case 28: return GetField(nameof(AThreadState.V28)); + case 29: return GetField(nameof(AThreadState.V29)); + case 30: return GetField(nameof(AThreadState.V30)); + case 31: return GetField(nameof(AThreadState.V31)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetField(string Name) + { + return typeof(AThreadState).GetField(Name); + } + } +} \ No newline at end of file diff --git a/State/ARegisterSize.cs b/State/ARegisterSize.cs new file mode 100644 index 0000000..144f36b --- /dev/null +++ b/State/ARegisterSize.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.State +{ + enum ARegisterSize + { + Int32, + Int64, + SIMD64, + SIMD128 + } +} \ No newline at end of file diff --git a/State/ARegisterType.cs b/State/ARegisterType.cs new file mode 100644 index 0000000..f9776bb --- /dev/null +++ b/State/ARegisterType.cs @@ -0,0 +1,9 @@ +namespace ChocolArm64.State +{ + enum ARegisterType + { + Flag, + Int, + Vector + } +} \ No newline at end of file diff --git a/State/AThreadState.cs b/State/AThreadState.cs new file mode 100644 index 0000000..cdab403 --- /dev/null +++ b/State/AThreadState.cs @@ -0,0 +1,64 @@ +using System; + +namespace ChocolArm64.State +{ + public class AThreadState + { + internal const int LRIndex = 30; + internal const int ZRIndex = 31; + + internal const int ErgSizeLog2 = 4; + internal const int DczSizeLog2 = 4; + + 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, + X24, X25, X26, X27, X28, X29, X30, X31; + + public AVec V0, V1, V2, V3, V4, V5, V6, V7, + V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, V22, V23, + V24, V25, V26, V27, V28, V29, V30, V31; + + public bool Overflow; + public bool Carry; + public bool Zero; + public bool Negative; + + public int ProcessId; + public int ThreadId; + + public long TpidrEl0 { get; set; } + public long Tpidr { get; set; } + + public int Fpcr { get; set; } + public int Fpsr { get; set; } + + public uint CtrEl0 => 0x8444c004; + public uint DczidEl0 => 0x00000004; + + private const long TicksPerS = 19_200_000; + private const long TicksPerMS = TicksPerS / 1_000; + + public long CntpctEl0 => Environment.TickCount * TicksPerMS; + + public event EventHandler Break; + public event EventHandler SvcCall; + public event EventHandler Undefined; + + internal void OnBreak(int Imm) + { + Break?.Invoke(this, new AInstExceptEventArgs(Imm)); + } + + internal void OnSvcCall(int Imm) + { + SvcCall?.Invoke(this, new AInstExceptEventArgs(Imm)); + } + + internal void OnUndefined(long Position, int RawOpCode) + { + Undefined?.Invoke(this, new AInstUndEventArgs(Position, RawOpCode)); + } + } +} \ No newline at end of file diff --git a/State/AVec.cs b/State/AVec.cs new file mode 100644 index 0000000..f7eb2e2 --- /dev/null +++ b/State/AVec.cs @@ -0,0 +1,243 @@ +using System; +using System.Runtime.InteropServices; + +namespace ChocolArm64.State +{ + [StructLayout(LayoutKind.Explicit, Size = 16)] + public struct AVec + { + [FieldOffset(0x0)] public byte B0; + [FieldOffset(0x1)] public byte B1; + [FieldOffset(0x2)] public byte B2; + [FieldOffset(0x3)] public byte B3; + [FieldOffset(0x4)] public byte B4; + [FieldOffset(0x5)] public byte B5; + [FieldOffset(0x6)] public byte B6; + [FieldOffset(0x7)] public byte B7; + [FieldOffset(0x8)] public byte B8; + [FieldOffset(0x9)] public byte B9; + [FieldOffset(0xa)] public byte B10; + [FieldOffset(0xb)] public byte B11; + [FieldOffset(0xc)] public byte B12; + [FieldOffset(0xd)] public byte B13; + [FieldOffset(0xe)] public byte B14; + [FieldOffset(0xf)] public byte B15; + + [FieldOffset(0x0)] public ushort H0; + [FieldOffset(0x2)] public ushort H1; + [FieldOffset(0x4)] public ushort H2; + [FieldOffset(0x6)] public ushort H3; + [FieldOffset(0x8)] public ushort H4; + [FieldOffset(0xa)] public ushort H5; + [FieldOffset(0xc)] public ushort H6; + [FieldOffset(0xe)] public ushort H7; + + [FieldOffset(0x0)] public uint W0; + [FieldOffset(0x4)] public uint W1; + [FieldOffset(0x8)] public uint W2; + [FieldOffset(0xc)] public uint W3; + + [FieldOffset(0x0)] public float S0; + [FieldOffset(0x4)] public float S1; + [FieldOffset(0x8)] public float S2; + [FieldOffset(0xc)] public float S3; + + [FieldOffset(0x0)] public ulong X0; + [FieldOffset(0x8)] public ulong X1; + + [FieldOffset(0x0)] public double D0; + [FieldOffset(0x8)] public double D1; + + public byte ExtractByte(int Index) + { + switch (Index) + { + case 0: return B0; + case 1: return B1; + case 2: return B2; + case 3: return B3; + case 4: return B4; + case 5: return B5; + case 6: return B6; + case 7: return B7; + case 8: return B8; + case 9: return B9; + case 10: return B10; + case 11: return B11; + case 12: return B12; + case 13: return B13; + case 14: return B14; + case 15: return B15; + } + + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + public ushort ExtractUInt16(int Index) + { + switch (Index) + { + case 0: return H0; + case 1: return H1; + case 2: return H2; + case 3: return H3; + case 4: return H4; + case 5: return H5; + case 6: return H6; + case 7: return H7; + } + + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + public uint ExtractUInt32(int Index) + { + switch (Index) + { + case 0: return W0; + case 1: return W1; + case 2: return W2; + case 3: return W3; + } + + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + public float ExtractSingle(int Index) + { + switch (Index) + { + case 0: return S0; + case 1: return S1; + case 2: return S2; + case 3: return S3; + } + + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + public ulong ExtractUInt64(int Index) + { + switch (Index) + { + case 0: return X0; + case 1: return X1; + } + + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + public double ExtractDouble(int Index) + { + switch (Index) + { + case 0: return D0; + case 1: return D1; + } + + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + public static AVec InsertByte(AVec Vec, int Index, byte Value) + { + switch (Index) + { + case 0: Vec.B0 = Value; break; + case 1: Vec.B1 = Value; break; + case 2: Vec.B2 = Value; break; + case 3: Vec.B3 = Value; break; + case 4: Vec.B4 = Value; break; + case 5: Vec.B5 = Value; break; + case 6: Vec.B6 = Value; break; + case 7: Vec.B7 = Value; break; + case 8: Vec.B8 = Value; break; + case 9: Vec.B9 = Value; break; + case 10: Vec.B10 = Value; break; + case 11: Vec.B11 = Value; break; + case 12: Vec.B12 = Value; break; + case 13: Vec.B13 = Value; break; + case 14: Vec.B14 = Value; break; + case 15: Vec.B15 = Value; break; + + default: throw new ArgumentOutOfRangeException(nameof(Index)); + } + + return Vec; + } + + public static AVec InsertUInt16(AVec Vec, int Index, ushort Value) + { + switch (Index) + { + case 0: Vec.H0 = Value; break; + case 1: Vec.H1 = Value; break; + case 2: Vec.H2 = Value; break; + case 3: Vec.H3 = Value; break; + case 4: Vec.H4 = Value; break; + case 5: Vec.H5 = Value; break; + case 6: Vec.H6 = Value; break; + case 7: Vec.H7 = Value; break; + + default: throw new ArgumentOutOfRangeException(nameof(Index)); + } + + return Vec; + } + + public static AVec InsertUInt32(AVec Vec, int Index, uint Value) + { + switch (Index) + { + case 0: Vec.W0 = Value; break; + case 1: Vec.W1 = Value; break; + case 2: Vec.W2 = Value; break; + case 3: Vec.W3 = Value; break; + + default: throw new ArgumentOutOfRangeException(nameof(Index)); + } + + return Vec; + } + + public static AVec InsertSingle(AVec Vec, int Index, float Value) + { + switch (Index) + { + case 0: Vec.S0 = Value; break; + case 1: Vec.S1 = Value; break; + case 2: Vec.S2 = Value; break; + case 3: Vec.S3 = Value; break; + + default: throw new ArgumentOutOfRangeException(nameof(Index)); + } + + return Vec; + } + + public static AVec InsertUInt64(AVec Vec, int Index, ulong Value) + { + switch (Index) + { + case 0: Vec.X0 = Value; break; + case 1: Vec.X1 = Value; break; + + default: throw new ArgumentOutOfRangeException(nameof(Index)); + } + + return Vec; + } + + public static AVec InsertDouble(AVec Vec, int Index, double Value) + { + switch (Index) + { + case 0: Vec.D0 = Value; break; + case 1: Vec.D1 = Value; break; + + default: throw new ArgumentOutOfRangeException(nameof(Index)); + } + + return Vec; + } + } +} \ No newline at end of file diff --git a/Translation/AILBlock.cs b/Translation/AILBlock.cs new file mode 100644 index 0000000..bed195a --- /dev/null +++ b/Translation/AILBlock.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Translation +{ + class AILBlock : IAILEmit + { + public long IntInputs { get; private set; } + public long IntOutputs { get; private set; } + + public long VecInputs { get; private set; } + public long VecOutputs { get; private set; } + + public bool HasStateStore { get; private set; } + + public List ILEmitters { get; private set; } + + public AILBlock Next { get; set; } + public AILBlock Branch { get; set; } + + public AILBlock() + { + ILEmitters = new List(); + } + + public void Add(IAILEmit ILEmitter) + { + if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index)) + { + switch (Ld.IoType) + { + case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break; + case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break; + case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecOutputs; break; + } + } + else if (ILEmitter is AILOpCodeStore St) + { + if (AILEmitter.IsRegIndex(St.Index)) + { + switch (St.IoType) + { + case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break; + case AIoType.Int: IntOutputs |= 1L << St.Index; break; + case AIoType.Vector: VecOutputs |= 1L << St.Index; break; + } + } + + if (St.IoType == AIoType.Fields) + { + HasStateStore = true; + } + } + + ILEmitters.Add(ILEmitter); + } + + public void Emit(AILEmitter Context) + { + foreach (IAILEmit ILEmitter in ILEmitters) + { + ILEmitter.Emit(Context); + } + } + } +} \ No newline at end of file diff --git a/Translation/AILEmitter.cs b/Translation/AILEmitter.cs new file mode 100644 index 0000000..8f6e121 --- /dev/null +++ b/Translation/AILEmitter.cs @@ -0,0 +1,187 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class AILEmitter + { + public ALocalAlloc LocalAlloc { get; private set; } + + public ILGenerator Generator { get; private set; } + + private Dictionary Locals; + + private AILBlock[] ILBlocks; + + private AILBlock Root; + + private ATranslatedSub Subroutine; + + private string SubName; + + private int LocalsCount; + + public AILEmitter(ABlock[] Graph, ABlock Root, string SubName) + { + this.SubName = SubName; + + Locals = new Dictionary(); + + ILBlocks = new AILBlock[Graph.Length]; + + AILBlock GetBlock(int Index) + { + if (Index < 0 || Index >= ILBlocks.Length) + { + return null; + } + + if (ILBlocks[Index] == null) + { + ILBlocks[Index] = new AILBlock(); + } + + return ILBlocks[Index]; + } + + for (int Index = 0; Index < ILBlocks.Length; Index++) + { + AILBlock Block = GetBlock(Index); + + Block.Next = GetBlock(Array.IndexOf(Graph, Graph[Index].Next)); + Block.Branch = GetBlock(Array.IndexOf(Graph, Graph[Index].Branch)); + } + + this.Root = ILBlocks[Array.IndexOf(Graph, Root)]; + } + + public ATranslatedSub GetSubroutine() + { + LocalAlloc = new ALocalAlloc(ILBlocks, Root); + + InitSubroutine(); + InitLocals(); + + foreach (AILBlock ILBlock in ILBlocks) + { + ILBlock.Emit(this); + } + + return Subroutine; + } + + public AILBlock GetILBlock(int Index) => ILBlocks[Index]; + + private void InitLocals() + { + int ParamsStart = ATranslatedSub.FixedArgTypes.Length; + + Locals = new Dictionary(); + + for (int Index = 0; Index < Subroutine.Params.Count; Index++) + { + ARegister Reg = Subroutine.Params[Index]; + + Generator.EmitLdarg(Index + ParamsStart); + Generator.EmitStloc(GetLocalIndex(Reg)); + } + } + + private void InitSubroutine() + { + List Params = new List(); + + void SetParams(long Inputs, ARegisterType BaseType) + { + for (int Bit = 0; Bit < 64; Bit++) + { + long Mask = 1L << Bit; + + if ((Inputs & Mask) != 0) + { + Params.Add(GetRegFromBit(Bit, BaseType)); + } + } + } + + SetParams(LocalAlloc.GetIntInputs(Root), ARegisterType.Int); + SetParams(LocalAlloc.GetVecInputs(Root), ARegisterType.Vector); + + DynamicMethod Mthd = new DynamicMethod(SubName, typeof(long), GetParamTypes(Params)); + + Generator = Mthd.GetILGenerator(); + + Subroutine = new ATranslatedSub(Mthd, Params); + } + + private Type[] GetParamTypes(IList Params) + { + Type[] FixedArgs = ATranslatedSub.FixedArgTypes; + + Type[] Output = new Type[Params.Count + FixedArgs.Length]; + + FixedArgs.CopyTo(Output, 0); + + int TypeIdx = FixedArgs.Length; + + for (int Index = 0; Index < Params.Count; Index++) + { + Output[TypeIdx++] = GetFieldType(Params[Index].Type); + } + + return Output; + } + + public int GetLocalIndex(ARegister Reg) + { + if (!Locals.TryGetValue(Reg, out int Index)) + { + Generator.DeclareLocal(GetLocalType(Reg)); + + Index = LocalsCount++; + + Locals.Add(Reg, Index); + } + + return Index; + } + + public Type GetLocalType(ARegister Reg) => GetFieldType(Reg.Type); + + public Type GetFieldType(ARegisterType RegType) + { + switch (RegType) + { + case ARegisterType.Flag: return typeof(bool); + case ARegisterType.Int: return typeof(ulong); + case ARegisterType.Vector: return typeof(AVec); + } + + throw new ArgumentException(nameof(RegType)); + } + + public static ARegister GetRegFromBit(int Bit, ARegisterType BaseType) + { + if (Bit < 32) + { + return new ARegister(Bit, BaseType); + } + else if (BaseType == ARegisterType.Int) + { + return new ARegister(Bit & 0x1f, ARegisterType.Flag); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Bit)); + } + } + + public static bool IsRegIndex(int Index) + { + return Index >= 0 && Index < 32; + } + } +} \ No newline at end of file diff --git a/Translation/AILEmitterCtx.cs b/Translation/AILEmitterCtx.cs new file mode 100644 index 0000000..cf64454 --- /dev/null +++ b/Translation/AILEmitterCtx.cs @@ -0,0 +1,487 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Instruction; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class AILEmitterCtx + { + private ATranslator Translator; + + private Dictionary Labels; + + private AILEmitter Emitter; + + private AILBlock ILBlock; + + private AOpCode LastCmpOp; + private AOpCode LastFlagOp; + + private int BlkIndex; + private int OpcIndex; + + private ABlock[] Graph; + private ABlock Root; + public ABlock CurrBlock => Graph[BlkIndex]; + public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex]; + + //This is the index of the temporary register, used to store temporary + //values needed by some functions, since IL doesn't have a swap instruction. + //You can use any value here as long it doesn't conflict with the indices + //for the other registers. Any value >= 64 or < 0 will do. + private const int Tmp1Index = -1; + private const int Tmp2Index = -2; + private const int Tmp3Index = -3; + private const int Tmp4Index = -4; + private const int Tmp5Index = -5; + + public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root) + { + this.Translator = Translator; + this.Graph = Graph; + this.Root = Root; + + string SubName = $"Sub{Root.Position:X16}"; + + Labels = new Dictionary(); + + Emitter = new AILEmitter(Graph, Root, SubName); + + ILBlock = Emitter.GetILBlock(0); + + OpcIndex = -1; + + if (!AdvanceOpCode()) + { + throw new ArgumentException(nameof(Graph)); + } + } + + public ATranslatedSub GetSubroutine() => Emitter.GetSubroutine(); + + public bool AdvanceOpCode() + { + while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) + { + if (BlkIndex + 1 >= Graph.Length) + { + return false; + } + + BlkIndex++; + OpcIndex = -1; + + ILBlock = Emitter.GetILBlock(BlkIndex); + } + + return true; + } + + public void EmitOpCode() + { + if (OpcIndex == 0) + { + MarkLabel(GetLabel(CurrBlock.Position)); + } + + CurrOp.Emitter(this); + } + + public bool TryOptEmitSubroutineCall() + { + if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) + { + return false; + } + + for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++) + { + EmitLdarg(Index); + } + + foreach (ARegister Reg in Sub.Params) + { + switch (Reg.Type) + { + case ARegisterType.Flag: Ldloc(Reg.Index, AIoType.Flag); break; + case ARegisterType.Int: Ldloc(Reg.Index, AIoType.Int); break; + case ARegisterType.Vector: Ldloc(Reg.Index, AIoType.Vector); break; + } + } + + EmitCall(Sub.Method); + + return true; + } + + public void TryOptMarkCondWithoutCmp() + { + LastCmpOp = CurrOp; + + AInstEmitAluHelper.EmitDataLoadOpers(this); + + Stloc(Tmp4Index, AIoType.Int); + Stloc(Tmp3Index, AIoType.Int); + } + + private Dictionary BranchOps = new Dictionary() + { + { ACond.Eq, OpCodes.Beq }, + { ACond.Ne, OpCodes.Bne_Un }, + { ACond.Ge_Un, OpCodes.Bge_Un }, + { ACond.Lt_Un, OpCodes.Blt_Un }, + { ACond.Gt_Un, OpCodes.Bgt_Un }, + { ACond.Le_Un, OpCodes.Ble_Un }, + { ACond.Ge, OpCodes.Bge }, + { ACond.Lt, OpCodes.Blt }, + { ACond.Gt, OpCodes.Bgt }, + { ACond.Le, OpCodes.Ble } + }; + + public void EmitCondBranch(AILLabel Target, ACond Cond) + { + OpCode ILOp; + + int IntCond = (int)Cond; + + if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond)) + { + Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize); + Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize); + + if (LastCmpOp.Emitter == AInstEmit.Adds) + { + Emit(OpCodes.Neg); + } + + ILOp = BranchOps[Cond]; + } + else if (IntCond < 14) + { + int CondTrue = IntCond >> 1; + + switch (CondTrue) + { + case 0: EmitLdflg((int)APState.ZBit); break; + case 1: EmitLdflg((int)APState.CBit); break; + case 2: EmitLdflg((int)APState.NBit); break; + case 3: EmitLdflg((int)APState.VBit); break; + + case 4: + EmitLdflg((int)APState.CBit); + EmitLdflg((int)APState.ZBit); + + Emit(OpCodes.Not); + Emit(OpCodes.And); + break; + + case 5: + case 6: + EmitLdflg((int)APState.NBit); + EmitLdflg((int)APState.VBit); + + Emit(OpCodes.Ceq); + + if (CondTrue == 6) + { + EmitLdflg((int)APState.ZBit); + + Emit(OpCodes.Not); + Emit(OpCodes.And); + } + break; + } + + ILOp = (IntCond & 1) != 0 + ? OpCodes.Brfalse + : OpCodes.Brtrue; + } + else + { + ILOp = OpCodes.Br; + } + + Emit(ILOp, Target); + } + + public void EmitCast(AIntType IntType) + { + switch (IntType) + { + case AIntType.UInt8: Emit(OpCodes.Conv_U1); break; + case AIntType.UInt16: Emit(OpCodes.Conv_U2); break; + case AIntType.UInt32: Emit(OpCodes.Conv_U4); break; + case AIntType.UInt64: Emit(OpCodes.Conv_U8); break; + case AIntType.Int8: Emit(OpCodes.Conv_I1); break; + case AIntType.Int16: Emit(OpCodes.Conv_I2); break; + case AIntType.Int32: Emit(OpCodes.Conv_I4); break; + case AIntType.Int64: Emit(OpCodes.Conv_I8); break; + } + + if (IntType == AIntType.UInt64 || + IntType == AIntType.Int64) + { + return; + } + + if (CurrOp.RegisterSize != ARegisterSize.Int32) + { + Emit(IntType >= AIntType.Int8 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + } + + public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl); + public void EmitLsr(int Amount) => EmitILShift(Amount, OpCodes.Shr_Un); + public void EmitAsr(int Amount) => EmitILShift(Amount, OpCodes.Shr); + + private void EmitILShift(int Amount, OpCode ILOp) + { + if (Amount > 0) + { + EmitLdc_I4(Amount); + + Emit(ILOp); + } + } + + public void EmitRor(int Amount) + { + if (Amount > 0) + { + Stloc(Tmp2Index, AIoType.Int); + Ldloc(Tmp2Index, AIoType.Int); + + EmitLdc_I4(Amount); + + Emit(OpCodes.Shr_Un); + + Ldloc(Tmp2Index, AIoType.Int); + + EmitLdc_I4(CurrOp.GetBitsCount() - Amount); + + Emit(OpCodes.Shl); + Emit(OpCodes.Or); + } + } + + public AILLabel GetLabel(long Position) + { + if (!Labels.TryGetValue(Position, out AILLabel Output)) + { + Output = new AILLabel(); + + Labels.Add(Position, Output); + } + + return Output; + } + + public void MarkLabel(AILLabel Label) + { + ILBlock.Add(Label); + } + + public void Emit(OpCode ILOp) + { + ILBlock.Add(new AILOpCode(ILOp)); + } + + public void Emit(OpCode ILOp, AILLabel Label) + { + ILBlock.Add(new AILOpCodeBranch(ILOp, Label)); + } + + public void Emit(string Text) + { + ILBlock.Add(new AILOpCodeLog(Text)); + } + + public void EmitLdarg(int Index) + { + ILBlock.Add(new AILOpCodeLoad(Index, AIoType.Arg)); + } + + public void EmitLdintzr(int Index) + { + if (Index != AThreadState.ZRIndex) + { + EmitLdint(Index); + } + else + { + EmitLdc_I(0); + } + } + + public void EmitStintzr(int Index) + { + if (Index != AThreadState.ZRIndex) + { + EmitStint(Index); + } + else + { + Emit(OpCodes.Pop); + } + } + + public void EmitLoadState(ABlock RetBlk) + { + ILBlock.Add(new AILOpCodeLoad(Array.IndexOf(Graph, RetBlk), AIoType.Fields)); + } + + public void EmitStoreState() + { + ILBlock.Add(new AILOpCodeStore(Array.IndexOf(Graph, CurrBlock), AIoType.Fields)); + } + + public void EmitLdtmp() => EmitLdint(Tmp1Index); + public void EmitSttmp() => EmitStint(Tmp1Index); + + public void EmitLdvectmp() => EmitLdvec(Tmp5Index); + public void EmitStvectmp() => EmitStvec(Tmp5Index); + + public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int); + public void EmitStint(int Index) => Stloc(Index, AIoType.Int); + + public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector); + public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector); + + public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag); + public void EmitStflg(int Index) + { + LastFlagOp = CurrOp; + + Stloc(Index, AIoType.Flag); + } + + private void Ldloc(int Index, AIoType IoType) + { + ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize)); + } + + private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize) + { + ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize)); + } + + private void Stloc(int Index, AIoType IoType) + { + ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize)); + } + + public void EmitCallPropGet(Type ObjType, string PropName) + { + if (ObjType == null) + { + throw new ArgumentNullException(nameof(ObjType)); + } + + if (PropName == null) + { + throw new ArgumentNullException(nameof(PropName)); + } + + EmitCall(ObjType.GetMethod($"get_{PropName}")); + } + + public void EmitCallPropSet(Type ObjType, string PropName) + { + if (ObjType == null) + { + throw new ArgumentNullException(nameof(ObjType)); + } + + if (PropName == null) + { + throw new ArgumentNullException(nameof(PropName)); + } + + EmitCall(ObjType.GetMethod($"set_{PropName}")); + } + + public void EmitCall(Type ObjType, string MthdName) + { + if (ObjType == null) + { + throw new ArgumentNullException(nameof(ObjType)); + } + + if (MthdName == null) + { + throw new ArgumentNullException(nameof(MthdName)); + } + + EmitCall(ObjType.GetMethod(MthdName)); + } + + public void EmitCall(MethodInfo MthdInfo) + { + if (MthdInfo == null) + { + throw new ArgumentNullException(nameof(MthdInfo)); + } + + ILBlock.Add(new AILOpCodeCall(MthdInfo)); + } + + public void EmitLdc_I(long Value) + { + if (CurrOp.RegisterSize == ARegisterSize.Int32) + { + EmitLdc_I4((int)Value); + } + else + { + EmitLdc_I8(Value); + } + } + + public void EmitLdc_I4(int Value) + { + ILBlock.Add(new AILOpCodeConst(Value)); + } + + public void EmitLdc_I8(long Value) + { + ILBlock.Add(new AILOpCodeConst(Value)); + } + + public void EmitLdc_R4(float Value) + { + ILBlock.Add(new AILOpCodeConst(Value)); + } + + public void EmitLdc_R8(double Value) + { + ILBlock.Add(new AILOpCodeConst(Value)); + } + + public void EmitZNFlagCheck() + { + EmitZNCheck(OpCodes.Ceq, (int)APState.ZBit); + EmitZNCheck(OpCodes.Clt, (int)APState.NBit); + } + + private void EmitZNCheck(OpCode ILCmpOp, int Flag) + { + Emit(OpCodes.Dup); + Emit(OpCodes.Ldc_I4_0); + + if (CurrOp.RegisterSize != ARegisterSize.Int32) + { + Emit(OpCodes.Conv_I8); + } + + Emit(ILCmpOp); + + EmitStflg(Flag); + } + } +} \ No newline at end of file diff --git a/Translation/AILLabel.cs b/Translation/AILLabel.cs new file mode 100644 index 0000000..0ee39ad --- /dev/null +++ b/Translation/AILLabel.cs @@ -0,0 +1,28 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class AILLabel : IAILEmit + { + private bool HasLabel; + + private Label Lbl; + + public void Emit(AILEmitter Context) + { + Context.Generator.MarkLabel(GetLabel(Context)); + } + + public Label GetLabel(AILEmitter Context) + { + if (!HasLabel) + { + Lbl = Context.Generator.DefineLabel(); + + HasLabel = true; + } + + return Lbl; + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCode.cs b/Translation/AILOpCode.cs new file mode 100644 index 0000000..a4bc93a --- /dev/null +++ b/Translation/AILOpCode.cs @@ -0,0 +1,19 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct AILOpCode : IAILEmit + { + private OpCode ILOp; + + public AILOpCode(OpCode ILOp) + { + this.ILOp = ILOp; + } + + public void Emit(AILEmitter Context) + { + Context.Generator.Emit(ILOp); + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCodeBranch.cs b/Translation/AILOpCodeBranch.cs new file mode 100644 index 0000000..e4caad1 --- /dev/null +++ b/Translation/AILOpCodeBranch.cs @@ -0,0 +1,21 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct AILOpCodeBranch : IAILEmit + { + private OpCode ILOp; + private AILLabel Label; + + public AILOpCodeBranch(OpCode ILOp, AILLabel Label) + { + this.ILOp = ILOp; + this.Label = Label; + } + + public void Emit(AILEmitter Context) + { + Context.Generator.Emit(ILOp, Label.GetLabel(Context)); + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCodeCall.cs b/Translation/AILOpCodeCall.cs new file mode 100644 index 0000000..8cd944e --- /dev/null +++ b/Translation/AILOpCodeCall.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct AILOpCodeCall : IAILEmit + { + private MethodInfo MthdInfo; + + public AILOpCodeCall(MethodInfo MthdInfo) + { + this.MthdInfo = MthdInfo; + } + + public void Emit(AILEmitter Context) + { + Context.Generator.Emit(OpCodes.Call, MthdInfo); + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCodeConst.cs b/Translation/AILOpCodeConst.cs new file mode 100644 index 0000000..80150ec --- /dev/null +++ b/Translation/AILOpCodeConst.cs @@ -0,0 +1,81 @@ +using System.Reflection.Emit; +using System.Runtime.InteropServices; + +namespace ChocolArm64.Translation +{ + class AILOpCodeConst : IAILEmit + { + [StructLayout(LayoutKind.Explicit, Size = 8)] + private struct ImmVal + { + [FieldOffset(0)] public int I4; + [FieldOffset(0)] public long I8; + [FieldOffset(0)] public float R4; + [FieldOffset(0)] public double R8; + } + + private ImmVal Value; + + private enum ConstType + { + Int32, + Int64, + Single, + Double + } + + private ConstType Type; + + private AILOpCodeConst(ConstType Type) + { + this.Type = Type; + } + + public AILOpCodeConst(int Value) : this(ConstType.Int32) + { + this.Value = new ImmVal { I4 = Value }; + } + + public AILOpCodeConst(long Value) : this(ConstType.Int64) + { + this.Value = new ImmVal { I8 = Value }; + } + + public AILOpCodeConst(float Value) : this(ConstType.Single) + { + this.Value = new ImmVal { R4 = Value }; + } + + public AILOpCodeConst(double Value) : this(ConstType.Double) + { + this.Value = new ImmVal { R8 = Value }; + } + + public void Emit(AILEmitter Context) + { + switch (Type) + { + case ConstType.Int32: Context.Generator.EmitLdc_I4(Value.I4); break; + + case ConstType.Int64: + { + if (Value.I8 >= int.MinValue && + Value.I8 <= int.MaxValue) + { + Context.Generator.EmitLdc_I4(Value.I4); + + Context.Generator.Emit(OpCodes.Conv_I8); + } + else + { + Context.Generator.Emit(OpCodes.Ldc_I8, Value.I8); + } + break; + } + + case ConstType.Single: Context.Generator.Emit(OpCodes.Ldc_R4, Value.R4); break; + case ConstType.Double: Context.Generator.Emit(OpCodes.Ldc_R8, Value.R8); break; + } + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCodeLoad.cs b/Translation/AILOpCodeLoad.cs new file mode 100644 index 0000000..7cb431e --- /dev/null +++ b/Translation/AILOpCodeLoad.cs @@ -0,0 +1,77 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct AILOpCodeLoad : IAILEmit + { + public int Index { get; private set; } + + public AIoType IoType { get; private set; } + + public ARegisterSize RegisterSize { get; private set; } + + public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { } + + public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize) + { + this.IoType = IoType; + this.Index = Index; + this.RegisterSize = RegisterSize; + } + + public void Emit(AILEmitter Context) + { + switch (IoType) + { + case AIoType.Arg: Context.Generator.EmitLdarg(Index); break; + + case AIoType.Fields: + { + long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index)); + long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index)); + + LoadLocals(Context, IntInputs, ARegisterType.Int); + LoadLocals(Context, VecInputs, ARegisterType.Vector); + + break; + } + + case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break; + case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break; + case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break; + } + } + + private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType) + { + for (int Bit = 0; Bit < 64; Bit++) + { + long Mask = 1L << Bit; + + if ((Inputs & Mask) != 0) + { + ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); + + Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField()); + + Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); + } + } + } + + private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType) + { + ARegister Reg = new ARegister(Index, RegisterType); + + Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); + + if (RegisterType == ARegisterType.Int && + RegisterSize == ARegisterSize.Int32) + { + Context.Generator.Emit(OpCodes.Conv_U4); + } + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCodeLog.cs b/Translation/AILOpCodeLog.cs new file mode 100644 index 0000000..1338ca1 --- /dev/null +++ b/Translation/AILOpCodeLog.cs @@ -0,0 +1,17 @@ +namespace ChocolArm64.Translation +{ + struct AILOpCodeLog : IAILEmit + { + private string Text; + + public AILOpCodeLog(string Text) + { + this.Text = Text; + } + + public void Emit(AILEmitter Context) + { + Context.Generator.EmitWriteLine(Text); + } + } +} \ No newline at end of file diff --git a/Translation/AILOpCodeStore.cs b/Translation/AILOpCodeStore.cs new file mode 100644 index 0000000..c4ea53a --- /dev/null +++ b/Translation/AILOpCodeStore.cs @@ -0,0 +1,75 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct AILOpCodeStore : IAILEmit + { + public int Index { get; private set; } + + public AIoType IoType { get; private set; } + + public ARegisterSize RegisterSize { get; private set; } + + public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize = ARegisterSize.Int64) + { + this.IoType = IoType; + this.Index = Index; + this.RegisterSize = RegisterSize; + } + + public void Emit(AILEmitter Context) + { + switch (IoType) + { + case AIoType.Arg: Context.Generator.EmitStarg(Index); break; + + case AIoType.Fields: + { + long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index)); + long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index)); + + StoreLocals(Context, IntOutputs, ARegisterType.Int); + StoreLocals(Context, VecOutputs, ARegisterType.Vector); + + break; + } + + case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break; + case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break; + case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break; + } + } + + private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType) + { + for (int Bit = 0; Bit < 64; Bit++) + { + long Mask = 1L << Bit; + + if ((Outputs & Mask) != 0) + { + ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); + + Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); + + Context.Generator.Emit(OpCodes.Stfld, Reg.GetField()); + } + } + } + + private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType) + { + ARegister Reg = new ARegister(Index, RegisterType); + + if (RegisterType == ARegisterType.Int && + RegisterSize == ARegisterSize.Int32) + { + Context.Generator.Emit(OpCodes.Conv_U8); + } + + Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); + } + } +} \ No newline at end of file diff --git a/Translation/AIoType.cs b/Translation/AIoType.cs new file mode 100644 index 0000000..94f8908 --- /dev/null +++ b/Translation/AIoType.cs @@ -0,0 +1,15 @@ +using System; + +namespace ChocolArm64.Translation +{ + [Flags] + enum AIoType + { + Arg, + Fields, + Flag, + Int, + Float, + Vector + } +} \ No newline at end of file diff --git a/Translation/ALocalAlloc.cs b/Translation/ALocalAlloc.cs new file mode 100644 index 0000000..0661ddc --- /dev/null +++ b/Translation/ALocalAlloc.cs @@ -0,0 +1,231 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Translation +{ + class ALocalAlloc + { + private class PathIo + { + private Dictionary AllInputs; + private Dictionary CmnOutputs; + + private long AllOutputs; + + public PathIo() + { + AllInputs = new Dictionary(); + CmnOutputs = new Dictionary(); + } + + public PathIo(AILBlock Root, long Inputs, long Outputs) : this() + { + Set(Root, Inputs, Outputs); + } + + public void Set(AILBlock Root, long Inputs, long Outputs) + { + if (!AllInputs.TryAdd(Root, Inputs)) + { + AllInputs[Root] |= Inputs; + } + + if (!CmnOutputs.TryAdd(Root, Outputs)) + { + CmnOutputs[Root] &= Outputs; + } + + AllOutputs |= Outputs; + } + + public long GetInputs(AILBlock Root) + { + if (AllInputs.TryGetValue(Root, out long Inputs)) + { + return Inputs | (AllOutputs & ~CmnOutputs[Root]); + } + + return 0; + } + + public long GetOutputs() + { + return AllOutputs; + } + } + + private Dictionary IntPaths; + private Dictionary VecPaths; + + private struct BlockIo + { + public AILBlock Block; + public AILBlock Entry; + + public long IntInputs; + public long VecInputs; + public long IntOutputs; + public long VecOutputs; + } + + private const int MaxOptGraphLength = 120; + + public ALocalAlloc(AILBlock[] Graph, AILBlock Root) + { + IntPaths = new Dictionary(); + VecPaths = new Dictionary(); + + if (Graph.Length < MaxOptGraphLength) + { + InitializeOptimal(Graph, Root); + } + else + { + InitializeFast(Graph); + } + } + + private void InitializeOptimal(AILBlock[] Graph, AILBlock Root) + { + //This will go through all possible paths on the graph, + //and store all inputs/outputs for each block. A register + //that was previously written to already is not considered an input. + //When a block can be reached by more than one path, then the + //output from all paths needs to be set for this block, and + //only outputs present in all of the parent blocks can be considered + //when doing input elimination. Each block chain have a root, that's where + //the code starts executing. They are present on the subroutine start point, + //and on call return points too (address written to X30 by BL). + HashSet Visited = new HashSet(); + + Queue Unvisited = new Queue(); + + void Enqueue(BlockIo Block) + { + if (!Visited.Contains(Block)) + { + Unvisited.Enqueue(Block); + + Visited.Add(Block); + } + } + + Enqueue(new BlockIo() + { + Block = Root, + Entry = Root + }); + + while (Unvisited.Count > 0) + { + BlockIo Current = Unvisited.Dequeue(); + + Current.IntInputs |= Current.Block.IntInputs & ~Current.IntOutputs; + Current.VecInputs |= Current.Block.VecInputs & ~Current.VecOutputs; + Current.IntOutputs |= Current.Block.IntOutputs; + Current.VecOutputs |= Current.Block.VecOutputs; + + //Check if this is a exit block + //(a block that returns or calls another sub). + if ((Current.Block.Next == null && + Current.Block.Branch == null) || Current.Block.HasStateStore) + { + if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath)) + { + IntPaths.Add(Current.Block, IntPath = new PathIo()); + } + + if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath)) + { + VecPaths.Add(Current.Block, VecPath = new PathIo()); + } + + IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs); + VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs); + } + + void EnqueueFromCurrent(AILBlock Block, bool RetTarget) + { + BlockIo BlkIO = new BlockIo() { Block = Block }; + + if (RetTarget) + { + BlkIO.Entry = Block; + BlkIO.IntInputs = 0; + BlkIO.VecInputs = 0; + BlkIO.IntOutputs = 0; + BlkIO.VecOutputs = 0; + } + else + { + BlkIO.Entry = Current.Entry; + BlkIO.IntInputs = Current.IntInputs; + BlkIO.VecInputs = Current.VecInputs; + BlkIO.IntOutputs = Current.IntOutputs; + BlkIO.VecOutputs = Current.VecOutputs; + } + + Enqueue(BlkIO); + } + + if (Current.Block.Next != null) + { + EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore); + } + + if (Current.Block.Branch != null) + { + EnqueueFromCurrent(Current.Block.Branch, false); + } + } + } + + private void InitializeFast(AILBlock[] Graph) + { + //This is WAY faster than InitializeOptimal, but results in + //uneeded loads and stores, so the resulting code will be slower. + long IntInputs = 0; + long IntOutputs = 0; + long VecInputs = 0; + long VecOutputs = 0; + + foreach (AILBlock Block in Graph) + { + IntInputs |= Block.IntInputs; + IntOutputs |= Block.IntOutputs; + VecInputs |= Block.VecInputs; + VecOutputs |= Block.VecOutputs; + } + + //It's possible that not all code paths writes to those output registers, + //in those cases if we attempt to write an output registers that was + //not written, we will be just writing zero and messing up the old register value. + //So we just need to ensure that all outputs are loaded. + IntInputs |= IntOutputs; + VecInputs |= VecOutputs; + + foreach (AILBlock Block in Graph) + { + IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs)); + VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs)); + } + } + + public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values); + public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values); + + private long GetInputsImpl(AILBlock Root, IEnumerable Values) + { + long Inputs = 0; + + foreach (PathIo Path in Values) + { + Inputs |= Path.GetInputs(Root); + } + + return Inputs; + } + + public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs(); + public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs(); + } +} \ No newline at end of file diff --git a/Translation/IAILEmit.cs b/Translation/IAILEmit.cs new file mode 100644 index 0000000..6e4e9a7 --- /dev/null +++ b/Translation/IAILEmit.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Translation +{ + interface IAILEmit + { + void Emit(AILEmitter Context); + } +} \ No newline at end of file diff --git a/Translation/ILGeneratorEx.cs b/Translation/ILGeneratorEx.cs new file mode 100644 index 0000000..abb35ec --- /dev/null +++ b/Translation/ILGeneratorEx.cs @@ -0,0 +1,129 @@ +using System; + +namespace ChocolArm64 +{ + using System.Reflection.Emit; + + static class ILGeneratorEx + { + public static void EmitLdc_I4(this ILGenerator Generator,int Value) + { + switch (Value) + { + case 0: Generator.Emit(OpCodes.Ldc_I4_0); break; + case 1: Generator.Emit(OpCodes.Ldc_I4_1); break; + case 2: Generator.Emit(OpCodes.Ldc_I4_2); break; + case 3: Generator.Emit(OpCodes.Ldc_I4_3); break; + case 4: Generator.Emit(OpCodes.Ldc_I4_4); break; + case 5: Generator.Emit(OpCodes.Ldc_I4_5); break; + case 6: Generator.Emit(OpCodes.Ldc_I4_6); break; + case 7: Generator.Emit(OpCodes.Ldc_I4_7); break; + case 8: Generator.Emit(OpCodes.Ldc_I4_8); break; + case -1: Generator.Emit(OpCodes.Ldc_I4_M1); break; + default: Generator.Emit(OpCodes.Ldc_I4, Value); break; + } + } + + public static void EmitLdarg(this ILGenerator Generator, int Index) + { + switch (Index) + { + case 0: Generator.Emit(OpCodes.Ldarg_0); break; + case 1: Generator.Emit(OpCodes.Ldarg_1); break; + case 2: Generator.Emit(OpCodes.Ldarg_2); break; + case 3: Generator.Emit(OpCodes.Ldarg_3); break; + + default: + if ((uint)Index <= byte.MaxValue) + { + Generator.Emit(OpCodes.Ldarg_S, (byte)Index); + } + else if ((uint)Index < ushort.MaxValue) + { + Generator.Emit(OpCodes.Ldarg, (short)Index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Index)); + } + break; + } + } + + public static void EmitStarg(this ILGenerator Generator, int Index) + { + if ((uint)Index <= byte.MaxValue) + { + Generator.Emit(OpCodes.Starg_S, (byte)Index); + } + else if ((uint)Index < ushort.MaxValue) + { + Generator.Emit(OpCodes.Starg, (short)Index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Index)); + } + } + + public static void EmitLdloc(this ILGenerator Generator, int Index) + { + switch (Index) + { + case 0: Generator.Emit(OpCodes.Ldloc_0); break; + case 1: Generator.Emit(OpCodes.Ldloc_1); break; + case 2: Generator.Emit(OpCodes.Ldloc_2); break; + case 3: Generator.Emit(OpCodes.Ldloc_3); break; + + default: + if ((uint)Index <= byte.MaxValue) + { + Generator.Emit(OpCodes.Ldloc_S, (byte)Index); + } + else if ((uint)Index < ushort.MaxValue) + { + Generator.Emit(OpCodes.Ldloc, (short)Index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Index)); + } + break; + } + } + + public static void EmitStloc(this ILGenerator Generator, int Index) + { + switch (Index) + { + case 0: Generator.Emit(OpCodes.Stloc_0); break; + case 1: Generator.Emit(OpCodes.Stloc_1); break; + case 2: Generator.Emit(OpCodes.Stloc_2); break; + case 3: Generator.Emit(OpCodes.Stloc_3); break; + + default: + if ((uint)Index <= byte.MaxValue) + { + Generator.Emit(OpCodes.Stloc_S, (byte)Index); + } + else if ((uint)Index < ushort.MaxValue) + { + Generator.Emit(OpCodes.Stloc, (short)Index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Index)); + } + break; + } + } + + public static void EmitLdargSeq(this ILGenerator Generator, int Count) + { + for (int Index = 0; Index < Count; Index++) + { + Generator.EmitLdarg(Index); + } + } + } +} \ No newline at end of file