Merge branch 'profiling' into actually_profiling

This commit is contained in:
Andy Adshead 2019-02-02 13:15:30 +00:00
commit fad35bd2b5
91 changed files with 2727 additions and 769 deletions

View file

@ -25,8 +25,6 @@ namespace ChocolArm64
ThreadState = new CpuThreadState(); ThreadState = new CpuThreadState();
ThreadState.ExecutionMode = ExecutionMode.AArch64;
ThreadState.Running = true; ThreadState.Running = true;
Work = new Thread(delegate() Work = new Thread(delegate()

View file

@ -1,4 +1,4 @@
namespace ChocolArm64 namespace ChocolArm64.Decoders
{ {
static class BitUtils static class BitUtils
{ {
@ -36,6 +36,16 @@ namespace ChocolArm64
return bits == 64 ? -1L : (1L << bits) - 1; return bits == 64 ? -1L : (1L << bits) - 1;
} }
public static int RotateRight(int bits, int shift, int size)
{
return (int)RotateRight((uint)bits, shift, size);
}
public static uint RotateRight(uint bits, int shift, int size)
{
return (bits >> shift) | (bits << (size - shift));
}
public static long RotateRight(long bits, int shift, int size) public static long RotateRight(long bits, int shift, int size)
{ {
return (long)RotateRight((ulong)bits, shift, size); return (long)RotateRight((ulong)bits, shift, size);

View file

@ -1,6 +1,6 @@
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {
enum Cond enum Condition
{ {
Eq = 0, Eq = 0,
Ne = 1, Ne = 1,

View file

@ -19,20 +19,20 @@ namespace ChocolArm64.Decoders
_opActivators = new ConcurrentDictionary<Type, OpActivator>(); _opActivators = new ConcurrentDictionary<Type, OpActivator>();
} }
public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start) public static Block DecodeBasicBlock(MemoryManager memory, long start, ExecutionMode mode)
{ {
Block block = new Block(start); Block block = new Block(start);
FillBlock(state, memory, block); FillBlock(memory, mode, block);
return block; return block;
} }
public static Block DecodeSubroutine( public static Block DecodeSubroutine(
TranslatorCache cache, TranslatorCache cache,
CpuThreadState state,
MemoryManager memory, MemoryManager memory,
long start) long start,
ExecutionMode mode)
{ {
Dictionary<long, Block> visited = new Dictionary<long, Block>(); Dictionary<long, Block> visited = new Dictionary<long, Block>();
Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>(); Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>();
@ -59,7 +59,7 @@ namespace ChocolArm64.Decoders
{ {
Block current = blocks.Dequeue(); Block current = blocks.Dequeue();
FillBlock(state, memory, current); FillBlock(memory, mode, current);
//Set child blocks. "Branch" is the block the branch instruction //Set child blocks. "Branch" is the block the branch instruction
//points to (when taken), "Next" is the block at the next address, //points to (when taken), "Next" is the block at the next address,
@ -71,7 +71,7 @@ namespace ChocolArm64.Decoders
OpCode64 lastOp = current.GetLastOp(); OpCode64 lastOp = current.GetLastOp();
if (lastOp is OpCodeBImm64 op) if (lastOp is IOpCodeBImm op)
{ {
if (op.Emitter == InstEmit.Bl) if (op.Emitter == InstEmit.Bl)
{ {
@ -83,8 +83,7 @@ namespace ChocolArm64.Decoders
} }
} }
if (!((lastOp is OpCodeBImmAl64) || if (!IsUnconditionalBranch(lastOp) || hasCachedSub)
(lastOp is OpCodeBReg64)) || hasCachedSub)
{ {
current.Next = Enqueue(current.EndPosition); current.Next = Enqueue(current.EndPosition);
} }
@ -121,7 +120,7 @@ namespace ChocolArm64.Decoders
return entry; return entry;
} }
private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block) private static void FillBlock(MemoryManager memory, ExecutionMode mode, Block block)
{ {
long position = block.Position; long position = block.Position;
@ -129,13 +128,11 @@ namespace ChocolArm64.Decoders
do do
{ {
//TODO: This needs to be changed to support both AArch32 and AArch64, opCode = DecodeOpCode(memory, position, mode);
//once JIT support is introduced on AArch32 aswell.
opCode = DecodeOpCode(state, memory, position);
block.OpCodes.Add(opCode); block.OpCodes.Add(opCode);
position += 4; position += opCode.OpCodeSizeInBytes;
} }
while (!(IsBranch(opCode) || IsException(opCode))); while (!(IsBranch(opCode) || IsException(opCode)));
@ -145,7 +142,85 @@ namespace ChocolArm64.Decoders
private static bool IsBranch(OpCode64 opCode) private static bool IsBranch(OpCode64 opCode)
{ {
return opCode is OpCodeBImm64 || return opCode is OpCodeBImm64 ||
opCode is OpCodeBReg64; opCode is OpCodeBReg64 || IsAarch32Branch(opCode);
}
private static bool IsUnconditionalBranch(OpCode64 opCode)
{
return opCode is OpCodeBImmAl64 ||
opCode is OpCodeBReg64 || IsAarch32UnconditionalBranch(opCode);
}
private static bool IsAarch32UnconditionalBranch(OpCode64 opCode)
{
if (!(opCode is OpCode32 op))
{
return false;
}
//Note: On ARM32, most instructions have conditional execution,
//so there's no "Always" (unconditional) branch like on ARM64.
//We need to check if the condition is "Always" instead.
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
}
private static bool IsAarch32Branch(OpCode64 opCode)
{
//Note: On ARM32, most ALU operations can write to R15 (PC),
//so we must consider such operations as a branch in potential aswell.
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
{
return true;
}
//Same thing for memory operations. We have the cases where PC is a target
//register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
//a write back to PC (wback == true && Rn == 15), however the later may
//be "undefined" depending on the CPU, so compilers should not produce that.
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
{
int rt, rn;
bool wBack, isLoad;
if (opCode is IOpCode32Mem opMem)
{
rt = opMem.Rt;
rn = opMem.Rn;
wBack = opMem.WBack;
isLoad = opMem.IsLoad;
//For the dual load, we also need to take into account the
//case were Rt2 == 15 (PC).
if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd)
{
rt = RegisterAlias.Aarch32Pc;
}
}
else if (opCode is IOpCode32MemMult opMemMult)
{
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
rn = opMemMult.Rn;
wBack = opMemMult.PostOffset != 0;
isLoad = opMemMult.IsLoad;
}
else
{
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
}
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
(rn == RegisterAlias.Aarch32Pc && wBack))
{
return true;
}
}
//Explicit branch instructions.
return opCode is IOpCode32BImm ||
opCode is IOpCode32BReg;
} }
private static bool IsException(OpCode64 opCode) private static bool IsException(OpCode64 opCode)
@ -155,21 +230,27 @@ namespace ChocolArm64.Decoders
opCode.Emitter == InstEmit.Und; opCode.Emitter == InstEmit.Und;
} }
public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position) public static OpCode64 DecodeOpCode(MemoryManager memory, long position, ExecutionMode mode)
{ {
int opCode = memory.ReadInt32(position); int opCode = memory.ReadInt32(position);
Inst inst; Inst inst;
if (state.ExecutionMode == ExecutionMode.AArch64) if (mode == ExecutionMode.Aarch64)
{ {
inst = OpCodeTable.GetInstA64(opCode); inst = OpCodeTable.GetInstA64(opCode);
} }
else else
{ {
//TODO: Thumb support. if (mode == ExecutionMode.Aarch32Arm)
{
inst = OpCodeTable.GetInstA32(opCode); inst = OpCodeTable.GetInstA32(opCode);
} }
else /* if (mode == ExecutionMode.Aarch32Thumb) */
{
inst = OpCodeTable.GetInstT32(opCode);
}
}
OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode); OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode);

View file

@ -89,6 +89,11 @@ namespace ChocolArm64.Decoders
return value; return value;
} }
public static long DecodeImm24_2(int opCode)
{
return ((long)opCode << 40) >> 38;
}
public static long DecodeImm26_2(int opCode) public static long DecodeImm26_2(int opCode)
{ {
return ((long)opCode << 38) >> 36; return ((long)opCode << 38) >> 36;

View file

@ -0,0 +1,9 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32 : IOpCode64
{
Condition Cond { get; }
uint GetPc();
}
}

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32Alu : IOpCode32
{
int Rd { get; }
int Rn { get; }
bool SetFlags { get; }
}
}

View file

@ -0,0 +1,4 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32BImm : IOpCode32, IOpCodeBImm { }
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32BReg : IOpCode32
{
int Rm { get; }
}
}

View file

@ -0,0 +1,12 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32Mem : IOpCode32
{
int Rt { get; }
int Rn { get; }
bool WBack { get; }
bool IsLoad { get; }
}
}

View file

@ -0,0 +1,13 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32MemMult : IOpCode32
{
int Rn { get; }
int RegisterMask { get; }
int PostOffset { get; }
bool IsLoad { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoders
{
interface IOpCodeBImm : IOpCode64
{
long Imm { get; }
}
}

View file

@ -2,6 +2,6 @@ namespace ChocolArm64.Decoders
{ {
interface IOpCodeCond64 : IOpCode64 interface IOpCodeCond64 : IOpCode64
{ {
Cond Cond { get; } Condition Cond { get; }
} }
} }

View file

@ -0,0 +1,24 @@
using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders
{
class OpCode32 : OpCode64
{
public Condition Cond { get; protected set; }
public OpCode32(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
RegisterSize = RegisterSize.Int32;
Cond = (Condition)((uint)opCode >> 28);
}
public uint GetPc()
{
//Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
//the PC actually points 2 instructions ahead.
return (uint)Position + (uint)OpCodeSizeInBytes * 2;
}
}
}

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32Alu : OpCode32, IOpCode32Alu
{
public int Rd { get; private set; }
public int Rn { get; private set; }
public bool SetFlags { get; private set; }
public OpCode32Alu(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rd = (opCode >> 12) & 0xf;
Rn = (opCode >> 16) & 0xf;
SetFlags = ((opCode >> 20) & 1) != 0;
}
}
}

View file

@ -0,0 +1,21 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32AluImm : OpCode32Alu
{
public int Imm { get; private set; }
public bool IsRotated { get; private set; }
public OpCode32AluImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
int value = (opCode >> 0) & 0xff;
int shift = (opCode >> 8) & 0xf;
Imm = BitUtils.RotateRight(value, shift * 2, 32);
IsRotated = shift != 0;
}
}
}

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32AluRsImm : OpCode32Alu
{
public int Rm { get; private set; }
public int Imm { get; private set; }
public ShiftType ShiftType { get; private set; }
public OpCode32AluRsImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = (opCode >> 0) & 0xf;
Imm = (opCode >> 7) & 0x1f;
ShiftType = (ShiftType)((opCode >> 5) & 3);
}
}
}

View file

@ -0,0 +1,29 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32BImm : OpCode32, IOpCode32BImm
{
public long Imm { get; private set; }
public OpCode32BImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
uint pc = GetPc();
//When the codition is never, the instruction is BLX to Thumb mode.
if (Cond != Condition.Nv)
{
pc &= ~3u;
}
Imm = pc + DecoderHelper.DecodeImm24_2(opCode);
if (Cond == Condition.Nv)
{
long H = (opCode >> 23) & 2;
Imm |= H;
}
}
}
}

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32BReg : OpCode32, IOpCode32BReg
{
public int Rm { get; private set; }
public OpCode32BReg(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = opCode & 0xf;
}
}
}

View file

@ -0,0 +1,37 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32Mem : OpCode32, IOpCode32Mem
{
public int Rt { get; private set; }
public int Rn { get; private set; }
public int Imm { get; protected set; }
public bool Index { get; private set; }
public bool Add { get; private set; }
public bool WBack { get; private set; }
public bool Unprivileged { get; private set; }
public bool IsLoad { get; private set; }
public OpCode32Mem(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rt = (opCode >> 12) & 0xf;
Rn = (opCode >> 16) & 0xf;
bool isLoad = (opCode & (1 << 20)) != 0;
bool w = (opCode & (1 << 21)) != 0;
bool u = (opCode & (1 << 23)) != 0;
bool p = (opCode & (1 << 24)) != 0;
Index = p;
Add = u;
WBack = !p || w;
Unprivileged = !p && w;
IsLoad = isLoad || inst.Emitter == InstEmit32.Ldrd;
}
}
}

View file

@ -0,0 +1,12 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32MemImm : OpCode32Mem
{
public OpCode32MemImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = opCode & 0xfff;
}
}
}

View file

@ -0,0 +1,15 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32MemImm8 : OpCode32Mem
{
public OpCode32MemImm8(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
int imm4L = (opCode >> 0) & 0xf;
int imm4H = (opCode >> 8) & 0xf;
Imm = imm4L | (imm4H << 4);
}
}
}

View file

@ -0,0 +1,57 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32MemMult : OpCode32, IOpCode32MemMult
{
public int Rn { get; private set; }
public int RegisterMask { get; private set; }
public int Offset { get; private set; }
public int PostOffset { get; private set; }
public bool IsLoad { get; private set; }
public OpCode32MemMult(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rn = (opCode >> 16) & 0xf;
bool isLoad = (opCode & (1 << 20)) != 0;
bool w = (opCode & (1 << 21)) != 0;
bool u = (opCode & (1 << 23)) != 0;
bool p = (opCode & (1 << 24)) != 0;
RegisterMask = opCode & 0xffff;
int regsSize = 0;
for (int index = 0; index < 16; index++)
{
regsSize += (RegisterMask >> index) & 1;
}
regsSize *= 4;
if (!u)
{
Offset -= regsSize;
}
if (u == p)
{
Offset += 4;
}
if (w)
{
PostOffset = u ? regsSize : -regsSize;
}
else
{
PostOffset = 0;
}
IsLoad = isLoad;
}
}
}

View file

@ -9,8 +9,9 @@ namespace ChocolArm64.Decoders
public long Position { get; private set; } public long Position { get; private set; }
public int RawOpCode { get; private set; } public int RawOpCode { get; private set; }
public int OpCodeSizeInBytes { get; protected set; } = 4;
public InstEmitter Emitter { get; protected set; } public InstEmitter Emitter { get; protected set; }
public InstInterpreter Interpreter { get; protected set; }
public RegisterSize RegisterSize { get; protected set; } public RegisterSize RegisterSize { get; protected set; }
public OpCode64(Inst inst, long position, int opCode) public OpCode64(Inst inst, long position, int opCode)
@ -21,7 +22,6 @@ namespace ChocolArm64.Decoders
RegisterSize = RegisterSize.Int64; RegisterSize = RegisterSize.Int64;
Emitter = inst.Emitter; Emitter = inst.Emitter;
Interpreter = inst.Interpreter;
} }
public int GetBitsCount() public int GetBitsCount()

View file

@ -2,7 +2,7 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {
class OpCodeBImm64 : OpCode64 class OpCodeBImm64 : OpCode64, IOpCodeBImm
{ {
public long Imm { get; protected set; } public long Imm { get; protected set; }

View file

@ -4,7 +4,7 @@ namespace ChocolArm64.Decoders
{ {
class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64 class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64
{ {
public Cond Cond { get; private set; } public Condition Cond { get; private set; }
public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode) public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{ {
@ -17,7 +17,7 @@ namespace ChocolArm64.Decoders
return; return;
} }
Cond = (Cond)(opCode & 0xf); Cond = (Condition)(opCode & 0xf);
Imm = position + DecoderHelper.DecodeImmS19_2(opCode); Imm = position + DecoderHelper.DecodeImmS19_2(opCode);
} }

View file

@ -8,7 +8,7 @@ namespace ChocolArm64.Decoders
public int Nzcv { get; private set; } public int Nzcv { get; private set; }
protected int RmImm; protected int RmImm;
public Cond Cond { get; private set; } public Condition Cond { get; private set; }
public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode) public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{ {
@ -22,10 +22,10 @@ namespace ChocolArm64.Decoders
} }
Nzcv = (opCode >> 0) & 0xf; Nzcv = (opCode >> 0) & 0xf;
Cond = (Cond)((opCode >> 12) & 0xf); Cond = (Condition)((opCode >> 12) & 0xf);
RmImm = (opCode >> 16) & 0x1f; RmImm = (opCode >> 16) & 0x1f;
Rd = CpuThreadState.ZrIndex; Rd = RegisterAlias.Zr;
} }
} }
} }

View file

@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
{ {
public int Rm { get; private set; } public int Rm { get; private set; }
public Cond Cond { get; private set; } public Condition Cond { get; private set; }
public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode) public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{ {
Rm = (opCode >> 16) & 0x1f; Rm = (opCode >> 16) & 0x1f;
Cond = (Cond)((opCode >> 12) & 0xf); Cond = (Condition)((opCode >> 12) & 0xf);
} }
} }
} }

View file

