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:
parent
72157e03eb
commit
36b9ab0e48
57 changed files with 1274 additions and 495 deletions
|
@ -4,17 +4,15 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
struct Inst
|
||||
{
|
||||
public InstInterpreter Interpreter { get; private set; }
|
||||
public InstEmitter Emitter { get; private set; }
|
||||
public Type Type { get; private set; }
|
||||
public InstEmitter Emitter { get; }
|
||||
public Type Type { get; }
|
||||
|
||||
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;
|
||||
Type = type;
|
||||
Emitter = emitter;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
99
ChocolArm64/Instructions/InstEmit32Helper.cs
Normal file
99
ChocolArm64/Instructions/InstEmit32Helper.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
private static void EmitAdc(ILEmitterCtx context, bool setFlags)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
|
@ -44,14 +44,14 @@ namespace ChocolArm64.Instructions
|
|||
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)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
|
@ -59,14 +59,14 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
EmitAddsCCheck(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)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.And);
|
||||
|
||||
|
@ -74,17 +74,17 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
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 Bics(ILEmitterCtx context) => EmitBic(context, true);
|
||||
|
||||
private static void EmitBic(ILEmitterCtx context, bool setFlags)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Not);
|
||||
context.Emit(OpCodes.And);
|
||||
|
@ -96,7 +96,7 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitZnFlagCheck();
|
||||
}
|
||||
|
||||
EmitDataStore(context, setFlags);
|
||||
EmitAluStore(context, setFlags);
|
||||
}
|
||||
|
||||
public static void Cls(ILEmitterCtx context)
|
||||
|
@ -136,15 +136,15 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
public static void Eon(ILEmitterCtx context)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Not);
|
||||
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)
|
||||
{
|
||||
|
@ -166,18 +166,18 @@ namespace ChocolArm64.Instructions
|
|||
context.Emit(OpCodes.Or);
|
||||
}
|
||||
|
||||
EmitDataStore(context);
|
||||
EmitAluStore(context);
|
||||
}
|
||||
|
||||
public static void Lslv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl);
|
||||
public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un);
|
||||
public static void Lslv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shl);
|
||||
public static void Lsrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr_Un);
|
||||
|
||||
public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false);
|
||||
public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true);
|
||||
|
||||
private static void EmitSbc(ILEmitterCtx context, bool setFlags)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Sub);
|
||||
|
||||
|
@ -208,16 +208,16 @@ namespace ChocolArm64.Instructions
|
|||
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)
|
||||
{
|
||||
context.TryOptMarkCondWithoutCmp();
|
||||
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Sub);
|
||||
|
||||
|
@ -225,20 +225,20 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
EmitSubsCCheck(context);
|
||||
EmitSubsVCheck(context);
|
||||
EmitDataStoreS(context);
|
||||
EmitAluStoreS(context);
|
||||
}
|
||||
|
||||
public static void Orn(ILEmitterCtx context)
|
||||
{
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Not);
|
||||
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,
|
||||
nameof(SoftFallback.ReverseBits32),
|
||||
|
@ -283,22 +283,22 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
public static void Rorv(ILEmitterCtx context)
|
||||
{
|
||||
EmitDataLoadRn(context);
|
||||
EmitDataLoadShift(context);
|
||||
EmitAluLoadRn(context);
|
||||
EmitAluLoadShift(context);
|
||||
|
||||
context.Emit(OpCodes.Shr_Un);
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.EmitLdc_I4(context.CurrOp.GetBitsCount());
|
||||
|
||||
EmitDataLoadShift(context);
|
||||
EmitAluLoadShift(context);
|
||||
|
||||
context.Emit(OpCodes.Sub);
|
||||
context.Emit(OpCodes.Shl);
|
||||
context.Emit(OpCodes.Or);
|
||||
|
||||
EmitDataStore(context);
|
||||
EmitAluStore(context);
|
||||
}
|
||||
|
||||
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).
|
||||
context.EmitLdc_I(0);
|
||||
|
||||
EmitDataLoadRm(context);
|
||||
EmitAluLoadRm(context);
|
||||
|
||||
context.EmitLdc_I(0);
|
||||
|
||||
|
@ -325,13 +325,13 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
context.EmitLdc_I(intMin);
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.EmitLdc_I(intMin);
|
||||
|
||||
context.Emit(OpCodes.Ceq);
|
||||
|
||||
EmitDataLoadRm(context);
|
||||
EmitAluLoadRm(context);
|
||||
|
||||
context.EmitLdc_I(-1);
|
||||
|
||||
|
@ -341,38 +341,38 @@ namespace ChocolArm64.Instructions
|
|||
context.Emit(OpCodes.Pop);
|
||||
}
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitDataLoadRm(context);
|
||||
EmitAluLoadRn(context);
|
||||
EmitAluLoadRm(context);
|
||||
|
||||
context.Emit(ilOp);
|
||||
|
||||
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);
|
||||
|
||||
EmitDataStore(context);
|
||||
EmitAluStore(context);
|
||||
}
|
||||
|
||||
private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp)
|
||||
private static void EmitAluOpShift(ILEmitterCtx context, OpCode ilOp)
|
||||
{
|
||||
EmitDataLoadRn(context);
|
||||
EmitDataLoadShift(context);
|
||||
EmitAluLoadRn(context);
|
||||
EmitAluLoadShift(context);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -398,5 +398,22 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
123
ChocolArm64/Instructions/InstEmitAlu32.cs
Normal file
123
ChocolArm64/Instructions/InstEmitAlu32.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using ChocolArm64.Decoders;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instructions
|
||||
|
@ -14,7 +15,7 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitLdtmp();
|
||||
context.EmitLdtmp();
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.Emit(OpCodes.Ceq);
|
||||
|
||||
|
@ -24,7 +25,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
context.EmitLdtmp();
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.Emit(OpCodes.Clt_Un);
|
||||
context.Emit(OpCodes.Or);
|
||||
|
@ -37,7 +38,7 @@ namespace ChocolArm64.Instructions
|
|||
//C = Rd < Rn
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.Emit(OpCodes.Clt_Un);
|
||||
|
||||
|
@ -49,11 +50,11 @@ namespace ChocolArm64.Instructions
|
|||
//V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.Emit(OpCodes.Xor);
|
||||
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Xor);
|
||||
context.Emit(OpCodes.Not);
|
||||
|
@ -69,7 +70,7 @@ namespace ChocolArm64.Instructions
|
|||
public static void EmitSbcsCCheck(ILEmitterCtx context)
|
||||
{
|
||||
//C = (Rn == Rm && CIn) || Rn > Rm
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Ceq);
|
||||
|
||||
|
@ -77,7 +78,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
context.Emit(OpCodes.And);
|
||||
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Cgt_Un);
|
||||
context.Emit(OpCodes.Or);
|
||||
|
@ -88,7 +89,7 @@ namespace ChocolArm64.Instructions
|
|||
public static void EmitSubsCCheck(ILEmitterCtx context)
|
||||
{
|
||||
//C = Rn == Rm || Rn > Rm = !(Rn < Rm)
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Clt_Un);
|
||||
|
||||
|
@ -104,11 +105,11 @@ namespace ChocolArm64.Instructions
|
|||
//V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
EmitDataLoadRn(context);
|
||||
EmitAluLoadRn(context);
|
||||
|
||||
context.Emit(OpCodes.Xor);
|
||||
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Xor);
|
||||
context.Emit(OpCodes.And);
|
||||
|
@ -120,35 +121,76 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitStflg((int)PState.VBit);
|
||||
}
|
||||
|
||||
public static void EmitDataLoadRm(ILEmitterCtx context)
|
||||
public static void EmitAluLoadRm(ILEmitterCtx context)
|
||||
{
|
||||
context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm);
|
||||
}
|
||||
|
||||
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)
|
||||
if (context.CurrOp is IOpCodeAluRs64 op)
|
||||
{
|
||||
context.EmitLdintzr(op.Rn);
|
||||
context.EmitLdintzr(op.Rm);
|
||||
}
|
||||
else if (context.CurrOp is OpCodeAluRsImm32 op32)
|
||||
{
|
||||
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
|
||||
}
|
||||
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)
|
||||
{
|
||||
//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:
|
||||
context.EmitLdc_I(op.Imm);
|
||||
break;
|
||||
|
@ -170,23 +212,8 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitCast(op.IntType);
|
||||
context.EmitLsl(op.Shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false);
|
||||
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);
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,5 +244,219 @@ namespace ChocolArm64.Instructions
|
|||
context.Emit(OpCodes.And);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
context.MarkLabel(lblTrue);
|
||||
|
||||
EmitDataLoadOpers(context);
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
if (cmpOp == CcmpOp.Cmp)
|
||||
{
|
||||
|
|
|
@ -36,36 +36,10 @@ namespace ChocolArm64.Instructions
|
|||
OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp;
|
||||
|
||||
context.EmitLdc_I(op.Position + 4);
|
||||
context.EmitStint(CpuThreadState.LrIndex);
|
||||
context.EmitStint(RegisterAlias.Lr);
|
||||
context.EmitStoreState();
|
||||
|
||||
if (context.TryOptEmitSubroutineCall())
|
||||
{
|
||||
//Note: the return value of the called method will be placed
|
||||
//at the Stack, the return value is always a Int64 with the
|
||||
//return address of the function. We check if the address is
|
||||
//correct, if it isn't we keep returning until we reach the dispatcher.
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitLdc_I8(op.Position + 4);
|
||||
|
||||
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);
|
||||
}
|
||||
InstEmitFlowHelper.EmitCall(context, op.Imm);
|
||||
}
|
||||
|
||||
public static void Blr(ILEmitterCtx context)
|
||||
|
@ -74,7 +48,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
context.EmitLdintzr(op.Rn);
|
||||
context.EmitLdc_I(op.Position + 4);
|
||||
context.EmitStint(CpuThreadState.LrIndex);
|
||||
context.EmitStint(RegisterAlias.Lr);
|
||||
context.EmitStoreState();
|
||||
|
||||
context.Emit(OpCodes.Ret);
|
||||
|
@ -106,7 +80,7 @@ namespace ChocolArm64.Instructions
|
|||
public static void Ret(ILEmitterCtx context)
|
||||
{
|
||||
context.EmitStoreState();
|
||||
context.EmitLdint(CpuThreadState.LrIndex);
|
||||
context.EmitLdint(RegisterAlias.Lr);
|
||||
|
||||
context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
@ -128,7 +102,7 @@ namespace ChocolArm64.Instructions
|
|||
EmitBranch(context, ilOp);
|
||||
}
|
||||
|
||||
private static void EmitBranch(ILEmitterCtx context, Cond cond)
|
||||
private static void EmitBranch(ILEmitterCtx context, Condition cond)
|
||||
{
|
||||
OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp;
|
||||
|
||||
|
|
99
ChocolArm64/Instructions/InstEmitFlow32.cs
Normal file
99
ChocolArm64/Instructions/InstEmitFlow32.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
39
ChocolArm64/Instructions/InstEmitFlowHelper.cs
Normal file
39
ChocolArm64/Instructions/InstEmitFlowHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -168,7 +168,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
context.EmitLdint(op.Rn);
|
||||
|
||||
if (op.Rm != CpuThreadState.ZrIndex)
|
||||
if (op.Rm != RegisterAlias.Zr)
|
||||
{
|
||||
context.EmitLdint(op.Rm);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue