Split main project into core,graphics and chocolarm4 subproject (#29)

This commit is contained in:
emmauss 2018-02-20 22:09:23 +02:00 committed by gdkchan
commit 7601c05662
121 changed files with 9976 additions and 0 deletions

57
ABitUtils.cs Normal file
View File

@ -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;
}
}
}

408
AOpCodeTable.cs Normal file
View File

@ -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<<x101xxxxxxxxxx", AInstEmit.Bic_Vi, typeof(AOpCodeSimdImm));
Set("0x101110011xxxxx000111xxxxxxxxxx", AInstEmit.Bsl_V, typeof(AOpCodeSimdReg));
Set("0>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<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElem));
Set("00011110xx100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
Set("x0011110xx100110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi, typeof(AOpCodeSimdCvt));
Set("x0011110xx100111000000xxxxxxxxxx", AInstEmit.Fmov_Itof, typeof(AOpCodeSimdCvt));
Set("x0011110xx101110000000xxxxxxxxxx", AInstEmit.Fmov_Ftoi1, typeof(AOpCodeSimdCvt));
Set("x0011110xx101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
Set("00011111xx0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
Set("00011110xx1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElem));
Set("00011110xx100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
Set("00011111xx1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
Set("00011110xx1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
Set("00011110xx100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
Set("00011110xx100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
Set("00011110xx100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
Set("00011110xx1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
Set("0>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<<x101xxxxxxxxxx", AInstEmit.Orr_Vi, typeof(AOpCodeSimdImm));
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd));
Set("010111110>>>>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;
}
}
}

4
AOptimizations.cs Normal file
View File

@ -0,0 +1,4 @@
public static class AOptimizations
{
}

72
AThread.cs Normal file
View File

@ -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;
}
}
}

104
ATranslatedSub.cs Normal file
View File

@ -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<long> SubCalls { get; private set; }
public List<ARegister> Params { get; private set; }
public bool NeedsReJit { get; private set; }
public ATranslatedSub()
{
SubCalls = new HashSet<long>();
}
public ATranslatedSub(DynamicMethod Method, List<ARegister> 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;
}
}

106
ATranslator.cs Normal file
View File

@ -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<long, ATranslatedSub> CachedSubs;
private bool KeepRunning;
public ATranslator(AThread Parent)
{
this.Thread = Parent;
CachedSubs = new Dictionary<long, ATranslatedSub>();
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;
}
}
}

15
ChocolArm64.csproj Normal file
View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

35
Decoder/ABlock.cs Normal file
View File

@ -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<AOpCode> OpCodes { get; private set; }
public ABlock()
{
OpCodes = new List<AOpCode>();
}
public ABlock(long Position) : this()
{
this.Position = Position;
}
public AOpCode GetLastOp()
{
if (OpCodes.Count > 0)
{
return OpCodes[OpCodes.Count - 1];
}
return null;
}
}
}

22
Decoder/ACond.cs Normal file
View File

@ -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
}
}

10
Decoder/ADataOp.cs Normal file
View File

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
enum ADataOp
{
Adr = 0,
Arithmetic = 1,
Logical = 2,
BitField = 3
}
}

207
Decoder/ADecoder.cs Normal file
View File

@ -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<long, ABlock> Visited = new Dictionary<long, ABlock>();
Dictionary<long, ABlock> VisitedEnd = new Dictionary<long, ABlock>();
Queue<ABlock> Blocks = new Queue<ABlock>();
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<Type, OpActivator> Activators = new Dictionary<Type, OpActivator>();
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);
}
}
}

107
Decoder/ADecoderHelper.cs Normal file
View File

@ -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;
}
}
}

14
Decoder/AIntType.cs Normal file
View File

@ -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
}
}

38
Decoder/AOpCode.cs Normal file
View File

@ -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();
}
}
}

18
Decoder/AOpCodeAdr.cs Normal file
View File

@ -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;
}
}
}

24
Decoder/AOpCodeAlu.cs Normal file
View File

@ -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;
}
}
}

39
Decoder/AOpCodeAluImm.cs Normal file
View File

@ -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));
}
}
}
}

29
Decoder/AOpCodeAluRs.cs Normal file
View File

@ -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);
}
}
}

19
Decoder/AOpCodeAluRx.cs Normal file
View File

@ -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;
}
}
}

11
Decoder/AOpCodeBImm.cs Normal file
View File

@ -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) { }
}
}

12
Decoder/AOpCodeBImmAl.cs Normal file
View File

@ -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);
}
}
}

16
Decoder/AOpCodeBImmCmp.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

24
Decoder/AOpCodeBReg.cs Normal file
View File

@ -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;
}
}
}

29
Decoder/AOpCodeBfm.cs Normal file
View File

@ -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;
}
}
}

31
Decoder/AOpCodeCcmp.cs Normal file
View File

@ -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;
}
}
}

11
Decoder/AOpCodeCcmpImm.cs Normal file
View File

@ -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) { }
}
}

15
Decoder/AOpCodeCcmpReg.cs Normal file
View File

@ -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) { }
}
}

17
Decoder/AOpCodeCsel.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

19
Decoder/AOpCodeMem.cs Normal file
View File

@ -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;
}
}
}

16
Decoder/AOpCodeMemEx.cs Normal file
View File

@ -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;
}
}
}

53
Decoder/AOpCodeMemImm.cs Normal file
View File

@ -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;
}
}
}
}

28
Decoder/AOpCodeMemLit.cs Normal file
View File

@ -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;
}
}
}
}

25
Decoder/AOpCodeMemPair.cs Normal file
View File

@ -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);
}
}
}

20
Decoder/AOpCodeMemReg.cs Normal file
View File

@ -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;
}
}
}

36
Decoder/AOpCodeMov.cs Normal file
View File

@ -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;
}
}
}

16
Decoder/AOpCodeMul.cs Normal file
View File

@ -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;
}
}
}

27
Decoder/AOpCodeSimd.cs Normal file
View File

@ -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;
}
}
}

31
Decoder/AOpCodeSimdCvt.cs Normal file
View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

94
Decoder/AOpCodeSimdImm.cs Normal file
View File

@ -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));
}
}
}

36
Decoder/AOpCodeSimdIns.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

18
Decoder/AOpCodeSimdReg.cs Normal file
View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

12
Decoder/AOpCodeSimdTbl.cs Normal file
View File

@ -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;
}
}
}

24
Decoder/AOpCodeSystem.cs Normal file
View File

@ -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;
}
}
}

10
Decoder/AShiftType.cs Normal file
View File

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
enum AShiftType
{
Lsl,
Lsr,
Asr,
Ror
}
}

13
Decoder/IAOpCode.cs Normal file
View File

@ -0,0 +1,13 @@
using ChocolArm64.Instruction;
using ChocolArm64.State;
namespace ChocolArm64.Decoder
{
interface IAOpCode
{
long Position { get; }
AInstEmitter Emitter { get; }
ARegisterSize RegisterSize { get; }
}
}

10
Decoder/IAOpCodeAlu.cs Normal file
View File

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAlu : IAOpCode
{
int Rd { get; }
int Rn { get; }
ADataOp DataOp { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAluImm : IAOpCodeAlu
{
long Imm { get; }
}
}

10
Decoder/IAOpCodeAluRs.cs Normal file
View File

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAluRs : IAOpCodeAlu
{
int Shift { get; }
int Rm { get; }
AShiftType ShiftType { get; }
}
}

10
Decoder/IAOpCodeAluRx.cs Normal file
View File

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeAluRx : IAOpCodeAlu
{
int Shift { get; }
int Rm { get; }
AIntType IntType { get; }
}
}

7
Decoder/IAOpCodeCond.cs Normal file
View File

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeCond : IAOpCode
{
ACond Cond { get; }
}
}

11
Decoder/IAOpCodeLit.cs Normal file
View File

@ -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; }
}
}

7
Decoder/IAOpCodeSimd.cs Normal file
View File

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoder
{
interface IAOpCodeSimd : IAOpCode
{
int Size { get; }
}
}

View File

@ -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)) { }
}
}

View File

@ -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)) { }
}
}

View File

@ -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)) { }
}
}

18
Instruction/AInst.cs Normal file
View File

@ -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;
}
}
}

364
Instruction/AInstEmitAlu.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

217
Instruction/AInstEmitBfm.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,6 @@
using ChocolArm64.Translation;
namespace ChocolArm64.Instruction
{
delegate void AInstEmitter(AILEmitterCtx Context);
}

View File

@ -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));
}
}
}

345
Memory/AMemory.cs Normal file
View File

@ -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<int, ExMonitor> Monitors;
private HashSet<long> ExAddrs;
private byte* RamPtr;
public AMemory(IntPtr Ram, AMemoryAlloc Allocator)
{
Manager = new AMemoryMgr(Allocator);
Monitors = new Dictionary<int, ExMonitor>();
ExAddrs = new HashSet<long>();
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);
}
}
}
}

35
Memory/AMemoryAlloc.cs Normal file
View File

@ -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;
}
}
}

73
Memory/AMemoryHelper.cs Normal file
View File

@ -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;
}
}
}

21
Memory/AMemoryMapInfo.cs Normal file
View File

@ -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;
}
}
}

286
Memory/AMemoryMgr.cs Normal file
View File

@ -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;
}
}
}

15
Memory/AMemoryPerm.cs Normal file
View File

@ -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
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

Some files were not shown because too many files have changed in this diff Show More