Add ARM32 support on the translator (#561)

* Remove ARM32 interpreter and add ARM32 support on the translator

* Nits.

* Rename Cond -> Condition

* Align code again

* Rename Data to Alu

* Enable ARM32 support and handle undefined instructions

* Use the IsThumb method to check if its a thumb opcode

* Remove another 32-bits check
This commit is contained in:
gdkchan 2019-01-24 23:59:53 -02:00 committed by GitHub
parent 72157e03eb
commit 36b9ab0e48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 1274 additions and 495 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,35 @@ 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.
return opCode is IOpCodeBImm32 ||
opCode is IOpCodeBReg32 ||
(opCode is IOpCodeAlu32 op && op.Rd == RegisterAlias.Aarch32Pc);
} }
private static bool IsException(OpCode64 opCode) private static bool IsException(OpCode64 opCode)
@ -155,20 +180,26 @@ 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 IOpCodeAlu32 : IOpCode32
{
int Rd { get; }
int Rn { get; }
bool SetFlags { get; }
}
}

View file

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

View file

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

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoders
{
interface IOpCodeBReg32 : IOpCode32
{
int Rm { 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

@ -9,9 +9,10 @@ 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 InstEmitter Emitter { get; protected set; } public int OpCodeSizeInBytes { get; protected set; } = 4;
public InstInterpreter Interpreter { get; protected set; }
public RegisterSize RegisterSize { get; protected set; } public InstEmitter Emitter { 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)
{ {
@ -20,8 +21,7 @@ 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

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAlu32 : OpCode32, IOpCodeAlu32
{
public int Rd { get; private set; }
public int Rn { get; private set; }
public bool SetFlags { get; private set; }
public OpCodeAlu32(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 OpCodeAluImm32 : OpCodeAlu32
{
public int Imm { get; private set; }
public bool IsRotated { get; private set; }
public OpCodeAluImm32(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,22 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAluImm8T16 : OpCodeT16, IOpCodeAlu32
{
private int _rdn;
public int Rd => _rdn;
public int Rn => _rdn;
public bool SetFlags => false;
public int Imm { get; private set; }
public OpCodeAluImm8T16(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7;
}
}
}

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAluRsImm32 : OpCodeAlu32
{
public int Rm { get; private set; }
public int Imm { get; private set; }
public ShiftType ShiftType { get; private set; }
public OpCodeAluRsImm32(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 OpCodeBImm32 : OpCode32, IOpCodeBImm32
{
public long Imm { get; private set; }
public OpCodeBImm32(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

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

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

View file

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

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)
{ {
@ -21,11 +21,11 @@ namespace ChocolArm64.Decoders
return; return;
} }
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

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

@ -4,17 +4,15 @@ 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,99 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
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 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,123 @@
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)
{
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Add);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
EmitAddsCCheck(context);
EmitAddsVCheck(context);
}
EmitAluStore(context);
}
public static void Mov(ILEmitterCtx context)
{
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
EmitAluLoadOper2(context);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
}
EmitAluStore(context);
}
public static void Sub(ILEmitterCtx context)
{
IOpCodeAlu32 op = (IOpCodeAlu32)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)
{
IOpCodeAlu32 op = (IOpCodeAlu32)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)
{
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,35 +121,76 @@ 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)
}
public static void EmitDataLoadOpers(ILEmitterCtx context)
{
EmitDataLoadRn(context);
EmitDataLoadOper2(context);
}
public static void EmitDataLoadRn(ILEmitterCtx context)
{
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
{ {
context.EmitLdintzr(op.Rn); context.EmitLdintzr(op.Rm);
}
else if (context.CurrOp is OpCodeAluRsImm32 op32)
{
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
} }
else else
{ {
context.EmitLdint(op.Rn); throw new InvalidOperationException();
} }
} }
public static void EmitDataLoadOper2(ILEmitterCtx context) public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true)
{
EmitAluLoadRn(context);
EmitAluLoadOper2(context, setCarry);
}
public static void EmitAluLoadRn(ILEmitterCtx context)
{
if (context.CurrOp is IOpCodeAlu64 op)
{
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
{
context.EmitLdintzr(op.Rn);
}
else
{
context.EmitLdint(op.Rn);
}
}
else if (context.CurrOp is IOpCodeAlu32 op32)
{
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn);
}
else
{
throw new InvalidOperationException();
}
}
public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true)
{ {
switch (context.CurrOp) switch (context.CurrOp)
{ {
//ARM32.
case OpCodeAluImm32 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 OpCodeAluRsImm32 op:
EmitLoadRmShiftedByImmediate(context, op, setCarry);
break;
case OpCodeAluImm8T16 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, OpCodeAluRsImm32 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,99 @@
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)
{
IOpCodeBImm32 op = (IOpCodeBImm32)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)
{
IOpCodeBReg32 op = (IOpCodeBReg32)context.CurrOp;
context.EmitStoreState();
EmitLoadFromRegister(context, op.Rm);
EmitBxWritePc(context);
}
private static void Blx(ILEmitterCtx context, bool x)
{
IOpCodeBImm32 op = (IOpCodeBImm32)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);
}
private static void EmitBxWritePc(ILEmitterCtx context)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.TBit);
context.EmitLdc_I4(~1);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
}
}

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

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

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

@ -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,47 @@ 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(OpCodeAluImm32));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCodeAluRsImm32));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCodeBImm32));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCodeBImm32));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCodeBImm32));
SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCodeBReg32));
SetT32( "010001110xxxx000", InstEmit32.Bx, typeof(OpCodeBRegT16));
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm32));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCodeAluRsImm32));
SetT32( "00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm8T16));
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCodeAluImm32));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCodeAluRsImm32));
#endregion #endregion
#region "OpCode Table (AArch64)" #region "OpCode Table (AArch64)"
@ -544,63 +576,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)
{
int mask = ToFastLookupIndex(inst.Mask);
int value = ToFastLookupIndex(inst.Value);
for (int i = 0; i < _fastLookupSize; i++)
{
if ((i & mask) == value)
{
tmp[i].Add(inst);
}
}
}
for (int i = 0; i < _fastLookupSize; i++)
{
_instA64FastLookup[i] = tmp[i].ToArray();
}
#endregion
} }
private class InstInfo private static void SetA32(string encoding, InstEmitter emitter, Type type)
{ {
public int Mask; Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Arm);
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 void SetT32(string encoding, InstEmitter emitter, Type type)
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); if (encoding.Length == 16)
{
encoding = "xxxxxxxxxxxxxxxx" + encoding;
}
Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Thumb);
} }
private static void SetA64(string encoding, InstEmitter emitter, Type type) 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 +671,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)
{
List<InstInfo>[] tmp = new List<InstInfo>[FastLookupSize];
for (int i = 0; i < FastLookupSize; i++)
{ {
_allInstA32.Add(info); 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 +734,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,13 @@ 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; 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,
@ -42,6 +30,10 @@ namespace ChocolArm64.State
public bool Zero; public bool Zero;
public bool Negative; public bool Negative;
public bool IsAarch32;
public int ElrHyp;
public bool Running { get; set; } public bool Running { get; set; }
public int Core { get; set; } public int Core { get; set; }
@ -146,6 +138,18 @@ namespace ChocolArm64.State
Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode)); Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode));
} }
internal ExecutionMode GetExecutionMode()
{
if (!IsAarch32)
{
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,11 +5,15 @@ namespace ChocolArm64.State
[Flags] [Flags]
enum PState enum PState
{ {
TBit = 5,
VBit = 28, VBit = 28,
CBit = 29, CBit = 29,
ZBit = 30, ZBit = 30,
NBit = 31, NBit = 31,
T = 1 << TBit,
V = 1 << VBit, V = 1 << VBit,
C = 1 << CBit, C = 1 << CBit,
Z = 1 << ZBit, Z = 1 << ZBit,

View file

@ -43,6 +43,8 @@ namespace ChocolArm64.State
{ {
switch ((PState)Index) switch ((PState)Index)
{ {
case PState.TBit: return GetField(nameof(CpuThreadState.Thumb));
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);
} }

View file

@ -4,7 +4,6 @@ using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using ChocolArm64.Translation; using ChocolArm64.Translation;
using System; using System;
using System.Reflection.Emit;
namespace ChocolArm64 namespace ChocolArm64
{ {
@ -23,34 +22,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)
{ {
do do
{ {
@ -61,12 +36,12 @@ namespace ChocolArm64
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub)) if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
{ {
sub = TranslateTier0(state, memory, position); sub = TranslateTier0(memory, position, state.GetExecutionMode());
} }
if (sub.ShouldReJit()) if (sub.ShouldReJit())
{ {
TranslateTier1(state, memory, position); TranslateTier1(memory, position, state.GetExecutionMode());
} }
position = sub.Execute(state, memory); position = sub.Execute(state, memory);
@ -79,9 +54,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);
@ -98,9 +73,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

@ -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");
@ -428,11 +423,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");
@ -543,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,6 +152,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint); Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint);
Context.ThreadState.IsAarch32 = (Owner.MmuFlags & 1) == 0;
Context.ThreadState.X0 = argsPtr; Context.ThreadState.X0 = argsPtr;
Context.ThreadState.X31 = stackTop; Context.ThreadState.X31 = stackTop;

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];