@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
{ {
public int Nzcv { get; private set; } public int Nzcv { get; private set; }
public Cond Cond { get; private set; } public Condition Cond { get; private set; }
public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode) public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{ {
Nzcv = (opCode >> 0) & 0xf; Nzcv = (opCode >> 0) & 0xf;
Cond = (Cond)((opCode >> 12) & 0xf); Cond = (Condition)((opCode >> 12) & 0xf);
} }
} }
} }

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16 : OpCode32
{
public OpCodeT16(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Cond = Condition.Al;
OpCodeSizeInBytes = 2;
}
}
}

View file

@ -0,0 +1,22 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu
{
private int _rdn;
public int Rd => _rdn;
public int Rn => _rdn;
public bool SetFlags => false;
public int Imm { get; private set; }
public OpCodeT16AluImm8(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7;
}
}
}

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16BReg : OpCodeT16, IOpCode32BReg
{
public int Rm { get; private set; }
public OpCodeT16BReg(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = (opCode >> 3) & 0xf;
}
}
}

View file

@ -1,15 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders32
{
class A32OpCode : OpCode64
{
public Cond Cond { get; private set; }
public A32OpCode(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Cond = (Cond)((uint)opCode >> 28);
}
}
}

View file

@ -1,16 +0,0 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders32
{
class A32OpCodeBImmAl : A32OpCode
{
public int Imm;
public int H;
public A32OpCodeBImmAl(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode << 8) >> 6;
H = (opCode >> 23) & 2;
}
}
}

View file

@ -9,7 +9,7 @@ namespace ChocolArm64.Instructions
static class CryptoHelper static class CryptoHelper
{ {
#region "LookUp Tables" #region "LookUp Tables"
private static byte[] _sBox = private static readonly byte[] _sBox = new byte[]
{ {
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
@ -29,7 +29,7 @@ namespace ChocolArm64.Instructions
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
}; };
private static byte[] _invSBox = private static readonly byte[] _invSBox = new byte[]
{ {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
@ -49,7 +49,7 @@ namespace ChocolArm64.Instructions
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
}; };
private static byte[] _gfMul02 = private static readonly byte[] _gfMul02 = new byte[]
{ {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
@ -69,7 +69,7 @@ namespace ChocolArm64.Instructions
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
}; };
private static byte[] _gfMul03 = private static readonly byte[] _gfMul03 = new byte[]
{ {
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
@ -89,7 +89,7 @@ namespace ChocolArm64.Instructions
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
}; };
private static byte[] _gfMul09 = private static readonly byte[] _gfMul09 = new byte[]
{ {
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
@ -109,7 +109,7 @@ namespace ChocolArm64.Instructions
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
}; };
private static byte[] _gfMul0B = private static readonly byte[] _gfMul0B = new byte[]
{ {
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
@ -129,7 +129,7 @@ namespace ChocolArm64.Instructions
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
}; };
private static byte[] _gfMul0D = private static readonly byte[] _gfMul0D = new byte[]
{ {
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
@ -149,7 +149,7 @@ namespace ChocolArm64.Instructions
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
}; };
private static byte[] _gfMul0E = private static readonly byte[] _gfMul0E = new byte[]
{ {
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
@ -169,9 +169,15 @@ namespace ChocolArm64.Instructions
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
}; };
private static byte[] _srPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }; private static readonly byte[] _srPerm = new byte[]
{
0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3
};
private static byte[] _isrPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }; private static readonly byte[] _isrPerm = new byte[]
{
0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11
};
#endregion #endregion
public static Vector128<float> AesInvMixColumns(Vector128<float> op) public static Vector128<float> AesInvMixColumns(Vector128<float> op)
@ -179,7 +185,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16]; byte[] inState = new byte[16];
byte[] outState = new byte[16]; byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op); FromVectorToByteArray(op, inState);
for (int columns = 0; columns <= 3; columns++) for (int columns = 0; columns <= 3; columns++)
{ {
@ -206,7 +212,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16]; byte[] inState = new byte[16];
byte[] outState = new byte[16]; byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op); FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++) for (int idx = 0; idx <= 15; idx++)
{ {
@ -223,7 +229,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16]; byte[] inState = new byte[16];
byte[] outState = new byte[16]; byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op); FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++) for (int idx = 0; idx <= 15; idx++)
{ {
@ -240,7 +246,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16]; byte[] inState = new byte[16];
byte[] outState = new byte[16]; byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op); FromVectorToByteArray(op, inState);
for (int columns = 0; columns <= 3; columns++) for (int columns = 0; columns <= 3; columns++)
{ {
@ -267,7 +273,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16]; byte[] inState = new byte[16];
byte[] outState = new byte[16]; byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op); FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++) for (int idx = 0; idx <= 15; idx++)
{ {
@ -284,7 +290,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16]; byte[] inState = new byte[16];
byte[] outState = new byte[16]; byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op); FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++) for (int idx = 0; idx <= 15; idx++)
{ {
@ -296,33 +302,30 @@ namespace ChocolArm64.Instructions
return op; return op;
} }
private static void FromVectorToByteArray(byte[] state, ref Vector128<float> op) private unsafe static void FromVectorToByteArray(Vector128<float> op, byte[] state)
{
ulong uLongLow = VectorHelper.VectorExtractIntZx((op), (byte)0, 3);
ulong uLongHigh = VectorHelper.VectorExtractIntZx((op), (byte)1, 3);
for (int idx = 0; idx <= 7; idx++)
{
state[idx + 0] = (byte)(uLongLow & 0xFFUL);
state[idx + 8] = (byte)(uLongHigh & 0xFFUL);
uLongLow >>= 8;
uLongHigh >>= 8;
}
}
private static void FromByteArrayToVector(byte[] state, ref Vector128<float> op)
{ {
if (!Sse2.IsSupported) if (!Sse2.IsSupported)
{ {
throw new PlatformNotSupportedException(); throw new PlatformNotSupportedException();
} }
op = Sse.StaticCast<byte, float>(Sse2.SetVector128( fixed (byte* ptr = &state[0])
state[15], state[14], state[13], state[12], {
state[11], state[10], state[9], state[8], Sse2.Store(ptr, Sse.StaticCast<float, byte>(op));
state[7], state[6], state[5], state[4], }
state[3], state[2], state[1], state[0])); }
private unsafe static void FromByteArrayToVector(byte[] state, ref Vector128<float> op)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
fixed (byte* ptr = &state[0])
{
op = Sse.StaticCast<byte, float>(Sse2.LoadVector128(ptr));
}
} }
} }
} }

View file

@ -4,15 +4,13 @@ namespace ChocolArm64.Instructions
{ {
struct Inst struct Inst
{ {
public InstInterpreter Interpreter { get; private set; } public InstEmitter Emitter { get; }
public InstEmitter Emitter { get; private set; } public Type Type { get; }
public Type Type { get; private set; }
public static Inst Undefined => new Inst(null, InstEmit.Und, null); public static Inst Undefined => new Inst(InstEmit.Und, null);
public Inst(InstInterpreter interpreter, InstEmitter emitter, Type type) public Inst(InstEmitter emitter, Type type)
{ {
Interpreter = interpreter;
Emitter = emitter; Emitter = emitter;
Type = type; Type = type;
} }

View file

@ -0,0 +1,145 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
namespace ChocolArm64.Instructions
{
static class InstEmit32Helper
{
public static bool IsThumb(OpCode64 op)
{
return op is OpCodeT16;
}
public static void EmitLoadFromRegister(ILEmitterCtx context, int register)
{
if (register == RegisterAlias.Aarch32Pc)
{
OpCode32 op = (OpCode32)context.CurrOp;
context.EmitLdc_I4((int)op.GetPc());
}
else
{
context.EmitLdint(InstEmit32Helper.GetRegisterAlias(context.Mode, register));
}
}
public static void EmitStoreToRegister(ILEmitterCtx context, int register)
{
if (register == RegisterAlias.Aarch32Pc)
{
context.EmitStoreState();
EmitBxWritePc(context);
}
else
{
context.EmitStint(GetRegisterAlias(context.Mode, register));
}
}
public static void EmitBxWritePc(ILEmitterCtx context)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Dup);
context.EmitStflg((int)PState.TBit);
ILLabel lblArmMode = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblArmMode);
context.EmitLdc_I4(~1);
context.Emit(OpCodes.Br_S, lblEnd);
context.MarkLabel(lblArmMode);
context.EmitLdc_I4(~3);
context.MarkLabel(lblEnd);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
public static int GetRegisterAlias(Aarch32Mode mode, int register)
{
//Only registers >= 8 are banked, with registers in the range [8, 12] being
//banked for the FIQ mode, and registers 13 and 14 being banked for all modes.
if ((uint)register < 8)
{
return register;
}
return GetBankedRegisterAlias(mode, register);
}
public static int GetBankedRegisterAlias(Aarch32Mode mode, int register)
{
switch (register)
{
case 8: return mode == Aarch32Mode.Fiq
? RegisterAlias.R8Fiq
: RegisterAlias.R8Usr;
case 9: return mode == Aarch32Mode.Fiq
? RegisterAlias.R9Fiq
: RegisterAlias.R9Usr;
case 10: return mode == Aarch32Mode.Fiq
? RegisterAlias.R10Fiq
: RegisterAlias.R10Usr;
case 11: return mode == Aarch32Mode.Fiq
? RegisterAlias.R11Fiq
: RegisterAlias.R11Usr;
case 12: return mode == Aarch32Mode.Fiq
? RegisterAlias.R12Fiq
: RegisterAlias.R12Usr;
case 13:
switch (mode)
{
case Aarch32Mode.User:
case Aarch32Mode.System: return RegisterAlias.SpUsr;
case Aarch32Mode.Fiq: return RegisterAlias.SpFiq;
case Aarch32Mode.Irq: return RegisterAlias.SpIrq;
case Aarch32Mode.Supervisor: return RegisterAlias.SpSvc;
case Aarch32Mode.Abort: return RegisterAlias.SpAbt;
case Aarch32Mode.Hypervisor: return RegisterAlias.SpHyp;
case Aarch32Mode.Undefined: return RegisterAlias.SpUnd;
default: throw new ArgumentException(nameof(mode));
}
case 14:
switch (mode)
{
case Aarch32Mode.User:
case Aarch32Mode.Hypervisor:
case Aarch32Mode.System: return RegisterAlias.LrUsr;
case Aarch32Mode.Fiq: return RegisterAlias.LrFiq;
case Aarch32Mode.Irq: return RegisterAlias.LrIrq;
case Aarch32Mode.Supervisor: return RegisterAlias.LrSvc;
case Aarch32Mode.Abort: return RegisterAlias.LrAbt;
case Aarch32Mode.Undefined: return RegisterAlias.LrUnd;
default: throw new ArgumentException(nameof(mode));
}
default: throw new ArgumentOutOfRangeException(nameof(register));
}
}
}
}

View file

@ -17,7 +17,7 @@ namespace ChocolArm64.Instructions
private static void EmitAdc(ILEmitterCtx context, bool setFlags) private static void EmitAdc(ILEmitterCtx context, bool setFlags)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Add); context.Emit(OpCodes.Add);
@ -44,14 +44,14 @@ namespace ChocolArm64.Instructions
EmitAddsVCheck(context); EmitAddsVCheck(context);
} }
EmitDataStore(context); EmitAluStore(context);
} }
public static void Add(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Add); public static void Add(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Add);
public static void Adds(ILEmitterCtx context) public static void Adds(ILEmitterCtx context)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Add); context.Emit(OpCodes.Add);
@ -59,14 +59,14 @@ namespace ChocolArm64.Instructions
EmitAddsCCheck(context); EmitAddsCCheck(context);
EmitAddsVCheck(context); EmitAddsVCheck(context);
EmitDataStoreS(context); EmitAluStoreS(context);
} }
public static void And(ILEmitterCtx context) => EmitDataOp(context, OpCodes.And); public static void And(ILEmitterCtx context) => EmitAluOp(context, OpCodes.And);
public static void Ands(ILEmitterCtx context) public static void Ands(ILEmitterCtx context)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.And); context.Emit(OpCodes.And);
@ -74,17 +74,17 @@ namespace ChocolArm64.Instructions
context.EmitZnFlagCheck(); context.EmitZnFlagCheck();
EmitDataStoreS(context); EmitAluStoreS(context);
} }
public static void Asrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr); public static void Asrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr);
public static void Bic(ILEmitterCtx context) => EmitBic(context, false); public static void Bic(ILEmitterCtx context) => EmitBic(context, false);
public static void Bics(ILEmitterCtx context) => EmitBic(context, true); public static void Bics(ILEmitterCtx context) => EmitBic(context, true);
private static void EmitBic(ILEmitterCtx context, bool setFlags) private static void EmitBic(ILEmitterCtx context, bool setFlags)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Not); context.Emit(OpCodes.Not);
context.Emit(OpCodes.And); context.Emit(OpCodes.And);
@ -96,7 +96,7 @@ namespace ChocolArm64.Instructions
context.EmitZnFlagCheck(); context.EmitZnFlagCheck();
} }
EmitDataStore(context, setFlags); EmitAluStore(context, setFlags);
} }
public static void Cls(ILEmitterCtx context) public static void Cls(ILEmitterCtx context)
@ -136,15 +136,15 @@ namespace ChocolArm64.Instructions
public static void Eon(ILEmitterCtx context) public static void Eon(ILEmitterCtx context)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Not); context.Emit(OpCodes.Not);
context.Emit(OpCodes.Xor); context.Emit(OpCodes.Xor);
EmitDataStore(context); EmitAluStore(context);
} }
public static void Eor(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Xor); public static void Eor(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Xor);
public static void Extr(ILEmitterCtx context) public static void Extr(ILEmitterCtx context)
{ {
@ -166,18 +166,18 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.Or); context.Emit(OpCodes.Or);
} }
EmitDataStore(context); EmitAluStore(context);
} }
public static void Lslv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl); public static void Lslv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shl);
public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un); public static void Lsrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr_Un);
public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false); public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false);
public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true); public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true);
private static void EmitSbc(ILEmitterCtx context, bool setFlags) private static void EmitSbc(ILEmitterCtx context, bool setFlags)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Sub); context.Emit(OpCodes.Sub);
@ -208,16 +208,16 @@ namespace ChocolArm64.Instructions
EmitSubsVCheck(context); EmitSubsVCheck(context);
} }
EmitDataStore(context); EmitAluStore(context);
} }
public static void Sub(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Sub); public static void Sub(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Sub);
public static void Subs(ILEmitterCtx context) public static void Subs(ILEmitterCtx context)
{ {
context.TryOptMarkCondWithoutCmp(); context.TryOptMarkCondWithoutCmp();
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Sub); context.Emit(OpCodes.Sub);
@ -225,20 +225,20 @@ namespace ChocolArm64.Instructions
EmitSubsCCheck(context); EmitSubsCCheck(context);
EmitSubsVCheck(context); EmitSubsVCheck(context);
EmitDataStoreS(context); EmitAluStoreS(context);
} }
public static void Orn(ILEmitterCtx context) public static void Orn(ILEmitterCtx context)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Not); context.Emit(OpCodes.Not);
context.Emit(OpCodes.Or); context.Emit(OpCodes.Or);
EmitDataStore(context); EmitAluStore(context);
} }
public static void Orr(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Or); public static void Orr(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Or);
public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context, public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context,
nameof(SoftFallback.ReverseBits32), nameof(SoftFallback.ReverseBits32),
@ -283,22 +283,22 @@ namespace ChocolArm64.Instructions
public static void Rorv(ILEmitterCtx context) public static void Rorv(ILEmitterCtx context)
{ {
EmitDataLoadRn(context); EmitAluLoadRn(context);
EmitDataLoadShift(context); EmitAluLoadShift(context);
context.Emit(OpCodes.Shr_Un); context.Emit(OpCodes.Shr_Un);
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.EmitLdc_I4(context.CurrOp.GetBitsCount()); context.EmitLdc_I4(context.CurrOp.GetBitsCount());
EmitDataLoadShift(context); EmitAluLoadShift(context);
context.Emit(OpCodes.Sub); context.Emit(OpCodes.Sub);
context.Emit(OpCodes.Shl); context.Emit(OpCodes.Shl);
context.Emit(OpCodes.Or); context.Emit(OpCodes.Or);
EmitDataStore(context); EmitAluStore(context);
} }
public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div); public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div);
@ -309,7 +309,7 @@ namespace ChocolArm64.Instructions
//If Rm == 0, Rd = 0 (division by zero). //If Rm == 0, Rd = 0 (division by zero).
context.EmitLdc_I(0); context.EmitLdc_I(0);
EmitDataLoadRm(context); EmitAluLoadRm(context);
context.EmitLdc_I(0); context.EmitLdc_I(0);
@ -325,13 +325,13 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I(intMin); context.EmitLdc_I(intMin);
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.EmitLdc_I(intMin); context.EmitLdc_I(intMin);
context.Emit(OpCodes.Ceq); context.Emit(OpCodes.Ceq);
EmitDataLoadRm(context); EmitAluLoadRm(context);
context.EmitLdc_I(-1); context.EmitLdc_I(-1);
@ -341,38 +341,38 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.Pop); context.Emit(OpCodes.Pop);
} }
EmitDataLoadRn(context); EmitAluLoadRn(context);
EmitDataLoadRm(context); EmitAluLoadRm(context);
context.Emit(ilOp); context.Emit(ilOp);
context.MarkLabel(badDiv); context.MarkLabel(badDiv);
EmitDataStore(context); EmitAluStore(context);
} }
private static void EmitDataOp(ILEmitterCtx context, OpCode ilOp) private static void EmitAluOp(ILEmitterCtx context, OpCode ilOp)
{ {
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(ilOp); context.Emit(ilOp);
EmitDataStore(context); EmitAluStore(context);
} }
private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp) private static void EmitAluOpShift(ILEmitterCtx context, OpCode ilOp)
{ {
EmitDataLoadRn(context); EmitAluLoadRn(context);
EmitDataLoadShift(context); EmitAluLoadShift(context);
context.Emit(ilOp); context.Emit(ilOp);
EmitDataStore(context); EmitAluStore(context);
} }
private static void EmitDataLoadShift(ILEmitterCtx context) private static void EmitAluLoadShift(ILEmitterCtx context)
{ {
EmitDataLoadRm(context); EmitAluLoadRm(context);
context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1); context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1);
@ -398,5 +398,22 @@ namespace ChocolArm64.Instructions
context.EmitStflg((int)PState.CBit); context.EmitStflg((int)PState.CBit);
} }
public static void EmitAluStore(ILEmitterCtx context) => EmitAluStore(context, false);
public static void EmitAluStoreS(ILEmitterCtx context) => EmitAluStore(context, true);
public static void EmitAluStore(ILEmitterCtx context, bool setFlags)
{
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (setFlags || op is IOpCodeAluRs64)
{
context.EmitStintzr(op.Rd);
}
else
{
context.EmitStint(op.Rd);
}
}
} }
} }

