ARMeilleure: Thumb support (All T16 instructions) (#3105)

* Decoders: Add InITBlock argument

* OpCodeTable: Minor cleanup

* OpCodeTable: Remove existing thumb instruction implementations

* OpCodeTable: Prepare for thumb instructions

* OpCodeTables: Improve thumb fast lookup

* Tests: Prepare for thumb tests

* T16: Implement BX

* T16: Implement LSL/LSR/ASR (imm)

* T16: Implement ADDS, SUBS (reg)

* T16: Implement ADDS, SUBS (3-bit immediate)

* T16: Implement MOVS, CMP, ADDS, SUBS (8-bit immediate)

* T16: Implement ANDS, EORS, LSLS, LSRS, ASRS, ADCS, SBCS, RORS, TST, NEGS, CMP, CMN, ORRS, MULS, BICS, MVNS (low registers)

* T16: Implement ADD, CMP, MOV (high reg)

* T16: Implement BLX (reg)

* T16: Implement LDR (literal)

* T16: Implement {LDR,STR}{,H,B,SB,SH} (register)

* T16: Implement {LDR,STR}{,B,H} (immediate)

* T16: Implement LDR/STR (SP)

* T16: Implement ADR

* T16: Implement Add to SP (immediate)

* T16: Implement ADD/SUB (SP)

* T16: Implement SXTH, SXTB, UXTH, UTXB

* T16: Implement CBZ, CBNZ

* T16: Implement PUSH, POP

* T16: Implement REV, REV16, REVSH

* T16: Implement NOP

* T16: Implement LDM, STM

* T16: Implement SVC

* T16: Implement B (conditional)

* T16: Implement B (unconditional)

* T16: Implement IT

* fixup! T16: Implement ADD/SUB (SP)

* fixup! T16: Implement Add to SP (immediate)

* fixup! T16: Implement IT

* CpuTestThumb: Add randomized tests

* Remove inITBlock argument

* Address nits

* Use index to handle IfThenBlockState

* Reduce line noise

* fixup

* nit
This commit is contained in:
merry 2022-02-17 22:39:45 +00:00 committed by GitHub
parent 868919e101
commit 98e05ee4b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1852 additions and 102 deletions

View File

@ -195,12 +195,13 @@ namespace ARMeilleure.Decoders
ulong limitAddress) ulong limitAddress)
{ {
ulong address = block.Address; ulong address = block.Address;
int itBlockSize = 0;
OpCode opCode; OpCode opCode;
do do
{ {
if (address >= limitAddress) if (address >= limitAddress && itBlockSize == 0)
{ {
break; break;
} }
@ -210,6 +211,15 @@ namespace ARMeilleure.Decoders
block.OpCodes.Add(opCode); block.OpCodes.Add(opCode);
address += (ulong)opCode.OpCodeSizeInBytes; address += (ulong)opCode.OpCodeSizeInBytes;
if (opCode is OpCodeT16IfThen it)
{
itBlockSize = it.IfThenBlockSize;
}
else if (itBlockSize > 0)
{
itBlockSize--;
}
} }
while (!(IsBranch(opCode) || IsException(opCode))); while (!(IsBranch(opCode) || IsException(opCode)));
@ -344,9 +354,16 @@ namespace ARMeilleure.Decoders
return makeOp(inst, address, opCode); return makeOp(inst, address, opCode);
} }
else else
{
if (mode == ExecutionMode.Aarch32Thumb)
{
return new OpCodeT16(inst, address, opCode);
}
else
{ {
return new OpCode(inst, address, opCode); return new OpCode(inst, address, opCode);
} }
} }
} }
}
} }

View File

@ -0,0 +1,9 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32Adr
{
int Rd { get; }
int Immediate { get; }
}
}

View File

@ -5,6 +5,6 @@ namespace ARMeilleure.Decoders
int Rd { get; } int Rd { get; }
int Rn { get; } int Rn { get; }
bool SetFlags { get; } bool? SetFlags { get; }
} }
} }

View File

@ -0,0 +1,9 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32AluImm : IOpCode32Alu
{
int Immediate { get; }
bool IsRotated { get; }
}
}

View File

@ -0,0 +1,10 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32AluRsImm : IOpCode32Alu
{
int Rm { get; }
int Immediate { get; }
ShiftType ShiftType { get; }
}
}

View File

@ -0,0 +1,10 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32AluRsReg : IOpCode32Alu
{
int Rm { get; }
int Rs { get; }
ShiftType ShiftType { get; }
}
}

View File

@ -0,0 +1,6 @@
namespace ARMeilleure.Decoders;
interface IOpCode32Exception
{
int Id { get; }
}

View File

@ -7,5 +7,9 @@ namespace ARMeilleure.Decoders
bool WBack { get; } bool WBack { get; }
bool IsLoad { get; } bool IsLoad { get; }
bool Index { get; }
bool Add { get; }
int Immediate { get; }
} }
} }

View File

@ -9,5 +9,7 @@ namespace ARMeilleure.Decoders
int PostOffset { get; } int PostOffset { get; }
bool IsLoad { get; } bool IsLoad { get; }
int Offset { get; }
} }
} }

View File

@ -0,0 +1,7 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32MemReg : IOpCode32Mem
{
int Rm { get; }
}
}

View File

@ -18,11 +18,10 @@ namespace ARMeilleure.Decoders
public OpCode(InstDescriptor inst, ulong address, int opCode) public OpCode(InstDescriptor inst, ulong address, int opCode)
{ {
Instruction = inst;
Address = address; Address = address;
RawOpCode = opCode; RawOpCode = opCode;
Instruction = inst;
RegisterSize = RegisterSize.Int64; RegisterSize = RegisterSize.Int64;
} }

View File

@ -5,7 +5,7 @@ namespace ARMeilleure.Decoders
public int Rd { get; } public int Rd { get; }
public int Rn { get; } public int Rn { get; }
public bool SetFlags { get; } public bool? SetFlags { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32Alu(inst, address, opCode); public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32Alu(inst, address, opCode);

View File

@ -2,7 +2,7 @@ using ARMeilleure.Common;
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCode32AluImm : OpCode32Alu class OpCode32AluImm : OpCode32Alu, IOpCode32AluImm
{ {
public int Immediate { get; } public int Immediate { get; }

View File

@ -10,7 +10,7 @@
public bool NHigh { get; } public bool NHigh { get; }
public bool MHigh { get; } public bool MHigh { get; }
public bool R { get; } public bool R { get; }
public bool SetFlags { get; } public bool? SetFlags { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluMla(inst, address, opCode); public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluMla(inst, address, opCode);

View File

@ -1,6 +1,6 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCode32AluRsImm : OpCode32Alu class OpCode32AluRsImm : OpCode32Alu, IOpCode32AluRsImm
{ {
public int Rm { get; } public int Rm { get; }
public int Immediate { get; } public int Immediate { get; }

View File

@ -1,6 +1,6 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCode32AluRsReg : OpCode32Alu class OpCode32AluRsReg : OpCode32Alu, IOpCode32AluRsReg
{ {
public int Rm { get; } public int Rm { get; }
public int Rs { get; } public int Rs { get; }

View File

@ -10,7 +10,7 @@
public bool NHigh { get; } public bool NHigh { get; }
public bool MHigh { get; } public bool MHigh { get; }
public bool SetFlags { get; } public bool? SetFlags { get; }
public DataOp DataOp { get; } public DataOp DataOp { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluUmull(inst, address, opCode); public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluUmull(inst, address, opCode);

View File

@ -1,6 +1,6 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCode32Exception : OpCode32 class OpCode32Exception : OpCode32, IOpCode32Exception
{ {
public int Id { get; } public int Id { get; }

View File

@ -1,6 +1,6 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCode32MemReg : OpCode32Mem class OpCode32MemReg : OpCode32Mem, IOpCode32MemReg
{ {
public int Rm { get; } public int Rm { get; }

View File

@ -0,0 +1,24 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16AddSubImm3: OpCodeT16, IOpCode32AluImm
{
public int Rd { get; }
public int Rn { get; }
public bool? SetFlags => null;
public int Immediate { get; }
public bool IsRotated { get; }
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AddSubImm3(inst, address, opCode);
public OpCodeT16AddSubImm3(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 0x7;
Rn = (opCode >> 3) & 0x7;
Immediate = (opCode >> 6) & 0x7;
IsRotated = false;
}
}
}

View File

@ -0,0 +1,20 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16AddSubReg : OpCodeT16, IOpCode32AluReg
{
public int Rm { get; }
public int Rd { get; }
public int Rn { get; }
public bool? SetFlags => null;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AddSubReg(inst, address, opCode);
public OpCodeT16AddSubReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 0x7;
Rn = (opCode >> 3) & 0x7;
Rm = (opCode >> 6) & 0x7;
}
}
}

View File

@ -0,0 +1,23 @@
using ARMeilleure.State;
namespace ARMeilleure.Decoders
{
class OpCodeT16AddSubSp : OpCodeT16, IOpCode32AluImm
{
public int Rd => RegisterAlias.Aarch32Sp;
public int Rn => RegisterAlias.Aarch32Sp;
public bool? SetFlags => false;
public int Immediate { get; }
public bool IsRotated => false;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AddSubSp(inst, address, opCode);
public OpCodeT16AddSubSp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Immediate = ((opCode >> 0) & 0x7f) << 2;
}
}
}

View File

@ -0,0 +1,20 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16Adr : OpCodeT16, IOpCode32Adr
{
public int Rd { get; }
public bool Add => true;
public int Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16Adr(inst, address, opCode);
public OpCodeT16Adr(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 8) & 7;
int imm = (opCode & 0xff) << 2;
Immediate = (int)(GetPc() & 0xfffffffc) + imm;
}
}
}

View File

@ -1,22 +1,24 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu class OpCodeT16AluImm8 : OpCodeT16, IOpCode32AluImm
{ {
private int _rdn; public int Rd { get; }
public int Rn { get; }
public int Rd => _rdn; public bool? SetFlags => null;
public int Rn => _rdn;
public bool SetFlags => false;
public int Immediate { get; } public int Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluImm8(inst, address, opCode); public bool IsRotated { get; }
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluImm8(inst, address, opCode);
public OpCodeT16AluImm8(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) public OpCodeT16AluImm8(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{ {
Rd = (opCode >> 8) & 0x7;
Rn = (opCode >> 8) & 0x7;
Immediate = (opCode >> 0) & 0xff; Immediate = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7; IsRotated = false;
} }
} }
} }

View File

@ -0,0 +1,24 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16AluImmZero : OpCodeT16, IOpCode32AluImm
{
public int Rd { get; }
public int Rn { get; }
public bool? SetFlags => null;
public int Immediate { get; }
public bool IsRotated { get; }
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluImmZero(inst, address, opCode);
public OpCodeT16AluImmZero(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 0x7;
Rn = (opCode >> 3) & 0x7;
Immediate = 0;
IsRotated = false;
}
}
}

View File

@ -0,0 +1,20 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16AluRegHigh : OpCodeT16, IOpCode32AluReg
{
public int Rm { get; }
public int Rd { get; }
public int Rn { get; }
public bool? SetFlags => false;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluRegHigh(inst, address, opCode);
public OpCodeT16AluRegHigh(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = ((opCode >> 0) & 0x7) | ((opCode >> 4) & 0x8);
Rn = ((opCode >> 0) & 0x7) | ((opCode >> 4) & 0x8);
Rm = (opCode >> 3) & 0xf;
}
}
}

View File

@ -0,0 +1,20 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16AluRegLow : OpCodeT16, IOpCode32AluReg
{
public int Rm { get; }
public int Rd { get; }
public int Rn { get; }
public bool? SetFlags => null;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluRegLow(inst, address, opCode);
public OpCodeT16AluRegLow(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 0x7;
Rn = (opCode >> 0) & 0x7;
Rm = (opCode >> 3) & 0x7;
}
}
}

View File

@ -0,0 +1,22 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16AluUx : OpCodeT16, IOpCode32AluUx
{
public int Rm { get; }
public int Rd { get; }
public int Rn { get; }
public bool? SetFlags => false;
public int RotateBits => 0;
public bool Add => false;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluUx(inst, address, opCode);
public OpCodeT16AluUx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 0x7;
Rm = (opCode >> 3) & 0x7;
}
}
}

View File

@ -0,0 +1,15 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16BImm11 : OpCode32, IOpCode32BImm
{
public long Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16BImm11(inst, address, opCode);
public OpCodeT16BImm11(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
int imm = (opCode << 21) >> 20;
Immediate = GetPc() + imm;
}
}
}

View File

@ -0,0 +1,17 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16BImm8 : OpCode32, IOpCode32BImm
{
public long Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16BImm8(inst, address, opCode);
public OpCodeT16BImm8(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Cond = (Condition)((opCode >> 8) & 0xf);
int imm = (opCode << 24) >> 23;
Immediate = GetPc() + imm;
}
}
}

View File

@ -0,0 +1,19 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16BImmCmp : OpCodeT16
{
public int Rn { get; }
public int Immediate { get; }
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16BImmCmp(inst, address, opCode);
public OpCodeT16BImmCmp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rn = (opCode >> 0) & 0x7;
int imm = ((opCode >> 2) & 0x3e) | ((opCode >> 3) & 0x40);
Immediate = (int)GetPc() + imm;
}
}
}

View File

@ -1,4 +1,4 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCodeT16BReg : OpCodeT16, IOpCode32BReg class OpCodeT16BReg : OpCodeT16, IOpCode32BReg
{ {

View File

@ -0,0 +1,14 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16Exception : OpCodeT16, IOpCode32Exception
{
public int Id { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16Exception(inst, address, opCode);
public OpCodeT16Exception(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Id = opCode & 0xFF;
}
}
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Reflection.Emit;
namespace ARMeilleure.Decoders
{
class OpCodeT16IfThen : OpCodeT16
{
public Condition[] IfThenBlockConds { get; }
public int IfThenBlockSize { get { return IfThenBlockConds.Length; } }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16IfThen(inst, address, opCode);
public OpCodeT16IfThen(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
List<Condition> conds = new();
int cond = (opCode >> 4) & 0xf;
int mask = opCode & 0xf;
conds.Add((Condition)cond);
while ((mask & 7) != 0)
{
int newLsb = (mask >> 3) & 1;
cond = (cond & 0xe) | newLsb;
mask <<= 1;
conds.Add((Condition)cond);
}
IfThenBlockConds = conds.ToArray();
}
}
}

View File

@ -0,0 +1,58 @@
using ARMeilleure.Instructions;
using System;
namespace ARMeilleure.Decoders
{
class OpCodeT16MemImm5 : OpCodeT16, IOpCode32Mem
{
public int Rt { get; }
public int Rn { get; }
public bool WBack => false;
public bool IsLoad { get; }
public bool Index => true;
public bool Add => true;
public int Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemImm5(inst, address, opCode);
public OpCodeT16MemImm5(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rt = (opCode >> 0) & 7;
Rn = (opCode >> 3) & 7;
switch (inst.Name)
{
case InstName.Ldr:
case InstName.Ldrb:
case InstName.Ldrh:
IsLoad = true;
break;
case InstName.Str:
case InstName.Strb:
case InstName.Strh:
IsLoad = false;
break;
}
switch (inst.Name)
{
case InstName.Str:
case InstName.Ldr:
Immediate = ((opCode >> 6) & 0x1f) << 2;
break;
case InstName.Strb:
case InstName.Ldrb:
Immediate = ((opCode >> 6) & 0x1f);
break;
case InstName.Strh:
case InstName.Ldrh:
Immediate = ((opCode >> 6) & 0x1f) << 1;
break;
default:
throw new InvalidOperationException();
}
}
}
}

View File

@ -0,0 +1,26 @@
using ARMeilleure.State;
namespace ARMeilleure.Decoders
{
class OpCodeT16MemLit : OpCodeT16, IOpCode32Mem
{
public int Rt { get; }
public int Rn => RegisterAlias.Aarch32Pc;
public bool WBack => false;
public bool IsLoad => true;
public bool Index => true;
public bool Add => true;
public int Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemLit(inst, address, opCode);
public OpCodeT16MemLit(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rt = (opCode >> 8) & 7;
Immediate = (opCode & 0xff) << 2;
}
}
}

View File

@ -0,0 +1,34 @@
using ARMeilleure.Instructions;
using System;
using System.Numerics;
namespace ARMeilleure.Decoders
{
class OpCodeT16MemMult : OpCodeT16, IOpCode32MemMult
{
public int Rn { get; }
public int RegisterMask { get; }
public int PostOffset { get; }
public bool IsLoad { get; }
public int Offset { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemMult(inst, address, opCode);
public OpCodeT16MemMult(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
RegisterMask = opCode & 0xff;
Rn = (opCode >> 8) & 7;
int regCount = BitOperations.PopCount((uint)RegisterMask);
Offset = 0;
PostOffset = 4 * regCount;
IsLoad = inst.Name switch
{
InstName.Ldm => true,
InstName.Stm => false,
_ => throw new InvalidOperationException()
};
}
}
}

View File

@ -0,0 +1,27 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16MemReg : OpCodeT16, IOpCode32MemReg
{
public int Rm { get; }
public int Rt { get; }
public int Rn { get; }
public bool WBack => false;
public bool IsLoad { get; }
public bool Index => true;
public bool Add => true;
public int Immediate => throw new System.InvalidOperationException();
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemReg(inst, address, opCode);
public OpCodeT16MemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rt = (opCode >> 0) & 7;
Rn = (opCode >> 3) & 7;
Rm = (opCode >> 6) & 7;
IsLoad = ((opCode >> 9) & 7) >= 3;
}
}
}

View File

@ -0,0 +1,28 @@
using ARMeilleure.State;
namespace ARMeilleure.Decoders
{
class OpCodeT16MemSp : OpCodeT16, IOpCode32Mem
{
public int Rt { get; }
public int Rn => RegisterAlias.Aarch32Sp;
public bool WBack => false;
public bool IsLoad { get; }
public bool Index => true;
public bool Add => true;
public int Immediate { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemSp(inst, address, opCode);
public OpCodeT16MemSp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rt = (opCode >> 8) & 7;
IsLoad = ((opCode >> 11) & 1) != 0;
Immediate = ((opCode >> 0) & 0xff) << 2;
}
}
}

View File

@ -0,0 +1,42 @@
using ARMeilleure.Instructions;
using ARMeilleure.State;
using System;
using System.Numerics;
namespace ARMeilleure.Decoders
{
class OpCodeT16MemStack : OpCodeT16, IOpCode32MemMult
{
public int Rn => RegisterAlias.Aarch32Sp;
public int RegisterMask { get; }
public int PostOffset { get; }
public bool IsLoad { get; }
public int Offset { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemStack(inst, address, opCode);
public OpCodeT16MemStack(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
int extra = (opCode >> 8) & 1;
int regCount = BitOperations.PopCount((uint)opCode & 0x1ff);
switch (inst.Name)
{
case InstName.Push:
RegisterMask = (opCode & 0xff) | (extra << 14);
IsLoad = false;
Offset = -4 * regCount;
PostOffset = -4 * regCount;
break;
case InstName.Pop:
RegisterMask = (opCode & 0xff) | (extra << 15);
IsLoad = true;
Offset = 0;
PostOffset = 4 * regCount;
break;
default:
throw new InvalidOperationException();
}
}
}
}

View File

@ -0,0 +1,24 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16ShiftImm : OpCodeT16, IOpCode32AluRsImm
{
public int Rd { get; }
public int Rn { get; }
public int Rm { get; }
public int Immediate { get; }
public ShiftType ShiftType { get; }
public bool? SetFlags => null;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16ShiftImm(inst, address, opCode);
public OpCodeT16ShiftImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 0x7;
Rm = (opCode >> 3) & 0x7;
Immediate = (opCode >> 6) & 0x1F;
ShiftType = (ShiftType)((opCode >> 11) & 3);
}
}
}

View File

@ -0,0 +1,27 @@
namespace ARMeilleure.Decoders
{
class OpCodeT16ShiftReg : OpCodeT16, IOpCode32AluRsReg
{
public int Rm { get; }
public int Rs { get; }
public int Rd { get; }
public int Rn { get; }
public ShiftType ShiftType { get; }
public bool? SetFlags => null;
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16ShiftReg(inst, address, opCode);
public OpCodeT16ShiftReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 0) & 7;
Rm = (opCode >> 0) & 7;
Rn = (opCode >> 3) & 7;
Rs = (opCode >> 3) & 7;
ShiftType = (ShiftType)(((opCode >> 6) & 1) | ((opCode >> 7) & 2));
}
}
}

View File

@ -0,0 +1,24 @@
using ARMeilleure.State;
namespace ARMeilleure.Decoders
{
class OpCodeT16SpRel : OpCodeT16, IOpCode32AluImm
{
public int Rd { get; }
public int Rn => RegisterAlias.Aarch32Sp;
public bool? SetFlags => false;
public int Immediate { get; }
public bool IsRotated => false;
public static new OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16SpRel(inst, address, opCode);
public OpCodeT16SpRel(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 8) & 0x7;
Immediate = ((opCode >> 0) & 0xff) << 2;
}
}
}

View File

@ -1,5 +1,4 @@
using ARMeilleure.Instructions; using ARMeilleure.Instructions;
using ARMeilleure.State;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -29,9 +28,9 @@ namespace ARMeilleure.Decoders
} }
} }
private static List<InstInfo> AllInstA32 = new List<InstInfo>(); private static List<InstInfo> AllInstA32 = new();
private static List<InstInfo> AllInstT32 = new List<InstInfo>(); private static List<InstInfo> AllInstT32 = new();
private static List<InstInfo> AllInstA64 = new List<InstInfo>(); private static List<InstInfo> AllInstA64 = new();
private static InstInfo[][] InstA32FastLookup = new InstInfo[FastLookupSize][]; private static InstInfo[][] InstA32FastLookup = new InstInfo[FastLookupSize][];
private static InstInfo[][] InstT32FastLookup = new InstInfo[FastLookupSize][]; private static InstInfo[][] InstT32FastLookup = new InstInfo[FastLookupSize][];
@ -628,7 +627,7 @@ namespace ARMeilleure.Decoders
SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstName.Zip2_V, InstEmit.Zip2_V, OpCodeSimdReg.Create); SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstName.Zip2_V, InstEmit.Zip2_V, OpCodeSimdReg.Create);
#endregion #endregion
#region "OpCode Table (AArch32)" #region "OpCode Table (AArch32, A32)"
// Base // Base
SetA32("<<<<0010101xxxxxxxxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCode32AluImm.Create); SetA32("<<<<0010101xxxxxxxxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCode32AluImm.Create);
SetA32("<<<<0000101xxxxxxxxxxxxxxxx0xxxx", InstName.Adc, InstEmit32.Adc, OpCode32AluRsImm.Create); SetA32("<<<<0000101xxxxxxxxxxxxxxxx0xxxx", InstName.Adc, InstEmit32.Adc, OpCode32AluRsImm.Create);
@ -649,7 +648,6 @@ namespace ARMeilleure.Decoders
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, OpCode32BImm.Create); SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, OpCode32BImm.Create);
SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, OpCode32BReg.Create); SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, OpCode32BReg.Create);
SetA32("<<<<000100101111111111110001xxxx", InstName.Bx, InstEmit32.Bx, OpCode32BReg.Create); SetA32("<<<<000100101111111111110001xxxx", InstName.Bx, InstEmit32.Bx, OpCode32BReg.Create);
SetT32("xxxxxxxxxxxxxxxx010001110xxxx000", InstName.Bx, InstEmit32.Bx, OpCodeT16BReg.Create);
SetA32("11110101011111111111000000011111", InstName.Clrex, InstEmit32.Clrex, OpCode32.Create); SetA32("11110101011111111111000000011111", InstName.Clrex, InstEmit32.Clrex, OpCode32.Create);
SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, OpCode32AluReg.Create); SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, OpCode32AluReg.Create);
SetA32("<<<<00110111xxxx0000xxxxxxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCode32AluImm.Create); SetA32("<<<<00110111xxxx0000xxxxxxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCode32AluImm.Create);
@ -702,7 +700,6 @@ namespace ARMeilleure.Decoders
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, OpCode32AluRsImm.Create); SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, OpCode32AluRsImm.Create);
SetA32("<<<<0001101x0000xxxxxxxx0xx1xxxx", InstName.Mov, InstEmit32.Mov, OpCode32AluRsReg.Create); SetA32("<<<<0001101x0000xxxxxxxx0xx1xxxx", InstName.Mov, InstEmit32.Mov, OpCode32AluRsReg.Create);
SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCode32AluImm16.Create); SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCode32AluImm16.Create);
SetT32("xxxxxxxxxxxxxxxx00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16AluImm8.Create);
SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, OpCode32AluImm16.Create); SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, OpCode32AluImm16.Create);
SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, OpCode32System.Create); SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, OpCode32System.Create);
SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc, InstEmit32.Mrrc, OpCode32System.Create); SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc, InstEmit32.Mrrc, OpCode32System.Create);
@ -975,12 +972,85 @@ namespace ARMeilleure.Decoders
SetA32("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create); SetA32("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create);
#endregion #endregion
FillFastLookupTable(InstA32FastLookup, AllInstA32); #region "OpCode Table (AArch32, T16/T32)"
FillFastLookupTable(InstT32FastLookup, AllInstT32); // T16
FillFastLookupTable(InstA64FastLookup, AllInstA64); SetT16("000<<xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftImm.Create);
SetT16("0001100xxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AddSubReg.Create);
SetT16("0001101xxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AddSubReg.Create);
SetT16("0001110xxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AddSubImm3.Create);
SetT16("0001111xxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AddSubImm3.Create);
SetT16("00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16AluImm8.Create);
SetT16("00101xxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT16AluImm8.Create);
SetT16("00110xxxxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AluImm8.Create);
SetT16("00111xxxxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AluImm8.Create);
SetT16("0100000000xxxxxx", InstName.And, InstEmit32.And, OpCodeT16AluRegLow.Create);
SetT16("0100000001xxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT16AluRegLow.Create);
SetT16("0100000010xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create);
SetT16("0100000011xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create);
SetT16("0100000100xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create);
SetT16("0100000101xxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT16AluRegLow.Create);
SetT16("0100000110xxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT16AluRegLow.Create);
SetT16("0100000111xxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftReg.Create);
SetT16("0100001000xxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT16AluRegLow.Create);
SetT16("0100001001xxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT16AluImmZero.Create);
SetT16("0100001010xxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT16AluRegLow.Create);
SetT16("0100001011xxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT16AluRegLow.Create);
SetT16("0100001100xxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT16AluRegLow.Create);
SetT16("0100001101xxxxxx", InstName.Mul, InstEmit32.Mul, OpCodeT16AluRegLow.Create);
SetT16("0100001110xxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT16AluRegLow.Create);
SetT16("0100001111xxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT16AluRegLow.Create);
SetT16("01000100xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AluRegHigh.Create);
SetT16("01000101xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT16AluRegHigh.Create);
SetT16("01000110xxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16AluRegHigh.Create);
SetT16("010001110xxxx000", InstName.Bx, InstEmit32.Bx, OpCodeT16BReg.Create);
SetT16("010001111xxxx000", InstName.Blx, InstEmit32.Blx, OpCodeT16BReg.Create);
SetT16("01001xxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT16MemLit.Create);
SetT16("0101000xxxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT16MemReg.Create);
SetT16("0101001xxxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT16MemReg.Create);
SetT16("0101010xxxxxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT16MemReg.Create);
SetT16("0101011xxxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT16MemReg.Create);
SetT16("0101100xxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT16MemReg.Create);
SetT16("0101101xxxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT16MemReg.Create);
SetT16("0101110xxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT16MemReg.Create);
SetT16("0101111xxxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT16MemReg.Create);
SetT16("01100xxxxxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT16MemImm5.Create);
SetT16("01101xxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT16MemImm5.Create);
SetT16("01110xxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT16MemImm5.Create);
SetT16("01111xxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT16MemImm5.Create);
SetT16("10000xxxxxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT16MemImm5.Create);
SetT16("10001xxxxxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT16MemImm5.Create);
SetT16("10010xxxxxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT16MemSp.Create);
SetT16("10011xxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT16MemSp.Create);
SetT16("10100xxxxxxxxxxx", InstName.Adr, InstEmit32.Adr, OpCodeT16Adr.Create);
SetT16("10101xxxxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16SpRel.Create);
SetT16("101100000xxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AddSubSp.Create);
SetT16("101100001xxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AddSubSp.Create);
SetT16("1011001000xxxxxx", InstName.Sxth, InstEmit32.Sxth, OpCodeT16AluUx.Create);
SetT16("1011001001xxxxxx", InstName.Sxtb, InstEmit32.Sxtb, OpCodeT16AluUx.Create);
SetT16("1011001010xxxxxx", InstName.Uxth, InstEmit32.Uxth, OpCodeT16AluUx.Create);
SetT16("1011001011xxxxxx", InstName.Uxtb, InstEmit32.Uxtb, OpCodeT16AluUx.Create);
SetT16("101100x1xxxxxxxx", InstName.Cbz, InstEmit32.Cbz, OpCodeT16BImmCmp.Create);
SetT16("1011010xxxxxxxxx", InstName.Push, InstEmit32.Stm, OpCodeT16MemStack.Create);
SetT16("1011101000xxxxxx", InstName.Rev, InstEmit32.Rev, OpCodeT16AluRegLow.Create);
SetT16("1011101001xxxxxx", InstName.Rev16, InstEmit32.Rev16, OpCodeT16AluRegLow.Create);
SetT16("1011101011xxxxxx", InstName.Revsh, InstEmit32.Revsh, OpCodeT16AluRegLow.Create);
SetT16("101110x1xxxxxxxx", InstName.Cbnz, InstEmit32.Cbnz, OpCodeT16BImmCmp.Create);
SetT16("1011110xxxxxxxxx", InstName.Pop, InstEmit32.Ldm, OpCodeT16MemStack.Create);
SetT16("10111111xxxx0000", InstName.Nop, InstEmit32.Nop, OpCodeT16.Create);
SetT16("10111111xxxx>>>>", InstName.It, InstEmit32.It, OpCodeT16IfThen.Create);
SetT16("11000xxxxxxxxxxx", InstName.Stm, InstEmit32.Stm, OpCodeT16MemMult.Create);
SetT16("11001xxxxxxxxxxx", InstName.Ldm, InstEmit32.Ldm, OpCodeT16MemMult.Create);
SetT16("1101<<<xxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm8.Create);
SetT16("11011111xxxxxxxx", InstName.Svc, InstEmit32.Svc, OpCodeT16Exception.Create);
SetT16("11100xxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm11.Create);
#endregion
FillFastLookupTable(InstA32FastLookup, AllInstA32, ToFastLookupIndexA);
FillFastLookupTable(InstT32FastLookup, AllInstT32, ToFastLookupIndexT);
FillFastLookupTable(InstA64FastLookup, AllInstA64, ToFastLookupIndexA);
} }
private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts) private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts, Func<int, int> ToFastLookupIndex)
{ {
List<InstInfo>[] temp = new List<InstInfo>[FastLookupSize]; List<InstInfo>[] temp = new List<InstInfo>[FastLookupSize];
@ -1011,20 +1081,27 @@ namespace ARMeilleure.Decoders
private static void SetA32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) private static void SetA32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
{ {
Set(encoding, ExecutionMode.Aarch32Arm, new InstDescriptor(name, emitter), makeOp); Set(encoding, AllInstA32, new InstDescriptor(name, emitter), makeOp);
}
private static void SetT16(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
{
encoding = "xxxxxxxxxxxxxxxx" + encoding;
Set(encoding, AllInstT32, new InstDescriptor(name, emitter), makeOp);
} }
private static void SetT32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) private static void SetT32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
{ {
Set(encoding, ExecutionMode.Aarch32Thumb, new InstDescriptor(name, emitter), makeOp); encoding = encoding.Substring(16) + encoding.Substring(0, 16);
Set(encoding, AllInstT32, new InstDescriptor(name, emitter), makeOp);
} }
private static void SetA64(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) private static void SetA64(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
{ {
Set(encoding, ExecutionMode.Aarch64, new InstDescriptor(name, emitter), makeOp); Set(encoding, AllInstA64, new InstDescriptor(name, emitter), makeOp);
} }
private static void Set(string encoding, ExecutionMode mode, InstDescriptor inst, MakeOp makeOp) private static void Set(string encoding, List<InstInfo> list, InstDescriptor inst, MakeOp makeOp)
{ {
int bit = encoding.Length - 1; int bit = encoding.Length - 1;
int value = 0; int value = 0;
@ -1073,7 +1150,7 @@ namespace ARMeilleure.Decoders
if (xBits == 0) if (xBits == 0)
{ {
InsertInst(new InstInfo(xMask, value, inst, makeOp), mode); list.Add(new InstInfo(xMask, value, inst, makeOp));
return; return;
} }
@ -1089,34 +1166,24 @@ namespace ARMeilleure.Decoders
if (mask != blacklisted) if (mask != blacklisted)
{ {
InsertInst(new InstInfo(xMask, value | mask, inst, makeOp), mode); list.Add(new InstInfo(xMask, value | mask, inst, makeOp));
} }
} }
} }
private static void InsertInst(InstInfo info, ExecutionMode mode)
{
switch (mode)
{
case ExecutionMode.Aarch32Arm: AllInstA32.Add(info); break;
case ExecutionMode.Aarch32Thumb: AllInstT32.Add(info); break;
case ExecutionMode.Aarch64: AllInstA64.Add(info); break;
}
}
public static (InstDescriptor inst, MakeOp makeOp) GetInstA32(int opCode) public static (InstDescriptor inst, MakeOp makeOp) GetInstA32(int opCode)
{ {
return GetInstFromList(InstA32FastLookup[ToFastLookupIndex(opCode)], opCode); return GetInstFromList(InstA32FastLookup[ToFastLookupIndexA(opCode)], opCode);
} }
public static (InstDescriptor inst, MakeOp makeOp) GetInstT32(int opCode) public static (InstDescriptor inst, MakeOp makeOp) GetInstT32(int opCode)
{ {
return GetInstFromList(InstT32FastLookup[ToFastLookupIndex(opCode)], opCode); return GetInstFromList(InstT32FastLookup[ToFastLookupIndexT(opCode)], opCode);
} }
public static (InstDescriptor inst, MakeOp makeOp) GetInstA64(int opCode) public static (InstDescriptor inst, MakeOp makeOp) GetInstA64(int opCode)
{ {
return GetInstFromList(InstA64FastLookup[ToFastLookupIndex(opCode)], opCode); return GetInstFromList(InstA64FastLookup[ToFastLookupIndexA(opCode)], opCode);
} }
private static (InstDescriptor inst, MakeOp makeOp) GetInstFromList(InstInfo[] insts, int opCode) private static (InstDescriptor inst, MakeOp makeOp) GetInstFromList(InstInfo[] insts, int opCode)
@ -1132,9 +1199,14 @@ namespace ARMeilleure.Decoders
return (new InstDescriptor(InstName.Und, InstEmit.Und), null); return (new InstDescriptor(InstName.Und, InstEmit.Und), null);
} }
private static int ToFastLookupIndex(int value) private static int ToFastLookupIndexA(int value)
{ {
return ((value >> 10) & 0x00F) | ((value >> 18) & 0xFF0); return ((value >> 10) & 0x00F) | ((value >> 18) & 0xFF0);
} }
private static int ToFastLookupIndexT(int value)
{
return (value >> 4) & 0xFFF;
}
} }
} }

View File

@ -20,7 +20,7 @@ namespace ARMeilleure.Instructions
Operand res = context.Add(n, m); Operand res = context.Add(n, m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
@ -44,7 +44,7 @@ namespace ARMeilleure.Instructions
res = context.Add(res, carry); res = context.Add(res, carry);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
@ -64,7 +64,7 @@ namespace ARMeilleure.Instructions
Operand res = context.BitwiseAnd(n, m); Operand res = context.BitwiseAnd(n, m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -110,7 +110,7 @@ namespace ARMeilleure.Instructions
Operand res = context.BitwiseAnd(n, context.BitwiseNot(m)); Operand res = context.BitwiseAnd(n, context.BitwiseNot(m));
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -161,7 +161,7 @@ namespace ARMeilleure.Instructions
Operand res = context.BitwiseExclusiveOr(n, m); Operand res = context.BitwiseExclusiveOr(n, m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -175,7 +175,7 @@ namespace ARMeilleure.Instructions
Operand m = GetAluM(context); Operand m = GetAluM(context);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, m); EmitNZFlagsCheck(context, m);
} }
@ -204,7 +204,7 @@ namespace ARMeilleure.Instructions
Operand res = context.Multiply(n, m); Operand res = context.Multiply(n, m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -219,7 +219,7 @@ namespace ARMeilleure.Instructions
Operand res = context.BitwiseNot(m); Operand res = context.BitwiseNot(m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -236,7 +236,7 @@ namespace ARMeilleure.Instructions
Operand res = context.BitwiseOr(n, m); Operand res = context.BitwiseOr(n, m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -315,7 +315,7 @@ namespace ARMeilleure.Instructions
res = context.Subtract(res, borrow); res = context.Subtract(res, borrow);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
@ -335,7 +335,7 @@ namespace ARMeilleure.Instructions
Operand res = context.Subtract(m, n); Operand res = context.Subtract(m, n);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
@ -359,7 +359,7 @@ namespace ARMeilleure.Instructions
res = context.Subtract(res, borrow); res = context.Subtract(res, borrow);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
@ -420,7 +420,7 @@ namespace ARMeilleure.Instructions
Operand res = context.Subtract(n, m); Operand res = context.Subtract(n, m);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
@ -836,7 +836,7 @@ namespace ARMeilleure.Instructions
{ {
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitGenericAluStoreA32(context, op.Rd, op.SetFlags, value); EmitGenericAluStoreA32(context, op.Rd, ShouldSetFlags(context), value);
} }
} }
} }

View File

@ -12,6 +12,18 @@ namespace ARMeilleure.Instructions
{ {
static class InstEmitAluHelper static class InstEmitAluHelper
{ {
public static bool ShouldSetFlags(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
if (op.SetFlags == null)
{
return !context.IsInIfThenBlock;
}
return op.SetFlags.Value;
}
public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d) public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d)
{ {
SetFlag(context, PState.NFlag, context.ICompareLess (d, Const(d.Type, 0))); SetFlag(context, PState.NFlag, context.ICompareLess (d, Const(d.Type, 0)));
@ -183,9 +195,9 @@ namespace ARMeilleure.Instructions
switch (context.CurrOp) switch (context.CurrOp)
{ {
// ARM32. // ARM32.
case OpCode32AluImm op: case IOpCode32AluImm op:
{ {
if (op.SetFlags && op.IsRotated) if (ShouldSetFlags(context) && op.IsRotated)
{ {
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31)); SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
} }
@ -195,10 +207,8 @@ namespace ARMeilleure.Instructions
case OpCode32AluImm16 op: return Const(op.Immediate); case OpCode32AluImm16 op: return Const(op.Immediate);
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry); case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry); case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
case OpCodeT16AluImm8 op: return Const(op.Immediate);
case IOpCode32AluReg op: return GetIntA32(context, op.Rm); case IOpCode32AluReg op: return GetIntA32(context, op.Rm);
@ -249,7 +259,7 @@ namespace ARMeilleure.Instructions
} }
// ARM32 helpers. // ARM32 helpers.
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32AluRsImm op, bool setCarry) public static Operand GetMShiftedByImmediate(ArmEmitterContext context, IOpCode32AluRsImm op, bool setCarry)
{ {
Operand m = GetIntA32(context, op.Rm); Operand m = GetIntA32(context, op.Rm);
@ -267,7 +277,7 @@ namespace ARMeilleure.Instructions
if (shift != 0) if (shift != 0)
{ {
setCarry &= op.SetFlags; setCarry &= ShouldSetFlags(context);
switch (op.ShiftType) switch (op.ShiftType)
{ {
@ -305,7 +315,7 @@ namespace ARMeilleure.Instructions
return shift; return shift;
} }
public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry) public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry)
{ {
Operand m = GetIntA32(context, op.Rm); Operand m = GetIntA32(context, op.Rm);
Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs)); Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
@ -314,7 +324,7 @@ namespace ARMeilleure.Instructions
Operand zeroResult = m; Operand zeroResult = m;
Operand shiftResult = m; Operand shiftResult = m;
setCarry &= op.SetFlags; setCarry &= ShouldSetFlags(context);
switch (op.ShiftType) switch (op.ShiftType)
{ {

View File

@ -20,11 +20,11 @@ namespace ARMeilleure.Instructions
private static void EmitExceptionCall(ArmEmitterContext context, string name) private static void EmitExceptionCall(ArmEmitterContext context, string name)
{ {
OpCode32Exception op = (OpCode32Exception)context.CurrOp; IOpCode32Exception op = (IOpCode32Exception)context.CurrOp;
context.StoreToContext(); context.StoreToContext();
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id)); context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
context.LoadFromContext(); context.LoadFromContext();

View File

@ -64,7 +64,7 @@ namespace ARMeilleure.Instructions
bool isThumb = IsThumb(context.CurrOp); bool isThumb = IsThumb(context.CurrOp);
uint currentPc = isThumb uint currentPc = isThumb
? pc | 1 ? (pc - 2) | 1
: pc - 4; : pc - 4;
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc)); SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
@ -80,5 +80,32 @@ namespace ARMeilleure.Instructions
EmitBxWritePc(context, GetIntA32(context, op.Rm), op.Rm); EmitBxWritePc(context, GetIntA32(context, op.Rm), op.Rm);
} }
public static void Cbnz(ArmEmitterContext context) => EmitCb(context, onNotZero: true);
public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false);
private static void EmitCb(ArmEmitterContext context, bool onNotZero)
{
OpCodeT16BImmCmp op = (OpCodeT16BImmCmp)context.CurrOp;
Operand value = GetIntOrZR(context, op.Rn);
Operand lblTarget = context.GetLabel((ulong)op.Immediate);
if (onNotZero)
{
context.BranchIfTrue(lblTarget, value);
}
else
{
context.BranchIfFalse(lblTarget, value);
}
}
public static void It(ArmEmitterContext context)
{
OpCodeT16IfThen op = (OpCodeT16IfThen)context.CurrOp;
context.SetIfThenBlockState(op.IfThenBlockConds);
}
} }
} }

View File

@ -32,7 +32,7 @@ namespace ARMeilleure.Instructions
public static void Ldm(ArmEmitterContext context) public static void Ldm(ArmEmitterContext context)
{ {
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp; IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
Operand n = GetIntA32(context, op.Rn); Operand n = GetIntA32(context, op.Rn);
@ -95,7 +95,7 @@ namespace ARMeilleure.Instructions
public static void Stm(ArmEmitterContext context) public static void Stm(ArmEmitterContext context)
{ {
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp; IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
Operand n = context.Copy(GetIntA32(context, op.Rn)); Operand n = context.Copy(GetIntA32(context, op.Rn));
@ -151,7 +151,7 @@ namespace ARMeilleure.Instructions
private static void EmitLoadOrStore(ArmEmitterContext context, int size, AccessType accType) private static void EmitLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
{ {
OpCode32Mem op = (OpCode32Mem)context.CurrOp; IOpCode32Mem op = (IOpCode32Mem)context.CurrOp;
Operand n = context.Copy(GetIntA32AlignedPC(context, op.Rn)); Operand n = context.Copy(GetIntA32AlignedPC(context, op.Rn));
Operand m = GetMemM(context, setCarry: false); Operand m = GetMemM(context, setCarry: false);
@ -255,5 +255,11 @@ namespace ARMeilleure.Instructions
} }
} }
} }
public static void Adr(ArmEmitterContext context)
{
IOpCode32Adr op = (IOpCode32Adr)context.CurrOp;
SetIntA32(context, op.Rd, Const(op.Immediate));
}
} }
} }

View File

@ -549,9 +549,9 @@ namespace ARMeilleure.Instructions
{ {
case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry); case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
case OpCode32MemReg op: return GetIntA32(context, op.Rm); case IOpCode32MemReg op: return GetIntA32(context, op.Rm);
case OpCode32Mem op: return Const(op.Immediate); case IOpCode32Mem op: return Const(op.Immediate);
case OpCode32SimdMemImm op: return Const(op.Immediate); case OpCode32SimdMemImm op: return Const(op.Immediate);

View File

@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
Operand res = context.Add(a, context.Multiply(n, m)); Operand res = context.Add(a, context.Multiply(n, m));
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
@ -250,13 +250,13 @@ namespace ARMeilleure.Instructions
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res); Operand lo = context.ConvertI64ToI32(res);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi); EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo); EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
} }
public static void Smulw_(ArmEmitterContext context) public static void Smulw_(ArmEmitterContext context)
@ -320,13 +320,13 @@ namespace ARMeilleure.Instructions
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res); Operand lo = context.ConvertI64ToI32(res);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi); EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo); EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
} }
private static void EmitMlal(ArmEmitterContext context, bool signed) private static void EmitMlal(ArmEmitterContext context, bool signed)
@ -356,13 +356,13 @@ namespace ARMeilleure.Instructions
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res); Operand lo = context.ConvertI64ToI32(res);
if (op.SetFlags) if (ShouldSetFlags(context))
{ {
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi); EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo); EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
} }
private static void UpdateQFlag(ArmEmitterContext context, Operand q) private static void UpdateQFlag(ArmEmitterContext context, Operand q)

View File

@ -48,6 +48,7 @@ namespace ARMeilleure.Instructions
Extr, Extr,
Hint, Hint,
Isb, Isb,
It,
Ldar, Ldar,
Ldaxp, Ldaxp,
Ldaxr, Ldaxr,
@ -512,6 +513,8 @@ namespace ARMeilleure.Instructions
Mvn, Mvn,
Pkh, Pkh,
Pld, Pld,
Pop,
Push,
Rev, Rev,
Revsh, Revsh,
Rsb, Rsb,

View File

@ -54,6 +54,11 @@ namespace ARMeilleure.Translation
public bool HighCq { get; } public bool HighCq { get; }
public Aarch32Mode Mode { get; } public Aarch32Mode Mode { get; }
private int _ifThenBlockStateIndex = 0;
private Condition[] _ifThenBlockState = { };
public bool IsInIfThenBlock => _ifThenBlockStateIndex < _ifThenBlockState.Length;
public Condition CurrentIfThenBlockCond => _ifThenBlockState[_ifThenBlockStateIndex];
public ArmEmitterContext( public ArmEmitterContext(
IMemoryManager memory, IMemoryManager memory,
EntryTable<uint> countTable, EntryTable<uint> countTable,
@ -196,5 +201,19 @@ namespace ARMeilleure.Translation
return default; return default;
} }
public void SetIfThenBlockState(Condition[] state)
{
_ifThenBlockState = state;
_ifThenBlockStateIndex = 0;
}
public void AdvanceIfThenBlockState()
{
if (IsInIfThenBlock)
{
_ifThenBlockStateIndex++;
}
}
} }
} }

View File

@ -380,6 +380,13 @@ namespace ARMeilleure.Translation
Operand lblPredicateSkip = default; Operand lblPredicateSkip = default;
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
{
lblPredicateSkip = Label();
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Invert());
}
if (opCode is OpCode32 op && op.Cond < Condition.Al) if (opCode is OpCode32 op && op.Cond < Condition.Al)
{ {
lblPredicateSkip = Label(); lblPredicateSkip = Label();
@ -400,6 +407,11 @@ namespace ARMeilleure.Translation
{ {
context.MarkLabel(lblPredicateSkip); context.MarkLabel(lblPredicateSkip);
} }
if (context.IsInIfThenBlock && opCode.Instruction.Name != InstName.It)
{
context.AdvanceIfThenBlockState();
}
} }
} }
} }

View File

@ -41,8 +41,8 @@ namespace Ryujinx.Tests.Unicorn
public uint PC public uint PC
{ {
get => GetRegister(Arm32Register.PC); get => GetRegister(Arm32Register.PC) & 0xfffffffeu;
set => SetRegister(Arm32Register.PC, value); set => SetRegister(Arm32Register.PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u));
} }
public uint CPSR public uint CPSR
@ -87,6 +87,16 @@ namespace Ryujinx.Tests.Unicorn
set => CPSR = (CPSR & ~0x80000000u) | (value ? 0x80000000u : 0u); set => CPSR = (CPSR & ~0x80000000u) | (value ? 0x80000000u : 0u);
} }
public bool ThumbFlag
{
get => (CPSR & 0x00000020u) != 0;
set
{
CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u);
SetRegister(Arm32Register.PC, (GetRegister(Arm32Register.PC) & 0xfffffffeu) | (value ? 1u : 0u));
}
}
public UnicornAArch32() public UnicornAArch32()
{ {
Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc)); Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));

View File

@ -106,6 +106,18 @@ namespace Ryujinx.Tests.Cpu
_currAddress += 4; _currAddress += 4;
} }
protected void ThumbOpcode(ushort opcode)
{
_memory.Write(_currAddress, opcode);
if (_unicornAvailable)
{
_unicornEmu.MemoryWrite16(_currAddress, opcode);
}
_currAddress += 2;
}
protected ExecutionContext GetContext() => _context; protected ExecutionContext GetContext() => _context;
protected void SetContext(uint r0 = 0, protected void SetContext(uint r0 = 0,
@ -126,7 +138,8 @@ namespace Ryujinx.Tests.Cpu
bool carry = false, bool carry = false,
bool zero = false, bool zero = false,
bool negative = false, bool negative = false,
int fpscr = 0) int fpscr = 0,
bool thumb = false)
{ {
_context.SetX(0, r0); _context.SetX(0, r0);
_context.SetX(1, r1); _context.SetX(1, r1);
@ -151,6 +164,8 @@ namespace Ryujinx.Tests.Cpu
SetFpscr((uint)fpscr); SetFpscr((uint)fpscr);
_context.SetPstateFlag(PState.TFlag, thumb);
if (_unicornAvailable) if (_unicornAvailable)
{ {
_unicornEmu.R[0] = r0; _unicornEmu.R[0] = r0;
@ -175,6 +190,8 @@ namespace Ryujinx.Tests.Cpu
_unicornEmu.NegativeFlag = negative; _unicornEmu.NegativeFlag = negative;
_unicornEmu.Fpscr = fpscr; _unicornEmu.Fpscr = fpscr;
_unicornEmu.ThumbFlag = thumb;
} }
} }
@ -218,6 +235,28 @@ namespace Ryujinx.Tests.Cpu
return GetContext(); return GetContext();
} }
protected ExecutionContext SingleThumbOpcode(ushort opcode,
uint r0 = 0,
uint r1 = 0,
uint r2 = 0,
uint r3 = 0,
uint sp = 0,
bool saturation = false,
bool overflow = false,
bool carry = false,
bool zero = false,
bool negative = false,
int fpscr = 0,
bool runUnicorn = true)
{
ThumbOpcode(opcode);
ThumbOpcode(0x4770); // BX LR
SetContext(r0, r1, r2, r3, sp, default, default, default, default, default, default, default, default, saturation, overflow, carry, zero, negative, fpscr, thumb: true);
ExecuteOpcodes(runUnicorn);
return GetContext();
}
protected void SetWorkingMemory(uint offset, byte[] data) protected void SetWorkingMemory(uint offset, byte[] data)
{ {
_memory.Write(DataBaseAddress + offset, data); _memory.Write(DataBaseAddress + offset, data);

File diff suppressed because one or more lines are too long