1
0
Fork 0
mirror of https://github.com/Ryujinx/Ryujinx.git synced 2024-12-28 20:46:02 +00:00

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
This commit is contained in:
gdkchan 2019-01-29 13:06:11 -03:00 committed by GitHub
parent 8f7fcede7f
commit c1bdf19061
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 686 additions and 87 deletions

View file

@ -168,9 +168,59 @@ namespace ChocolArm64.Decoders
{ {
//Note: On ARM32, most ALU operations can write to R15 (PC), //Note: On ARM32, most ALU operations can write to R15 (PC),
//so we must consider such operations as a branch in potential aswell. //so we must consider such operations as a branch in potential aswell.
return opCode is IOpCodeBImm32 || if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
opCode is IOpCodeBReg32 || {
(opCode is IOpCodeAlu32 op && op.Rd == RegisterAlias.Aarch32Pc); return true;
}
//Same thing for memory operations. We have the cases where PC is a target
//register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
//a write back to PC (wback == true && Rn == 15), however the later may
//be "undefined" depending on the CPU, so compilers should not produce that.
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
{
int rt, rn;
bool wBack, isLoad;
if (opCode is IOpCode32Mem opMem)
{
rt = opMem.Rt;
rn = opMem.Rn;
wBack = opMem.WBack;
isLoad = opMem.IsLoad;
//For the dual load, we also need to take into account the
//case were Rt2 == 15 (PC).
if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd)
{
rt = RegisterAlias.Aarch32Pc;
}
}
else if (opCode is IOpCode32MemMult opMemMult)
{
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
rn = opMemMult.Rn;
wBack = opMemMult.PostOffset != 0;
isLoad = opMemMult.IsLoad;
}
else
{
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
}
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
(rn == RegisterAlias.Aarch32Pc && wBack))
{
return true;
}
}
//Explicit branch instructions.
return opCode is IOpCode32BImm ||
opCode is IOpCode32BReg;
} }
private static bool IsException(OpCode64 opCode) private static bool IsException(OpCode64 opCode)

View file