View file

@ -0,0 +1,141 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmit32Helper;
using static ChocolArm64.Instructions.InstEmitAluHelper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit32
{
public static void Add(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Add);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
EmitAddsCCheck(context);
EmitAddsVCheck(context);
}
EmitAluStore(context);
}
public static void Cmp(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Sub);
context.EmitZnFlagCheck();
EmitSubsCCheck(context);
EmitSubsVCheck(context);
context.Emit(OpCodes.Pop);
}
public static void Mov(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOper2(context);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
}
EmitAluStore(context);
}
public static void Sub(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Sub);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
EmitSubsCCheck(context);
EmitSubsVCheck(context);
}
EmitAluStore(context);
}
private static void EmitAluStore(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
if (op.Rd == RegisterAlias.Aarch32Pc)
{
if (op.SetFlags)
{
//TODO: Load SPSR etc.
context.EmitLdflg((int)PState.TBit);
ILLabel lblThumb = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblThumb);
context.EmitLdc_I4(~3);
context.Emit(OpCodes.Br_S, lblEnd);
context.MarkLabel(lblThumb);
context.EmitLdc_I4(~1);
context.MarkLabel(lblEnd);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
else
{
EmitAluWritePc(context);
}
}
else
{
context.EmitStint(GetRegisterAlias(context.Mode, op.Rd));
}
}
private static void EmitAluWritePc(ILEmitterCtx context)
{
context.EmitStoreState();
if (IsThumb(context.CurrOp))
{
context.EmitLdc_I4(~1);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
else
{
EmitBxWritePc(context);
}
}
}
}

View file

@ -1,6 +1,7 @@
using ChocolArm64.Decoders; using ChocolArm64.Decoders;
using ChocolArm64.State; using ChocolArm64.State;
using ChocolArm64.Translation; using ChocolArm64.Translation;
using System;
using System.Reflection.Emit; using System.Reflection.Emit;
namespace ChocolArm64.Instructions namespace ChocolArm64.Instructions
@ -14,7 +15,7 @@ namespace ChocolArm64.Instructions
context.EmitLdtmp(); context.EmitLdtmp();
context.EmitLdtmp(); context.EmitLdtmp();
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.Emit(OpCodes.Ceq); context.Emit(OpCodes.Ceq);
@ -24,7 +25,7 @@ namespace ChocolArm64.Instructions
context.EmitLdtmp(); context.EmitLdtmp();
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Clt_Un);
context.Emit(OpCodes.Or); context.Emit(OpCodes.Or);
@ -37,7 +38,7 @@ namespace ChocolArm64.Instructions
//C = Rd < Rn //C = Rd < Rn
context.Emit(OpCodes.Dup); context.Emit(OpCodes.Dup);
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Clt_Un);
@ -49,11 +50,11 @@ namespace ChocolArm64.Instructions
//V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
context.Emit(OpCodes.Dup); context.Emit(OpCodes.Dup);
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.Emit(OpCodes.Xor); context.Emit(OpCodes.Xor);
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Xor); context.Emit(OpCodes.Xor);
context.Emit(OpCodes.Not); context.Emit(OpCodes.Not);
@ -69,7 +70,7 @@ namespace ChocolArm64.Instructions
public static void EmitSbcsCCheck(ILEmitterCtx context) public static void EmitSbcsCCheck(ILEmitterCtx context)
{ {
//C = (Rn == Rm && CIn) || Rn > Rm //C = (Rn == Rm && CIn) || Rn > Rm
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Ceq); context.Emit(OpCodes.Ceq);
@ -77,7 +78,7 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.And); context.Emit(OpCodes.And);
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Cgt_Un); context.Emit(OpCodes.Cgt_Un);
context.Emit(OpCodes.Or); context.Emit(OpCodes.Or);
@ -88,7 +89,7 @@ namespace ChocolArm64.Instructions
public static void EmitSubsCCheck(ILEmitterCtx context) public static void EmitSubsCCheck(ILEmitterCtx context)
{ {
//C = Rn == Rm || Rn > Rm = !(Rn < Rm) //C = Rn == Rm || Rn > Rm = !(Rn < Rm)
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Clt_Un);
@ -104,11 +105,11 @@ namespace ChocolArm64.Instructions
//V = (Rd ^ Rn) & (Rn ^ Rm) < 0 //V = (Rd ^ Rn) & (Rn ^ Rm) < 0
context.Emit(OpCodes.Dup); context.Emit(OpCodes.Dup);
EmitDataLoadRn(context); EmitAluLoadRn(context);
context.Emit(OpCodes.Xor); context.Emit(OpCodes.Xor);
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
context.Emit(OpCodes.Xor); context.Emit(OpCodes.Xor);
context.Emit(OpCodes.And); context.Emit(OpCodes.And);
@ -120,21 +121,32 @@ namespace ChocolArm64.Instructions
context.EmitStflg((int)PState.VBit); context.EmitStflg((int)PState.VBit);
} }
public static void EmitDataLoadRm(ILEmitterCtx context) public static void EmitAluLoadRm(ILEmitterCtx context)
{ {
context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm); if (context.CurrOp is IOpCodeAluRs64 op)
{
context.EmitLdintzr(op.Rm);
}
else if (context.CurrOp is OpCode32AluRsImm op32)
{
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
}
else
{
throw new InvalidOperationException();
}
} }
public static void EmitDataLoadOpers(ILEmitterCtx context) public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true)
{ {
EmitDataLoadRn(context); EmitAluLoadRn(context);
EmitDataLoadOper2(context); EmitAluLoadOper2(context, setCarry);
} }
public static void EmitDataLoadRn(ILEmitterCtx context) public static void EmitAluLoadRn(ILEmitterCtx context)
{
if (context.CurrOp is IOpCodeAlu64 op)
{ {
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
{ {
context.EmitLdintzr(op.Rn); context.EmitLdintzr(op.Rn);
@ -144,11 +156,41 @@ namespace ChocolArm64.Instructions
context.EmitLdint(op.Rn); context.EmitLdint(op.Rn);
} }
} }
else if (context.CurrOp is IOpCode32Alu op32)
{
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn);
}
else
{
throw new InvalidOperationException();
}
}
public static void EmitDataLoadOper2(ILEmitterCtx context) public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true)
{ {
switch (context.CurrOp) switch (context.CurrOp)
{ {
//ARM32.
case OpCode32AluImm op:
context.EmitLdc_I4(op.Imm);
if (op.SetFlags && op.IsRotated)
{
context.EmitLdc_I4((int)((uint)op.Imm >> 31));
context.EmitStflg((int)PState.CBit);
}
break;
case OpCode32AluRsImm op:
EmitLoadRmShiftedByImmediate(context, op, setCarry);
break;
case OpCodeT16AluImm8 op:
context.EmitLdc_I4(op.Imm);
break;
//ARM64.
case IOpCodeAluImm64 op: case IOpCodeAluImm64 op:
context.EmitLdc_I(op.Imm); context.EmitLdc_I(op.Imm);
break; break;
@ -170,23 +212,8 @@ namespace ChocolArm64.Instructions
context.EmitCast(op.IntType); context.EmitCast(op.IntType);
context.EmitLsl(op.Shift); context.EmitLsl(op.Shift);
break; break;
}
}
public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false); default: throw new InvalidOperationException();
public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true);
public static void EmitDataStore(ILEmitterCtx context, bool setFlags)
{
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (setFlags || op is IOpCodeAluRs64)
{
context.EmitStintzr(op.Rd);
}
else
{
context.EmitStint(op.Rd);
} }
} }
@ -217,5 +244,219 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.And); context.Emit(OpCodes.And);
context.EmitStflg((int)PState.NBit); context.EmitStflg((int)PState.NBit);
} }
//ARM32 helpers.
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm op, bool setCarry)
{
int shift = op.Imm;
if (shift == 0)
{
switch (op.ShiftType)
{
case ShiftType.Lsr: shift = 32; break;
case ShiftType.Asr: shift = 32; break;
case ShiftType.Ror: shift = 1; break;
}
}
context.EmitLdint(op.Rm);
if (shift != 0)
{
setCarry &= op.SetFlags;
switch (op.ShiftType)
{
case ShiftType.Lsl: EmitLslC(context, setCarry, shift); break;
case ShiftType.Lsr: EmitLsrC(context, setCarry, shift); break;
case ShiftType.Asr: EmitAsrC(context, setCarry, shift); break;
case ShiftType.Ror:
if (op.Imm != 0)
{
EmitRorC(context, setCarry, shift);
}
else
{
EmitRrxC(context, setCarry);
}
break;
}
}
}
private static void EmitLslC(ILEmitterCtx context, bool setCarry, int shift)
{
if ((uint)shift > 32)
{
EmitShiftByMoreThan32(context, setCarry);
}
else if (shift == 32)
{
if (setCarry)
{
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
else
{
context.Emit(OpCodes.Pop);
}
context.EmitLdc_I4(0);
}
else
{
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLsr(32 - shift);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
context.EmitLsl(shift);
}
}
private static void EmitLsrC(ILEmitterCtx context, bool setCarry, int shift)
{
if ((uint)shift > 32)
{
EmitShiftByMoreThan32(context, setCarry);
}
else if (shift == 32)
{
if (setCarry)
{
context.EmitLsr(31);
context.EmitStflg((int)PState.CBit);
}
else
{
context.Emit(OpCodes.Pop);
}
context.EmitLdc_I4(0);
}
else
{
context.Emit(OpCodes.Dup);
context.EmitLsr(shift - 1);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
context.EmitLsr(shift);
}
}
private static void EmitShiftByMoreThan32(ILEmitterCtx context, bool setCarry)
{
context.Emit(OpCodes.Pop);
context.EmitLdc_I4(0);
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitStflg((int)PState.CBit);
}
}
private static void EmitAsrC(ILEmitterCtx context, bool setCarry, int shift)
{
if ((uint)shift >= 32)
{
context.EmitAsr(31);
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
}
else
{
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLsr(shift - 1);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
context.EmitAsr(shift);
}
}
private static void EmitRorC(ILEmitterCtx context, bool setCarry, int shift)
{
shift &= 0x1f;
context.EmitRor(shift);
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLsr(31);
context.EmitStflg((int)PState.CBit);
}
}
private static void EmitRrxC(ILEmitterCtx context, bool setCarry)
{
//Rotate right by 1 with carry.
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitSttmp();
}
context.EmitLsr(1);
context.EmitLdflg((int)PState.CBit);
context.EmitLsl(31);
context.Emit(OpCodes.Or);
if (setCarry)
{
context.EmitLdtmp();
context.EmitStflg((int)PState.CBit);
}
}
} }
} }

View file

@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions
context.MarkLabel(lblTrue); context.MarkLabel(lblTrue);
EmitDataLoadOpers(context); EmitAluLoadOpers(context);
if (cmpOp == CcmpOp.Cmp) if (cmpOp == CcmpOp.Cmp)
{ {

View file

@ -36,36 +36,10 @@ namespace ChocolArm64.Instructions
OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp; OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp;
context.EmitLdc_I(op.Position + 4); context.EmitLdc_I(op.Position + 4);
context.EmitStint(CpuThreadState.LrIndex); context.EmitStint(RegisterAlias.Lr);
context.EmitStoreState(); context.EmitStoreState();
if (context.TryOptEmitSubroutineCall()) InstEmitFlowHelper.EmitCall(context, op.Imm);
{
//Note: the return value of the called method will be placed
//at the Stack, the return value is always a Int64 with the
//return address of the function. We check if the address is
//correct, if it isn't we keep returning until we reach the dispatcher.
context.Emit(OpCodes.Dup);
context.EmitLdc_I8(op.Position + 4);
ILLabel lblContinue = new ILLabel();
context.Emit(OpCodes.Beq_S, lblContinue);
context.Emit(OpCodes.Ret);
context.MarkLabel(lblContinue);
context.Emit(OpCodes.Pop);
context.EmitLoadState();
}
else
{
context.EmitLdc_I8(op.Imm);
context.Emit(OpCodes.Ret);
}
} }
public static void Blr(ILEmitterCtx context) public static void Blr(ILEmitterCtx context)
@ -74,7 +48,7 @@ namespace ChocolArm64.Instructions
context.EmitLdintzr(op.Rn); context.EmitLdintzr(op.Rn);
context.EmitLdc_I(op.Position + 4); context.EmitLdc_I(op.Position + 4);
context.EmitStint(CpuThreadState.LrIndex); context.EmitStint(RegisterAlias.Lr);
context.EmitStoreState(); context.EmitStoreState();
context.Emit(OpCodes.Ret); context.Emit(OpCodes.Ret);
@ -106,7 +80,7 @@ namespace ChocolArm64.Instructions
public static void Ret(ILEmitterCtx context) public static void Ret(ILEmitterCtx context)
{ {
context.EmitStoreState(); context.EmitStoreState();
context.EmitLdint(CpuThreadState.LrIndex); context.EmitLdint(RegisterAlias.Lr);
context.Emit(OpCodes.Ret); context.Emit(OpCodes.Ret);
} }
@ -128,7 +102,7 @@ namespace ChocolArm64.Instructions
EmitBranch(context, ilOp); EmitBranch(context, ilOp);
} }
private static void EmitBranch(ILEmitterCtx context, Cond cond) private static void EmitBranch(ILEmitterCtx context, Condition cond)
{ {
OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp;

View file

@ -0,0 +1,82 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmit32Helper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit32
{
public static void B(ILEmitterCtx context)
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
if (context.CurrBlock.Branch != null)
{
context.Emit(OpCodes.Br, context.GetLabel(op.Imm));
}
else
{
context.EmitStoreState();
context.EmitLdc_I8(op.Imm);
context.Emit(OpCodes.Ret);
}
}
public static void Bl(ILEmitterCtx context)
{
Blx(context, x: false);
}
public static void Blx(ILEmitterCtx context)
{
Blx(context, x: true);
}
public static void Bx(ILEmitterCtx context)
{
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
context.EmitStoreState();
EmitLoadFromRegister(context, op.Rm);
EmitBxWritePc(context);
}
private static void Blx(ILEmitterCtx context, bool x)
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
uint pc = op.GetPc();
bool isThumb = IsThumb(context.CurrOp);
if (!isThumb)
{
context.EmitLdc_I(op.GetPc() - 4);
}
else
{
context.EmitLdc_I(op.GetPc() | 1);
}
context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr));
context.EmitStoreState();
//If x is true, then this is a branch with link and exchange.
//In this case we need to swap the mode between Arm <-> Thumb.
if (x)
{
context.EmitLdc_I4(isThumb ? 0 : 1);
context.EmitStflg((int)PState.TBit);
}
InstEmitFlowHelper.EmitCall(context, op.Imm);
}
}
}

View file

@ -0,0 +1,39 @@
using ChocolArm64.Translation;
using System.Reflection.Emit;
namespace ChocolArm64.Instructions
{
static class InstEmitFlowHelper
{
public static void EmitCall(ILEmitterCtx context, long imm)
{
if (context.TryOptEmitSubroutineCall())
{
//Note: the return value of the called method will be placed
//at the Stack, the return value is always a Int64 with the
//return address of the function. We check if the address is
//correct, if it isn't we keep returning until we reach the dispatcher.
context.Emit(OpCodes.Dup);
context.EmitLdc_I8(context.CurrOp.Position + 4);
ILLabel lblContinue = new ILLabel();
context.Emit(OpCodes.Beq_S, lblContinue);
context.Emit(OpCodes.Ret);
context.MarkLabel(lblContinue);
context.Emit(OpCodes.Pop);
context.EmitLoadState();
}
else
{
context.EmitLdc_I8(imm);
context.Emit(OpCodes.Ret);
}
}
}
}

View file

@ -0,0 +1,325 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmit32Helper;
using static ChocolArm64.Instructions.InstEmitMemoryHelper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit32
{
private const int ByteSizeLog2 = 0;
private const int HWordSizeLog2 = 1;
private const int WordSizeLog2 = 2;
private const int DWordSizeLog2 = 3;
[Flags]
enum AccessType
{
Store = 0,
Signed = 1,
Load = 2,
LoadZx = Load,
LoadSx = Load | Signed,
}
public static void Ldm(ILEmitterCtx context)
{
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
EmitLoadFromRegister(context, op.Rn);
bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0;
bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc);
if (writeBack)
{
context.Emit(OpCodes.Dup);
}
context.EmitLdc_I4(op.Offset);
context.Emit(OpCodes.Add);
context.EmitSttmp();
if (writeBack)
{
context.EmitLdc_I4(op.PostOffset);
context.Emit(OpCodes.Add);
EmitStoreToRegister(context, op.Rn);
}
int mask = op.RegisterMask;
int offset = 0;
for (int register = 0; mask != 0; mask >>= 1, register++)
{
if ((mask & 1) != 0)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(offset);
context.Emit(OpCodes.Add);
EmitReadZxCall(context, WordSizeLog2);
EmitStoreToRegister(context, register);
offset += 4;
}
}
}
public static void Ldr(ILEmitterCtx context)
{
EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx);
}
public static void Ldrb(ILEmitterCtx context)
{
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx);
}
public static void Ldrd(ILEmitterCtx context)
{
EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx);
}
public static void Ldrh(ILEmitterCtx context)
{
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx);
}
public static void Ldrsb(ILEmitterCtx context)
{
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx);
}
public static void Ldrsh(ILEmitterCtx context)
{
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx);
}
public static void Stm(ILEmitterCtx context)
{
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
EmitLoadFromRegister(context, op.Rn);
context.EmitLdc_I4(op.Offset);
context.Emit(OpCodes.Add);
context.EmitSttmp();
int mask = op.RegisterMask;
int offset = 0;
for (int register = 0; mask != 0; mask >>= 1, register++)
{
if ((mask & 1) != 0)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(offset);
context.Emit(OpCodes.Add);
EmitLoadFromRegister(context, register);
EmitWriteCall(context, WordSizeLog2);
//Note: If Rn is also specified on the register list,
//and Rn is the first register on this list, then the
//value that is written to memory is the unmodified value,
//before the write back. If it is on the list, but it's
//not the first one, then the value written to memory
//varies between CPUs.
if (offset == 0 && op.PostOffset != 0)
{
//Emit write back after the first write.
EmitLoadFromRegister(context, op.Rn);
context.EmitLdc_I4(op.PostOffset);
context.Emit(OpCodes.Add);
EmitStoreToRegister(context, op.Rn);
}
offset += 4;
}
}
}
public static void Str(ILEmitterCtx context)
{
EmitLoadOrStore(context, WordSizeLog2, AccessType.Store);
}
public static void Strb(ILEmitterCtx context)
{
EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store);
}
public static void Strd(ILEmitterCtx context)
{
EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store);
}
public static void Strh(ILEmitterCtx context)
{
EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store);
}
private static void EmitLoadOrStore(ILEmitterCtx context, int size, AccessType accType)
{
OpCode32Mem op = (OpCode32Mem)context.CurrOp;
if (op.Index || op.WBack)
{
EmitLoadFromRegister(context, op.Rn);
context.EmitLdc_I4(op.Imm);
context.Emit(op.Add ? OpCodes.Add : OpCodes.Sub);
context.EmitSttmp();
}
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
if (op.Index)
{
context.EmitLdtmp();
}
else
{
EmitLoadFromRegister(context, op.Rn);
}
if ((accType & AccessType.Load) != 0)
{
if ((accType & AccessType.Signed) != 0)
{
EmitReadSx32Call(context, size);
}
else
{
EmitReadZxCall(context, size);
}
if (op.WBack)
{
context.EmitLdtmp();
EmitStoreToRegister(context, op.Rn);
}
if (size == DWordSizeLog2)
{
context.Emit(OpCodes.Dup);
context.EmitLdflg((int)PState.EBit);
ILLabel lblBigEndian = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
//Little endian mode.
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt);
context.EmitLsr(32);
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt | 1);
context.Emit(OpCodes.Br_S, lblEnd);
//Big endian mode.
context.MarkLabel(lblBigEndian);
context.EmitLsr(32);
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt);
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt | 1);
context.MarkLabel(lblEnd);
}
else
{
EmitStoreToRegister(context, op.Rt);
}
}
else
{
if (op.WBack)
{
context.EmitLdtmp();
EmitStoreToRegister(context, op.Rn);
}
EmitLoadFromRegister(context, op.Rt);
if (size == DWordSizeLog2)
{
context.Emit(OpCodes.Conv_U8);
context.EmitLdflg((int)PState.EBit);
ILLabel lblBigEndian = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
//Little endian mode.
EmitLoadFromRegister(context, op.Rt | 1);
context.Emit(OpCodes.Conv_U8);
context.EmitLsl(32);
context.Emit(OpCodes.Or);
context.Emit(OpCodes.Br_S, lblEnd);
//Big endian mode.
context.MarkLabel(lblBigEndian);
context.EmitLsl(32);
EmitLoadFromRegister(context, op.Rt | 1);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Or);
context.MarkLabel(lblEnd);
}
EmitWriteCall(context, size);
}
}
}
}

View file

@ -392,8 +392,7 @@ namespace ChocolArm64.Instructions
public static void Fadd_S(ILEmitterCtx context) public static void Fadd_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.AddScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.AddScalar));
} }
@ -408,8 +407,7 @@ namespace ChocolArm64.Instructions
public static void Fadd_V(ILEmitterCtx context) public static void Fadd_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Add)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Add));
} }
@ -470,8 +468,7 @@ namespace ChocolArm64.Instructions
public static void Faddp_V(ILEmitterCtx context) public static void Faddp_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Add)); EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Add));
} }
@ -486,8 +483,7 @@ namespace ChocolArm64.Instructions
public static void Fdiv_S(ILEmitterCtx context) public static void Fdiv_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.DivideScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.DivideScalar));
} }
@ -502,8 +498,7 @@ namespace ChocolArm64.Instructions
public static void Fdiv_V(ILEmitterCtx context) public static void Fdiv_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Divide)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Divide));
} }
@ -564,8 +559,7 @@ namespace ChocolArm64.Instructions
public static void Fmax_S(ILEmitterCtx context) public static void Fmax_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.MaxScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.MaxScalar));
} }
@ -580,8 +574,7 @@ namespace ChocolArm64.Instructions
public static void Fmax_V(ILEmitterCtx context) public static void Fmax_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Max)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Max));
} }
@ -612,8 +605,7 @@ namespace ChocolArm64.Instructions
public static void Fmaxp_V(ILEmitterCtx context) public static void Fmaxp_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Max)); EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Max));
} }
@ -628,8 +620,7 @@ namespace ChocolArm64.Instructions
public static void Fmin_S(ILEmitterCtx context) public static void Fmin_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.MinScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.MinScalar));
} }
@ -644,8 +635,7 @@ namespace ChocolArm64.Instructions
public static void Fmin_V(ILEmitterCtx context) public static void Fmin_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Min)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Min));
} }
@ -676,8 +666,7 @@ namespace ChocolArm64.Instructions
public static void Fminp_V(ILEmitterCtx context) public static void Fminp_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Min)); EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Min));
} }
@ -984,8 +973,7 @@ namespace ChocolArm64.Instructions
public static void Fmul_S(ILEmitterCtx context) public static void Fmul_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.MultiplyScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.MultiplyScalar));
} }
@ -1005,8 +993,7 @@ namespace ChocolArm64.Instructions
public static void Fmul_V(ILEmitterCtx context) public static void Fmul_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Multiply)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Multiply));
} }
@ -1753,8 +1740,7 @@ namespace ChocolArm64.Instructions
public static void Fsqrt_S(ILEmitterCtx context) public static void Fsqrt_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.SqrtScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.SqrtScalar));
} }
@ -1769,8 +1755,7 @@ namespace ChocolArm64.Instructions
public static void Fsqrt_V(ILEmitterCtx context) public static void Fsqrt_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Sqrt)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Sqrt));
} }
@ -1785,8 +1770,7 @@ namespace ChocolArm64.Instructions
public static void Fsub_S(ILEmitterCtx context) public static void Fsub_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitScalarSseOrSse2OpF(context, nameof(Sse.SubtractScalar)); EmitScalarSseOrSse2OpF(context, nameof(Sse.SubtractScalar));
} }
@ -1801,8 +1785,7 @@ namespace ChocolArm64.Instructions
public static void Fsub_V(ILEmitterCtx context) public static void Fsub_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitVectorSseOrSse2OpF(context, nameof(Sse.Subtract)); EmitVectorSseOrSse2OpF(context, nameof(Sse.Subtract));
} }
@ -2268,6 +2251,15 @@ namespace ChocolArm64.Instructions
} }
} }
public static void Smlal_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemSx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Add);
});
}
public static void Smlsl_V(ILEmitterCtx context) public static void Smlsl_V(ILEmitterCtx context)
{ {
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
@ -2319,11 +2311,25 @@ namespace ChocolArm64.Instructions
} }
} }
public static void Smlsl_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemSx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Sub);
});
}
public static void Smull_V(ILEmitterCtx context) public static void Smull_V(ILEmitterCtx context)
{ {
EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Mul)); EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Mul));
} }
public static void Smull_Ve(ILEmitterCtx context)
{
EmitVectorWidenBinaryOpByElemSx(context, () => context.Emit(OpCodes.Mul));
}
public static void Sqabs_S(ILEmitterCtx context) public static void Sqabs_S(ILEmitterCtx context)
{ {
EmitScalarSaturatingUnaryOpSx(context, () => EmitAbs(context)); EmitScalarSaturatingUnaryOpSx(context, () => EmitAbs(context));
@ -2929,6 +2935,15 @@ namespace ChocolArm64.Instructions
} }
} }
public static void Umlal_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemZx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Add);
});
}
public static void Umlsl_V(ILEmitterCtx context) public static void Umlsl_V(ILEmitterCtx context)
{ {
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
@ -2980,11 +2995,25 @@ namespace ChocolArm64.Instructions
} }
} }
public static void Umlsl_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemZx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Sub);
});
}
public static void Umull_V(ILEmitterCtx context) public static void Umull_V(ILEmitterCtx context)
{ {
EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Mul));
} }
public static void Umull_Ve(ILEmitterCtx context)
{
EmitVectorWidenBinaryOpByElemZx(context, () => context.Emit(OpCodes.Mul));
}
public static void Uqadd_S(ILEmitterCtx context) public static void Uqadd_S(ILEmitterCtx context)
{ {
EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add); EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add);

View file

@ -173,8 +173,7 @@ namespace ChocolArm64.Instructions
public static void Fcmeq_S(ILEmitterCtx context) public static void Fcmeq_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar), scalar: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar), scalar: true);
} }
@ -186,8 +185,7 @@ namespace ChocolArm64.Instructions
public static void Fcmeq_V(ILEmitterCtx context) public static void Fcmeq_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqual), scalar: false); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqual), scalar: false);
} }
@ -199,8 +197,7 @@ namespace ChocolArm64.Instructions
public static void Fcmge_S(ILEmitterCtx context) public static void Fcmge_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true);
} }
@ -212,8 +209,7 @@ namespace ChocolArm64.Instructions
public static void Fcmge_V(ILEmitterCtx context) public static void Fcmge_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false);
} }
@ -225,8 +221,7 @@ namespace ChocolArm64.Instructions
public static void Fcmgt_S(ILEmitterCtx context) public static void Fcmgt_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true);
} }
@ -238,8 +233,7 @@ namespace ChocolArm64.Instructions
public static void Fcmgt_V(ILEmitterCtx context) public static void Fcmgt_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false);
} }
@ -251,8 +245,7 @@ namespace ChocolArm64.Instructions
public static void Fcmle_S(ILEmitterCtx context) public static void Fcmle_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true, isLeOrLt: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true, isLeOrLt: true);
} }
@ -264,8 +257,7 @@ namespace ChocolArm64.Instructions
public static void Fcmle_V(ILEmitterCtx context) public static void Fcmle_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false, isLeOrLt: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false, isLeOrLt: true);
} }
@ -277,8 +269,7 @@ namespace ChocolArm64.Instructions
public static void Fcmlt_S(ILEmitterCtx context) public static void Fcmlt_S(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true, isLeOrLt: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true, isLeOrLt: true);
} }
@ -290,8 +281,7 @@ namespace ChocolArm64.Instructions
public static void Fcmlt_V(ILEmitterCtx context) public static void Fcmlt_V(ILEmitterCtx context)
{ {
if (Optimizations.FastFP && Optimizations.UseSse if (Optimizations.FastFP && Optimizations.UseSse2)
&& Optimizations.UseSse2)
{ {
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false, isLeOrLt: true); EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false, isLeOrLt: true);
} }

View file

@ -78,7 +78,6 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2 && sizeF == 1) if (Optimizations.UseSse2 && sizeF == 1)
{ {
Type[] typesMov = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
Type[] typesCvt = new Type[] { typeof(Vector128<float>) }; Type[] typesCvt = new Type[] { typeof(Vector128<float>) };
string nameMov = op.RegisterSize == RegisterSize.Simd128 string nameMov = op.RegisterSize == RegisterSize.Simd128
@ -88,7 +87,7 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rn); context.EmitLdvec(op.Rn);
context.Emit(OpCodes.Dup); context.Emit(OpCodes.Dup);
context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Double), typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Double), typesCvt));
@ -144,7 +143,6 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2 && sizeF == 1) if (Optimizations.UseSse2 && sizeF == 1)
{ {
Type[] typesMov = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
Type[] typesCvt = new Type[] { typeof(Vector128<double>) }; Type[] typesCvt = new Type[] { typeof(Vector128<double>) };
string nameMov = op.RegisterSize == RegisterSize.Simd128 string nameMov = op.RegisterSize == RegisterSize.Simd128
@ -154,15 +152,15 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rd); context.EmitLdvec(op.Rd);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
EmitLdvecWithCastToDouble(context, op.Rn); EmitLdvecWithCastToDouble(context, op.Rn);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt));
context.Emit(OpCodes.Dup); context.Emit(OpCodes.Dup);
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitStvec(op.Rd); context.EmitStvec(op.Rd);
} }

View file

