2019-01-25 02:59:53 +01:00
|
|
|
using ChocolArm64.Decoders;
|
|
|
|
using ChocolArm64.State;
|
|
|
|
using ChocolArm64.Translation;
|
|
|
|
using System;
|
Implement some ARM32 memory instructions and CMP (#565)
* Implement ARM32 memory instructions: LDM, LDR, LDRB, LDRD, LDRH, LDRSB, LDRSH, STM, STR, STRB, STRD, STRH (immediate and register + immediate variants), implement CMP (immediate and register shifted by immediate variants)
* Rename some opcode classes and flag masks for consistency
* Fix a few suboptimal ARM32 codegen issues, only loads should be considered on decoder when checking if Rt == PC, and only NZCV flags should be considered for comparison optimizations
* Take into account Rt2 for LDRD instructions aswell when checking if the instruction changes PC
* Re-align arm32 instructions on the opcode table
2019-01-29 17:06:11 +01:00
|
|
|
using System.Reflection.Emit;
|
2019-01-25 02:59:53 +01:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement some ARM32 memory instructions and CMP (#565)
* Implement ARM32 memory instructions: LDM, LDR, LDRB, LDRD, LDRH, LDRSB, LDRSH, STM, STR, STRB, STRD, STRH (immediate and register + immediate variants), implement CMP (immediate and register shifted by immediate variants)
* Rename some opcode classes and flag masks for consistency
* Fix a few suboptimal ARM32 codegen issues, only loads should be considered on decoder when checking if Rt == PC, and only NZCV flags should be considered for comparison optimizations
* Take into account Rt2 for LDRD instructions aswell when checking if the instruction changes PC
* Re-align arm32 instructions on the opcode table
2019-01-29 17:06:11 +01:00
|
|
|
public static void EmitStoreToRegister(ILEmitterCtx context, int register)
|
|
|
|
{
|
|
|
|
if (register == RegisterAlias.Aarch32Pc)
|
|
|
|
{
|
|
|
|
context.EmitStoreState();
|
|
|
|
|
|
|
|
EmitBxWritePc(context);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
context.EmitStint(GetRegisterAlias(context.Mode, register));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void EmitBxWritePc(ILEmitterCtx context)
|
|
|
|
{
|
|
|
|
context.Emit(OpCodes.Dup);
|
|
|
|
|
|
|
|
context.EmitLdc_I4(1);
|
|
|
|
|
|
|
|
context.Emit(OpCodes.And);
|
|
|
|
context.Emit(OpCodes.Dup);
|
|
|
|
|
|
|
|
context.EmitStflg((int)PState.TBit);
|
|
|
|
|
|
|
|
ILLabel lblArmMode = new ILLabel();
|
|
|
|
ILLabel lblEnd = new ILLabel();
|
|
|
|
|
|
|
|
context.Emit(OpCodes.Brtrue_S, lblArmMode);
|
|
|
|
|
|
|
|
context.EmitLdc_I4(~1);
|
|
|
|
|
|
|
|
context.Emit(OpCodes.Br_S, lblEnd);
|
|
|
|
|
|
|
|
context.MarkLabel(lblArmMode);
|
|
|
|
|
|
|
|
context.EmitLdc_I4(~3);
|
|
|
|
|
|
|
|
context.MarkLabel(lblEnd);
|
|
|
|
|
|
|
|
context.Emit(OpCodes.And);
|
|
|
|
context.Emit(OpCodes.Conv_U8);
|
|
|
|
context.Emit(OpCodes.Ret);
|
|
|
|
}
|
|
|
|
|
2019-01-25 02:59:53 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|