@ -1,6 +1,6 @@
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {
interface IOpCodeAlu32 : IOpCode32 interface IOpCode32Alu : IOpCode32
{ {
int Rd { get; } int Rd { get; }
int Rn { get; } int Rn { get; }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,11 +2,11 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {
class OpCodeBImm32 : OpCode32, IOpCodeBImm32 class OpCode32BImm : OpCode32, IOpCode32BImm
{ {
public long Imm { get; private set; } public long Imm { get; private set; }
public OpCodeBImm32(Inst inst, long position, int opCode) : base(inst, position, opCode) public OpCode32BImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{ {
uint pc = GetPc(); uint pc = GetPc();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders namespace ChocolArm64.Decoders
{ {
class OpCodeAluImm8T16 : OpCodeT16, IOpCodeAlu32 class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu
{ {
private int _rdn; private int _rdn;
@ -13,7 +13,7 @@ namespace ChocolArm64.Decoders
public int Imm { get; private set; } public int Imm { get; private set; }
public OpCodeAluImm8T16(Inst inst, long position, int opCode) : base(inst, position, opCode) public OpCodeT16AluImm8(Inst inst, long position, int opCode) : base(inst, position, opCode)
{ {
Imm = (opCode >> 0) & 0xff; Imm = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7; _rdn = (opCode >> 8) & 0x7;

View file

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

View file

@ -2,6 +2,7 @@ using ChocolArm64.Decoders;
using ChocolArm64.State; using ChocolArm64.State;
using ChocolArm64.Translation; using ChocolArm64.Translation;
using System; using System;
using System.Reflection.Emit;
namespace ChocolArm64.Instructions namespace ChocolArm64.Instructions
{ {
@ -26,6 +27,51 @@ namespace ChocolArm64.Instructions
} }
} }
public static void EmitStoreToRegister(ILEmitterCtx context, int register)
{
if (register == RegisterAlias.Aarch32Pc)
{
context.EmitStoreState();
EmitBxWritePc(context);
}
else
{
context.EmitStint(GetRegisterAlias(context.Mode, register));
}
}
public static void EmitBxWritePc(ILEmitterCtx context)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Dup);
context.EmitStflg((int)PState.TBit);
ILLabel lblArmMode = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblArmMode);
context.EmitLdc_I4(~1);
context.Emit(OpCodes.Br_S, lblEnd);
context.MarkLabel(lblArmMode);
context.EmitLdc_I4(~3);
context.MarkLabel(lblEnd);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
public static int GetRegisterAlias(Aarch32Mode mode, int register) public static int GetRegisterAlias(Aarch32Mode mode, int register)
{ {
//Only registers >= 8 are banked, with registers in the range [8, 12] being //Only registers >= 8 are banked, with registers in the range [8, 12] being

View file

@ -12,7 +12,7 @@ namespace ChocolArm64.Instructions
{ {
public static void Add(ILEmitterCtx context) public static void Add(ILEmitterCtx context)
{ {
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false); EmitAluLoadOpers(context, setCarry: false);
@ -29,9 +29,25 @@ namespace ChocolArm64.Instructions
EmitAluStore(context); EmitAluStore(context);
} }
public static void Cmp(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Sub);
context.EmitZnFlagCheck();
EmitSubsCCheck(context);
EmitSubsVCheck(context);
context.Emit(OpCodes.Pop);
}
public static void Mov(ILEmitterCtx context) public static void Mov(ILEmitterCtx context)
{ {
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOper2(context); EmitAluLoadOper2(context);
@ -45,7 +61,7 @@ namespace ChocolArm64.Instructions
public static void Sub(ILEmitterCtx context) public static void Sub(ILEmitterCtx context)
{ {
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false); EmitAluLoadOpers(context, setCarry: false);
@ -64,7 +80,7 @@ namespace ChocolArm64.Instructions
private static void EmitAluStore(ILEmitterCtx context) private static void EmitAluStore(ILEmitterCtx context)
{ {
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp; IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
if (op.Rd == RegisterAlias.Aarch32Pc) if (op.Rd == RegisterAlias.Aarch32Pc)
{ {
@ -106,6 +122,8 @@ namespace ChocolArm64.Instructions
private static void EmitAluWritePc(ILEmitterCtx context) private static void EmitAluWritePc(ILEmitterCtx context)
{ {
context.EmitStoreState();
if (IsThumb(context.CurrOp)) if (IsThumb(context.CurrOp))
{ {
context.EmitLdc_I4(~1); context.EmitLdc_I4(~1);

View file

@ -127,7 +127,7 @@ namespace ChocolArm64.Instructions
{ {
context.EmitLdintzr(op.Rm); context.EmitLdintzr(op.Rm);
} }
else if (context.CurrOp is OpCodeAluRsImm32 op32) else if (context.CurrOp is OpCode32AluRsImm op32)
{ {
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm); InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
} }
@ -156,7 +156,7 @@ namespace ChocolArm64.Instructions
context.EmitLdint(op.Rn); context.EmitLdint(op.Rn);
} }
} }
else if (context.CurrOp is IOpCodeAlu32 op32) else if (context.CurrOp is IOpCode32Alu op32)
{ {
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn); InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn);
} }
@ -171,7 +171,7 @@ namespace ChocolArm64.Instructions
switch (context.CurrOp) switch (context.CurrOp)
{ {
//ARM32. //ARM32.
case OpCodeAluImm32 op: case OpCode32AluImm op:
context.EmitLdc_I4(op.Imm); context.EmitLdc_I4(op.Imm);
if (op.SetFlags && op.IsRotated) if (op.SetFlags && op.IsRotated)
@ -182,11 +182,11 @@ namespace ChocolArm64.Instructions
} }
break; break;
case OpCodeAluRsImm32 op: case OpCode32AluRsImm op:
EmitLoadRmShiftedByImmediate(context, op, setCarry); EmitLoadRmShiftedByImmediate(context, op, setCarry);
break; break;
case OpCodeAluImm8T16 op: case OpCodeT16AluImm8 op:
context.EmitLdc_I4(op.Imm); context.EmitLdc_I4(op.Imm);
break; break;
@ -246,7 +246,7 @@ namespace ChocolArm64.Instructions
} }
//ARM32 helpers. //ARM32 helpers.
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCodeAluRsImm32 op, bool setCarry) private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm op, bool setCarry)
{ {
int shift = op.Imm; int shift = op.Imm;

View file

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

View file

@ -37,18 +37,32 @@ namespace ChocolArm64
{ {
#region "OpCode Table (AArch32)" #region "OpCode Table (AArch32)"
//Integer //Integer
SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCodeAluImm32)); SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCode32AluImm));
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCodeAluRsImm32)); SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCode32AluRsImm));
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCodeBImm32)); SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCode32BImm));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCodeBImm32)); SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCode32BImm));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCodeBImm32)); SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCode32BImm));
SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCodeBReg32)); SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCode32BReg));
SetT32( "010001110xxxx000", InstEmit32.Bx, typeof(OpCodeBRegT16)); SetT32("010001110xxxx000", InstEmit32.Bx, typeof(OpCodeT16BReg));
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm32)); SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstEmit32.Cmp, typeof(OpCode32AluImm));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCodeAluRsImm32)); SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstEmit32.Cmp, typeof(OpCode32AluRsImm));
SetT32( "00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm8T16)); SetA32("<<<<100xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldm, typeof(OpCode32MemMult));
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCodeAluImm32)); SetA32("<<<<010xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldr, typeof(OpCode32MemImm));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCodeAluRsImm32)); SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldrb, typeof(OpCode32MemImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstEmit32.Ldrh, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrsb, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstEmit32.Ldrsh, typeof(OpCode32MemImm8));
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCode32AluImm));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCode32AluRsImm));
SetT32("00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeT16AluImm8));
SetA32("<<<<100xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Stm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Str, typeof(OpCode32MemImm));
SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Strb, typeof(OpCode32MemImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstEmit32.Strd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstEmit32.Strh, typeof(OpCode32MemImm8));
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCode32AluImm));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCode32AluRsImm));
#endregion #endregion
#region "OpCode Table (AArch64)" #region "OpCode Table (AArch64)"

View file

@ -13,8 +13,6 @@ namespace ChocolArm64.State
private const int MinInstForCheck = 4000000; private const int MinInstForCheck = 4000000;
public bool Thumb;
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,
@ -25,13 +23,16 @@ namespace ChocolArm64.State
V16, V17, V18, V19, V20, V21, V22, V23, V16, V17, V18, V19, V20, V21, V22, V23,
V24, V25, V26, V27, V28, V29, V30, V31; V24, V25, V26, V27, V28, V29, V30, V31;
public bool Aarch32;
public bool Thumb;
public bool BigEndian;
public bool Overflow; public bool Overflow;
public bool Carry; public bool Carry;
public bool Zero; public bool Zero;
public bool Negative; public bool Negative;
public bool IsAarch32;
public int ElrHyp; public int ElrHyp;
public bool Running { get; set; } public bool Running { get; set; }
@ -51,10 +52,10 @@ namespace ChocolArm64.State
{ {
get get
{ {
return (Negative ? (int)PState.N : 0) | return (Negative ? (int)PState.NMask : 0) |
(Zero ? (int)PState.Z : 0) | (Zero ? (int)PState.ZMask : 0) |
(Carry ? (int)PState.C : 0) | (Carry ? (int)PState.CMask : 0) |
(Overflow ? (int)PState.V : 0); (Overflow ? (int)PState.VMask : 0);
} }
} }
@ -140,7 +141,7 @@ namespace ChocolArm64.State
internal ExecutionMode GetExecutionMode() internal ExecutionMode GetExecutionMode()
{ {
if (!IsAarch32) if (!Aarch32)
{ {
return ExecutionMode.Aarch64; return ExecutionMode.Aarch64;
} }

View file

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

View file

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

View file

@ -529,8 +529,16 @@ namespace ChocolArm64.Translation
public void EmitLdflg(int index) => Ldloc(index, IoType.Flag); public void EmitLdflg(int index) => Ldloc(index, IoType.Flag);
public void EmitStflg(int index) public void EmitStflg(int index)
{
//Set this only if any of the NZCV flag bits were modified.
//This is used to ensure that, when emiting a direct IL branch
//instruction for compare + branch sequences, we're not expecting
//to use comparison values from an old instruction, when in fact
//the flags were already overwritten by another instruction further along.
if (index >= (int)PState.VBit)
{ {
_optOpLastFlagSet = CurrOp; _optOpLastFlagSet = CurrOp;
}
Stloc(index, IoType.Flag); Stloc(index, IoType.Flag);
} }

View file

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