@ -642,21 +642,21 @@ namespace ChocolArm64.Instructions
{ {
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorOpByElem(context, emit, op.Index, false, true); EmitVectorOpByElem(context, emit, op.Index, ternary: false, signed: true);
} }
public static void EmitVectorBinaryOpByElemZx(ILEmitterCtx context, Action emit) public static void EmitVectorBinaryOpByElemZx(ILEmitterCtx context, Action emit)
{ {
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorOpByElem(context, emit, op.Index, false, false); EmitVectorOpByElem(context, emit, op.Index, ternary: false, signed: false);
} }
public static void EmitVectorTernaryOpByElemZx(ILEmitterCtx context, Action emit) public static void EmitVectorTernaryOpByElemZx(ILEmitterCtx context, Action emit)
{ {
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorOpByElem(context, emit, op.Index, true, false); EmitVectorOpByElem(context, emit, op.Index, ternary: true, signed: false);
} }
public static void EmitVectorOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed) public static void EmitVectorOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed)
@ -809,6 +809,64 @@ namespace ChocolArm64.Instructions
context.EmitStvec(op.Rd); context.EmitStvec(op.Rd);
} }
public static void EmitVectorWidenBinaryOpByElemSx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: false, signed: true);
}
public static void EmitVectorWidenBinaryOpByElemZx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: false, signed: false);
}
public static void EmitVectorWidenTernaryOpByElemSx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: true, signed: true);
}
public static void EmitVectorWidenTernaryOpByElemZx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: true, signed: false);
}
public static void EmitVectorWidenOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed)
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
int elems = 8 >> op.Size;
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
EmitVectorExtract(context, op.Rm, elem, op.Size, signed);
context.EmitSttmp();
for (int index = 0; index < elems; index++)
{
if (ternary)
{
EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed);
}
EmitVectorExtract(context, op.Rn, part + index, op.Size, signed);
context.EmitLdtmp();
emit();
EmitVectorInsertTmp(context, index, op.Size + 1);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
}
public static void EmitVectorPairwiseOpSx(ILEmitterCtx context, Action emit) public static void EmitVectorPairwiseOpSx(ILEmitterCtx context, Action emit)
{ {
EmitVectorPairwiseOp(context, emit, true); EmitVectorPairwiseOp(context, emit, true);
@ -1416,7 +1474,7 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse) if (Optimizations.UseSse)
{ {
//TODO: Use Sse2.MoveScalar once it is fixed, //TODO: Use Sse2.MoveScalar once it is fixed,
//as of the time of writing it just crashes the JIT (SDK 2.1.500). //as of the time of writing it just crashes the JIT (SDK 2.1.503).
/*Type[] typesMov = new Type[] { typeof(Vector128<ulong>) }; /*Type[] typesMov = new Type[] { typeof(Vector128<ulong>) };

View file

@ -168,7 +168,7 @@ namespace ChocolArm64.Instructions
context.EmitLdint(op.Rn); context.EmitLdint(op.Rn);
if (op.Rm != CpuThreadState.ZrIndex) if (op.Rm != RegisterAlias.Zr)
{ {
context.EmitLdint(op.Rm); context.EmitLdint(op.Rm);
} }

View file

@ -12,6 +12,34 @@ namespace ChocolArm64.Instructions
{ {
static partial class InstEmit static partial class InstEmit
{ {
#region "Masks"
private static readonly long[] _masksE0_TrnUzpXtn = new long[]
{
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
};
private static readonly long[] _masksE1_TrnUzp = new long[]
{
15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0,
15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0,
15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0
};
private static readonly long[] _masksE0_Uzp = new long[]
{
13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0
};
private static readonly long[] _masksE1_Uzp = new long[]
{
15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0,
15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0
};
#endregion
public static void Dup_Gp(ILEmitterCtx context) public static void Dup_Gp(ILEmitterCtx context)
{ {
OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp;
@ -379,15 +407,6 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSsse3) if (Optimizations.UseSsse3)
{ {
long[] masks = new long[]
{
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
};
Type[] typesMov = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
Type[] typesSve = new Type[] { typeof(long), typeof(long) }; Type[] typesSve = new Type[] { typeof(long), typeof(long) };
string nameMov = op.RegisterSize == RegisterSize.Simd128 string nameMov = op.RegisterSize == RegisterSize.Simd128
@ -397,18 +416,18 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rd); context.EmitLdvec(op.Rd);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
EmitLdvecWithSignedCast(context, op.Rn, 0); EmitLdvecWithSignedCast(context, op.Rn, 0); // value
context.EmitLdc_I8(masks[op.Size]); context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // mask
context.Emit(OpCodes.Dup); context.Emit(OpCodes.Dup); // mask
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl)); context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitStvec(op.Rd); context.EmitStvec(op.Rd);
} }
@ -465,6 +484,44 @@ namespace ChocolArm64.Instructions
{ {
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
if (Optimizations.UseSsse3)
{
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
string nameUpk = part == 0
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
else
{
int words = op.GetBitsCount() >> 4; int words = op.GetBitsCount() >> 4;
int pairs = words >> op.Size; int pairs = words >> op.Size;
@ -481,6 +538,7 @@ namespace ChocolArm64.Instructions
context.EmitLdvectmp(); context.EmitLdvectmp();
context.EmitStvec(op.Rd); context.EmitStvec(op.Rd);
}
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
{ {
@ -492,6 +550,70 @@ namespace ChocolArm64.Instructions
{ {
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
if (Optimizations.UseSsse3)
{
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
string nameUpk = part == 0
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
if (op.RegisterSize == RegisterSize.Simd128)
{
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
else
{
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
EmitLdvecWithSignedCast(context, op.Rm, op.Size);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size))); // value
if (op.Size < 2)
{
context.EmitLdc_I8(_masksE1_Uzp[op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_Uzp[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero));
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
}
else
{
int words = op.GetBitsCount() >> 4; int words = op.GetBitsCount() >> 4;
int pairs = words >> op.Size; int pairs = words >> op.Size;
@ -514,6 +636,7 @@ namespace ChocolArm64.Instructions
EmitVectorZeroUpper(context, op.Rd); EmitVectorZeroUpper(context, op.Rd);
} }
} }
}
private static void EmitVectorZip(ILEmitterCtx context, int part) private static void EmitVectorZip(ILEmitterCtx context, int part)
{ {
@ -521,36 +644,26 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2) if (Optimizations.UseSse2)
{ {
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); string nameUpk = part == 0
EmitLdvecWithUnsignedCast(context, op.Rm, op.Size);
Type[] types = new Type[]
{
VectorUIntTypesPerSizeLog2[op.Size],
VectorUIntTypesPerSizeLog2[op.Size]
};
string name = part == 0 || (part != 0 && op.RegisterSize == RegisterSize.Simd64)
? nameof(Sse2.UnpackLow) ? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh); : nameof(Sse2.UnpackHigh);
context.EmitCall(typeof(Sse2).GetMethod(name, types)); EmitLdvecWithSignedCast(context, op.Rn, op.Size);
EmitLdvecWithSignedCast(context, op.Rm, op.Size);
if (op.RegisterSize == RegisterSize.Simd64 && part != 0) if (op.RegisterSize == RegisterSize.Simd128)
{ {
context.EmitLdc_I4(8); context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size)));
}
else
{
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size)));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero));
Type[] shTypes = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), shTypes));
} }
EmitStvecWithUnsignedCast(context, op.Rd, op.Size); EmitStvecWithSignedCast(context, op.Rd, op.Size);
if (op.RegisterSize == RegisterSize.Simd64 && part == 0)
{
EmitVectorZeroUpper(context, op.Rd);
}
} }
else else
{ {
@ -579,5 +692,10 @@ namespace ChocolArm64.Instructions
} }
} }
} }
private static Type[] GetTypesSflUpk(int size)
{
return new Type[] { VectorIntTypesPerSizeLog2[size], VectorIntTypesPerSizeLog2[size] };
}
} }
} }

View file

@ -1,8 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.Memory;
using ChocolArm64.State;
namespace ChocolArm64.Instructions
{
delegate void InstInterpreter(CpuThreadState state, MemoryManager memory, OpCode64 opCode);
}

View file

@ -664,7 +664,7 @@ namespace ChocolArm64.Instructions
for (int bit = highBit; bit >= 0; bit--) for (int bit = highBit; bit >= 0; bit--)
{ {
if (((value >> bit) & 0b1) != 0) if (((int)(value >> bit) & 0b1) != 0)
{ {
return (ulong)(highBit - bit); return (ulong)(highBit - bit);
} }
@ -688,7 +688,7 @@ namespace ChocolArm64.Instructions
do do
{ {
nibbleIdx -= 4; nibbleIdx -= 4;
preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111]; preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
count += preCount; count += preCount;
} }
while (preCount == 4); while (preCount == 4);
@ -698,11 +698,6 @@ namespace ChocolArm64.Instructions
public static ulong CountSetBits8(ulong value) // "size" is 8 (SIMD&FP Inst.). public static ulong CountSetBits8(ulong value) // "size" is 8 (SIMD&FP Inst.).
{ {
if (value == 0xfful)
{
return 8ul;
}
value = ((value >> 1) & 0x55ul) + (value & 0x55ul); value = ((value >> 1) & 0x55ul) + (value & 0x55ul);
value = ((value >> 2) & 0x33ul) + (value & 0x33ul); value = ((value >> 2) & 0x33ul) + (value & 0x33ul);

View file

@ -1545,9 +1545,9 @@ namespace ChocolArm64.Instructions
return -value; return -value;
} }
private static float ZerosOrOnes(bool zeros) private static float ZerosOrOnes(bool ones)
{ {
return BitConverter.Int32BitsToSingle(!zeros ? 0 : -1); return BitConverter.Int32BitsToSingle(ones ? -1 : 0);
} }
private static float FPUnpack( private static float FPUnpack(
@ -2629,9 +2629,9 @@ namespace ChocolArm64.Instructions
return -value; return -value;
} }
private static double ZerosOrOnes(bool zeros) private static double ZerosOrOnes(bool ones)
{ {
return BitConverter.Int64BitsToDouble(!zeros ? 0L : -1L); return BitConverter.Int64BitsToDouble(ones ? -1L : 0L);
} }
private static double FPUnpack( private static double FPUnpack(

View file

@ -1,7 +0,0 @@
namespace ChocolArm64.Instructions32
{
static partial class A32InstInterpret
{
}
}

View file

@ -1,70 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.Decoders32;
using ChocolArm64.Memory;
using ChocolArm64.State;
using static ChocolArm64.Instructions32.A32InstInterpretHelper;
namespace ChocolArm64.Instructions32
{
static partial class A32InstInterpret
{
public static void B(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
{
A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode;
if (IsConditionTrue(state, op.Cond))
{
BranchWritePc(state, GetPc(state) + (uint)op.Imm);
}
}
public static void Bl(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
{
Blx(state, memory, opCode, false);
}
public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
{
Blx(state, memory, opCode, true);
}
public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode, bool x)
{
A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode;
if (IsConditionTrue(state, op.Cond))
{
uint pc = GetPc(state);
if (state.Thumb)
{
state.R14 = pc | 1;
}
else
{
state.R14 = pc - 4U;
}
if (x)
{
state.Thumb = !state.Thumb;
}
if (!state.Thumb)
{
pc &= ~3U;
}
BranchWritePc(state, pc + (uint)op.Imm);
}
}
private static void BranchWritePc(CpuThreadState state, uint pc)
{
state.R15 = state.Thumb
? pc & ~1U
: pc & ~3U;
}
}
}

View file

@ -1,65 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using System;
namespace ChocolArm64.Instructions32
{
static class A32InstInterpretHelper
{
public static bool IsConditionTrue(CpuThreadState state, Cond cond)
{
switch (cond)
{
case Cond.Eq: return state.Zero;
case Cond.Ne: return !state.Zero;
case Cond.GeUn: return state.Carry;
case Cond.LtUn: return !state.Carry;
case Cond.Mi: return state.Negative;
case Cond.Pl: return !state.Negative;
case Cond.Vs: return state.Overflow;
case Cond.Vc: return !state.Overflow;
case Cond.GtUn: return state.Carry && !state.Zero;
case Cond.LeUn: return !state.Carry && state.Zero;
case Cond.Ge: return state.Negative == state.Overflow;
case Cond.Lt: return state.Negative != state.Overflow;
case Cond.Gt: return state.Negative == state.Overflow && !state.Zero;
case Cond.Le: return state.Negative != state.Overflow && state.Zero;
}
return true;
}
public unsafe static uint GetReg(CpuThreadState state, int reg)
{
if ((uint)reg > 15)
{
throw new ArgumentOutOfRangeException(nameof(reg));
}
fixed (uint* ptr = &state.R0)
{
return *(ptr + reg);
}
}
public unsafe static void SetReg(CpuThreadState state, int reg, uint value)
{
if ((uint)reg > 15)
{
throw new ArgumentOutOfRangeException(nameof(reg));
}
fixed (uint* ptr = &state.R0)
{
*(ptr + reg) = value;
}
}
public static uint GetPc(CpuThreadState state)
{
//Due to the old fetch-decode-execute pipeline of old ARM CPUs,
//the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction.
return state.R15 + (state.Thumb ? 2U : 4U);
}
}
}

View file

@ -1,7 +1,5 @@
using ChocolArm64.Decoders; using ChocolArm64.Decoders;
using ChocolArm64.Decoders32;
using ChocolArm64.Instructions; using ChocolArm64.Instructions;
using ChocolArm64.Instructions32;
using ChocolArm64.State; using ChocolArm64.State;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -10,13 +8,61 @@ namespace ChocolArm64
{ {
static class OpCodeTable static class OpCodeTable
{ {
private const int FastLookupSize = 0x1000;
private class InstInfo
{
public int Mask;
public int Value;
public Inst Inst;
public InstInfo(int mask, int value, Inst inst)
{
Mask = mask;
Value = value;
Inst = inst;
}
}
private static List<InstInfo> _allInstA32 = new List<InstInfo>();
private static List<InstInfo> _allInstT32 = new List<InstInfo>();
private static List<InstInfo> _allInstA64 = new List<InstInfo>();
private static InstInfo[][] _instA32FastLookup = new InstInfo[FastLookupSize][];
private static InstInfo[][] _instT32FastLookup = new InstInfo[FastLookupSize][];
private static InstInfo[][] _instA64FastLookup = new InstInfo[FastLookupSize][];
static OpCodeTable() static OpCodeTable()
{ {
#region "OpCode Table (AArch32)" #region "OpCode Table (AArch32)"
//Integer //Integer
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl)); SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCode32AluImm));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCode32AluRsImm));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCode32BImm));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCode32BImm));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCode32BImm));
SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCode32BReg));
SetT32("010001110xxxx000", InstEmit32.Bx, typeof(OpCodeT16BReg));
SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstEmit32.Cmp, typeof(OpCode32AluImm));
SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstEmit32.Cmp, typeof(OpCode32AluRsImm));
SetA32("<<<<100xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldr, typeof(OpCode32MemImm));
SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldrb, typeof(OpCode32MemImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstEmit32.Ldrh, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrsb, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstEmit32.Ldrsh, typeof(OpCode32MemImm8));
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCode32AluImm));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCode32AluRsImm));
SetT32("00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeT16AluImm8));
SetA32("<<<<100xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Stm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Str, typeof(OpCode32MemImm));
SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Strb, typeof(OpCode32MemImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstEmit32.Strd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstEmit32.Strh, typeof(OpCode32MemImm8));
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCode32AluImm));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCode32AluRsImm));
#endregion #endregion
#region "OpCode Table (AArch64)" #region "OpCode Table (AArch64)"
@ -413,9 +459,12 @@ namespace ChocolArm64
SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", InstEmit.Smin_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", InstEmit.Smin_V, typeof(OpCodeSimdReg64));
SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", InstEmit.Sminp_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", InstEmit.Sminp_V, typeof(OpCodeSimdReg64));
SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", InstEmit.Smlal_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", InstEmit.Smlal_V, typeof(OpCodeSimdReg64));
SetA64("0x001111xxxxxxxx0010x0xxxxxxxxxx", InstEmit.Smlal_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", InstEmit.Smlsl_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", InstEmit.Smlsl_V, typeof(OpCodeSimdReg64));
SetA64("0x001111xxxxxxxx0110x0xxxxxxxxxx", InstEmit.Smlsl_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x001110000xxxxx001011xxxxxxxxxx", InstEmit.Smov_S, typeof(OpCodeSimdIns64)); SetA64("0x001110000xxxxx001011xxxxxxxxxx", InstEmit.Smov_S, typeof(OpCodeSimdIns64));
SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", InstEmit.Smull_V, typeof(OpCodeSimdReg64)); SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", InstEmit.Smull_V, typeof(OpCodeSimdReg64));
SetA64("0x001111xxxxxxxx1010x0xxxxxxxxxx", InstEmit.Smull_Ve, typeof(OpCodeSimdRegElem64));
SetA64("01011110xx100000011110xxxxxxxxxx", InstEmit.Sqabs_S, typeof(OpCodeSimd64)); SetA64("01011110xx100000011110xxxxxxxxxx", InstEmit.Sqabs_S, typeof(OpCodeSimd64));
SetA64("0>001110<<100000011110xxxxxxxxxx", InstEmit.Sqabs_V, typeof(OpCodeSimd64)); SetA64("0>001110<<100000011110xxxxxxxxxx", InstEmit.Sqabs_V, typeof(OpCodeSimd64));
SetA64("01011110xx1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_S, typeof(OpCodeSimdReg64)); SetA64("01011110xx1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_S, typeof(OpCodeSimdReg64));
@ -502,9 +551,12 @@ namespace ChocolArm64
SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", InstEmit.Umin_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", InstEmit.Umin_V, typeof(OpCodeSimdReg64));
SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", InstEmit.Uminp_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", InstEmit.Uminp_V, typeof(OpCodeSimdReg64));
SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", InstEmit.Umlal_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", InstEmit.Umlal_V, typeof(OpCodeSimdReg64));
SetA64("0x101111xxxxxxxx0010x0xxxxxxxxxx", InstEmit.Umlal_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", InstEmit.Umlsl_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", InstEmit.Umlsl_V, typeof(OpCodeSimdReg64));
SetA64("0x101111xxxxxxxx0110x0xxxxxxxxxx", InstEmit.Umlsl_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x001110000xxxxx001111xxxxxxxxxx", InstEmit.Umov_S, typeof(OpCodeSimdIns64)); SetA64("0x001110000xxxxx001111xxxxxxxxxx", InstEmit.Umov_S, typeof(OpCodeSimdIns64));
SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", InstEmit.Umull_V, typeof(OpCodeSimdReg64)); SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", InstEmit.Umull_V, typeof(OpCodeSimdReg64));
SetA64("0x101111xxxxxxxx1010x0xxxxxxxxxx", InstEmit.Umull_Ve, typeof(OpCodeSimdRegElem64));
SetA64("01111110xx1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_S, typeof(OpCodeSimdReg64)); SetA64("01111110xx1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_S, typeof(OpCodeSimdReg64));
SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_V, typeof(OpCodeSimdReg64)); SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_V, typeof(OpCodeSimdReg64));
SetA64("0>101110<<1xxxxx010111xxxxxxxxxx", InstEmit.Uqrshl_V, typeof(OpCodeSimdReg64)); SetA64("0>101110<<1xxxxx010111xxxxxxxxxx", InstEmit.Uqrshl_V, typeof(OpCodeSimdReg64));
@ -544,63 +596,29 @@ namespace ChocolArm64
SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V, typeof(OpCodeSimdReg64)); SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V, typeof(OpCodeSimdReg64));
#endregion #endregion
#region "Generate InstA64FastLookup Table (AArch64)" FillFastLookupTable(_instA32FastLookup, _allInstA32);
var tmp = new List<InstInfo>[_fastLookupSize]; FillFastLookupTable(_instT32FastLookup, _allInstT32);
for (int i = 0; i < _fastLookupSize; i++) FillFastLookupTable(_instA64FastLookup, _allInstA64);
{
tmp[i] = new List<InstInfo>();
} }
foreach (var inst in _allInstA64) private static void SetA32(string encoding, InstEmitter emitter, Type type)
{ {
int mask = ToFastLookupIndex(inst.Mask); Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Arm);
int value = ToFastLookupIndex(inst.Value);
for (int i = 0; i < _fastLookupSize; i++)
{
if ((i & mask) == value)
{
tmp[i].Add(inst);
}
}
} }
for (int i = 0; i < _fastLookupSize; i++) private static void SetT32(string encoding, InstEmitter emitter, Type type)
{ {
_instA64FastLookup[i] = tmp[i].ToArray(); if (encoding.Length == 16)
} {
#endregion encoding = "xxxxxxxxxxxxxxxx" + encoding;
} }
private class InstInfo Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Thumb);
{
public int Mask;
public int Value;
public Inst Inst;
public InstInfo(int mask, int value, Inst inst)
{
Mask = mask;
Value = value;
Inst = inst;
}
}
private static List<InstInfo> _allInstA32 = new List<InstInfo>();
private static List<InstInfo> _allInstA64 = new List<InstInfo>();
private static int _fastLookupSize = 0x1000;
private static InstInfo[][] _instA64FastLookup = new InstInfo[_fastLookupSize][];
private static void SetA32(string encoding, InstInterpreter interpreter, Type type)
{
Set(encoding, new Inst(interpreter, null, type), ExecutionMode.AArch32);
} }
private static void SetA64(string encoding, InstEmitter emitter, Type type) private static void SetA64(string encoding, InstEmitter emitter, Type type)
{ {
Set(encoding, new Inst(null, emitter, type), ExecutionMode.AArch64); Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch64);
} }
private static void Set(string encoding, Inst inst, ExecutionMode mode) private static void Set(string encoding, Inst inst, ExecutionMode mode)
@ -673,27 +691,55 @@ namespace ChocolArm64
} }
} }
private static void InsertInst( private static void InsertInst(int xMask, int value, Inst inst, ExecutionMode mode)
int xMask,
int value,
Inst inst,
ExecutionMode mode)
{ {
InstInfo info = new InstInfo(xMask, value, inst); InstInfo info = new InstInfo(xMask, value, inst);
if (mode == ExecutionMode.AArch64) switch (mode)
{ {
_allInstA64.Add(info); case ExecutionMode.Aarch32Arm: _allInstA32.Add(info); break;
case ExecutionMode.Aarch32Thumb: _allInstT32.Add(info); break;
case ExecutionMode.Aarch64: _allInstA64.Add(info); break;
} }
else }
private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts)
{ {
_allInstA32.Add(info); List<InstInfo>[] tmp = new List<InstInfo>[FastLookupSize];
for (int i = 0; i < FastLookupSize; i++)
{
tmp[i] = new List<InstInfo>();
}
foreach (InstInfo inst in allInsts)
{
int mask = ToFastLookupIndex(inst.Mask);
int value = ToFastLookupIndex(inst.Value);
for (int i = 0; i < FastLookupSize; i++)
{
if ((i & mask) == value)
{
tmp[i].Add(inst);
}
}
}
for (int i = 0; i < FastLookupSize; i++)
{
table[i] = tmp[i].ToArray();
} }
} }
public static Inst GetInstA32(int opCode) public static Inst GetInstA32(int opCode)
{ {
return GetInstFromList(_allInstA32, opCode); return GetInstFromList(_instA32FastLookup[ToFastLookupIndex(opCode)], opCode);
}
public static Inst GetInstT32(int opCode)
{
return GetInstFromList(_instT32FastLookup[ToFastLookupIndex(opCode)], opCode);
} }
public static Inst GetInstA64(int opCode) public static Inst GetInstA64(int opCode)
@ -708,7 +754,7 @@ namespace ChocolArm64
private static Inst GetInstFromList(IEnumerable<InstInfo> instList, int opCode) private static Inst GetInstFromList(IEnumerable<InstInfo> instList, int opCode)
{ {
foreach (var node in instList) foreach (InstInfo node in instList)
{ {
if ((opCode & node.Mask) == node.Value) if ((opCode & node.Mask) == node.Value)
{ {

View file

@ -0,0 +1,15 @@
namespace ChocolArm64.State
{
enum Aarch32Mode
{
User = 0b10000,
Fiq = 0b10001,
Irq = 0b10010,
Supervisor = 0b10011,
Monitor = 0b10110,
Abort = 0b10111,
Hypervisor = 0b11010,
Undefined = 0b11011,
System = 0b11111
}
}

View file

@ -8,25 +8,11 @@ namespace ChocolArm64.State
{ {
public class CpuThreadState public class CpuThreadState
{ {
internal const int LrIndex = 30;
internal const int ZrIndex = 31;
internal const int ErgSizeLog2 = 4; internal const int ErgSizeLog2 = 4;
internal const int DczSizeLog2 = 4; internal const int DczSizeLog2 = 4;
private const int MinInstForCheck = 4000000; private const int MinInstForCheck = 4000000;
internal ExecutionMode ExecutionMode;
//AArch32 state.
public uint R0, R1, R2, R3,
R4, R5, R6, R7,
R8, R9, R10, R11,
R12, R13, R14, R15;
public bool Thumb;
//AArch64 state.
public ulong X0, X1, X2, X3, X4, X5, X6, X7, public ulong X0, X1, X2, X3, X4, X5, X6, X7,
X8, X9, X10, X11, X12, X13, X14, X15, X8, X9, X10, X11, X12, X13, X14, X15,
X16, X17, X18, X19, X20, X21, X22, X23, X16, X17, X18, X19, X20, X21, X22, X23,
@ -37,11 +23,18 @@ namespace ChocolArm64.State
V16, V17, V18, V19, V20, V21, V22, V23, V16, V17, V18, V19, V20, V21, V22, V23,
V24, V25, V26, V27, V28, V29, V30, V31; V24, V25, V26, V27, V28, V29, V30, V31;
public bool Aarch32;
public bool Thumb;
public bool BigEndian;
public bool Overflow; public bool Overflow;
public bool Carry; public bool Carry;
public bool Zero; public bool Zero;
public bool Negative; public bool Negative;
public int ElrHyp;
public bool Running { get; set; } public bool Running { get; set; }
public int Core { get; set; } public int Core { get; set; }
@ -59,10 +52,10 @@ namespace ChocolArm64.State
{ {
get get
{ {
return (Negative ? (int)PState.N : 0) | return (Negative ? (int)PState.NMask : 0) |
(Zero ? (int)PState.Z : 0) | (Zero ? (int)PState.ZMask : 0) |
(Carry ? (int)PState.C : 0) | (Carry ? (int)PState.CMask : 0) |
(Overflow ? (int)PState.V : 0); (Overflow ? (int)PState.VMask : 0);
} }
} }
@ -146,6 +139,18 @@ namespace ChocolArm64.State
Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode)); Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode));
} }
internal ExecutionMode GetExecutionMode()
{
if (!Aarch32)
{
return ExecutionMode.Aarch64;
}
else
{
return Thumb ? ExecutionMode.Aarch32Thumb : ExecutionMode.Aarch32Arm;
}
}
internal bool GetFpcrFlag(Fpcr flag) internal bool GetFpcrFlag(Fpcr flag)
{ {
return (Fpcr & (1 << (int)flag)) != 0; return (Fpcr & (1 << (int)flag)) != 0;

View file

@ -2,7 +2,8 @@ namespace ChocolArm64.State
{ {
enum ExecutionMode enum ExecutionMode
{ {
AArch32, Aarch64,
AArch64 Aarch32Arm,
Aarch32Thumb
} }
} }

View file

@ -5,19 +5,20 @@ namespace ChocolArm64.State
[Flags] [Flags]
enum PState enum PState
{ {
TBit = 5,
EBit = 9,
VBit = 28, VBit = 28,
CBit = 29, CBit = 29,
ZBit = 30, ZBit = 30,
NBit = 31, NBit = 31,
V = 1 << VBit, TMask = 1 << TBit,
C = 1 << CBit, EMask = 1 << EBit,
Z = 1 << ZBit,
N = 1 << NBit,
Nz = N | Z, VMask = 1 << VBit,
Cv = C | V, CMask = 1 << CBit,
ZMask = 1 << ZBit,
Nzcv = Nz | Cv NMask = 1 << NBit
} }
} }

View file

@ -43,6 +43,9 @@ namespace ChocolArm64.State
{ {
switch ((PState)Index) switch ((PState)Index)
{ {
case PState.TBit: return GetField(nameof(CpuThreadState.Thumb));
case PState.EBit: return GetField(nameof(CpuThreadState.BigEndian));
case PState.VBit: return GetField(nameof(CpuThreadState.Overflow)); case PState.VBit: return GetField(nameof(CpuThreadState.Overflow));
case PState.CBit: return GetField(nameof(CpuThreadState.Carry)); case PState.CBit: return GetField(nameof(CpuThreadState.Carry));
case PState.ZBit: return GetField(nameof(CpuThreadState.Zero)); case PState.ZBit: return GetField(nameof(CpuThreadState.Zero));

View file

@ -0,0 +1,41 @@
namespace ChocolArm64.State
{
static class RegisterAlias
{
public const int R8Usr = 8;
public const int R9Usr = 9;
public const int R10Usr = 10;
public const int R11Usr = 11;
public const int R12Usr = 12;
public const int SpUsr = 13;
public const int LrUsr = 14;
public const int SpHyp = 15;
public const int LrIrq = 16;
public const int SpIrq = 17;
public const int LrSvc = 18;
public const int SpSvc = 19;
public const int LrAbt = 20;
public const int SpAbt = 21;
public const int LrUnd = 22;
public const int SpUnd = 23;
public const int R8Fiq = 24;
public const int R9Fiq = 25;
public const int R10Fiq = 26;
public const int R11Fiq = 27;
public const int R12Fiq = 28;
public const int SpFiq = 29;
public const int LrFiq = 30;
public const int Aarch32Lr = 14;
public const int Aarch32Pc = 15;
public const int Lr = 30;
public const int Zr = 31;
}
}

View file

@ -23,6 +23,8 @@ namespace ChocolArm64.Translation
public Block CurrBlock => _currBlock; public Block CurrBlock => _currBlock;
public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex]; public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex];
public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO
private Dictionary<Block, ILBlock> _visitedBlocks; private Dictionary<Block, ILBlock> _visitedBlocks;
private Queue<Block> _branchTargets; private Queue<Block> _branchTargets;
@ -97,11 +99,52 @@ namespace ChocolArm64.Translation
EmitSynchronization(); EmitSynchronization();
} }
//On AARCH32 mode, (almost) all instruction can be conditionally
//executed, and the required condition is encoded on the opcode.
//We handle that here, skipping the instruction if the condition
//is not met. We can just ignore it when the condition is "Always",
//because in this case the instruction is always going to be executed.
//Condition "Never" is also ignored because this is a special encoding
//used by some unconditional instructions.
ILLabel lblSkip = null;
if (CurrOp is OpCode32 op && op.Cond < Condition.Al)
{
lblSkip = new ILLabel();
EmitCondBranch(lblSkip, GetInverseCond(op.Cond));
}
CurrOp.Emitter(this); CurrOp.Emitter(this);
if (lblSkip != null)
{
MarkLabel(lblSkip);
//If this is the last op on the block, and there's no "next" block
//after this one, then we have to return right now, with the address
//of the next instruction to be executed (in the case that the condition
//is false, and the branch was not taken, as all basic blocks should end with
//some kind of branch).
if (CurrOp == CurrBlock.GetLastOp() && CurrBlock.Next == null)
{
EmitStoreState();
EmitLdc_I8(CurrOp.Position + CurrOp.OpCodeSizeInBytes);
Emit(OpCodes.Ret);
}
}
_ilBlock.Add(new ILBarrier()); _ilBlock.Add(new ILBarrier());
} }
private Condition GetInverseCond(Condition cond)
{
//Bit 0 of all conditions is basically a negation bit, so
//inverting this bit has the effect of inverting the condition.
return (Condition)((int)cond ^ 1);
}
private void EmitSynchronization() private void EmitSynchronization()
{ {
EmitLdarg(TranslatedSub.StateArgIdx); EmitLdarg(TranslatedSub.StateArgIdx);
@ -243,27 +286,27 @@ namespace ChocolArm64.Translation
{ {
_optOpLastCompare = CurrOp; _optOpLastCompare = CurrOp;
InstEmitAluHelper.EmitDataLoadOpers(this); InstEmitAluHelper.EmitAluLoadOpers(this);
Stloc(CmpOptTmp2Index, IoType.Int); Stloc(CmpOptTmp2Index, IoType.Int);
Stloc(CmpOptTmp1Index, IoType.Int); Stloc(CmpOptTmp1Index, IoType.Int);
} }
private Dictionary<Cond, OpCode> _branchOps = new Dictionary<Cond, OpCode>() private Dictionary<Condition, OpCode> _branchOps = new Dictionary<Condition, OpCode>()
{ {
{ Cond.Eq, OpCodes.Beq }, { Condition.Eq, OpCodes.Beq },
{ Cond.Ne, OpCodes.Bne_Un }, { Condition.Ne, OpCodes.Bne_Un },
{ Cond.GeUn, OpCodes.Bge_Un }, { Condition.GeUn, OpCodes.Bge_Un },
{ Cond.LtUn, OpCodes.Blt_Un }, { Condition.LtUn, OpCodes.Blt_Un },
{ Cond.GtUn, OpCodes.Bgt_Un }, { Condition.GtUn, OpCodes.Bgt_Un },
{ Cond.LeUn, OpCodes.Ble_Un }, { Condition.LeUn, OpCodes.Ble_Un },
{ Cond.Ge, OpCodes.Bge }, { Condition.Ge, OpCodes.Bge },
{ Cond.Lt, OpCodes.Blt }, { Condition.Lt, OpCodes.Blt },
{ Cond.Gt, OpCodes.Bgt }, { Condition.Gt, OpCodes.Bgt },
{ Cond.Le, OpCodes.Ble } { Condition.Le, OpCodes.Ble }
}; };
public void EmitCondBranch(ILLabel target, Cond cond) public void EmitCondBranch(ILLabel target, Condition cond)
{ {
OpCode ilOp; OpCode ilOp;
@ -432,7 +475,7 @@ namespace ChocolArm64.Translation
public void EmitLdintzr(int index) public void EmitLdintzr(int index)
{ {
if (index != CpuThreadState.ZrIndex) if (index != RegisterAlias.Zr)
{ {
EmitLdint(index); EmitLdint(index);
} }
@ -444,7 +487,7 @@ namespace ChocolArm64.Translation
public void EmitStintzr(int index) public void EmitStintzr(int index)
{ {
if (index != CpuThreadState.ZrIndex) if (index != RegisterAlias.Zr)
{ {
EmitStint(index); EmitStint(index);
} }
@ -486,8 +529,16 @@ namespace ChocolArm64.Translation
public void EmitLdflg(int index) => Ldloc(index, IoType.Flag); public void EmitLdflg(int index) => Ldloc(index, IoType.Flag);
public void EmitStflg(int index) public void EmitStflg(int index)
{
//Set this only if any of the NZCV flag bits were modified.
//This is used to ensure that, when emiting a direct IL branch
//instruction for compare + branch sequences, we're not expecting
//to use comparison values from an old instruction, when in fact
//the flags were already overwritten by another instruction further along.
if (index >= (int)PState.VBit)
{ {
_optOpLastFlagSet = CurrOp; _optOpLastFlagSet = CurrOp;
}
Stloc(index, IoType.Flag); Stloc(index, IoType.Flag);
} }

View file

@ -24,34 +24,10 @@ namespace ChocolArm64
internal void ExecuteSubroutine(CpuThread thread, long position) internal void ExecuteSubroutine(CpuThread thread, long position)
{ {
//TODO: Both the execute A32/A64 methods should be merged on the future, ExecuteSubroutine(thread.ThreadState, thread.Memory, position);
//when both ISAs are implemented with the interpreter and JIT.
//As of now, A32 only has a interpreter and A64 a JIT.
CpuThreadState state = thread.ThreadState;
MemoryManager memory = thread.Memory;
if (state.ExecutionMode == ExecutionMode.AArch32)
{
ExecuteSubroutineA32(state, memory);
}
else
{
ExecuteSubroutineA64(state, memory, position);
}
} }
private void ExecuteSubroutineA32(CpuThreadState state, MemoryManager memory) private void ExecuteSubroutine(CpuThreadState state, MemoryManager memory, long position)
{
do
{
OpCode64 opCode = Decoder.DecodeOpCode(state, memory, state.R15);
opCode.Interpreter(state, memory, opCode);
}
while (state.R15 != 0 && state.Running);
}
private void ExecuteSubroutineA64(CpuThreadState state, MemoryManager memory, long position)
{ {
ProfileConfig tier0 = Profiles.CPU.TranslateTier0; ProfileConfig tier0 = Profiles.CPU.TranslateTier0;
ProfileConfig tier1 = Profiles.CPU.TranslateTier1; ProfileConfig tier1 = Profiles.CPU.TranslateTier1;
@ -69,14 +45,14 @@ namespace ChocolArm64
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub)) if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
{ {
Profile.Begin(tier0); Profile.Begin(tier0);
sub = TranslateTier0(state, memory, position); sub = TranslateTier0(memory, position, state.GetExecutionMode());
Profile.End(tier0); Profile.End(tier0);
} }
if (sub.ShouldReJit()) if (sub.ShouldReJit())
{ {
Profile.Begin(tier1); Profile.Begin(tier1);
TranslateTier1(state, memory, position); TranslateTier1(memory, position, state.GetExecutionMode());
Profile.End(tier1); Profile.End(tier1);
} }
@ -90,9 +66,9 @@ namespace ChocolArm64
return _cache.HasSubroutine(position); return _cache.HasSubroutine(position);
} }
private TranslatedSub TranslateTier0(CpuThreadState state, MemoryManager memory, long position) private TranslatedSub TranslateTier0(MemoryManager memory, long position, ExecutionMode mode)
{ {
Block block = Decoder.DecodeBasicBlock(state, memory, position); Block block = Decoder.DecodeBasicBlock(memory, position, mode);
ILEmitterCtx context = new ILEmitterCtx(_cache, block); ILEmitterCtx context = new ILEmitterCtx(_cache, block);
@ -109,9 +85,9 @@ namespace ChocolArm64
return subroutine; return subroutine;
} }
private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position) private void TranslateTier1(MemoryManager memory, long position, ExecutionMode mode)
{ {
Block graph = Decoder.DecodeSubroutine(_cache, state, memory, position); Block graph = Decoder.DecodeSubroutine(_cache, memory, position, mode);
ILEmitterCtx context = new ILEmitterCtx(_cache, graph); ILEmitterCtx context = new ILEmitterCtx(_cache, graph);

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;

View file

@ -5,6 +5,7 @@ namespace Ryujinx.Common.Logging
Audio, Audio,
Cpu, Cpu,
Font, Font,
Emulation,
Gpu, Gpu,
Hid, Hid,
Kernel, Kernel,

View file

@ -16,6 +16,8 @@ namespace Ryujinx.Common.Logging
public static event EventHandler<LogEventArgs> Updated; public static event EventHandler<LogEventArgs> Updated;
public static bool EnableFileLog { get; set; }
static Logger() static Logger()
{ {
m_EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length]; m_EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length];

View file

@ -278,11 +278,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
forceUpdate = false; forceUpdate = false;
if (New.ScissorTestEnabled[Index] != Old.ScissorTestEnabled[Index])
{
if (New.ScissorTestEnabled[Index]) if (New.ScissorTestEnabled[Index])
{ {
// If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports // If there is only 1 scissor test, geometry shaders are disabled so the scissor test applies to all viewports
if (New.ScissorTestCount == 1) if (New.ScissorTestCount == 1)
{ {
GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.ScissorTest);
@ -297,7 +295,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
GL.Disable(IndexedEnableCap.ScissorTest, Index); GL.Disable(IndexedEnableCap.ScissorTest, Index);
} }
}
if (New.ScissorTestEnabled[Index] && if (New.ScissorTestEnabled[Index] &&
(New.ScissorTestX[Index] != Old.ScissorTestX[Index] || (New.ScissorTestX[Index] != Old.ScissorTestX[Index] ||

View file

@ -367,6 +367,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Disable(EnableCap.FramebufferSrgb); GL.Disable(EnableCap.FramebufferSrgb);
// Will be re-enabled if needed while binding, called before any game GL calls
GL.Disable(EnableCap.ScissorTest);
GL.BlitFramebuffer( GL.BlitFramebuffer(
SrcX0, SrcX0,
SrcY0, SrcY0,

View file

@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private Dictionary<int, ShaderDeclInfo> m_OutAttributes; private Dictionary<int, ShaderDeclInfo> m_OutAttributes;
private Dictionary<int, ShaderDeclInfo> m_Gprs; private Dictionary<int, ShaderDeclInfo> m_Gprs;
private Dictionary<int, ShaderDeclInfo> m_GprsHalf;
private Dictionary<int, ShaderDeclInfo> m_Preds; private Dictionary<int, ShaderDeclInfo> m_Preds;
public IReadOnlyDictionary<ShaderIrOp, ShaderDeclInfo> CbTextures => m_CbTextures; public IReadOnlyDictionary<ShaderIrOp, ShaderDeclInfo> CbTextures => m_CbTextures;
@ -75,6 +76,7 @@ namespace Ryujinx.Graphics.Gal.Shader
public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes; public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs => m_Gprs; public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs => m_Gprs;
public IReadOnlyDictionary<int, ShaderDeclInfo> GprsHalf => m_GprsHalf;
public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds; public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds;
public GalShaderType ShaderType { get; private set; } public GalShaderType ShaderType { get; private set; }
@ -93,6 +95,7 @@ namespace Ryujinx.Graphics.Gal.Shader
m_OutAttributes = new Dictionary<int, ShaderDeclInfo>(); m_OutAttributes = new Dictionary<int, ShaderDeclInfo>();
m_Gprs = new Dictionary<int, ShaderDeclInfo>(); m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_GprsHalf = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>(); m_Preds = new Dictionary<int, ShaderDeclInfo>();
} }
@ -147,6 +150,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Merge(Combined.m_OutAttributes, VpA.m_OutAttributes, VpB.m_OutAttributes); Merge(Combined.m_OutAttributes, VpA.m_OutAttributes, VpB.m_OutAttributes);
Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs); Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs);
Merge(Combined.m_GprsHalf, VpA.m_GprsHalf, VpB.m_GprsHalf);
Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds); Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds);
//Merge input attributes. //Merge input attributes.
@ -343,8 +347,21 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
string Name = GetGprName(Gpr.Index); string Name = GetGprName(Gpr.Index);
if (Gpr.RegisterSize == ShaderRegisterSize.Single)
{
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index)); m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
} }
else if (Gpr.RegisterSize == ShaderRegisterSize.Half)
{
Name += "_h" + Gpr.HalfPart;
m_GprsHalf.TryAdd((Gpr.Index << 1) | Gpr.HalfPart, new ShaderDeclInfo(Name, Gpr.Index));
}
else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */
{
throw new NotImplementedException("Double types are not supported.");
}
}
break; break;
} }

View file

@ -377,6 +377,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclGprs() private void PrintDeclGprs()
{ {
PrintDecls(Decl.Gprs); PrintDecls(Decl.Gprs);
PrintDecls(Decl.GprsHalf);
} }
private void PrintDeclPreds() private void PrintDeclPreds()
@ -910,7 +911,23 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperGpr Gpr) private string GetName(ShaderIrOperGpr Gpr)
{ {
return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index); if (Gpr.IsConst)
{
return "0";
}
if (Gpr.RegisterSize == ShaderRegisterSize.Single)
{
return GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
}
else if (Gpr.RegisterSize == ShaderRegisterSize.Half)
{
return GetNameWithSwizzle(Decl.GprsHalf, (Gpr.Index << 1) | Gpr.HalfPart);
}
else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */
{
throw new NotImplementedException("Double types are not supported.");
}
} }
private string GetValue(ShaderIrOperImm Imm) private string GetValue(ShaderIrOperImm Imm)

View file

@ -6,6 +6,14 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
static partial class ShaderDecode static partial class ShaderDecode
{ {
private enum HalfOutputType
{
PackedFp16,
Fp32,
MergeH0,
MergeH1
}
public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position) public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position)
{ {
EmitBfe(Block, OpCode, ShaderOper.CR); EmitBfe(Block, OpCode, ShaderOper.CR);
@ -144,6 +152,16 @@ namespace Ryujinx.Graphics.Gal.Shader
EmitFsetp(Block, OpCode, ShaderOper.RR); EmitFsetp(Block, OpCode, ShaderOper.RR);
} }
public static void Hadd2_R(ShaderIrBlock Block, long OpCode, int Position)
{
EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fadd);
}
public static void Hmul2_R(ShaderIrBlock Block, long OpCode, int Position)
{
EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fmul);
}
public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position) public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position)
{ {
EmitIadd(Block, OpCode, ShaderOper.CR); EmitIadd(Block, OpCode, ShaderOper.CR);
@ -1041,6 +1059,47 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
} }
private static void EmitBinaryHalfOp(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
{
bool AbsB = OpCode.Read(30);
bool NegB = OpCode.Read(31);
bool Sat = OpCode.Read(32);
bool AbsA = OpCode.Read(44);
ShaderIrOperGpr[] VecA = OpCode.GprHalfVec8();
ShaderIrOperGpr[] VecB = OpCode.GprHalfVec20();
HalfOutputType OutputType = (HalfOutputType)OpCode.Read(49, 3);
int Elems = OutputType == HalfOutputType.PackedFp16 ? 2 : 1;
int First = OutputType == HalfOutputType.MergeH1 ? 1 : 0;
for (int Index = First; Index < Elems; Index++)
{
ShaderIrNode OperA = GetAluFabs (VecA[Index], AbsA);
ShaderIrNode OperB = GetAluFabsFneg(VecB[Index], AbsB, NegB);
ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
ShaderIrOperGpr Dst = GetHalfDst(OpCode, OutputType, Index);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, GetAluFsat(Op, Sat))));
}
}
private static ShaderIrOperGpr GetHalfDst(long OpCode, HalfOutputType OutputType, int Index)
{
switch (OutputType)
{
case HalfOutputType.PackedFp16: return OpCode.GprHalf0(Index);
case HalfOutputType.Fp32: return OpCode.Gpr0();
case HalfOutputType.MergeH0: return OpCode.GprHalf0(0);
case HalfOutputType.MergeH1: return OpCode.GprHalf0(1);
}
throw new ArgumentException(nameof(OutputType));
}
private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper) private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{ {
int SubOp = OpCode.Read(41, 3); int SubOp = OpCode.Read(41, 3);

View file

@ -6,8 +6,6 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
static partial class ShaderDecode static partial class ShaderDecode
{ {
private const int TempRegStart = 0x100;
private const int ____ = 0x0; private const int ____ = 0x0;
private const int R___ = 0x1; private const int R___ = 0x1;
private const int _G__ = 0x2; private const int _G__ = 0x2;
@ -149,14 +147,18 @@ namespace Ryujinx.Graphics.Gal.Shader
for (int Index = 0; Index < Coords.Length; Index++) for (int Index = 0; Index < Coords.Length; Index++)
{ {
Coords[Index] = OpCode.Gpr8(); ShaderIrOperGpr CoordReg = OpCode.Gpr8();
Coords[Index].Index += Index; CoordReg.Index += Index;
if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex) if (!CoordReg.IsValidRegister)
{ {
Coords[Index].Index = ShaderIrOperGpr.ZRIndex; CoordReg.Index = ShaderIrOperGpr.ZRIndex;
} }
Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index);
Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg));
} }
int ChMask = OpCode.Read(31, 0xf); int ChMask = OpCode.Read(31, 0xf);
@ -167,17 +169,6 @@ namespace Ryujinx.Graphics.Gal.Shader
ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs;
for (int Ch = 0; Ch < 4; Ch++)
{
ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
int RegInc = 0; int RegInc = 0;
for (int Ch = 0; Ch < 4; Ch++) for (int Ch = 0; Ch < 4; Ch++)
@ -187,18 +178,20 @@ namespace Ryujinx.Graphics.Gal.Shader
continue; continue;
} }
ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrOperGpr Dst = OpCode.Gpr0(); ShaderIrOperGpr Dst = OpCode.Gpr0();
Dst.Index += RegInc++; Dst.Index += RegInc++;
if (Dst.Index >= ShaderIrOperGpr.ZRIndex) if (!Dst.IsValidRegister || Dst.IsConst)
{ {
continue; continue;
} }
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src))); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
} }
} }
@ -215,57 +208,81 @@ namespace Ryujinx.Graphics.Gal.Shader
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
{ {
//TODO: Support other formats. //TODO: Support other formats.
ShaderIrNode OperA = OpCode.Gpr8();
ShaderIrNode OperB = OpCode.Gpr20();
ShaderIrNode OperC = OpCode.Imm13_36();
int LutIndex; int LutIndex;
LutIndex = OpCode.Gpr0 ().Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0;
LutIndex |= OpCode.Gpr28().Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0;
if (LutIndex == 0) if (LutIndex == 0)
{ {
//Both registers are RZ, color is not written anywhere. //Both destination registers are RZ, do nothing.
//So, the intruction is basically a no-op.
return; return;
} }
int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)]; bool Fp16 = !OpCode.Read(59);
for (int Ch = 0; Ch < 4; Ch++) int DstIncrement = 0;
{
ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
int RegInc = 0;
ShaderIrOperGpr GetDst() ShaderIrOperGpr GetDst()
{ {
ShaderIrOperGpr Dst; ShaderIrOperGpr Dst;
if (Fp16)
{
//FP16 mode, two components are packed on the two
//halfs of a 32-bits register, as two half-float values.
int HalfPart = DstIncrement & 1;
switch (LutIndex)
{
case 1: Dst = OpCode.GprHalf0(HalfPart); break;
case 2: Dst = OpCode.GprHalf28(HalfPart); break;
case 3: Dst = (DstIncrement >> 1) != 0
? OpCode.GprHalf28(HalfPart)
: OpCode.GprHalf0(HalfPart); break;
default: throw new InvalidOperationException();
}
}
else
{
//32-bits mode, each component uses one register.
//Two components uses two consecutive registers.
switch (LutIndex) switch (LutIndex)
{ {
case 1: Dst = OpCode.Gpr0(); break; case 1: Dst = OpCode.Gpr0(); break;
case 2: Dst = OpCode.Gpr28(); break; case 2: Dst = OpCode.Gpr28(); break;
case 3: Dst = (RegInc >> 1) != 0 case 3: Dst = (DstIncrement >> 1) != 0
? OpCode.Gpr28() ? OpCode.Gpr28()
: OpCode.Gpr0 (); break; : OpCode.Gpr0(); break;
default: throw new InvalidOperationException(); default: throw new InvalidOperationException();
} }
Dst.Index += RegInc++ & 1; Dst.Index += DstIncrement & 1;
}
DstIncrement++;
return Dst; return Dst;
} }
int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)];
if (ChMask == 0)
{
//All channels are disabled, do nothing.
return;
}
ShaderIrNode OperC = OpCode.Imm13_36();
ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0);
ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1);
Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8()));
Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20()));
for (int Ch = 0; Ch < 4; Ch++) for (int Ch = 0; Ch < 4; Ch++)
{ {
if (!IsChannelUsed(ChMask, Ch)) if (!IsChannelUsed(ChMask, Ch))
@ -273,13 +290,15 @@ namespace Ryujinx.Graphics.Gal.Shader
continue; continue;
} }
ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta);
ShaderIrOperGpr Dst = GetDst(); ShaderIrOperGpr Dst = GetDst();
if (Dst.Index != ShaderIrOperGpr.ZRIndex) if (Dst.IsValidRegister && !Dst.IsConst)
{ {
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src))); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
} }
} }
} }

View file

@ -75,6 +75,49 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperGpr(OpCode.Read(28, 0xff)); return new ShaderIrOperGpr(OpCode.Read(28, 0xff));
} }
private static ShaderIrOperGpr[] GprHalfVec8(this long OpCode)
{
return GetGprHalfVec2(OpCode.Read(8, 0xff), OpCode.Read(47, 3));
}
private static ShaderIrOperGpr[] GprHalfVec20(this long OpCode)
{
return GetGprHalfVec2(OpCode.Read(20, 0xff), OpCode.Read(28, 3));
}
private static ShaderIrOperGpr[] GetGprHalfVec2(int Gpr, int Mask)
{
if (Mask == 1)
{
//This value is used for FP32, the whole 32-bits register
//is used as each element on the vector.
return new ShaderIrOperGpr[]
{
new ShaderIrOperGpr(Gpr),
new ShaderIrOperGpr(Gpr)
};
}
ShaderIrOperGpr Low = new ShaderIrOperGpr(Gpr, 0);
ShaderIrOperGpr High = new ShaderIrOperGpr(Gpr, 1);
return new ShaderIrOperGpr[]
{
(Mask & 1) != 0 ? High : Low,
(Mask & 2) != 0 ? High : Low
};
}
private static ShaderIrOperGpr GprHalf0(this long OpCode, int HalfPart)
{
return new ShaderIrOperGpr(OpCode.Read(0, 0xff), HalfPart);
}
private static ShaderIrOperGpr GprHalf28(this long OpCode, int HalfPart)
{
return new ShaderIrOperGpr(OpCode.Read(28, 0xff), HalfPart);
}
private static ShaderIrOperImm Imm5_39(this long OpCode) private static ShaderIrOperImm Imm5_39(this long OpCode)
{ {
return new ShaderIrOperImm(OpCode.Read(39, 0x1f)); return new ShaderIrOperImm(OpCode.Read(39, 0x1f));

View file

@ -6,13 +6,26 @@ namespace Ryujinx.Graphics.Gal.Shader
public bool IsConst => Index == ZRIndex; public bool IsConst => Index == ZRIndex;
public bool IsValidRegister => (Index <= ZRIndex); public bool IsValidRegister => (uint)Index <= ZRIndex;
public int Index { get; set; } public int Index { get; set; }
public int HalfPart { get; set; }
public ShaderRegisterSize RegisterSize { get; private set; }
public ShaderIrOperGpr(int Index) public ShaderIrOperGpr(int Index)
{ {
this.Index = Index; this.Index = Index;
RegisterSize = ShaderRegisterSize.Single;
}
public ShaderIrOperGpr(int Index, int HalfPart)
{
this.Index = Index;
this.HalfPart = HalfPart;
RegisterSize = ShaderRegisterSize.Half;
} }
public static ShaderIrOperGpr MakeTemporary(int Index = 0) public static ShaderIrOperGpr MakeTemporary(int Index = 0)

View file

@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("010010111011xx", ShaderDecode.Fsetp_C); Set("010010111011xx", ShaderDecode.Fsetp_C);
Set("0011011x1011xx", ShaderDecode.Fsetp_I); Set("0011011x1011xx", ShaderDecode.Fsetp_I);
Set("010110111011xx", ShaderDecode.Fsetp_R); Set("010110111011xx", ShaderDecode.Fsetp_R);
Set("0101110100010x", ShaderDecode.Hadd2_R);
Set("0101110100001x", ShaderDecode.Hmul2_R);
Set("0100110010111x", ShaderDecode.I2f_C); Set("0100110010111x", ShaderDecode.I2f_C);
Set("0011100x10111x", ShaderDecode.I2f_I); Set("0011100x10111x", ShaderDecode.I2f_I);
Set("0101110010111x", ShaderDecode.I2f_R); Set("0101110010111x", ShaderDecode.I2f_R);
@ -118,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("110000xxxx111x", ShaderDecode.Tex); Set("110000xxxx111x", ShaderDecode.Tex);
Set("1101111010111x", ShaderDecode.Tex_B); Set("1101111010111x", ShaderDecode.Tex_B);
Set("1101111101001x", ShaderDecode.Texq); Set("1101111101001x", ShaderDecode.Texq);
Set("1101100xxxxxxx", ShaderDecode.Texs); Set("1101x00xxxxxxx", ShaderDecode.Texs);
Set("1101101xxxxxxx", ShaderDecode.Tlds); Set("1101101xxxxxxx", ShaderDecode.Tlds);
Set("01011111xxxxxx", ShaderDecode.Vmad); Set("01011111xxxxxx", ShaderDecode.Vmad);
Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); Set("0100111xxxxxxx", ShaderDecode.Xmad_CR);

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderRegisterSize
{
Half,
Single,
Double
}
}

View file

@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gal
ulong Instruction = 0; ulong Instruction = 0;
//Dump until a NOP instruction is found //Dump until a NOP instruction is found
while ((Instruction >> 52 & 0xfff8) != 0x50b0) while ((Instruction >> 48 & 0xfff8) != 0x50b0)
{ {
uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0); uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0);
uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4); uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4);

View file

@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Graphics3d
// Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1 // Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1
State.ScissorTestCount = 1; State.ScissorTestCount = 1;
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) for (int Index = 0; Index < State.ScissorTestCount; Index++)
{ {
State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4); State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4);
@ -473,6 +473,12 @@ namespace Ryujinx.Graphics.Graphics3d
if ((int)State.FlipY == -1) if ((int)State.FlipY == -1)
{ {
State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index]; State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index];
// Handle negative viewpont coordinate
if (State.ScissorTestY[Index] < 0)
{
State.ScissorTestY[Index] = 0;
}
} }
} }
} }

View file

@ -225,11 +225,6 @@ namespace Ryujinx.HLE.HOS
} }
} }
if (!metaData.Is64Bits)
{
throw new NotImplementedException("32-bit titles are unsupported!");
}
CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
LoadNso("rtld"); LoadNso("rtld");
@ -394,8 +389,58 @@ namespace Ryujinx.HLE.HOS
return; return;
} }
// This is not a normal NSP, it's actually a ExeFS as a NSP
Npdm metaData = null;
PfsFileEntry npdmFile = nsp.Files.FirstOrDefault(x => x.Name.Equals("main.npdm"));
if (npdmFile != null)
{
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
metaData = new Npdm(nsp.OpenFile(npdmFile).AsStream());
}
else
{
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
metaData = GetDefaultNpdm();
}
List<IExecutable> staticObjects = new List<IExecutable>();
void LoadNso(string searchPattern)
{
PfsFileEntry entry = nsp.Files.FirstOrDefault(x => x.Name.Equals(searchPattern));
if (entry != null)
{
Logger.PrintInfo(LogClass.Loader, $"Loading {entry.Name}...");
NxStaticObject staticObject = new NxStaticObject(nsp.OpenFile(entry).AsStream());
staticObjects.Add(staticObject);
}
}
CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
LoadNso("rtld");
LoadNso("main");
LoadNso("subsdk*");
LoadNso("sdk");
ContentManager.LoadEntries();
if (staticObjects.Count == 0)
{
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
} }
else
{
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
}
}
public void LoadNca(Nca mainNca, Nca controlNca) public void LoadNca(Nca mainNca, Nca controlNca)
{ {
@ -488,11 +533,6 @@ namespace Ryujinx.HLE.HOS
CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
} }
if (!metaData.Is64Bits)
{
throw new NotImplementedException("32-bit titles are not supported!");
}
LoadNso("rtld"); LoadNso("rtld");
LoadNso("main"); LoadNso("main");
LoadNso("subsdk"); LoadNso("subsdk");

View file

@ -78,6 +78,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
} }
} }
//TODO: ARM32.
long framePointer = (long)threadState.X29; long framePointer = (long)threadState.X29;
while (framePointer != 0) while (framePointer != 0)
@ -245,6 +246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset; long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset;
long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset; long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset;
//TODO: Elf32.
while (true) while (true)
{ {
long tagVal = memory.ReadInt64(dynamicOffset + 0); long tagVal = memory.ReadInt64(dynamicOffset + 0);

View file

@ -3,6 +3,7 @@ using ChocolArm64.Events;
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.SupervisorCall; using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
@ -797,6 +798,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
context.ThreadState.Interrupt += InterruptHandler; context.ThreadState.Interrupt += InterruptHandler;
context.ThreadState.SvcCall += _svcHandler.SvcCall; context.ThreadState.SvcCall += _svcHandler.SvcCall;
context.ThreadState.Undefined += UndefinedInstructionHandler;
} }
private void InterruptHandler(object sender, EventArgs e) private void InterruptHandler(object sender, EventArgs e)
@ -1021,5 +1023,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}."); Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
} }
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
{
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
}
} }
} }

View file

@ -152,8 +152,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint); Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint);
bool isAarch32 = (Owner.MmuFlags & 1) == 0;
Context.ThreadState.Aarch32 = isAarch32;
Context.ThreadState.X0 = argsPtr; Context.ThreadState.X0 = argsPtr;
if (isAarch32)
{
Context.ThreadState.X13 = (uint)stackTop;
}
else
{
Context.ThreadState.X31 = stackTop; Context.ThreadState.X31 = stackTop;
}
Context.ThreadState.CntfrqEl0 = 19200000; Context.ThreadState.CntfrqEl0 = 19200000;
Context.ThreadState.Tpidr = (long)_tlsAddress; Context.ThreadState.Tpidr = (long)_tlsAddress;

View file

@ -125,9 +125,14 @@ namespace Ryujinx.HLE.HOS
IExecutable[] staticObjects, IExecutable[] staticObjects,
byte[] arguments = null) byte[] arguments = null)
{ {
if (!metaData.Is64Bits)
{
Logger.PrintWarning(LogClass.Loader, "32-bits application detected!");
}
ulong argsStart = 0; ulong argsStart = 0;
int argsSize = 0; int argsSize = 0;
ulong codeStart = 0x8000000; ulong codeStart = metaData.Is64Bits ? 0x8000000UL : 0x200000UL;
int codeSize = 0; int codeSize = 0;
ulong[] nsoBase = new ulong[staticObjects.Length]; ulong[] nsoBase = new ulong[staticObjects.Length];

View file

@ -45,6 +45,32 @@ namespace Ryujinx.Tests.Cpu
0x0F808000u // MUL V0.2S, V0.2S, V0.S[0] 0x0F808000u // MUL V0.2S, V0.2S, V0.S[0]
}; };
} }
private static uint[] _SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_()
{
return new uint[]
{
0x0F402000u, // SMLAL V0.4S, V0.4H, V0.H[0]
0x0F406000u, // SMLSL V0.4S, V0.4H, V0.H[0]
0x0F40A000u, // SMULL V0.4S, V0.4H, V0.H[0]
0x2F402000u, // UMLAL V0.4S, V0.4H, V0.H[0]
0x2F406000u, // UMLSL V0.4S, V0.4H, V0.H[0]
0x2F40A000u // UMULL V0.4S, V0.4H, V0.H[0]
};
}
private static uint[] _SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_()
{
return new uint[]
{
0x0F802000u, // SMLAL V0.2D, V0.2S, V0.S[0]
0x0F806000u, // SMLSL V0.2D, V0.2S, V0.S[0]
0x0F80A000u, // SMULL V0.2D, V0.2S, V0.S[0]
0x2F802000u, // UMLAL V0.2D, V0.2S, V0.S[0]
0x2F806000u, // UMLSL V0.2D, V0.2S, V0.S[0]
0x2F80A000u // UMULL V0.2D, V0.2S, V0.S[0]
};
}
#endregion #endregion
private const int RndCnt = 2; private const int RndCnt = 2;
@ -103,6 +129,61 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn(); CompareAgainstUnicorn();
} }
[Test, Pairwise]
public void SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_")] uint opcodes,
[Values(0u)] uint rd,
[Values(1u, 0u)] uint rn,
[Values(2u, 0u)] uint rm,
[ValueSource("_4H_")] [Random(RndCnt)] ulong z,
[ValueSource("_4H_")] [Random(RndCnt)] ulong a,
[ValueSource("_4H_")] [Random(RndCnt)] ulong b,
[Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint index,
[Values(0b0u, 0b1u)] uint q) // <4H4S, 8H4S>
{
uint h = (index >> 2) & 1;
uint l = (index >> 1) & 1;
uint m = index & 1;
opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
opcodes |= (l << 21) | (m << 20) | (h << 11);
opcodes |= ((q & 1) << 30);
Vector128<float> v0 = MakeVectorE0E1(z, z);
Vector128<float> v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul);
Vector128<float> v2 = MakeVectorE0E1(b, b * h);
SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
[Test, Pairwise]
public void SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_")] uint opcodes,
[Values(0u)] uint rd,
[Values(1u, 0u)] uint rn,
[Values(2u, 0u)] uint rm,
[ValueSource("_2S_")] [Random(RndCnt)] ulong z,
[ValueSource("_2S_")] [Random(RndCnt)] ulong a,
[ValueSource("_2S_")] [Random(RndCnt)] ulong b,
[Values(0u, 1u, 2u, 3u)] uint index,
[Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D>
{
uint h = (index >> 1) & 1;
uint l = index & 1;
opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
opcodes |= (l << 21) | (h << 11);
opcodes |= ((q & 1) << 30);
Vector128<float> v0 = MakeVectorE0E1(z, z);
Vector128<float> v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul);
Vector128<float> v2 = MakeVectorE0E1(b, b * h);
SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
#endif #endif
} }
} }

View file

@ -78,6 +78,8 @@ namespace Ryujinx
MaxLevel = Convert.ToInt32(parser.Value("Profiling_Max_Level")), MaxLevel = Convert.ToInt32(parser.Value("Profiling_Max_Level")),
}); });
Logger.EnableFileLog = Convert.ToBoolean(parser.Value("Enable_File_Log"));
SystemLanguage SetLanguage = Enum.Parse<SystemLanguage>(parser.Value("System_Language")); SystemLanguage SetLanguage = Enum.Parse<SystemLanguage>(parser.Value("System_Language"));
device.System.State.SetLanguage(SetLanguage); device.System.State.SetLanguage(SetLanguage);
@ -134,7 +136,6 @@ namespace Ryujinx
}); });
NpadController = new NpadController( NpadController = new NpadController(
Convert.ToBoolean(parser.Value("GamePad_Enable")), Convert.ToBoolean(parser.Value("GamePad_Enable")),
Convert.ToInt32 (parser.Value("GamePad_Index")), Convert.ToInt32 (parser.Value("GamePad_Index")),
(float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), (float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture),

View file

@ -23,7 +23,10 @@ namespace Ryujinx
Config.Read(device); Config.Read(device);
Logger.Updated += ConsoleLog.Log; Logger.Updated += Log.LogMessage;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
if (args.Length == 1) if (args.Length == 1)
{ {
@ -62,6 +65,7 @@ namespace Ryujinx
device.LoadNca(args[0]); device.LoadNca(args[0]);
break; break;
case ".nsp": case ".nsp":
case ".pfs0":
Console.WriteLine("Loading as NSP."); Console.WriteLine("Loading as NSP.");
device.LoadNsp(args[0]); device.LoadNsp(args[0]);
break; break;
@ -89,6 +93,23 @@ namespace Ryujinx
audioOut.Dispose(); audioOut.Dispose();
} }
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Log.Close();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
if (e.IsTerminating)
{
Log.Close();
}
}
/// <summary> /// <summary>
/// Picks an <see cref="IAalOutput"/> audio output renderer supported on this machine /// Picks an <see cref="IAalOutput"/> audio output renderer supported on this machine
/// </summary> /// </summary>

View file

@ -37,6 +37,9 @@ Profiling_History = 5
#Set the maximum profiling level. Higher values may cause a heavy load on your system but will allow you to profile in more detail. #Set the maximum profiling level. Higher values may cause a heavy load on your system but will allow you to profile in more detail.
Profiling_Max_Level = 0 Profiling_Max_Level = 0
#Enable file logging
Enable_File_Log = false
#System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b #System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b
#Change System Language #Change System Language
System_Language = AmericanEnglish System_Language = AmericanEnglish

View file

@ -1,22 +1,27 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
namespace Ryujinx namespace Ryujinx
{ {
static class ConsoleLog static class Log
{ {
private static readonly string _path;
private static StreamWriter _logWriter;
private static Thread _messageThread; private static Thread _messageThread;
private static BlockingCollection<LogEventArgs> _messageQueue; private static BlockingCollection<LogEventArgs> _messageQueue;
private static Dictionary<LogLevel, ConsoleColor> _logColors; private static Dictionary<LogLevel, ConsoleColor> _logColors;
static ConsoleLog() static Log()
{ {
_logColors = new Dictionary<LogLevel, ConsoleColor>() _logColors = new Dictionary<LogLevel, ConsoleColor>()
{ {
@ -47,6 +52,13 @@ namespace Ryujinx
} }
}); });
_path = Path.Combine(Environment.CurrentDirectory, "Ryujinx.log");
if (Logger.EnableFileLog)
{
_logWriter = new StreamWriter(File.Open(_path,FileMode.Create, FileAccess.Write));
}
_messageThread.IsBackground = true; _messageThread.IsBackground = true;
_messageThread.Start(); _messageThread.Start();
} }
@ -82,26 +94,47 @@ namespace Ryujinx
} }
} }
string message = sb.ToString();
if (_logColors.TryGetValue(e.Level, out ConsoleColor color)) if (_logColors.TryGetValue(e.Level, out ConsoleColor color))
{ {
Console.ForegroundColor = color; Console.ForegroundColor = color;
Console.WriteLine(sb.ToString()); Console.WriteLine(message);
Console.ResetColor(); Console.ResetColor();
} }
else else
{ {
Console.WriteLine(sb.ToString()); Console.WriteLine(message);
}
if (Logger.EnableFileLog)
{
_logWriter.WriteLine(message);
} }
} }
public static void Log(object sender, LogEventArgs e) public static void LogMessage(object sender, LogEventArgs e)
{ {
if (!_messageQueue.IsAddingCompleted) if (!_messageQueue.IsAddingCompleted)
{ {
_messageQueue.Add(e); _messageQueue.Add(e);
} }
} }
public static void Close()
{
_messageQueue.CompleteAdding();
_messageThread.Join();
if (Logger.EnableFileLog)
{
_logWriter.Flush();
_logWriter.Close();
_logWriter.Dispose();
}
}
} }
} }