aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Instructions
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2020-02-23 21:20:40 +0000
committerGitHub <noreply@github.com>2020-02-24 08:20:40 +1100
commitb1b6f294f252e998920e8f1e6a8eefa2860f0d2c (patch)
treedddeb962c2b3a30096de9a69be8c90c2e6632ea4 /ARMeilleure/Instructions
parent165e658f0219e1666dc83bbadb453acb22df1e39 (diff)
Add most of the A32 instruction set to ARMeilleure (#897)
* Implement TEQ and MOV (Imm16) * Initial work on A32 instructions + SVC. No tests yet, hangs in rtld. * Implement CLZ, fix BFI and BFC Now stops on SIMD initialization. * Exclusive access instructions, fix to mul, system instructions. Now gets to a break after SignalProcessWideKey64. * Better impl of UBFX, add UDIV and SDIV Now boots way further - now stuck on VMOV instruction. * Many more instructions, start on SIMD and testing framework. * Fix build issues * svc: Rework 32 bit codepath Fixing once and for all argument ordering issues. * Fix 32 bits stacktrace * hle debug: Add 32 bits dynamic section parsing * Fix highCq mode, add many tests, fix some instruction bugs Still suffers from critical malloc failure :weary: * Fix incorrect opcode decoders and a few more instructions. * Add a few instructions and fix others. re-disable highCq for now. Disabled the svc memory clear since i'm not sure about it. * Fix build * Fix typo in ordered/exclusive stores. * Implement some more instructions, fix others. Uxtab16/Sxtab16 are untested. * Begin impl of pairwise, some other instructions. * Add a few more instructions, a quick hack to fix svcs for now. * Add tests and fix issues with VTRN, VZIP, VUZP * Add a few more instructions, fix Vmul_1 encoding. * Fix way too many instruction bugs, add tests for some of the more important ones. * Fix HighCq, enable FastFP paths for some floating point instructions (not entirely sure why these were disabled, so important to note this commit exists) Branching has been removed in A32 shifts until I figure out if it's worth it * Cleanup Part 1 There should be no functional change between these next few commits. Should is the key word. (except for removing break handler) * Implement 32 bits syscalls Co-authored-by: riperiperi <rhy3756547@hotmail.com> Implement all 32 bits counterparts of the 64 bits syscalls we currently have. * Refactor part 2: Move index/subindex logic to Operand May have inadvertently fixed one (1) bug * Add FlushProcessDataCache32 * Address jd's comments * Remove 16 bit encodings from OpCodeTable Still need to catch some edge cases (operands that use the "F" flag) and make Q encodings with non-even indexes undefined. * Correct Fpscr handling for FP vector slow paths WIP * Add StandardFPSCRValue behaviour for all Arithmetic instructions * Add StandardFPSCRValue behaviour to compare instructions. * Force passing of fpcr to FPProcessException and FPUnpack. Reduces potential for code error significantly * OpCode cleanup * Remove urgency from DMB comment in MRRC DMB is currently a no-op via the instruction, so it should likely still be a no-op here. * Test Cleanup * Fix FPDefaultNaN on Ryzen CPUs * Improve some tests, fix some shift instructions, add slow path for Vadd * Fix Typo * More test cleanup * Flip order of Fx and index, to indicate that the operand's is the "base" * Remove Simd32 register type, use Int32 and Int64 for scalars like A64 does. * Reintroduce alignment to DecoderHelper (removed by accident) * One more realign as reading diffs is hard * Use I32 registers in A32 (part 2) Swap default integer register type based on current execution mode. * FPSCR flags as Registers (part 1) Still need to change NativeContext and ExecutionContext to allow getting/setting with the flag values. * Use I32 registers in A32 (part 1) * FPSCR flags as registers (part 2) Only CMP flags are on the registers right now. It could be useful to use more of the space in non-fast-float when implementing A32 flags accurately in the fast path. * Address Feedback * Correct FP->Int behaviour (should saturate) * Make branches made by writing to PC eligible for Rejit Greatly improves performance in most games. * Remove unused branching for Vtbl * RejitRequest as a class rather than a tuple Makes a lot more sense than storing tuples on a dictionary. * Add VMOVN, VSHR (imm), VSHRN (imm) and related tests * Re-order InstEmitSystem32 Alphabetical sorting. * Address Feedback Feedback from Ac_K, remove and sort usings. * Address Feedback 2 * Address Feedback from LDj3SNuD Opcode table reordered to have alphabetical sorting within groups, Vmaxnm and Vminnm have split names to be less ambiguous, SoftFloat nits, Test nits and Test simplification with ValueSource. * Add Debug Asserts to A32 helpers Mainly to prevent the shift ones from being used on I64 operands, as they expect I32 input for most operations (eg. carry flag setting), and expect I32 input for shift and boolean amounts. Most other helper functions don't take Operands, throw on out of range values, and take specific types of OpCode, so didn't need any asserts. * Use ConstF rather than creating an operand. (useful for pooling in future) * Move exclusive load to helper, reference call flag rather than literal 1. * Address LDj feedback (minus table flatten) one final look before it's all gone. the world is so beautiful. * Flatten OpCodeTable oh no * Address more table ordering * Call Flag as int on A32 Co-authored-by: Natalie C. <cyuubiapps@gmail.com> Co-authored-by: Thog <thog@protonmail.com>
Diffstat (limited to 'ARMeilleure/Instructions')
-rw-r--r--ARMeilleure/Instructions/DelegateTypes.cs8
-rw-r--r--ARMeilleure/Instructions/InstEmitAlu.cs34
-rw-r--r--ARMeilleure/Instructions/InstEmitAlu32.cs545
-rw-r--r--ARMeilleure/Instructions/InstEmitAluHelper.cs260
-rw-r--r--ARMeilleure/Instructions/InstEmitException32.cs36
-rw-r--r--ARMeilleure/Instructions/InstEmitFlow32.cs50
-rw-r--r--ARMeilleure/Instructions/InstEmitHelper.cs30
-rw-r--r--ARMeilleure/Instructions/InstEmitMemory32.cs15
-rw-r--r--ARMeilleure/Instructions/InstEmitMemoryEx.cs90
-rw-r--r--ARMeilleure/Instructions/InstEmitMemoryEx32.cs240
-rw-r--r--ARMeilleure/Instructions/InstEmitMemoryExHelper.cs87
-rw-r--r--ARMeilleure/Instructions/InstEmitMemoryHelper.cs67
-rw-r--r--ARMeilleure/Instructions/InstEmitMul32.cs290
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs634
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdCmp32.cs273
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdCvt32.cs274
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdHelper.cs4
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdHelper32.cs581
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdLogical32.cs56
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdMemory32.cs352
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdMove32.cs336
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdShift32.cs100
-rw-r--r--ARMeilleure/Instructions/InstEmitSystem32.cs233
-rw-r--r--ARMeilleure/Instructions/InstName.cs126
-rw-r--r--ARMeilleure/Instructions/NativeInterface.cs45
-rw-r--r--ARMeilleure/Instructions/SoftFallback.cs20
-rw-r--r--ARMeilleure/Instructions/SoftFloat.cs858
27 files changed, 5254 insertions, 390 deletions
diff --git a/ARMeilleure/Instructions/DelegateTypes.cs b/ARMeilleure/Instructions/DelegateTypes.cs
index 424203ff..b65149cb 100644
--- a/ARMeilleure/Instructions/DelegateTypes.cs
+++ b/ARMeilleure/Instructions/DelegateTypes.cs
@@ -4,13 +4,19 @@ using System;
namespace ARMeilleure.Instructions
{
delegate double _F64_F64(double a1);
+ delegate double _F64_F64_Bool(double a1, bool a2);
delegate double _F64_F64_F64(double a1, double a2);
+ delegate double _F64_F64_F64_Bool(double a1, double a2, bool a3);
delegate double _F64_F64_F64_F64(double a1, double a2, double a3);
+ delegate double _F64_F64_F64_F64_Bool(double a1, double a2, double a3, bool a4);
delegate double _F64_F64_MidpointRounding(double a1, MidpointRounding a2);
delegate float _F32_F32(float a1);
+ delegate float _F32_F32_Bool(float a1, bool a2);
delegate float _F32_F32_F32(float a1, float a2);
+ delegate float _F32_F32_F32_Bool(float a1, float a2, bool a3);
delegate float _F32_F32_F32_F32(float a1, float a2, float a3);
+ delegate float _F32_F32_F32_F32_Bool(float a1, float a2, float a3, bool a4);
delegate float _F32_F32_MidpointRounding(float a1, MidpointRounding a2);
delegate float _F32_U16(ushort a1);
@@ -37,6 +43,7 @@ namespace ARMeilleure.Instructions
delegate ushort _U16_F32(float a1);
delegate ushort _U16_U64(ulong a1);
+ delegate uint _U32();
delegate uint _U32_F32(float a1);
delegate uint _U32_F64(double a1);
delegate uint _U32_U32(uint a1);
@@ -74,6 +81,7 @@ namespace ARMeilleure.Instructions
delegate V128 _V128_V128_V128_V128(V128 a1, V128 a2, V128 a3);
delegate void _Void();
+ delegate void _Void_U32(uint a1);
delegate void _Void_U64(ulong a1);
delegate void _Void_U64_S32(ulong a1, int a2);
delegate void _Void_U64_U16(ulong a1, ushort a2);
diff --git a/ARMeilleure/Instructions/InstEmitAlu.cs b/ARMeilleure/Instructions/InstEmitAlu.cs
index ed1faae4..6e2875e6 100644
--- a/ARMeilleure/Instructions/InstEmitAlu.cs
+++ b/ARMeilleure/Instructions/InstEmitAlu.cs
@@ -276,23 +276,6 @@ namespace ARMeilleure.Instructions
SetAluDOrZR(context, d);
}
- private static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
- {
- Debug.Assert(op.Type == OperandType.I32);
-
- Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
- context.ShiftLeft (context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
-
- val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
- context.ShiftLeft (context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
- val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
- context.ShiftLeft (context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
- val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
- context.ShiftLeft (context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
-
- return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
- }
-
private static Operand EmitReverseBits64Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);
@@ -331,23 +314,6 @@ namespace ARMeilleure.Instructions
SetAluDOrZR(context, d);
}
- private static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
- {
- Debug.Assert(op.Type == OperandType.I32);
-
- Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
-
- return context.ConvertI64ToI32(val);
- }
-
- private static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
- {
- Debug.Assert(op.Type == OperandType.I64);
-
- return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
- context.ShiftLeft (context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
- }
-
public static void Rev32(ArmEmitterContext context)
{
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
diff --git a/ARMeilleure/Instructions/InstEmitAlu32.cs b/ARMeilleure/Instructions/InstEmitAlu32.cs
index 79b0abbc..4d03f5c2 100644
--- a/ARMeilleure/Instructions/InstEmitAlu32.cs
+++ b/ARMeilleure/Instructions/InstEmitAlu32.cs
@@ -3,8 +3,8 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
-using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitAluHelper;
+using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
@@ -31,6 +31,101 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res);
}
+ public static void Adc(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context, setCarry: false);
+
+ Operand res = context.Add(n, m);
+
+ Operand carry = GetFlag(PState.CFlag);
+
+ res = context.Add(res, carry);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+
+ EmitAdcsCCheck(context, n, res);
+ EmitAddsVCheck(context, n, m, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void And(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseAnd(n, m);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Bfc(ArmEmitterContext context)
+ {
+ OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
+
+ Operand d = GetIntA32(context, op.Rd);
+ Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
+
+ SetIntA32(context, op.Rd, res);
+ }
+
+ public static void Bfi(ArmEmitterContext context)
+ {
+ OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand d = GetIntA32(context, op.Rd);
+ Operand part = context.BitwiseAnd(n, Const(op.SourceMask));
+
+ if (op.Lsb != 0)
+ {
+ part = context.ShiftLeft(part, Const(op.Lsb));
+ }
+
+ Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
+ res = context.BitwiseOr(res, context.BitwiseAnd(part, Const(op.DestMask)));
+
+ SetIntA32(context, op.Rd, res);
+ }
+
+ public static void Bic(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseAnd(n, context.BitwiseNot(m));
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Clz(ArmEmitterContext context)
+ {
+ Operand m = GetAluM(context, setCarry: false);
+
+ Operand res = context.CountLeadingZeros(m);
+ EmitAluStore(context, res);
+ }
+
public static void Cmp(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@@ -46,6 +141,36 @@ namespace ARMeilleure.Instructions
EmitSubsVCheck(context, n, m, res);
}
+ public static void Cmn(ArmEmitterContext context)
+ {
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context, setCarry: false);
+
+ Operand res = context.Add(n, m);
+
+ EmitNZFlagsCheck(context, res);
+
+ EmitAddsCCheck(context, n, res);
+ EmitAddsVCheck(context, n, m, res);
+ }
+
+ public static void Eor(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseExclusiveOr(n, m);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
public static void Mov(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@@ -60,7 +185,170 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, m);
}
- public static void Sub(ArmEmitterContext context)
+ public static void Movt(ArmEmitterContext context)
+ {
+ OpCode32AluImm16 op = (OpCode32AluImm16)context.CurrOp;
+
+ Operand d = GetIntA32(context, op.Rd);
+ Operand imm = Const(op.Immediate << 16); // Immeditate value as top halfword.
+ Operand res = context.BitwiseAnd(d, Const(0x0000ffff));
+ res = context.BitwiseOr(res, imm);
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Mul(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.Multiply(n, m);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Mvn(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseNot(m);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Orr(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseOr(n, m);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Pkh(ArmEmitterContext context)
+ {
+ OpCode32AluRsImm op = (OpCode32AluRsImm)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res;
+
+ bool tbform = op.ShiftType == ShiftType.Asr;
+ if (tbform)
+ {
+ res = context.BitwiseOr(context.BitwiseAnd(n, Const(0xFFFF0000)), context.BitwiseAnd(m, Const(0xFFFF)));
+ }
+ else
+ {
+ res = context.BitwiseOr(context.BitwiseAnd(m, Const(0xFFFF0000)), context.BitwiseAnd(n, Const(0xFFFF)));
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Rbit(ArmEmitterContext context)
+ {
+ Operand m = GetAluM(context);
+
+ Operand res = EmitReverseBits32Op(context, m);
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Rev(ArmEmitterContext context)
+ {
+ Operand m = GetAluM(context);
+
+ Operand res = context.ByteSwap(m);
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Rev16(ArmEmitterContext context)
+ {
+ Operand m = GetAluM(context);
+
+ Operand res = EmitReverseBytes16_32Op(context, m);
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Revsh(ArmEmitterContext context)
+ {
+ Operand m = GetAluM(context);
+
+ Operand res = EmitReverseBytes16_32Op(context, m);
+
+ EmitAluStore(context, context.SignExtend16(OperandType.I32, res));
+ }
+
+ public static void Rsc(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context, setCarry: false);
+
+ Operand res = context.Subtract(m, n);
+
+ Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
+
+ res = context.Subtract(res, borrow);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+
+ EmitSbcsCCheck(context, m, n);
+ EmitSubsVCheck(context, m, n, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Rsb(ArmEmitterContext context)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context, setCarry: false);
+
+ Operand res = context.Subtract(m, n);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+
+ EmitSubsCCheck(context, m, res);
+ EmitSubsVCheck(context, m, n, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Sbc(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@@ -69,61 +357,268 @@ namespace ARMeilleure.Instructions
Operand res = context.Subtract(n, m);
+ Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
+
+ res = context.Subtract(res, borrow);
+
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
- EmitSubsCCheck(context, n, res);
+ EmitSbcsCCheck(context, n, m);
EmitSubsVCheck(context, n, m, res);
}
EmitAluStore(context, res);
}
- private static void EmitAluStore(ArmEmitterContext context, Operand value)
+ public static void Sbfx(ArmEmitterContext context)
+ {
+ OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
+
+ var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand res = context.ShiftRightSI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
+
+ SetIntA32(context, op.Rd, res);
+ }
+
+ public static void Sdiv(ArmEmitterContext context)
+ {
+ EmitDiv(context, false);
+ }
+
+ public static void Sub(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
- if (op.Rd == RegisterAlias.Aarch32Pc)
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context, setCarry: false);
+
+ Operand res = context.Subtract(n, m);
+
+ if (op.SetFlags)
{
- if (op.SetFlags)
- {
- // TODO: Load SPSR etc.
- Operand isThumb = GetFlag(PState.TFlag);
+ EmitNZFlagsCheck(context, res);
- Operand lblThumb = Label();
+ EmitSubsCCheck(context, n, res);
+ EmitSubsVCheck(context, n, m, res);
+ }
- context.BranchIfTrue(lblThumb, isThumb);
+ EmitAluStore(context, res);
+ }
- context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~3))));
+ public static void Sxtb(ArmEmitterContext context)
+ {
+ EmitSignExtend(context, true, 8);
+ }
- context.MarkLabel(lblThumb);
+ public static void Sxtb16(ArmEmitterContext context)
+ {
+ EmitExtend16(context, true);
+ }
- context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1))));
- }
- else
- {
- EmitAluWritePc(context, value);
- }
+ public static void Sxth(ArmEmitterContext context)
+ {
+ EmitSignExtend(context, true, 16);
+ }
+
+ public static void Teq(ArmEmitterContext context)
+ {
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseExclusiveOr(n, m);
+
+ EmitNZFlagsCheck(context, res);
+ }
+
+ public static void Tst(ArmEmitterContext context)
+ {
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+
+ Operand res = context.BitwiseAnd(n, m);
+ EmitNZFlagsCheck(context, res);
+ }
+
+ public static void Ubfx(ArmEmitterContext context)
+ {
+ OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
+
+ var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
+
+ SetIntA32(context, op.Rd, res);
+ }
+
+ public static void Udiv(ArmEmitterContext context)
+ {
+ EmitDiv(context, true);
+ }
+
+ public static void Uxtb(ArmEmitterContext context)
+ {
+ EmitSignExtend(context, false, 8);
+ }
+
+ public static void Uxtb16(ArmEmitterContext context)
+ {
+ EmitExtend16(context, false);
+ }
+
+ public static void Uxth(ArmEmitterContext context)
+ {
+ EmitSignExtend(context, false, 16);
+ }
+
+ private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits)
+ {
+ IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
+
+ Operand m = GetAluM(context);
+ Operand res;
+
+ if (op.RotateBits == 0)
+ {
+ res = m;
}
else
{
- SetIntA32(context, op.Rd, value);
+ Operand rotate = Const(op.RotateBits);
+ res = context.RotateRight(m, rotate);
+ }
+
+ switch (bits)
+ {
+ case 8:
+ res = (signed) ? context.SignExtend8(OperandType.I32, res) : context.ZeroExtend8(OperandType.I32, res);
+ break;
+ case 16:
+ res = (signed) ? context.SignExtend16(OperandType.I32, res) : context.ZeroExtend16(OperandType.I32, res);
+ break;
+ }
+
+ if (op.Add)
+ {
+ res = context.Add(res, GetAluN(context));
}
+
+ EmitAluStore(context, res);
}
- private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
+ private static void EmitExtend16(ArmEmitterContext context, bool signed)
{
- context.StoreToContext();
+ IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
- if (IsThumb(context.CurrOp))
+ Operand m = GetAluM(context);
+ Operand res;
+
+ if (op.RotateBits == 0)
{
- context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1))));
+ res = m;
}
else
{
- EmitBxWritePc(context, value);
+ Operand rotate = Const(op.RotateBits);
+ res = context.RotateRight(m, rotate);
+ }
+
+ Operand low16, high16;
+ if (signed)
+ {
+ low16 = context.SignExtend8(OperandType.I32, res);
+ high16 = context.SignExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
}
+ else
+ {
+ low16 = context.ZeroExtend8(OperandType.I32, res);
+ high16 = context.ZeroExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
+ }
+
+ if (op.Add)
+ {
+ Operand n = GetAluN(context);
+ Operand lowAdd, highAdd;
+ if (signed)
+ {
+ lowAdd = context.SignExtend16(OperandType.I32, n);
+ highAdd = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
+ }
+ else
+ {
+ lowAdd = context.ZeroExtend16(OperandType.I32, n);
+ highAdd = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
+ }
+
+ low16 = context.Add(low16, lowAdd);
+ high16 = context.Add(high16, highAdd);
+ }
+
+ res = context.BitwiseOr(
+ context.ZeroExtend16(OperandType.I32, low16),
+ context.ShiftLeft(context.ZeroExtend16(OperandType.I32, high16), Const(16)));
+
+ EmitAluStore(context, res);
+ }
+
+ public static void EmitDiv(ArmEmitterContext context, bool unsigned)
+ {
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+ Operand zero = Const(m.Type, 0);
+
+ Operand divisorIsZero = context.ICompareEqual(m, zero);
+
+ Operand lblBadDiv = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBadDiv, divisorIsZero);
+
+ if (!unsigned)
+ {
+ // ARM64 behaviour: If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
+ // TODO: tests to ensure A32 works the same
+
+ Operand intMin = Const(int.MinValue);
+ Operand minus1 = Const(-1);
+
+ Operand nIsIntMin = context.ICompareEqual(n, intMin);
+ Operand mIsMinus1 = context.ICompareEqual(m, minus1);
+
+ Operand lblGoodDiv = Label();
+
+ context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1));
+
+ EmitAluStore(context, intMin);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblGoodDiv);
+ }
+
+ Operand res = unsigned
+ ? context.DivideUI(n, m)
+ : context.Divide(n, m);
+
+ EmitAluStore(context, res);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBadDiv);
+
+ EmitAluStore(context, zero);
+
+ context.MarkLabel(lblEnd);
+ }
+
+ private static void EmitAluStore(ArmEmitterContext context, Operand value)
+ {
+ IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
+ EmitGenericAluStoreA32(context, op.Rd, op.SetFlags, value);
}
}
} \ No newline at end of file
diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/ARMeilleure/Instructions/InstEmitAluHelper.cs
index d032b32e..3bb87f27 100644
--- a/ARMeilleure/Instructions/InstEmitAluHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitAluHelper.cs
@@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
+using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@@ -77,6 +78,89 @@ namespace ARMeilleure.Instructions
SetFlag(context, PState.VFlag, vOut);
}
+ public static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
+ {
+ Debug.Assert(op.Type == OperandType.I32);
+
+ Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
+ context.ShiftLeft(context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
+
+ val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
+ context.ShiftLeft(context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
+ val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
+ context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
+ val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
+ context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
+
+ return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
+ }
+
+ public static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
+ {
+ Debug.Assert(op.Type == OperandType.I64);
+
+ return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
+ context.ShiftLeft(context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
+ }
+
+ public static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
+ {
+ Debug.Assert(op.Type == OperandType.I32);
+
+ Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
+
+ return context.ConvertI64ToI32(val);
+ }
+
+ private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
+ {
+ Debug.Assert(value.Type == OperandType.I32);
+
+ context.StoreToContext();
+
+ if (IsThumb(context.CurrOp))
+ {
+ // Make this count as a call, the translator will ignore the low bit for the address.
+ context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(value, Const(1))));
+ }
+ else
+ {
+ EmitBxWritePc(context, value);
+ }
+ }
+
+ public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value)
+ {
+ Debug.Assert(value.Type == OperandType.I32);
+
+ if (rd == RegisterAlias.Aarch32Pc && setFlags)
+ {
+ if (setFlags)
+ {
+ // TODO: Load SPSR etc.
+ Operand isThumb = GetFlag(PState.TFlag);
+
+ Operand lblThumb = Label();
+
+ context.BranchIfTrue(lblThumb, isThumb);
+
+ // Make this count as a call, the translator will ignore the low bit for the address.
+ context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(context.BitwiseAnd(value, Const(~3)), Const(1))));
+
+ context.MarkLabel(lblThumb);
+
+ context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(value, Const(1))));
+ }
+ else
+ {
+ EmitAluWritePc(context, value);
+ }
+ }
+ else
+ {
+ SetIntA32(context, rd, value);
+ }
+ }
public static Operand GetAluN(ArmEmitterContext context)
{
@@ -116,10 +200,15 @@ namespace ARMeilleure.Instructions
return Const(op.Immediate);
}
+ case OpCode32AluImm16 op: return Const(op.Immediate);
+
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
+ case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
case OpCodeT16AluImm8 op: return Const(op.Immediate);
+ case IOpCode32AluReg op: return GetIntA32(context, op.Rm);
+
// ARM64.
case IOpCodeAluImm op:
{
@@ -167,11 +256,11 @@ namespace ARMeilleure.Instructions
}
// ARM32 helpers.
- private static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32AluRsImm op, bool setCarry)
+ public static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32AluRsImm op, bool setCarry)
{
Operand m = GetIntA32(context, op.Rm);
- int shift = op.Imm;
+ int shift = op.Immediate;
if (shift == 0)
{
@@ -193,7 +282,7 @@ namespace ARMeilleure.Instructions
case ShiftType.Lsr: m = GetLsrC(context, m, setCarry, shift); break;
case ShiftType.Asr: m = GetAsrC(context, m, setCarry, shift); break;
case ShiftType.Ror:
- if (op.Imm != 0)
+ if (op.Immediate != 0)
{
m = GetRorC(context, m, setCarry, shift);
}
@@ -208,8 +297,74 @@ namespace ARMeilleure.Instructions
return m;
}
- private static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry)
+ {
+ Operand m = GetIntA32(context, op.Rm);
+ Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
+ Operand shiftIsZero = context.ICompareEqual(s, Const(0));
+
+ Operand zeroResult = m;
+ Operand shiftResult = m;
+
+ setCarry &= op.SetFlags;
+
+ switch (op.ShiftType)
+ {
+ case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break;
+ case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
+ case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
+ case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
+ }
+
+ return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
+ }
+
+ public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true)
+ {
+ Debug.Assert(boolValue.Type == OperandType.I32);
+
+ Operand endLabel = Label();
+
+ if (expected)
+ {
+ context.BranchIfFalse(endLabel, boolValue);
+ }
+ else
+ {
+ context.BranchIfTrue(endLabel, boolValue);
+ }
+
+ action();
+
+ context.MarkLabel(endLabel);
+ }
+
+ public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
+ Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
+
+ Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
+ Operand result = context.ShiftLeft(m, shift);
+ if (setCarry)
+ {
+ EmitIfHelper(context, shiftIsZero, () =>
+ {
+ Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
+
+ cOut = context.BitwiseAnd(cOut, Const(1));
+ cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
+
+ SetFlag(context, PState.CFlag, cOut);
+ }, false);
+ }
+
+ return context.ConditionalSelect(shiftLarge, Const(0), result);
+ }
+
+ public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ {
+ Debug.Assert(m.Type == OperandType.I32);
+
if ((uint)shift > 32)
{
return GetShiftByMoreThan32(context, setCarry);
@@ -238,8 +393,32 @@ namespace ARMeilleure.Instructions
}
}
- private static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
+ Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
+
+ Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
+ Operand result = context.ShiftRightUI(m, shift);
+ if (setCarry)
+ {
+ EmitIfHelper(context, shiftIsZero, () =>
+ {
+ Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
+
+ cOut = context.BitwiseAnd(cOut, Const(1));
+ cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
+
+ SetFlag(context, PState.CFlag, cOut);
+ }, false);
+ }
+
+ return context.ConditionalSelect(shiftLarge, Const(0), result);
+ }
+
+ public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ {
+ Debug.Assert(m.Type == OperandType.I32);
+
if ((uint)shift > 32)
{
return GetShiftByMoreThan32(context, setCarry);
@@ -274,8 +453,45 @@ namespace ARMeilleure.Instructions
return Const(0);
}
- private static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
+ {
+ Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
+
+ Operand l32Result;
+ Operand ge32Result;
+
+ Operand less32 = context.ICompareLess(shift, Const(32));
+
+ ge32Result = context.ShiftRightSI(m, Const(31));
+
+ if (setCarry)
+ {
+ EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
+ {
+ SetCarryMLsb(context, ge32Result);
+ }, false);
+ }
+
+ l32Result = context.ShiftRightSI(m, shift);
+ if (setCarry)
+ {
+ EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
+ {
+ Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
+
+ cOut = context.BitwiseAnd(cOut, Const(1));
+
+ SetFlag(context, PState.CFlag, cOut);
+ });
+ }
+
+ return context.ConditionalSelect(less32, l32Result, ge32Result);
+ }
+
+ public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{
+ Debug.Assert(m.Type == OperandType.I32);
+
if ((uint)shift >= 32)
{
m = context.ShiftRightSI(m, Const(31));
@@ -298,8 +514,28 @@ namespace ARMeilleure.Instructions
}
}
- private static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
+ Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
+
+ shift = context.BitwiseAnd(shift, Const(0x1f));
+ m = context.RotateRight(m, shift);
+
+ if (setCarry)
+ {
+ EmitIfHelper(context, shiftIsZero, () =>
+ {
+ SetCarryMMsb(context, m);
+ }, false);
+ }
+
+ return m;
+ }
+
+ public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
+ {
+ Debug.Assert(m.Type == OperandType.I32);
+
shift &= 0x1f;
m = context.RotateRight(m, Const(shift));
@@ -312,8 +548,10 @@ namespace ARMeilleure.Instructions
return m;
}
- private static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
+ public static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
{
+ Debug.Assert(m.Type == OperandType.I32);
+
// Rotate right by 1 with carry.
Operand cIn = context.Copy(GetFlag(PState.CFlag));
@@ -331,16 +569,22 @@ namespace ARMeilleure.Instructions
private static void SetCarryMLsb(ArmEmitterContext context, Operand m)
{
+ Debug.Assert(m.Type == OperandType.I32);
+
SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1)));
}
private static void SetCarryMMsb(ArmEmitterContext context, Operand m)
{
+ Debug.Assert(m.Type == OperandType.I32);
+
SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31)));
}
private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift)
{
+ Debug.Assert(m.Type == OperandType.I32);
+
Operand cOut = context.ShiftRightUI(m, Const(shift - 1));
cOut = context.BitwiseAnd(cOut, Const(1));
diff --git a/ARMeilleure/Instructions/InstEmitException32.cs b/ARMeilleure/Instructions/InstEmitException32.cs
new file mode 100644
index 00000000..a73f0dec
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitException32.cs
@@ -0,0 +1,36 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.Translation;
+
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Svc(ArmEmitterContext context)
+ {
+ EmitExceptionCall(context, NativeInterface.SupervisorCall);
+ }
+
+ public static void Trap(ArmEmitterContext context)
+ {
+ EmitExceptionCall(context, NativeInterface.Break);
+ }
+
+ private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func)
+ {
+ OpCode32Exception op = (OpCode32Exception)context.CurrOp;
+
+ context.StoreToContext();
+
+ context.Call(func, Const(op.Address), Const(op.Id));
+
+ context.LoadFromContext();
+
+ if (context.CurrBlock.Next == null)
+ {
+ context.Return(Const(op.Address + 4));
+ }
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitFlow32.cs b/ARMeilleure/Instructions/InstEmitFlow32.cs
index 27addc78..cbb9ad5b 100644
--- a/ARMeilleure/Instructions/InstEmitFlow32.cs
+++ b/ARMeilleure/Instructions/InstEmitFlow32.cs
@@ -1,7 +1,9 @@
using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
+using static ARMeilleure.Instructions.InstEmitFlowHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@@ -20,7 +22,6 @@ namespace ARMeilleure.Instructions
else
{
context.StoreToContext();
-
context.Return(Const(op.Immediate));
}
}
@@ -35,15 +36,6 @@ namespace ARMeilleure.Instructions
Blx(context, x: true);
}
- public static void Bx(ArmEmitterContext context)
- {
- IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
-
- context.StoreToContext();
-
- EmitBxWritePc(context, GetIntA32(context, op.Rm));
- }
-
private static void Blx(ArmEmitterContext context, bool x)
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
@@ -53,10 +45,10 @@ namespace ARMeilleure.Instructions
bool isThumb = IsThumb(context.CurrOp);
uint currentPc = isThumb
- ? op.GetPc() | 1
- : op.GetPc() - 4;
+ ? pc | 1
+ : pc - 4;
- SetIntOrSP(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
+ SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
// If x is true, then this is a branch with link and exchange.
// In this case we need to swap the mode between Arm <-> Thumb.
@@ -67,5 +59,37 @@ namespace ARMeilleure.Instructions
InstEmitFlowHelper.EmitCall(context, (ulong)op.Immediate);
}
+
+ public static void Blxr(ArmEmitterContext context)
+ {
+ IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
+
+ uint pc = op.GetPc();
+
+ Operand addr = GetIntA32(context, op.Rm);
+ Operand bitOne = context.BitwiseAnd(addr, Const(1));
+ addr = context.BitwiseOr(addr, Const((int)CallFlag)); // Set call flag.
+
+ bool isThumb = IsThumb(context.CurrOp);
+
+ uint currentPc = isThumb
+ ? pc | 1
+ : pc - 4;
+
+ SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
+
+ SetFlag(context, PState.TFlag, bitOne);
+
+ context.Return(addr); // Call.
+ }
+
+ public static void Bx(ArmEmitterContext context)
+ {
+ IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
+
+ context.StoreToContext();
+
+ EmitBxWritePc(context, GetIntA32(context, op.Rm));
+ }
}
} \ No newline at end of file
diff --git a/ARMeilleure/Instructions/InstEmitHelper.cs b/ARMeilleure/Instructions/InstEmitHelper.cs
index 02e104a4..f5495c66 100644
--- a/ARMeilleure/Instructions/InstEmitHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitHelper.cs
@@ -43,10 +43,15 @@ namespace ARMeilleure.Instructions
}
else
{
- return GetIntOrSP(context, GetRegisterAlias(context.Mode, regIndex));
+ return Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
}
}
+ public static Operand GetVecA32(int regIndex)
+ {
+ return Register(regIndex, RegisterType.Vector, OperandType.V128);
+ }
+
public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
{
if (regIndex == RegisterAlias.Aarch32Pc)
@@ -57,7 +62,13 @@ namespace ARMeilleure.Instructions
}
else
{
- SetIntOrSP(context, GetRegisterAlias(context.Mode, regIndex), value);
+ if (value.Type == OperandType.I64)
+ {
+ value = context.ConvertI64ToI32(value);
+ }
+ Operand reg = Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
+
+ context.Copy(reg, value);
}
}
@@ -143,11 +154,12 @@ namespace ARMeilleure.Instructions
context.BranchIfTrue(lblArmMode, mode);
- context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~1))));
+ // Make this count as a call, the translator will ignore the low bit for the address.
+ context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(pc, Const((int)InstEmitFlowHelper.CallFlag))));
context.MarkLabel(lblArmMode);
- context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~3))));
+ context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(context.BitwiseAnd(pc, Const(~3)), Const((int)InstEmitFlowHelper.CallFlag))));
}
public static Operand GetIntOrZR(ArmEmitterContext context, int regIndex)
@@ -208,11 +220,21 @@ namespace ARMeilleure.Instructions
return Register((int)stateFlag, RegisterType.Flag, OperandType.I32);
}
+ public static Operand GetFpFlag(FPState stateFlag)
+ {
+ return Register((int)stateFlag, RegisterType.FpFlag, OperandType.I32);
+ }
+
public static void SetFlag(ArmEmitterContext context, PState stateFlag, Operand value)
{
context.Copy(GetFlag(stateFlag), value);
context.MarkFlagSet(stateFlag);
}
+
+ public static void SetFpFlag(ArmEmitterContext context, FPState stateFlag, Operand value)
+ {
+ context.Copy(GetFpFlag(stateFlag), value);
+ }
}
}
diff --git a/ARMeilleure/Instructions/InstEmitMemory32.cs b/ARMeilleure/Instructions/InstEmitMemory32.cs
index 002d2c5c..ffd816b2 100644
--- a/ARMeilleure/Instructions/InstEmitMemory32.cs
+++ b/ARMeilleure/Instructions/InstEmitMemory32.cs
@@ -20,9 +20,11 @@ namespace ARMeilleure.Instructions
[Flags]
enum AccessType
{
- Store = 0,
- Signed = 1,
- Load = 2,
+ Store = 0,
+ Signed = 1,
+ Load = 2,
+ Ordered = 4,
+ Exclusive = 8,
LoadZx = Load,
LoadSx = Load | Signed,
@@ -95,7 +97,7 @@ namespace ARMeilleure.Instructions
{
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
- Operand n = GetIntA32(context, op.Rn);
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
Operand baseAddress = context.Add(n, Const(op.Offset));
@@ -152,14 +154,15 @@ namespace ARMeilleure.Instructions
OpCode32Mem op = (OpCode32Mem)context.CurrOp;
Operand n = context.Copy(GetIntA32(context, op.Rn));
+ Operand m = GetMemM(context, setCarry: false);
Operand temp = null;
if (op.Index || op.WBack)
{
temp = op.Add
- ? context.Add (n, Const(op.Immediate))
- : context.Subtract(n, Const(op.Immediate));
+ ? context.Add (n, m)
+ : context.Subtract(n, m);
}
if (op.WBack)
diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx.cs b/ARMeilleure/Instructions/InstEmitMemoryEx.cs
index bcca7619..93c20cb5 100644
--- a/ARMeilleure/Instructions/InstEmitMemoryEx.cs
+++ b/ARMeilleure/Instructions/InstEmitMemoryEx.cs
@@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
@@ -66,7 +67,7 @@ namespace ARMeilleure.Instructions
// method to read 128-bits atomically.
if (op.Size == 2)
{
- Operand value = EmitLoad(context, address, exclusive, 3);
+ Operand value = EmitLoadExclusive(context, address, exclusive, 3);
Operand valueLow = context.ConvertI64ToI32(value);
@@ -79,7 +80,7 @@ namespace ARMeilleure.Instructions
}
else if (op.Size == 3)
{
- Operand value = EmitLoad(context, address, exclusive, 4);
+ Operand value = EmitLoadExclusive(context, address, exclusive, 4);
Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
@@ -95,46 +96,11 @@ namespace ARMeilleure.Instructions
else
{
// 8, 16, 32 or 64-bits (non-pairwise) load.
- Operand value = EmitLoad(context, address, exclusive, op.Size);
+ Operand value = EmitLoadExclusive(context, address, exclusive, op.Size);
SetIntOrZR(context, op.Rt, value);
}
}
-
- private static Operand EmitLoad(
- ArmEmitterContext context,
- Operand address,
- bool exclusive,
- int size)
- {
- Delegate fallbackMethodDlg = null;
-
- if (exclusive)
- {
- switch (size)
- {
- case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByteExclusive); break;
- case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16Exclusive); break;
- case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32Exclusive); break;
- case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64Exclusive); break;
- case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break;
- }
- }
- else
- {
- switch (size)
- {
- case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break;
- case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break;
- case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break;
- case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break;
- case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
- }
- }
-
- return context.Call(fallbackMethodDlg, address);
- }
-
public static void Pfrm(ArmEmitterContext context)
{
// Memory Prefetch, execute as no-op.
@@ -192,11 +158,11 @@ namespace ARMeilleure.Instructions
value = context.VectorInsert(value, t2, 1);
}
- s = EmitStore(context, address, value, exclusive, op.Size + 1);
+ s = EmitStoreExclusive(context, address, value, exclusive, op.Size + 1);
}
else
{
- s = EmitStore(context, address, t, exclusive, op.Size);
+ s = EmitStoreExclusive(context, address, t, exclusive, op.Size);
}
if (s != null)
@@ -207,50 +173,6 @@ namespace ARMeilleure.Instructions
}
}
- private static Operand EmitStore(
- ArmEmitterContext context,
- Operand address,
- Operand value,
- bool exclusive,
- int size)
- {
- if (size < 3)
- {
- value = context.ConvertI64ToI32(value);
- }
-
- Delegate fallbackMethodDlg = null;
-
- if (exclusive)
- {
- switch (size)
- {
- case 0: fallbackMethodDlg = new _S32_U64_U8 (NativeInterface.WriteByteExclusive); break;
- case 1: fallbackMethodDlg = new _S32_U64_U16 (NativeInterface.WriteUInt16Exclusive); break;
- case 2: fallbackMethodDlg = new _S32_U64_U32 (NativeInterface.WriteUInt32Exclusive); break;
- case 3: fallbackMethodDlg = new _S32_U64_U64 (NativeInterface.WriteUInt64Exclusive); break;
- case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break;
- }
-
- return context.Call(fallbackMethodDlg, address, value);
- }
- else
- {
- switch (size)
- {
- case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break;
- case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break;
- case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break;
- case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break;
- case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
- }
-
- context.Call(fallbackMethodDlg, address, value);
-
- return null;
- }
- }
-
private static void EmitBarrier(ArmEmitterContext context)
{
// Note: This barrier is most likely not necessary, and probably
diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx32.cs b/ARMeilleure/Instructions/InstEmitMemoryEx32.cs
new file mode 100644
index 00000000..0ab990f8
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitMemoryEx32.cs
@@ -0,0 +1,240 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.State;
+using ARMeilleure.Translation;
+
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Clrex(ArmEmitterContext context)
+ {
+ context.Call(new _Void(NativeInterface.ClearExclusive));
+ }
+
+ public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
+
+ public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
+
+ public static void Ldrex(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
+ }
+
+ public static void Ldrexb(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
+ }
+
+ public static void Ldrexd(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
+ }
+
+ public static void Ldrexh(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
+ }
+
+ public static void Lda(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Ordered);
+ }
+
+ public static void Ldab(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Ordered);
+ }
+
+ public static void Ldaex(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Ldaexb(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Ldaexd(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Ldaexh(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Ldah(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Ordered);
+ }
+
+ // Stores.
+
+ public static void Strex(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Exclusive);
+ }
+
+ public static void Strexb(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Exclusive);
+ }
+
+ public static void Strexd(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, DWordSizeLog2, AccessType.Store | AccessType.Exclusive);
+ }
+
+ public static void Strexh(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Exclusive);
+ }
+
+ public static void Stl(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Ordered);
+ }
+
+ public static void Stlb(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Ordered);
+ }
+
+ public static void Stlex(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Stlexb(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Stlexd(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, DWordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Stlexh(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
+ }
+
+ public static void Stlh(ArmEmitterContext context)
+ {
+ EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Ordered);
+ }
+
+ private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
+ {
+ IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp;
+
+ Operand address = context.Copy(GetIntA32(context, op.Rn));
+
+ var exclusive = (accType & AccessType.Exclusive) != 0;
+ var ordered = (accType & AccessType.Ordered) != 0;
+
+ if (ordered)
+ {
+ EmitBarrier(context);
+ }
+
+ if ((accType & AccessType.Load) != 0)
+ {
+ if (size == DWordSizeLog2)
+ {
+ // Keep loads atomic - make the call to get the whole region and then decompose it into parts
+ // for the registers.
+
+ Operand value = EmitLoadExclusive(context, address, exclusive, size);
+
+ Operand valueLow = context.ConvertI64ToI32(value);
+
+ valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
+
+ Operand valueHigh = context.ShiftRightUI(value, Const(32));
+
+ Operand lblBigEndian = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
+
+ SetIntA32(context, op.Rt, valueLow);
+ SetIntA32(context, op.Rt | 1, valueHigh);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBigEndian);
+
+ SetIntA32(context, op.Rt | 1, valueLow);
+ SetIntA32(context, op.Rt, valueHigh);
+
+ context.MarkLabel(lblEnd);
+ }
+ else
+ {
+ SetIntA32(context, op.Rt, EmitLoadExclusive(context, address, exclusive, size));
+ }
+ }
+ else
+ {
+ if (size == DWordSizeLog2)
+ {
+ // Split the result into 2 words (based on endianness)
+
+ Operand lo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
+ Operand hi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt | 1));
+
+ Operand lblBigEndian = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
+
+ Operand leResult = context.BitwiseOr(lo, context.ShiftLeft(hi, Const(32)));
+ Operand leS = EmitStoreExclusive(context, address, leResult, exclusive, size);
+ if (exclusive)
+ {
+ SetIntA32(context, op.Rd, leS);
+ }
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBigEndian);
+
+ Operand beResult = context.BitwiseOr(hi, context.ShiftLeft(lo, Const(32)));
+ Operand beS = EmitStoreExclusive(context, address, beResult, exclusive, size);
+ if (exclusive)
+ {
+ SetIntA32(context, op.Rd, beS);
+ }
+
+ context.MarkLabel(lblEnd);
+ }
+ else
+ {
+ Operand s = EmitStoreExclusive(context, address, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt)), exclusive, size);
+ // This is only needed for exclusive stores. The function returns 0
+ // when the store is successful, and 1 otherwise.
+ if (exclusive)
+ {
+ SetIntA32(context, op.Rd, s);
+ }
+ }
+ }
+ }
+
+ private static void EmitBarrier(ArmEmitterContext context)
+ {
+ // Note: This barrier is most likely not necessary, and probably
+ // doesn't make any difference since we need to do a ton of stuff
+ // (software MMU emulation) to read or write anything anyway.
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
new file mode 100644
index 00000000..00a5385b
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
@@ -0,0 +1,87 @@
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+
+namespace ARMeilleure.Instructions
+{
+ static class InstEmitMemoryExHelper
+ {
+ public static Operand EmitLoadExclusive(
+ ArmEmitterContext context,
+ Operand address,
+ bool exclusive,
+ int size)
+ {
+ Delegate fallbackMethodDlg = null;
+
+ if (exclusive)
+ {
+ switch (size)
+ {
+ case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByteExclusive); break;
+ case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16Exclusive); break;
+ case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32Exclusive); break;
+ case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64Exclusive); break;
+ case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break;
+ }
+ }
+ else
+ {
+ switch (size)
+ {
+ case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByte); break;
+ case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break;
+ case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break;
+ case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break;
+ case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
+ }
+ }
+
+ return context.Call(fallbackMethodDlg, address);
+ }
+
+ public static Operand EmitStoreExclusive(
+ ArmEmitterContext context,
+ Operand address,
+ Operand value,
+ bool exclusive,
+ int size)
+ {
+ if (size < 3)
+ {
+ value = context.ConvertI64ToI32(value);
+ }
+
+ Delegate fallbackMethodDlg = null;
+
+ if (exclusive)
+ {
+ switch (size)
+ {
+ case 0: fallbackMethodDlg = new _S32_U64_U8(NativeInterface.WriteByteExclusive); break;
+ case 1: fallbackMethodDlg = new _S32_U64_U16(NativeInterface.WriteUInt16Exclusive); break;
+ case 2: fallbackMethodDlg = new _S32_U64_U32(NativeInterface.WriteUInt32Exclusive); break;
+ case 3: fallbackMethodDlg = new _S32_U64_U64(NativeInterface.WriteUInt64Exclusive); break;
+ case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break;
+ }
+
+ return context.Call(fallbackMethodDlg, address, value);
+ }
+ else
+ {
+ switch (size)
+ {
+ case 0: fallbackMethodDlg = new _Void_U64_U8(NativeInterface.WriteByte); break;
+ case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break;
+ case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break;
+ case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break;
+ case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
+ }
+
+ context.Call(fallbackMethodDlg, address, value);
+
+ return null;
+ }
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
index e0b44353..70861d16 100644
--- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
@@ -53,7 +53,7 @@ namespace ARMeilleure.Instructions
if (!isSimd)
{
- Operand value = GetIntOrZR(context, rt);
+ Operand value = GetInt(context, rt);
if (ext == Extension.Sx32 || ext == Extension.Sx64)
{
@@ -67,7 +67,7 @@ namespace ARMeilleure.Instructions
}
}
- SetIntOrZR(context, rt, value);
+ SetInt(context, rt, value);
}
}
@@ -505,5 +505,68 @@ namespace ARMeilleure.Instructions
SetIntOrZR(context, rt, value);
}
}
+
+ // ARM32 helpers.
+ public static Operand GetMemM(ArmEmitterContext context, bool setCarry = true)
+ {
+ switch (context.CurrOp)
+ {
+ case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
+
+ case OpCode32MemReg op: return GetIntA32(context, op.Rm);
+
+ case OpCode32Mem op: return Const(op.Immediate);
+
+ case OpCode32SimdMemImm op: return Const(op.Immediate);
+
+ default: throw InvalidOpCodeType(context.CurrOp);
+ }
+ }
+
+ private static Exception InvalidOpCodeType(OpCode opCode)
+ {
+ return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\".");
+ }
+
+ public static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32MemRsImm op, bool setCarry)
+ {
+ Operand m = GetIntA32(context, op.Rm);
+
+ int shift = op.Immediate;
+
+ if (shift == 0)
+ {
+ switch (op.ShiftType)
+ {
+ case ShiftType.Lsr: shift = 32; break;
+ case ShiftType.Asr: shift = 32; break;
+ case ShiftType.Ror: shift = 1; break;
+ }
+ }
+
+ if (shift != 0)
+ {
+ setCarry &= false;
+
+ switch (op.ShiftType)
+ {
+ case ShiftType.Lsl: m = InstEmitAluHelper.GetLslC(context, m, setCarry, shift); break;
+ case ShiftType.Lsr: m = InstEmitAluHelper.GetLsrC(context, m, setCarry, shift); break;
+ case ShiftType.Asr: m = InstEmitAluHelper.GetAsrC(context, m, setCarry, shift); break;
+ case ShiftType.Ror:
+ if (op.Immediate != 0)
+ {
+ m = InstEmitAluHelper.GetRorC(context, m, setCarry, shift);
+ }
+ else
+ {
+ m = InstEmitAluHelper.GetRrxC(context, m, setCarry);
+ }
+ break;
+ }
+ }
+
+ return m;
+ }
}
} \ No newline at end of file
diff --git a/ARMeilleure/Instructions/InstEmitMul32.cs b/ARMeilleure/Instructions/InstEmitMul32.cs
new file mode 100644
index 00000000..e64f3568
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitMul32.cs
@@ -0,0 +1,290 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+
+using static ARMeilleure.Instructions.InstEmitAluHelper;
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ [Flags]
+ private enum MullFlags
+ {
+ Subtract = 1,
+ Add = 1 << 1,
+ Signed = 1 << 2,
+
+ SignedAdd = Signed | Add,
+ SignedSubtract = Signed | Subtract
+ }
+
+ public static void Mla(ArmEmitterContext context)
+ {
+ OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+ Operand a = GetIntA32(context, op.Ra);
+
+ Operand res = context.Add(a, context.Multiply(n, m));
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Mls(ArmEmitterContext context)
+ {
+ OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
+
+ Operand n = GetAluN(context);
+ Operand m = GetAluM(context);
+ Operand a = GetIntA32(context, op.Ra);
+
+ Operand res = context.Subtract(a, context.Multiply(n, m));
+
+ EmitAluStore(context, res);
+ }
+
+ public static void Smull(ArmEmitterContext context)
+ {
+ OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
+
+ Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
+ Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
+
+ Operand res = context.Multiply(n, m);
+
+ Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
+ Operand lo = context.ConvertI64ToI32(res);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
+ EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
+ }
+
+ public static void Smmla(ArmEmitterContext context)
+ {
+ EmitSmmul(context, MullFlags.SignedAdd);
+ }
+
+ public static void Smmls(ArmEmitterContext context)
+ {
+ EmitSmmul(context, MullFlags.SignedSubtract);
+ }
+
+ public static void Smmul(ArmEmitterContext context)
+ {
+ EmitSmmul(context, MullFlags.Signed);
+ }
+
+ private static void EmitSmmul(ArmEmitterContext context, MullFlags flags)
+ {
+ OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
+
+ Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
+ Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
+
+ Operand res = context.Multiply(n, m);
+
+ if (flags.HasFlag(MullFlags.Add) && op.Ra != 0xf)
+ {
+ res = context.Add(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
+ }
+ else if (flags.HasFlag(MullFlags.Subtract))
+ {
+ res = context.Subtract(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
+ }
+
+ if (op.R)
+ {
+ res = context.Add(res, Const(0x80000000L));
+ }
+
+ Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32)));
+
+ EmitGenericAluStoreA32(context, op.Rd, false, hi);
+ }
+
+ public static void Smlab(ArmEmitterContext context)
+ {
+ OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand m = GetIntA32(context, op.Rm);
+
+ if (op.NHigh)
+ {
+ n = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
+ }
+ else
+ {
+ n = context.SignExtend16(OperandType.I32, n);
+ }
+
+ if (op.MHigh)
+ {
+ m = context.SignExtend16(OperandType.I32, context.ShiftRightUI(m, Const(16)));
+ }
+ else
+ {
+ m = context.SignExtend16(OperandType.I32, m);
+ }
+
+ Operand res = context.Multiply(n, m);
+
+ Operand a = GetIntA32(context, op.Ra);
+ res = context.Add(res, a);
+
+ // TODO: set Q flag when last addition overflows (saturation)?
+
+ EmitGenericAluStoreA32(context, op.Rd, false, res);
+ }
+
+ public static void Smlal(ArmEmitterContext context)
+ {
+ EmitMlal(context, true);
+ }
+
+ public static void Smlalh(ArmEmitterContext context)
+ {
+ OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand m = GetIntA32(context, op.Rm);
+
+ if (op.NHigh)
+ {
+ n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
+ }
+ else
+ {
+ n = context.SignExtend16(OperandType.I64, n);
+ }
+
+ if (op.MHigh)
+ {
+ m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
+ }
+ else
+ {
+ m = context.SignExtend16(OperandType.I64, m);
+ }
+
+ Operand res = context.Multiply(n, m);
+
+ Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
+ toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
+ res = context.Add(res, toAdd);
+
+ Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
+ Operand lo = context.ConvertI64ToI32(res);
+
+ EmitGenericAluStoreA32(context, op.RdHi, false, hi);
+ EmitGenericAluStoreA32(context, op.RdLo, false, lo);
+ }
+
+ public static void Smulh(ArmEmitterContext context)
+ {
+ OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand m = GetIntA32(context, op.Rm);
+
+ if (op.NHigh)
+ {
+ n = context.ShiftRightSI(n, Const(16));
+ }
+ else
+ {
+ n = context.SignExtend16(OperandType.I32, n);
+ }
+
+ if (op.MHigh)
+ {
+ m = context.ShiftRightSI(m, Const(16));
+ }
+ else
+ {
+ m = context.SignExtend16(OperandType.I32, m);
+ }
+
+ Operand res = context.Multiply(n, m);
+
+ EmitGenericAluStoreA32(context, op.Rd, false, res);
+ }
+
+ public static void Umlal(ArmEmitterContext context)
+ {
+ EmitMlal(context, false);
+ }
+
+ public static void Umull(ArmEmitterContext context)
+ {
+ OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
+
+ Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
+ Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
+
+ Operand res = context.Multiply(n, m);
+
+ Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
+ Operand lo = context.ConvertI64ToI32(res);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
+ EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
+ }
+
+ public static void EmitMlal(ArmEmitterContext context, bool signed)
+ {
+ OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
+
+ Operand n = GetIntA32(context, op.Rn);
+ Operand m = GetIntA32(context, op.Rm);
+
+ if (signed)
+ {
+ n = context.SignExtend32(OperandType.I64, n);
+ m = context.SignExtend32(OperandType.I64, m);
+ }
+ else
+ {
+ n = context.ZeroExtend32(OperandType.I64, n);
+ m = context.ZeroExtend32(OperandType.I64, m);
+ }
+
+ Operand res = context.Multiply(n, m);
+
+ Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
+ toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
+ res = context.Add(res, toAdd);
+
+ Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
+ Operand lo = context.ConvertI64ToI32(res);
+
+ if (op.SetFlags)
+ {
+ EmitNZFlagsCheck(context, res);
+ }
+
+ EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
+ EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
new file mode 100644
index 00000000..4ee279ee
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
@@ -0,0 +1,634 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+
+using static ARMeilleure.Instructions.InstEmitFlowHelper;
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper32;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Vabs_S(ArmEmitterContext context)
+ {
+ EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1));
+ }
+
+ public static void Vabs_V(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1));
+ }
+ else
+ {
+ EmitVectorUnaryOpSx32(context, (op1) => EmitAbs(context, op1));
+ }
+ }
+
+ private static Operand EmitAbs(ArmEmitterContext context, Operand value)
+ {
+ Operand isPositive = context.ICompareGreaterOrEqual(value, Const(value.Type, 0));
+
+ return context.ConditionalSelect(isPositive, value, context.Negate(value));
+ }
+
+ public static void Vadd_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
+ }
+ else
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2));
+ }
+ }
+
+ public static void Vadd_V(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
+ }
+ else
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPAddFpscr, SoftFloat64.FPAddFpscr, op1, op2));
+ }
+ }
+
+ public static void Vadd_I(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => context.Add(op1, op2));
+ }
+
+ public static void Vdup(ArmEmitterContext context)
+ {
+ OpCode32SimdDupGP op = (OpCode32SimdDupGP)context.CurrOp;
+
+ Operand insert = GetIntA32(context, op.Rt);
+
+ // Zero extend into an I64, then replicate. Saves the most time over elementwise inserts.
+ switch (op.Size)
+ {
+ case 2:
+ insert = context.Multiply(context.ZeroExtend32(OperandType.I64, insert), Const(0x0000000100000001u));
+ break;
+ case 1:
+ insert = context.Multiply(context.ZeroExtend16(OperandType.I64, insert), Const(0x0001000100010001u));
+ break;
+ case 0:
+ insert = context.Multiply(context.ZeroExtend8(OperandType.I64, insert), Const(0x0101010101010101u));
+ break;
+ default:
+ throw new InvalidOperationException("Unknown Vdup Size.");
+ }
+
+ InsertScalar(context, op.Vd, insert);
+ if (op.Q)
+ {
+ InsertScalar(context, op.Vd + 1, insert);
+ }
+ }
+
+ public static void Vdup_1(ArmEmitterContext context)
+ {
+ OpCode32SimdDupElem op = (OpCode32SimdDupElem)context.CurrOp;
+
+ Operand insert = EmitVectorExtractZx32(context, op.Vm >> 1, ((op.Vm & 1) << (3 - op.Size)) + op.Index, op.Size);
+
+ // Zero extend into an I64, then replicate. Saves the most time over elementwise inserts.
+ switch (op.Size)
+ {
+ case 2:
+ insert = context.Multiply(context.ZeroExtend32(OperandType.I64, insert), Const(0x0000000100000001u));
+ break;
+ case 1:
+ insert = context.Multiply(context.ZeroExtend16(OperandType.I64, insert), Const(0x0001000100010001u));
+ break;
+ case 0:
+ insert = context.Multiply(context.ZeroExtend8(OperandType.I64, insert), Const(0x0101010101010101u));
+ break;
+ default:
+ throw new InvalidOperationException("Unknown Vdup Size.");
+ }
+
+ InsertScalar(context, op.Vd, insert);
+ if (op.Q)
+ {
+ InsertScalar(context, op.Vd | 1, insert);
+ }
+ }
+
+ public static void Vext(ArmEmitterContext context)
+ {
+ OpCode32SimdExt op = (OpCode32SimdExt)context.CurrOp;
+
+ int elems = op.GetBytesCount();
+ int byteOff = op.Immediate;
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand extract;
+
+ if (byteOff >= elems)
+ {
+ extract = EmitVectorExtractZx32(context, op.Qm, op.Im + (byteOff - elems), op.Size);
+ }
+ else
+ {
+ extract = EmitVectorExtractZx32(context, op.Qn, op.In + byteOff, op.Size);
+ }
+ byteOff++;
+
+ res = EmitVectorInsert(context, res, extract, op.Id + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void Vmov_S(ArmEmitterContext context)
+ {
+ EmitScalarUnaryOpF32(context, (op1) => op1);
+ }
+
+ public static void Vmovn(ArmEmitterContext context)
+ {
+ EmitVectorUnaryNarrowOp32(context, (op1) => op1);
+ }
+
+ public static void Vneg_S(ArmEmitterContext context)
+ {
+ EmitScalarUnaryOpF32(context, (op1) => context.Negate(op1));
+ }
+
+ public static void Vnmul_S(ArmEmitterContext context)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => context.Negate(context.Multiply(op1, op2)));
+ }
+
+ public static void Vnmla_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return context.Negate(context.Add(op1, context.Multiply(op2, op3)));
+ });
+ }
+ else
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPNegMulAdd, SoftFloat64.FPNegMulAdd, op1, op2, op3);
+ });
+ }
+ }
+
+ public static void Vnmls_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return context.Add(context.Negate(op1), context.Multiply(op2, op3));
+ });
+ }
+ else
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPNegMulSub, SoftFloat64.FPNegMulSub, op1, op2, op3);
+ });
+ }
+ }
+
+ public static void Vneg_V(ArmEmitterContext context)
+ {
+ if ((context.CurrOp as OpCode32Simd).F)
+ {
+ EmitVectorUnaryOpF32(context, (op1) => context.Negate(op1));
+ }
+ else
+ {
+ EmitVectorUnaryOpSx32(context, (op1) => context.Negate(op1));
+ }
+ }
+
+ public static void Vdiv_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => context.Divide(op1, op2));
+ }
+ else
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2);
+ });
+ }
+ }
+
+ public static void Vmaxnm_S(ArmEmitterContext context)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2));
+ }
+
+ public static void Vmaxnm_V(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMaxNumFpscr, SoftFloat64.FPMaxNumFpscr, op1, op2));
+ }
+
+ public static void Vminnm_S(ArmEmitterContext context)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2));
+ }
+
+ public static void Vminnm_V(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMinNumFpscr, SoftFloat64.FPMinNumFpscr, op1, op2));
+ }
+
+ public static void Vmax_V(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMaxFpscr, SoftFloat64.FPMaxFpscr, op1, op2);
+ });
+ }
+
+ public static void Vmax_I(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+ if (op.U)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareGreaterUI(op1, op2), op1, op2));
+ }
+ else
+ {
+ EmitVectorBinaryOpSx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareGreater(op1, op2), op1, op2));
+ }
+ }
+
+ public static void Vmin_V(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMinFpscr, SoftFloat64.FPMinFpscr, op1, op2);
+ });
+ }
+
+ public static void Vmin_I(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+ if (op.U)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareLessUI(op1, op2), op1, op2));
+ }
+ else
+ {
+ EmitVectorBinaryOpSx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareLess(op1, op2), op1, op2));
+ }
+ }
+
+ public static void Vmul_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => context.Multiply(op1, op2));
+ }
+ else
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2);
+ });
+ }
+ }
+
+ public static void Vmul_V(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) => context.Multiply(op1, op2));
+ }
+ else
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulFpscr, SoftFloat64.FPMulFpscr, op1, op2);
+ });
+ }
+ }
+
+ public static void Vmul_I(ArmEmitterContext context)
+ {
+ if ((context.CurrOp as OpCode32SimdReg).U) throw new NotImplementedException("Polynomial mode not implemented");
+ EmitVectorBinaryOpSx32(context, (op1, op2) => context.Multiply(op1, op2));
+ }
+
+ public static void Vmul_1(ArmEmitterContext context)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ if (op.F)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorByScalarOpF32(context, (op1, op2) => context.Multiply(op1, op2));
+ }
+ else
+ {
+ EmitVectorByScalarOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulFpscr, SoftFloat64.FPMulFpscr, op1, op2));
+ }
+ }
+ else
+ {
+ EmitVectorByScalarOpI32(context, (op1, op2) => context.Multiply(op1, op2), false);
+ }
+ }
+
+ public static void Vmla_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return context.Add(op1, context.Multiply(op2, op3));
+ });
+ }
+ else
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3);
+ });
+ }
+ }
+
+ public static void Vmla_V(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorTernaryOpF32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)));
+ }
+ else
+ {
+ EmitVectorTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulAddFpscr, SoftFloat64.FPMulAddFpscr, op1, op2, op3);
+ });
+ }
+ }
+
+ public static void Vmla_I(ArmEmitterContext context)
+ {
+ EmitVectorTernaryOpZx32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)));
+ }
+
+ public static void Vmla_1(ArmEmitterContext context)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ if (op.F)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorsByScalarOpF32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)));
+ }
+ else
+ {
+ EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulAddFpscr, SoftFloat64.FPMulAddFpscr, op1, op2, op3));
+ }
+ }
+ else
+ {
+ EmitVectorsByScalarOpI32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)), false);
+ }
+ }
+
+ public static void Vmls_S(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return context.Subtract(op1, context.Multiply(op2, op3));
+ });
+ }
+ else
+ {
+ EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3);
+ });
+ }
+ }
+
+ public static void Vmls_V(ArmEmitterContext context)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorTernaryOpF32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)));
+ }
+ else
+ {
+ EmitVectorTernaryOpF32(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulSubFpscr, SoftFloat64.FPMulSubFpscr, op1, op2, op3);
+ });
+ }
+ }
+
+ public static void Vmls_I(ArmEmitterContext context)
+ {
+ EmitVectorTernaryOpZx32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)));
+ }
+
+ public static void Vmls_1(ArmEmitterContext context)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ if (op.F)
+ {
+ if (Optimizations.FastFP)
+ {
+ EmitVectorsByScalarOpF32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)));
+ }
+ else
+ {
+ EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulSubFpscr, SoftFloat64.FPMulSubFpscr, op1, op2, op3));
+ }
+ }
+ else
+ {
+ EmitVectorsByScalarOpI32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)), false);
+ }
+ }
+
+ public static void Vpadd_V(ArmEmitterContext context)
+ {
+ EmitVectorPairwiseOpF32(context, (op1, op2) => context.Add(op1, op2));
+ }
+
+ public static void Vpadd_I(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ EmitVectorPairwiseOpI32(context, (op1, op2) => context.Add(op1, op2), !op.U);
+ }
+
+ public static void Vrev(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ EmitVectorUnaryOpZx32(context, (op1) =>
+ {
+ switch (op.Opc)
+ {
+ case 0:
+ switch (op.Size) // Swap bytes.
+ {
+ default:
+ return op1;
+ case 1:
+ return InstEmitAluHelper.EmitReverseBytes16_32Op(context, op1);
+ case 2:
+ case 3:
+ return context.ByteSwap(op1);
+ }
+ case 1:
+ switch (op.Size)
+ {
+ default:
+ return op1;
+ case 2:
+ return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffff0000)), Const(16)),
+ context.ShiftLeft(context.BitwiseAnd(op1, Const(0x0000ffff)), Const(16)));
+ case 3:
+ return context.BitwiseOr(
+ context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffff000000000000ul)), Const(48)),
+ context.ShiftLeft(context.BitwiseAnd(op1, Const(0x000000000000fffful)), Const(48))),
+ context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0x0000ffff00000000ul)), Const(16)),
+ context.ShiftLeft(context.BitwiseAnd(op1, Const(0x00000000ffff0000ul)), Const(16))));
+ }
+ case 2:
+ // Swap upper and lower halves.
+ return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffffffff00000000ul)), Const(32)),
+ context.ShiftLeft(context.BitwiseAnd(op1, Const(0x00000000fffffffful)), Const(32)));
+ }
+
+ return op1;
+ });
+ }
+
+ public static void Vrecpe(ArmEmitterContext context)
+ {
+ OpCode32SimdSqrte op = (OpCode32SimdSqrte)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitVectorUnaryOpF32(context, (op1) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPRecipEstimateFpscr, SoftFloat64.FPRecipEstimateFpscr, op1);
+ });
+ }
+ else
+ {
+ throw new NotImplementedException("Integer Vrecpe not currently implemented.");
+ }
+ }
+
+ public static void Vrecps(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPRecipStep, SoftFloat64.FPRecipStep, op1, op2);
+ });
+ }
+
+ public static void Vrsqrte(ArmEmitterContext context)
+ {
+ OpCode32SimdSqrte op = (OpCode32SimdSqrte)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitVectorUnaryOpF32(context, (op1) =>
+ {
+ return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPRSqrtEstimateFpscr, SoftFloat64.FPRSqrtEstimateFpscr, op1);
+ });
+ }
+ else
+ {
+ throw new NotImplementedException("Integer Vrsqrte not currently implemented.");
+ }
+ }
+
+ public static void Vrsqrts(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtStep, SoftFloat64.FPRSqrtStep, op1, op2);
+ });
+ }
+
+ public static void Vsel(ArmEmitterContext context)
+ {
+ OpCode32SimdSel op = (OpCode32SimdSel)context.CurrOp;
+
+ Operand condition = null;
+ switch (op.Cc)
+ {
+ case OpCode32SimdSelMode.Eq:
+ condition = GetCondTrue(context, Condition.Eq);
+ break;
+ case OpCode32SimdSelMode.Ge:
+ condition = GetCondTrue(context, Condition.Ge);
+ break;
+ case OpCode32SimdSelMode.Gt:
+ condition = GetCondTrue(context, Condition.Gt);
+ break;
+ case OpCode32SimdSelMode.Vs:
+ condition = GetCondTrue(context, Condition.Vs);
+ break;
+ }
+
+ EmitScalarBinaryOpI32(context, (op1, op2) =>
+ {
+ return context.ConditionalSelect(condition, op1, op2);
+ });
+ }
+
+ public static void Vsqrt_S(ArmEmitterContext context)
+ {
+ EmitScalarUnaryOpF32(context, (op1) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPSqrt, SoftFloat64.FPSqrt, op1);
+ });
+ }
+
+ public static void Vsub_S(ArmEmitterContext context)
+ {
+ EmitScalarBinaryOpF32(context, (op1, op2) => context.Subtract(op1, op2));
+ }
+
+ public static void Vsub_V(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpF32(context, (op1, op2) => context.Subtract(op1, op2));
+ }
+
+ public static void Vsub_I(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => context.Subtract(op1, op2));
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs
new file mode 100644
index 00000000..3b2483ce
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs
@@ -0,0 +1,273 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.State;
+using ARMeilleure.Translation;
+using System;
+
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper32;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ using Func2I = Func<Operand, Operand, Operand>;
+
+ static partial class InstEmit32
+ {
+ public static void Vceq_V(ArmEmitterContext context)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, false);
+ }
+
+ public static void Vceq_I(ArmEmitterContext context)
+ {
+ EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, false, false);
+ }
+
+ public static void Vceq_Z(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, true);
+ }
+ else
+ {
+ EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, true, false);
+ }
+ }
+
+ public static void Vcge_V(ArmEmitterContext context)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, false);
+ }
+
+ public static void Vcge_I(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, false, !op.U);
+ }
+
+ public static void Vcge_Z(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, true);
+ }
+ else
+ {
+ EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, true, true);
+ }
+ }
+
+ public static void Vcgt_V(ArmEmitterContext context)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, false);
+ }
+
+ public static void Vcgt_I(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, false, !op.U);
+ }
+
+ public static void Vcgt_Z(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, true);
+ }
+ else
+ {
+ EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, true, true);
+ }
+ }
+
+ public static void Vcle_Z(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareLEFpscr, SoftFloat64.FPCompareLEFpscr, true);
+ }
+ else
+ {
+ EmitCmpOpI32(context, context.ICompareLessOrEqual, context.ICompareLessOrEqualUI, true, true);
+ }
+ }
+
+ public static void Vclt_Z(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.F)
+ {
+ EmitCmpOpF32(context, SoftFloat32.FPCompareLTFpscr, SoftFloat64.FPCompareLTFpscr, true);
+ }
+ else
+ {
+ EmitCmpOpI32(context, context.ICompareLess, context.ICompareLessUI, true, true);
+ }
+ }
+
+ private static void EmitCmpOpF32(
+ ArmEmitterContext context,
+ _F32_F32_F32_Bool f32,
+ _F64_F64_F64_Bool f64,
+ bool zero)
+ {
+ Operand one = Const(1);
+ if (zero)
+ {
+ EmitVectorUnaryOpF32(context, (m) =>
+ {
+ OperandType type = m.Type;
+
+ if (type == OperandType.FP64)
+ {
+ return context.Call(f64, m, ConstF(0.0), one);
+ }
+ else
+ {
+ return context.Call(f32, m, ConstF(0.0f), one);
+ }
+ });
+ }
+ else
+ {
+ EmitVectorBinaryOpF32(context, (n, m) =>
+ {
+ OperandType type = n.Type;
+
+ if (type == OperandType.FP64)
+ {
+ return context.Call(f64, n, m, one);
+ }
+ else
+ {
+ return context.Call(f32, n, m, one);
+ }
+ });
+ }
+ }
+
+ private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
+ {
+ var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
+
+ return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
+ }
+
+ private static void EmitCmpOpI32(
+ ArmEmitterContext context,
+ Func2I signedOp,
+ Func2I unsignedOp,
+ bool zero,
+ bool signed)
+ {
+ if (zero)
+ {
+ if (signed)
+ {
+ EmitVectorUnaryOpSx32(context, (m) =>
+ {
+ OperandType type = m.Type;
+ Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
+
+ return ZerosOrOnes(context, signedOp(m, zeroV), type);
+ });
+ }
+ else
+ {
+ EmitVectorUnaryOpZx32(context, (m) =>
+ {
+ OperandType type = m.Type;
+ Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
+
+ return ZerosOrOnes(context, unsignedOp(m, zeroV), type);
+ });
+ }
+ }
+ else
+ {
+ if (signed)
+ {
+ EmitVectorBinaryOpSx32(context, (n, m) => ZerosOrOnes(context, signedOp(n, m), n.Type));
+ }
+ else
+ {
+ EmitVectorBinaryOpZx32(context, (n, m) => ZerosOrOnes(context, unsignedOp(n, m), n.Type));
+ }
+ }
+ }
+
+ public static void Vcmp(ArmEmitterContext context)
+ {
+ EmitVcmpOrVcmpe(context, false);
+ }
+
+ public static void Vcmpe(ArmEmitterContext context)
+ {
+ EmitVcmpOrVcmpe(context, true);
+ }
+
+ private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs)
+ {
+ OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
+
+ bool cmpWithZero = (op.Opc & 2) != 0;
+ {
+ int fSize = op.Size & 1;
+ OperandType type = fSize != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ Operand ne = ExtractScalar(context, type, op.Vd);
+ Operand me;
+
+ if (cmpWithZero)
+ {
+ me = fSize == 0 ? ConstF(0f) : ConstF(0d);
+ }
+ else
+ {
+ me = ExtractScalar(context, type, op.Vm);
+ }
+
+ Delegate dlg = fSize != 0
+ ? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare)
+ : (Delegate)new _S32_F32_F32_Bool(SoftFloat32.FPCompare);
+
+ Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs));
+
+ EmitSetFPSCRFlags(context, nzcv);
+ }
+ }
+
+ private static void EmitSetFPSCRFlags(ArmEmitterContext context, Operand nzcv)
+ {
+ Operand Extract(Operand value, int bit)
+ {
+ if (bit != 0)
+ {
+ value = context.ShiftRightUI(value, Const(bit));
+ }
+
+ value = context.BitwiseAnd(value, Const(1));
+
+ return value;
+ }
+
+ SetFpFlag(context, FPState.VFlag, Extract(nzcv, 0));
+ SetFpFlag(context, FPState.CFlag, Extract(nzcv, 1));
+ SetFpFlag(context, FPState.ZFlag, Extract(nzcv, 2));
+ SetFpFlag(context, FPState.NFlag, Extract(nzcv, 3));
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs
new file mode 100644
index 00000000..6ab089cb
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs
@@ -0,0 +1,274 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+using System.Diagnostics;
+
+using static ARMeilleure.Instructions.InstEmitSimdHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper32;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ private static int FlipVdBits(int vd, bool lowBit)
+ {
+ if (lowBit)
+ {
+ // Move the low bit to the top.
+ return ((vd & 0x1) << 4) | (vd >> 1);
+ }
+ else
+ {
+ // Move the high bit to the bottom.
+ return ((vd & 0xf) << 1) | (vd >> 4);
+ }
+ }
+
+ private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
+ {
+ if (op1.Type == OperandType.FP64)
+ {
+ if (unsigned)
+ {
+ return context.Call(new _U32_F64(SoftFallback.SatF64ToU32), op1);
+ }
+ else
+ {
+ return context.Call(new _S32_F64(SoftFallback.SatF64ToS32), op1);
+ }
+
+ }
+ else
+ {
+ if (unsigned)
+ {
+ return context.Call(new _U32_F32(SoftFallback.SatF32ToU32), op1);
+ }
+ else
+ {
+ return context.Call(new _S32_F32(SoftFallback.SatF32ToS32), op1);
+ }
+ }
+ }
+
+ public static void Vcvt_V(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ bool unsigned = (op.Opc & 1) != 0;
+ bool toInteger = (op.Opc & 2) != 0;
+ OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
+
+ if (toInteger)
+ {
+ EmitVectorUnaryOpF32(context, (op1) =>
+ {
+ return EmitSaturateFloatToInt(context, op1, unsigned);
+ });
+ }
+ else
+ {
+ if (unsigned)
+ {
+ EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
+ }
+ else
+ {
+ EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
+ }
+ }
+
+ }
+
+ public static void Vcvt_FD(ArmEmitterContext context)
+ {
+ OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
+
+ int vm = op.Vm;
+ int vd;
+ if (op.Size == 3)
+ {
+ vd = FlipVdBits(op.Vd, false);
+ // Double to single.
+ Operand fp = ExtractScalar(context, OperandType.FP64, vm);
+
+ Operand res = context.ConvertToFP(OperandType.FP32, fp);
+
+ InsertScalar(context, vd, res);
+ }
+ else
+ {
+ vd = FlipVdBits(op.Vd, true);
+ // Single to double.
+ Operand fp = ExtractScalar(context, OperandType.FP32, vm);
+
+ Operand res = context.ConvertToFP(OperandType.FP64, fp);
+
+ InsertScalar(context, vd, res);
+ }
+ }
+
+ public static void Vcvt_FI(ArmEmitterContext context)
+ {
+ OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
+
+ bool toInteger = (op.Opc2 & 0b100) != 0;
+
+ OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
+
+ if (toInteger)
+ {
+ bool unsigned = (op.Opc2 & 1) == 0;
+ bool roundWithFpscr = op.Opc != 1;
+
+ Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
+
+ Operand asInteger;
+
+ // TODO: Fast Path.
+ if (roundWithFpscr)
+ {
+ // These need to get the FPSCR value, so it's worth noting we'd need to do a c# call at some point.
+ if (floatSize == OperandType.FP64)
+ {
+ if (unsigned)
+ {
+ asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert);
+ }
+ else
+ {
+ asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert);
+ }
+ }
+ else
+ {
+ if (unsigned)
+ {
+ asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert);
+ }
+ else
+ {
+ asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert);
+ }
+ }
+ }
+ else
+ {
+ // Round towards zero.
+ asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
+ }
+
+ InsertScalar(context, op.Vd, asInteger);
+ }
+ else
+ {
+ bool unsigned = op.Opc == 0;
+
+ Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm);
+
+ Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned);
+
+ InsertScalar(context, op.Vd, asFloat);
+ }
+ }
+
+ public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
+ {
+ IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
+
+ Delegate dlg;
+
+ if ((op.Size & 1) == 0)
+ {
+ dlg = new _F32_F32_MidpointRounding(MathF.Round);
+ }
+ else /* if ((op.Size & 1) == 1) */
+ {
+ dlg = new _F64_F64_MidpointRounding(Math.Round);
+ }
+
+ return context.Call(dlg, n, Const((int)roundMode));
+ }
+
+ public static void Vcvt_R(ArmEmitterContext context)
+ {
+ OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
+
+ OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
+
+ bool unsigned = (op.Opc & 1) == 0;
+
+ Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
+
+ switch (op.Opc2)
+ {
+ case 0b00: // Away
+ toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
+ break;
+ case 0b01: // Nearest
+ toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
+ break;
+ case 0b10: // Towards positive infinity
+ toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
+ break;
+ case 0b11: // Towards negative infinity
+ toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
+ break;
+ }
+
+ Operand asInteger;
+
+ asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
+
+ InsertScalar(context, op.Vd, asInteger);
+ }
+
+ public static void Vrint_RM(ArmEmitterContext context)
+ {
+ OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
+
+ OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
+
+ Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
+
+ switch (op.Opc2)
+ {
+ case 0b00: // Away
+ toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
+ break;
+ case 0b01: // Nearest
+ toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
+ break;
+ case 0b10: // Towards positive infinity
+ toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
+ break;
+ case 0b11: // Towards negative infinity
+ toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
+ break;
+ }
+
+ InsertScalar(context, op.Vd, toConvert);
+ }
+
+ public static void Vrint_Z(ArmEmitterContext context)
+ {
+ EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1));
+ }
+
+ private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
+ {
+ Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
+
+ if (signed)
+ {
+ return context.ConvertToFP(type, value);
+ }
+ else
+ {
+ return context.ConvertToFPUI(type, value);
+ }
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs
index fce1bed5..a87dac01 100644
--- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs
@@ -1528,7 +1528,7 @@ namespace ARMeilleure.Instructions
{
ThrowIfInvalid(index, size);
- if (size < 3)
+ if (size < 3 && value.Type == OperandType.I64)
{
value = context.ConvertI64ToI32(value);
}
@@ -1544,7 +1544,7 @@ namespace ARMeilleure.Instructions
return vector;
}
- private static void ThrowIfInvalid(int index, int size)
+ public static void ThrowIfInvalid(int index, int size)
{
if ((uint)size > 3u)
{
diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs
new file mode 100644
index 00000000..b13b1d87
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs
@@ -0,0 +1,581 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+using System.Diagnostics;
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ using Func1I = Func<Operand, Operand>;
+ using Func2I = Func<Operand, Operand, Operand>;
+ using Func3I = Func<Operand, Operand, Operand, Operand>;
+
+ static class InstEmitSimdHelper32
+ {
+ public static (int, int) GetQuadwordAndSubindex(int index, RegisterSize size)
+ {
+ switch (size)
+ {
+ case RegisterSize.Simd128:
+ return (index >> 1, 0);
+ case RegisterSize.Simd64:
+ case RegisterSize.Int64:
+ return (index >> 1, index & 1);
+ case RegisterSize.Int32:
+ return (index >> 2, index & 3);
+ }
+
+ throw new ArgumentException("Unrecognized Vector Register Size.");
+ }
+
+ public static Operand ExtractScalar(ArmEmitterContext context, OperandType type, int reg)
+ {
+ Debug.Assert(type != OperandType.V128);
+
+ if (type == OperandType.FP64 || type == OperandType.I64)
+ {
+ // From dreg.
+ return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
+ }
+ else
+ {
+ // From sreg.
+ return context.VectorExtract(type, GetVecA32(reg >> 2), reg & 3);
+ }
+ }
+
+ public static void InsertScalar(ArmEmitterContext context, int reg, Operand value)
+ {
+ Debug.Assert(value.Type != OperandType.V128);
+
+ Operand vec, insert;
+ if (value.Type == OperandType.FP64 || value.Type == OperandType.I64)
+ {
+ // From dreg.
+ vec = GetVecA32(reg >> 1);
+ insert = context.VectorInsert(vec, value, reg & 1);
+
+ }
+ else
+ {
+ // From sreg.
+ vec = GetVecA32(reg >> 2);
+ insert = context.VectorInsert(vec, value, reg & 3);
+ }
+
+ context.Copy(vec, insert);
+ }
+
+ public static void EmitVectorImmUnaryOp32(ArmEmitterContext context, Func1I emit)
+ {
+ IOpCode32SimdImm op = (IOpCode32SimdImm)context.CurrOp;
+
+ Operand imm = Const(op.Immediate);
+
+ int elems = op.Elems;
+ (int index, int subIndex) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize);
+
+ Operand vec = GetVecA32(index);
+ Operand res = vec;
+
+ for (int item = 0; item < elems; item++)
+ {
+ res = EmitVectorInsert(context, res, emit(imm), item + subIndex * elems, op.Size);
+ }
+
+ context.Copy(vec, res);
+ }
+
+ public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Func1I emit)
+ {
+ OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
+
+ OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ Operand m = ExtractScalar(context, type, op.Vm);
+
+ InsertScalar(context, op.Vd, emit(m));
+ }
+
+ public static void EmitScalarBinaryOpF32(ArmEmitterContext context, Func2I emit)
+ {
+ OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
+
+ OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ Operand n = ExtractScalar(context, type, op.Vn);
+ Operand m = ExtractScalar(context, type, op.Vm);
+
+ InsertScalar(context, op.Vd, emit(n, m));
+ }
+
+ public static void EmitScalarBinaryOpI32(ArmEmitterContext context, Func2I emit)
+ {
+ OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
+
+ OperandType type = (op.Size & 1) != 0 ? OperandType.I64 : OperandType.I32;
+
+ if (op.Size < 2)
+ {
+ throw new NotSupportedException("Cannot perform a scalar SIMD operation on integers smaller than 32 bits.");
+ }
+
+ Operand n = ExtractScalar(context, type, op.Vn);
+ Operand m = ExtractScalar(context, type, op.Vm);
+
+ InsertScalar(context, op.Vd, emit(n, m));
+ }
+
+ public static void EmitScalarTernaryOpF32(ArmEmitterContext context, Func3I emit)
+ {
+ OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
+
+ OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ Operand a = ExtractScalar(context, type, op.Vd);
+ Operand n = ExtractScalar(context, type, op.Vn);
+ Operand m = ExtractScalar(context, type, op.Vm);
+
+ InsertScalar(context, op.Vd, emit(a, n, m));
+ }
+
+ public static void EmitVectorUnaryOpF32(ArmEmitterContext context, Func1I emit)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ int sizeF = op.Size & 1;
+
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ int elems = op.GetBytesCount() >> sizeF + 2;
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand me = context.VectorExtract(type, GetVecA32(op.Qm), op.Fm + index);
+
+ res = context.VectorInsert(res, emit(me), op.Fd + index);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorBinaryOpF32(ArmEmitterContext context, Func2I emit)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ int sizeF = op.Size & 1;
+
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ int elems = op.GetBytesCount() >> (sizeF + 2);
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
+ Operand me = context.VectorExtract(type, GetVecA32(op.Qm), op.Fm + index);
+
+ res = context.VectorInsert(res, emit(ne, me), op.Fd + index);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorTernaryOpF32(ArmEmitterContext context, Func3I emit)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ int sizeF = op.Size & 1;
+
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ int elems = op.GetBytesCount() >> sizeF + 2;
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand de = context.VectorExtract(type, GetVecA32(op.Qd), op.Fd + index);
+ Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
+ Operand me = context.VectorExtract(type, GetVecA32(op.Qm), op.Fm + index);
+
+ res = context.VectorInsert(res, emit(de, ne, me), op.Fd + index);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ // Integer
+
+ public static void EmitVectorUnaryOpI32(ArmEmitterContext context, Func1I emit, bool signed)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ Operand res = GetVecA32(op.Qd);
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
+
+ res = EmitVectorInsert(context, res, emit(me), op.Id + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorBinaryOpI32(ArmEmitterContext context, Func2I emit, bool signed)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ Operand res = GetVecA32(op.Qd);
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
+ Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
+
+ res = EmitVectorInsert(context, res, emit(ne, me), op.Id + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorTernaryOpI32(ArmEmitterContext context, Func3I emit, bool signed)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ Operand res = GetVecA32(op.Qd);
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand de = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size, signed);
+ Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
+ Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
+
+ res = EmitVectorInsert(context, res, emit(de, ne, me), op.Id + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorUnaryOpSx32(ArmEmitterContext context, Func1I emit)
+ {
+ EmitVectorUnaryOpI32(context, emit, true);
+ }
+
+ public static void EmitVectorBinaryOpSx32(ArmEmitterContext context, Func2I emit)
+ {
+ EmitVectorBinaryOpI32(context, emit, true);
+ }
+
+ public static void EmitVectorTernaryOpSx32(ArmEmitterContext context, Func3I emit)
+ {
+ EmitVectorTernaryOpI32(context, emit, true);
+ }
+
+ public static void EmitVectorUnaryOpZx32(ArmEmitterContext context, Func1I emit)
+ {
+ EmitVectorUnaryOpI32(context, emit, false);
+ }
+
+ public static void EmitVectorBinaryOpZx32(ArmEmitterContext context, Func2I emit)
+ {
+ EmitVectorBinaryOpI32(context, emit, false);
+ }
+
+ public static void EmitVectorTernaryOpZx32(ArmEmitterContext context, Func3I emit)
+ {
+ EmitVectorTernaryOpI32(context, emit, false);
+ }
+
+ // Vector by scalar
+
+ public static void EmitVectorByScalarOpF32(ArmEmitterContext context, Func2I emit)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ int sizeF = op.Size & 1;
+
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ int elems = op.GetBytesCount() >> sizeF + 2;
+
+ Operand m = ExtractScalar(context, type, op.Vm);
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
+
+ res = context.VectorInsert(res, emit(ne, m), op.Fd + index);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorByScalarOpI32(ArmEmitterContext context, Func2I emit, bool signed)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ Operand m = EmitVectorExtract32(context, op.Vm >> (4 - op.Size), op.Vm & ((1 << (4 - op.Size)) - 1), op.Size, signed);
+
+ Operand res = GetVecA32(op.Qd);
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
+
+ res = EmitVectorInsert(context, res, emit(ne, m), op.In + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorsByScalarOpF32(ArmEmitterContext context, Func3I emit)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ int sizeF = op.Size & 1;
+
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ int elems = op.GetBytesCount() >> sizeF + 2;
+
+ Operand m = ExtractScalar(context, type, op.Vm);
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand de = context.VectorExtract(type, GetVecA32(op.Qd), op.Fd + index);
+ Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
+
+ res = context.VectorInsert(res, emit(de, ne, m), op.Fd + index);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorsByScalarOpI32(ArmEmitterContext context, Func3I emit, bool signed)
+ {
+ OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
+
+ Operand m = EmitVectorExtract32(context, op.Vm >> (4 - op.Size), op.Vm & ((1 << (4 - op.Size)) - 1), op.Size, signed);
+
+ Operand res = GetVecA32(op.Qd);
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand de = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size, signed);
+ Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
+
+ res = EmitVectorInsert(context, res, emit(de, ne, m), op.Id + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ // Pairwise
+
+ public static void EmitVectorPairwiseOpF32(ArmEmitterContext context, Func2I emit)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ int sizeF = op.Size & 1;
+
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+
+ int elems = op.GetBytesCount() >> (sizeF + 2);
+ int pairs = elems >> 1;
+
+ Operand res = GetVecA32(op.Qd);
+ Operand mvec = GetVecA32(op.Qm);
+ Operand nvec = GetVecA32(op.Qn);
+
+ for (int index = 0; index < pairs; index++)
+ {
+ int pairIndex = index << 1;
+
+ Operand n1 = context.VectorExtract(type, nvec, op.Fn + pairIndex);
+ Operand n2 = context.VectorExtract(type, nvec, op.Fn + pairIndex + 1);
+
+ res = context.VectorInsert(res, emit(n1, n2), op.Fd + index);
+
+ Operand m1 = context.VectorExtract(type, mvec, op.Fm + pairIndex);
+ Operand m2 = context.VectorExtract(type, mvec, op.Fm + pairIndex + 1);
+
+ res = context.VectorInsert(res, emit(m1, m2), op.Fd + index + pairs);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void EmitVectorPairwiseOpI32(ArmEmitterContext context, Func2I emit, bool signed)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ int elems = op.GetBytesCount() >> op.Size;
+ int pairs = elems >> 1;
+
+ Operand res = GetVecA32(op.Qd);
+
+ for (int index = 0; index < pairs; index++)
+ {
+ int pairIndex = index << 1;
+ Operand n1 = EmitVectorExtract32(context, op.Qn, op.In + pairIndex, op.Size, signed);
+ Operand n2 = EmitVectorExtract32(context, op.Qn, op.In + pairIndex + 1, op.Size, signed);
+
+ Operand m1 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex, op.Size, signed);
+ Operand m2 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex + 1, op.Size, signed);
+
+ res = EmitVectorInsert(context, res, emit(n1, n2), op.Id + index, op.Size);
+ res = EmitVectorInsert(context, res, emit(m1, m2), op.Id + index + pairs, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ // Narrow
+
+ public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ int elems = 8 >> op.Size; // Size contains the target element size. (for when it becomes a doubleword)
+
+ Operand res = GetVecA32(op.Qd);
+ int id = (op.Vd & 1) << (3 - op.Size); // Target doubleword base.
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand m = EmitVectorExtract32(context, op.Qm, index, op.Size + 1, false);
+
+ res = EmitVectorInsert(context, res, emit(m), id + index, op.Size);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ // Generic Functions
+
+ public static Operand EmitSoftFloatCallDefaultFpscr(
+ ArmEmitterContext context,
+ _F32_F32_Bool f32,
+ _F64_F64_Bool f64,
+ params Operand[] callArgs)
+ {
+ IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
+
+ Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64;
+
+ Array.Resize(ref callArgs, callArgs.Length + 1);
+ callArgs[callArgs.Length - 1] = Const(1);
+
+ return context.Call(dlg, callArgs);
+ }
+
+ public static Operand EmitSoftFloatCallDefaultFpscr(
+ ArmEmitterContext context,
+ _F32_F32_F32_Bool f32,
+ _F64_F64_F64_Bool f64,
+ params Operand[] callArgs)
+ {
+ IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
+
+ Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64;
+
+ Array.Resize(ref callArgs, callArgs.Length + 1);
+ callArgs[callArgs.Length - 1] = Const(1);
+
+ return context.Call(dlg, callArgs);
+ }
+
+ public static Operand EmitSoftFloatCallDefaultFpscr(
+ ArmEmitterContext context,
+ _F32_F32_F32_F32_Bool f32,
+ _F64_F64_F64_F64_Bool f64,
+ params Operand[] callArgs)
+ {
+ IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
+
+ Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64;
+
+ Array.Resize(ref callArgs, callArgs.Length + 1);
+ callArgs[callArgs.Length - 1] = Const(1);
+
+ return context.Call(dlg, callArgs);
+ }
+
+ public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size)
+ {
+ return EmitVectorExtract32(context, reg, index, size, true);
+ }
+
+ public static Operand EmitVectorExtractZx32(ArmEmitterContext context, int reg, int index, int size)
+ {
+ return EmitVectorExtract32(context, reg, index, size, false);
+ }
+
+ public static Operand EmitVectorExtract32(ArmEmitterContext context, int reg, int index, int size, bool signed)
+ {
+ ThrowIfInvalid(index, size);
+
+ Operand res = null;
+
+ switch (size)
+ {
+ case 0:
+ res = context.VectorExtract8(GetVec(reg), index);
+ break;
+
+ case 1:
+ res = context.VectorExtract16(GetVec(reg), index);
+ break;
+
+ case 2:
+ res = context.VectorExtract(OperandType.I32, GetVec(reg), index);
+ break;
+
+ case 3:
+ res = context.VectorExtract(OperandType.I64, GetVec(reg), index);
+ break;
+ }
+
+ if (signed)
+ {
+ switch (size)
+ {
+ case 0: res = context.SignExtend8(OperandType.I32, res); break;
+ case 1: res = context.SignExtend16(OperandType.I32, res); break;
+ }
+ }
+ else
+ {
+ switch (size)
+ {
+ case 0: res = context.ZeroExtend8(OperandType.I32, res); break;
+ case 1: res = context.ZeroExtend16(OperandType.I32, res); break;
+ }
+ }
+
+ return res;
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical32.cs b/ARMeilleure/Instructions/InstEmitSimdLogical32.cs
new file mode 100644
index 00000000..e2e9e18e
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdLogical32.cs
@@ -0,0 +1,56 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.Translation;
+
+using static ARMeilleure.Instructions.InstEmitSimdHelper32;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Vand_I(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, op2));
+ }
+
+ public static void Vbif(ArmEmitterContext context)
+ {
+ EmitBifBit(context, true);
+ }
+
+ public static void Vbit(ArmEmitterContext context)
+ {
+ EmitBifBit(context, false);
+ }
+
+ public static void Vbsl(ArmEmitterContext context)
+ {
+ EmitVectorTernaryOpZx32(context, (op1, op2, op3) =>
+ {
+ return context.BitwiseExclusiveOr(
+ context.BitwiseAnd(op1,
+ context.BitwiseExclusiveOr(op2, op3)), op3);
+ });
+ }
+
+ public static void Vorr_I(ArmEmitterContext context)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, op2));
+ }
+
+ private static void EmitBifBit(ArmEmitterContext context, bool notRm)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ EmitVectorTernaryOpZx32(context, (d, n, m) =>
+ {
+ if (notRm)
+ {
+ m = context.BitwiseNot(m);
+ }
+ return context.BitwiseExclusiveOr(
+ context.BitwiseAnd(m,
+ context.BitwiseExclusiveOr(d, n)), d);
+ });
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdMemory32.cs b/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
new file mode 100644
index 00000000..fb9931d8
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
@@ -0,0 +1,352 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.State;
+using ARMeilleure.Translation;
+
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitMemoryHelper;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Vld1(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 1, true);
+ }
+
+ public static void Vld2(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 2, true);
+ }
+
+ public static void Vld3(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 3, true);
+ }
+
+ public static void Vld4(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 4, true);
+ }
+
+ public static void Vst1(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 1, false);
+ }
+
+ public static void Vst2(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 2, false);
+ }
+
+ public static void Vst3(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 3, false);
+ }
+
+ public static void Vst4(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 4, false);
+ }
+
+ public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load)
+ {
+ if (context.CurrOp is OpCode32SimdMemSingle)
+ {
+ OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
+
+ int eBytes = 1 << op.Size;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+
+ // TODO: Check alignment.
+ int offset = 0;
+ int d = op.Vd;
+
+ for (int i = 0; i < count; i++)
+ {
+ // Write an element from a double simd register.
+ Operand address = context.Add(n, Const(offset));
+ if (eBytes == 8)
+ {
+ if (load)
+ {
+ EmitDVectorLoad(context, address, d);
+ }
+ else
+ {
+ EmitDVectorStore(context, address, d);
+ }
+ }
+ else
+ {
+ int index = ((d & 1) << (3 - op.Size)) + op.Index;
+ if (load)
+ {
+ if (op.Replicate)
+ {
+ var regs = (count > 1) ? 1 : op.Increment;
+ for (int reg = 0; reg < regs; reg++)
+ {
+ int dreg = reg + d;
+ int rIndex = ((dreg & 1) << (3 - op.Size));
+ int limit = rIndex + (1 << (3 - op.Size));
+
+ while (rIndex < limit)
+ {
+ EmitLoadSimd(context, address, GetVecA32(dreg >> 1), dreg >> 1, rIndex++, op.Size);
+ }
+ }
+ }
+ else
+ {
+ EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
+ }
+ }
+ else
+ {
+ EmitStoreSimd(context, address, d >> 1, index, op.Size);
+ }
+ }
+ offset += eBytes;
+ d += op.Increment;
+ }
+
+ if (op.WBack)
+ {
+ if (op.RegisterIndex)
+ {
+ Operand m = GetIntA32(context, op.Rm);
+ SetIntA32(context, op.Rn, context.Add(n, m));
+ }
+ else
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes)));
+ }
+ }
+ }
+ else
+ {
+ OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp;
+
+ int eBytes = 1 << op.Size;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+ int offset = 0;
+ int d = op.Vd;
+
+ for (int reg = 0; reg < op.Regs; reg++)
+ {
+ for (int elem = 0; elem < op.Elems; elem++)
+ {
+ int elemD = d + reg;
+ for (int i = 0; i < count; i++)
+ {
+ // Write an element from a double simd register
+ // add ebytes for each element.
+ Operand address = context.Add(n, Const(offset));
+ int index = ((elemD & 1) << (3 - op.Size)) + elem;
+ if (eBytes == 8)
+ {
+ if (load)
+ {
+ EmitDVectorLoad(context, address, elemD);
+ }
+ else
+ {
+ EmitDVectorStore(context, address, elemD);
+ }
+ }
+ else
+ {
+
+ if (load)
+ {
+ EmitLoadSimd(context, address, GetVecA32(elemD >> 1), elemD >> 1, index, op.Size);
+ }
+ else
+ {
+ EmitStoreSimd(context, address, elemD >> 1, index, op.Size);
+ }
+ }
+
+ offset += eBytes;
+ elemD += op.Increment;
+ }
+ }
+ }
+
+ if (op.WBack)
+ {
+ if (op.RegisterIndex)
+ {
+ Operand m = GetIntA32(context, op.Rm);
+ SetIntA32(context, op.Rn, context.Add(n, m));
+ }
+ else
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs)));
+ }
+ }
+ }
+ }
+
+ public static void Vldm(ArmEmitterContext context)
+ {
+ OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+
+ Operand baseAddress = context.Add(n, Const(op.Offset));
+
+ bool writeBack = op.PostOffset != 0;
+
+ if (writeBack)
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
+ }
+
+ int range = op.RegisterRange;
+
+ int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
+ int offset = 0;
+ int byteSize = 4;
+
+ for (int num = 0; num < range; num++, sReg++)
+ {
+ Operand address = context.Add(baseAddress, Const(offset));
+ Operand vec = GetVecA32(sReg >> 2);
+
+ EmitLoadSimd(context, address, vec, sReg >> 2, sReg & 3, WordSizeLog2);
+ offset += byteSize;
+ }
+ }
+
+ public static void Vstm(ArmEmitterContext context)
+ {
+ OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+
+ Operand baseAddress = context.Add(n, Const(op.Offset));
+
+ bool writeBack = op.PostOffset != 0;
+
+ if (writeBack)
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
+ }
+
+ int offset = 0;
+
+ int range = op.RegisterRange;
+ int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
+ int byteSize = 4;
+
+ for (int num = 0; num < range; num++, sReg++)
+ {
+ Operand address = context.Add(baseAddress, Const(offset));
+
+ EmitStoreSimd(context, address, sReg >> 2, sReg & 3, WordSizeLog2);
+
+ offset += byteSize;
+ }
+ }
+
+ public static void Vldr(ArmEmitterContext context)
+ {
+ EmitVLoadOrStore(context, AccessType.Load);
+ }
+
+ public static void Vstr(ArmEmitterContext context)
+ {
+ EmitVLoadOrStore(context, AccessType.Store);
+ }
+
+ private static void EmitDVectorStore(ArmEmitterContext context, Operand address, int vecD)
+ {
+ int vecQ = vecD >> 1;
+ int vecSElem = (vecD & 1) << 1;
+ Operand lblBigEndian = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
+
+ EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2);
+ EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBigEndian);
+
+ EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2);
+ EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2);
+
+ context.MarkLabel(lblEnd);
+ }
+
+ private static void EmitDVectorLoad(ArmEmitterContext context, Operand address, int vecD)
+ {
+ int vecQ = vecD >> 1;
+ int vecSElem = (vecD & 1) << 1;
+ Operand vec = GetVecA32(vecQ);
+
+ Operand lblBigEndian = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
+
+ EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2);
+ EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBigEndian);
+
+ EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2);
+ EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2);
+
+ context.MarkLabel(lblEnd);
+ }
+
+ private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType)
+ {
+ OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+ Operand m = GetMemM(context, setCarry: false);
+
+ Operand address = op.Add
+ ? context.Add(n, m)
+ : context.Subtract(n, m);
+
+ int size = op.Size;
+
+ if ((accType & AccessType.Load) != 0)
+ {
+ if (size == DWordSizeLog2)
+ {
+ EmitDVectorLoad(context, address, op.Vd);
+ }
+ else
+ {
+ Operand vec = GetVecA32(op.Vd >> 2);
+ EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
+ }
+ }
+ else
+ {
+ if (size == DWordSizeLog2)
+ {
+ EmitDVectorStore(context, address, op.Vd);
+ }
+ else
+ {
+ EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
+ }
+ }
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/ARMeilleure/Instructions/InstEmitSimdMove32.cs
new file mode 100644
index 00000000..3fd42cbf
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdMove32.cs
@@ -0,0 +1,336 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper;
+using static ARMeilleure.Instructions.InstEmitSimdHelper32;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Vmov_I(ArmEmitterContext context)
+ {
+ EmitVectorImmUnaryOp32(context, (op1) => op1);
+ }
+
+ public static void Vmvn_I(ArmEmitterContext context)
+ {
+ EmitVectorImmUnaryOp32(context, (op1) => context.BitwiseExclusiveOr(op1, op1));
+ }
+
+ public static void Vmov_GS(ArmEmitterContext context)
+ {
+ OpCode32SimdMovGp op = (OpCode32SimdMovGp)context.CurrOp;
+
+ Operand vec = GetVecA32(op.Vn >> 2);
+ if (op.Op == 1)
+ {
+ // To general purpose.
+ Operand value = context.VectorExtract(OperandType.I32, vec, op.Vn & 0x3);
+ SetIntA32(context, op.Rt, value);
+ }
+ else
+ {
+ // From general purpose.
+ Operand value = GetIntA32(context, op.Rt);
+ context.Copy(vec, context.VectorInsert(vec, value, op.Vn & 0x3));
+ }
+ }
+
+ public static void Vmov_G1(ArmEmitterContext context)
+ {
+ OpCode32SimdMovGpElem op = (OpCode32SimdMovGpElem)context.CurrOp;
+
+ int index = op.Index + ((op.Vd & 1) << (3 - op.Size));
+ if (op.Op == 1)
+ {
+ // To general purpose.
+ Operand value = EmitVectorExtract32(context, op.Vd >> 1, index, op.Size, !op.U);
+ SetIntA32(context, op.Rt, value);
+ }
+ else
+ {
+ // From general purpose.
+ Operand vec = GetVecA32(op.Vd >> 1);
+ Operand value = GetIntA32(context, op.Rt);
+ context.Copy(vec, EmitVectorInsert(context, vec, value, index, op.Size));
+ }
+ }
+
+ public static void Vmov_G2(ArmEmitterContext context)
+ {
+ OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
+
+ Operand vec = GetVecA32(op.Vm >> 2);
+ int vm1 = op.Vm + 1;
+ bool sameOwnerVec = (op.Vm >> 2) == (vm1 >> 2);
+ Operand vec2 = sameOwnerVec ? vec : GetVecA32(vm1 >> 2);
+ if (op.Op == 1)
+ {
+ // To general purpose.
+ Operand lowValue = context.VectorExtract(OperandType.I32, vec, op.Vm & 3);
+ SetIntA32(context, op.Rt, lowValue);
+
+ Operand highValue = context.VectorExtract(OperandType.I32, vec2, vm1 & 3);
+ SetIntA32(context, op.Rt2, highValue);
+ }
+ else
+ {
+ // From general purpose.
+ Operand lowValue = GetIntA32(context, op.Rt);
+ Operand resultVec = context.VectorInsert(vec, lowValue, op.Vm & 3);
+
+ Operand highValue = GetIntA32(context, op.Rt2);
+
+ if (sameOwnerVec)
+ {
+ context.Copy(vec, context.VectorInsert(resultVec, highValue, vm1 & 3));
+ }
+ else
+ {
+ context.Copy(vec, resultVec);
+ context.Copy(vec2, context.VectorInsert(vec2, highValue, vm1 & 3));
+ }
+ }
+ }
+
+ public static void Vmov_GD(ArmEmitterContext context)
+ {
+ OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
+
+ Operand vec = GetVecA32(op.Vm >> 1);
+ if (op.Op == 1)
+ {
+ // To general purpose.
+ Operand value = context.VectorExtract(OperandType.I64, vec, op.Vm & 1);
+ SetIntA32(context, op.Rt, context.ConvertI64ToI32(value));
+ SetIntA32(context, op.Rt2, context.ConvertI64ToI32(context.ShiftRightUI(value, Const(32))));
+ }
+ else
+ {
+ // From general purpose.
+ Operand lowValue = GetIntA32(context, op.Rt);
+ Operand highValue = GetIntA32(context, op.Rt2);
+
+ Operand value = context.BitwiseOr(
+ context.ZeroExtend32(OperandType.I64, lowValue),
+ context.ShiftLeft(context.ZeroExtend32(OperandType.I64, highValue), Const(32)));
+
+ context.Copy(vec, context.VectorInsert(vec, value, op.Vm & 1));
+ }
+ }
+
+ public static void Vtbl(ArmEmitterContext context)
+ {
+ OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
+
+ bool extension = op.Opc == 1;
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ int length = op.Length + 1;
+
+ (int Qx, int Ix)[] tableTuples = new (int, int)[length];
+ for (int i = 0; i < length; i++)
+ {
+ (int vn, int en) = GetQuadwordAndSubindex(op.Vn + i, op.RegisterSize);
+ tableTuples[i] = (vn, en);
+ }
+
+ int byteLength = length * 8;
+
+ Operand res = GetVecA32(op.Qd);
+ Operand m = GetVecA32(op.Qm);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand selectedIndex = context.ZeroExtend8(OperandType.I32, context.VectorExtract8(m, index + op.Im));
+
+ Operand inRange = context.ICompareLess(selectedIndex, Const(byteLength));
+ Operand elemRes = null; // Note: This is I64 for ease of calculation.
+
+ // TODO: Branching rather than conditional select.
+
+ // Get indexed byte.
+ // To simplify (ha) the il, we get bytes from every vector and use a nested conditional select to choose the right result.
+ // This does have to extract `length` times for every element but certainly not as bad as it could be.
+
+ // Which vector number is the index on.
+ Operand vecIndex = context.ShiftRightUI(selectedIndex, Const(3));
+ // What should we shift by to extract it.
+ Operand subVecIndexShift = context.ShiftLeft(context.BitwiseAnd(selectedIndex, Const(7)), Const(3));
+
+ for (int i = 0; i < length; i++)
+ {
+ (int qx, int ix) = tableTuples[i];
+ // Get the whole vector, we'll get a byte out of it.
+ Operand lookupResult;
+ if (qx == op.Qd)
+ {
+ // Result contains the current state of the vector.
+ lookupResult = context.VectorExtract(OperandType.I64, res, ix);
+ }
+ else
+ {
+ lookupResult = EmitVectorExtract32(context, qx, ix, 3, false); // I64
+ }
+
+ lookupResult = context.ShiftRightUI(lookupResult, subVecIndexShift); // Get the relevant byte from this vector.
+
+ if (i == 0)
+ {
+ elemRes = lookupResult; // First result is always default.
+ }
+ else
+ {
+ Operand isThisElem = context.ICompareEqual(vecIndex, Const(i));
+ elemRes = context.ConditionalSelect(isThisElem, lookupResult, elemRes);
+ }
+ }
+
+ Operand fallback = (extension) ? context.ZeroExtend32(OperandType.I64, EmitVectorExtract32(context, op.Qd, index + op.Id, 0, false)) : Const(0L);
+
+ res = EmitVectorInsert(context, res, context.ConditionalSelect(inRange, elemRes, fallback), index + op.Id, 0);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
+ public static void Vtrn(ArmEmitterContext context)
+ {
+ OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
+
+ int elems = op.GetBytesCount() >> op.Size;
+ int pairs = elems >> 1;
+
+ bool overlap = op.Qm == op.Qd;
+
+ Operand resD = GetVecA32(op.Qd);
+ Operand resM = GetVecA32(op.Qm);
+
+ for (int index = 0; index < pairs; index++)
+ {
+ int pairIndex = index << 1;
+ Operand d2 = EmitVectorExtract32(context, op.Qd, pairIndex + 1 + op.Id, op.Size, false);
+ Operand m1 = EmitVectorExtract32(context, op.Qm, pairIndex + op.Im, op.Size, false);
+
+ resD = EmitVectorInsert(context, resD, m1, pairIndex + 1 + op.Id, op.Size);
+
+ if (overlap)
+ {
+ resM = resD;
+ }
+
+ resM = EmitVectorInsert(context, resM, d2, pairIndex + op.Im, op.Size);
+
+ if (overlap)
+ {
+ resD = resM;
+ }
+ }
+
+ context.Copy(GetVecA32(op.Qd), resD);
+ if (!overlap)
+ {
+ context.Copy(GetVecA32(op.Qm), resM);
+ }
+ }
+
+ public static void Vzip(ArmEmitterContext context)
+ {
+ OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
+
+ int elems = op.GetBytesCount() >> op.Size;
+ int pairs = elems >> 1;
+
+ bool overlap = op.Qm == op.Qd;
+
+ Operand resD = GetVecA32(op.Qd);
+ Operand resM = GetVecA32(op.Qm);
+
+ for (int index = 0; index < pairs; index++)
+ {
+ int pairIndex = index << 1;
+ Operand dRowD = EmitVectorExtract32(context, op.Qd, index + op.Id, op.Size, false);
+ Operand mRowD = EmitVectorExtract32(context, op.Qm, index + op.Im, op.Size, false);
+
+ Operand dRowM = EmitVectorExtract32(context, op.Qd, index + op.Id + pairs, op.Size, false);
+ Operand mRowM = EmitVectorExtract32(context, op.Qm, index + op.Im + pairs, op.Size, false);
+
+ resD = EmitVectorInsert(context, resD, dRowD, pairIndex + op.Id, op.Size);
+ resD = EmitVectorInsert(context, resD, mRowD, pairIndex + 1 + op.Id, op.Size);
+
+ if (overlap)
+ {
+ resM = resD;
+ }
+
+ resM = EmitVectorInsert(context, resM, dRowM, pairIndex + op.Im, op.Size);
+ resM = EmitVectorInsert(context, resM, mRowM, pairIndex + 1 + op.Im, op.Size);
+
+ if (overlap)
+ {
+ resD = resM;
+ }
+ }
+
+ context.Copy(GetVecA32(op.Qd), resD);
+ if (!overlap)
+ {
+ context.Copy(GetVecA32(op.Qm), resM);
+ }
+ }
+
+ public static void Vuzp(ArmEmitterContext context)
+ {
+ OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
+
+ int elems = op.GetBytesCount() >> op.Size;
+ int pairs = elems >> 1;
+
+ bool overlap = op.Qm == op.Qd;
+
+ Operand resD = GetVecA32(op.Qd);
+ Operand resM = GetVecA32(op.Qm);
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand dIns, mIns;
+ if (index >= pairs)
+ {
+ int pind = index - pairs;
+ dIns = EmitVectorExtract32(context, op.Qm, (pind << 1) + op.Im, op.Size, false);
+ mIns = EmitVectorExtract32(context, op.Qm, ((pind << 1) | 1) + op.Im, op.Size, false);
+ }
+ else
+ {
+ dIns = EmitVectorExtract32(context, op.Qd, (index << 1) + op.Id, op.Size, false);
+ mIns = EmitVectorExtract32(context, op.Qd, ((index << 1) | 1) + op.Id, op.Size, false);
+ }
+
+ resD = EmitVectorInsert(context, resD, dIns, index + op.Id, op.Size);
+
+ if (overlap)
+ {
+ resM = resD;
+ }
+
+ resM = EmitVectorInsert(context, resM, mIns, index + op.Im, op.Size);
+
+ if (overlap)
+ {
+ resD = resM;
+ }
+ }
+
+ context.Copy(GetVecA32(op.Qd), resD);
+ if (!overlap)
+ {
+ context.Copy(GetVecA32(op.Qm), resM);
+ }
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/ARMeilleure/Instructions/InstEmitSimdShift32.cs
new file mode 100644
index 00000000..89385476
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdShift32.cs
@@ -0,0 +1,100 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+using System.Diagnostics;
+
+using static ARMeilleure.Instructions.InstEmitSimdHelper32;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Vshl(ArmEmitterContext context)
+ {
+ OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
+
+ EmitVectorUnaryOpZx32(context, (op1) => context.ShiftLeft(op1, Const(op.Shift)));
+ }
+
+ public static void Vshl_I(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+
+ if (op.U)
+ {
+ EmitVectorBinaryOpZx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, true));
+ }
+ else
+ {
+ EmitVectorBinaryOpSx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, false));
+ }
+ }
+
+ public static void Vshr(ArmEmitterContext context)
+ {
+ OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
+ int shift = (8 << op.Size) - op.Shift; // Shr amount is flipped.
+ int maxShift = (8 << op.Size) - 1;
+
+ if (op.U)
+ {
+ EmitVectorUnaryOpZx32(context, (op1) => (shift > maxShift) ? Const(op1.Type, 0) : context.ShiftRightUI(op1, Const(shift)));
+ }
+ else
+ {
+ EmitVectorUnaryOpSx32(context, (op1) => context.ShiftRightSI(op1, Const(Math.Min(maxShift, shift))));
+ }
+ }
+
+ public static void Vshrn(ArmEmitterContext context)
+ {
+ OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
+ int shift = (8 << op.Size) - op.Shift; // Shr amount is flipped.
+
+ EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
+ }
+
+ private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
+ {
+ if (shiftLsB.Type == OperandType.I64)
+ {
+ shiftLsB = context.ConvertI64ToI32(shiftLsB);
+ }
+
+ shiftLsB = context.SignExtend8(OperandType.I32, shiftLsB);
+ Debug.Assert((uint)size < 4u);
+
+ Operand negShiftLsB = context.Negate(shiftLsB);
+
+ Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
+
+ Operand shl = context.ShiftLeft(op, shiftLsB);
+ Operand shr = unsigned ? context.ShiftRightUI(op, negShiftLsB) : context.ShiftRightSI(op, negShiftLsB);
+
+ Operand res = context.ConditionalSelect(isPositive, shl, shr);
+
+ if (unsigned)
+ {
+ Operand isOutOfRange = context.BitwiseOr(
+ context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
+ context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
+
+ return context.ConditionalSelect(isOutOfRange, Const(op.Type, 0), res);
+ }
+ else
+ {
+ Operand isOutOfRange0 = context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size));
+ Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
+
+ // Also zero if shift is too negative, but value was positive.
+ isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0))));
+
+ Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
+
+ return context.ConditionalSelect(isOutOfRange0, Const(op.Type, 0), context.ConditionalSelect(isOutOfRangeN, min, res));
+ }
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/ARMeilleure/Instructions/InstEmitSystem32.cs
new file mode 100644
index 00000000..808b4fdd
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSystem32.cs
@@ -0,0 +1,233 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.State;
+using ARMeilleure.Translation;
+using System;
+
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Mcr(ArmEmitterContext context)
+ {
+ OpCode32System op = (OpCode32System)context.CurrOp;
+
+ if (op.Coproc != 15)
+ {
+ throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
+ }
+
+ if (op.Opc1 != 0)
+ {
+ throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
+ }
+
+ Delegate dlg;
+ switch (op.CRn)
+ {
+ case 13: // Process and Thread Info.
+ if (op.CRm != 0)
+ {
+ throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
+ }
+ switch (op.Opc2)
+ {
+ case 2:
+ dlg = new _Void_U32(NativeInterface.SetTpidrEl032); break;
+ default:
+ throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
+ }
+ break;
+
+ case 7:
+ switch (op.CRm) // Cache and Memory barrier.
+ {
+ case 10:
+ switch (op.Opc2)
+ {
+ case 5: // Data Memory Barrier Register.
+ return; // No-op.
+ default:
+ throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
+ }
+ default:
+ throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
+ }
+
+ default:
+ throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
+ }
+
+ context.Call(dlg, GetIntA32(context, op.Rt));
+ }
+
+ public static void Mrc(ArmEmitterContext context)
+ {
+ OpCode32System op = (OpCode32System)context.CurrOp;
+
+ if (op.Coproc != 15)
+ {
+ throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
+ }
+
+ if (op.Opc1 != 0)
+ {
+ throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
+ }
+
+ Delegate dlg;
+ switch (op.CRn)
+ {
+ case 13: // Process and Thread Info.
+ if (op.CRm != 0)
+ {
+ throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
+ }
+ switch (op.Opc2)
+ {
+ case 2:
+ dlg = new _U32(NativeInterface.GetTpidrEl032); break;
+ case 3:
+ dlg = new _U32(NativeInterface.GetTpidr32); break;
+ default:
+ throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
+ }
+ break;
+ default:
+ throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
+ }
+
+ if (op.Rt == RegisterAlias.Aarch32Pc)
+ {
+ // Special behavior: copy NZCV flags into APSR.
+ EmitSetNzcv(context, context.Call(dlg));
+
+ return;
+ }
+ else
+ {
+ SetIntA32(context, op.Rt, context.Call(dlg));
+ }
+ }
+
+ public static void Mrrc(ArmEmitterContext context)
+ {
+ OpCode32System op = (OpCode32System)context.CurrOp;
+
+ if (op.Coproc != 15)
+ {
+ throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
+ }
+
+ var opc = op.MrrcOp;
+
+ Delegate dlg;
+ switch (op.CRm)
+ {
+ case 14: // Timer.
+ switch (opc)
+ {
+ case 0:
+ dlg = new _U64(NativeInterface.GetCntpctEl0); break;
+ default:
+ throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}.");
+ }
+ break;
+ default:
+ throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
+ }
+
+ Operand result = context.Call(dlg);
+
+ SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
+ SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
+ }
+
+ public static void Nop(ArmEmitterContext context) { }
+
+ public static void Vmrs(ArmEmitterContext context)
+ {
+ OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
+
+ if (op.Rt == RegisterAlias.Aarch32Pc && op.Sreg == 0b0001)
+ {
+ // Special behavior: copy NZCV flags into APSR.
+ SetFlag(context, PState.VFlag, GetFpFlag(FPState.VFlag));
+ SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag));
+ SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
+ SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
+ return;
+ }
+
+ Delegate dlg;
+ switch (op.Sreg)
+ {
+ case 0b0000: // FPSID
+ throw new NotImplementedException("Supervisor Only");
+ case 0b0001: // FPSCR
+ dlg = new _U32(NativeInterface.GetFpscr); break;
+ case 0b0101: // MVFR2
+ throw new NotImplementedException("MVFR2");
+ case 0b0110: // MVFR1
+ throw new NotImplementedException("MVFR1");
+ case 0b0111: // MVFR0
+ throw new NotImplementedException("MVFR0");
+ case 0b1000: // FPEXC
+ throw new NotImplementedException("Supervisor Only");
+ default:
+ throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
+ }
+
+ SetIntA32(context, op.Rt, context.Call(dlg));
+ }
+
+ public static void Vmsr(ArmEmitterContext context)
+ {
+ OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
+
+ Delegate dlg;
+ switch (op.Sreg)
+ {
+ case 0b0000: // FPSID
+ throw new NotImplementedException("Supervisor Only");
+ case 0b0001: // FPSCR
+ dlg = new _Void_U32(NativeInterface.SetFpscr); break;
+ case 0b0101: // MVFR2
+ throw new NotImplementedException("MVFR2");
+ case 0b0110: // MVFR1
+ throw new NotImplementedException("MVFR1");
+ case 0b0111: // MVFR0
+ throw new NotImplementedException("MVFR0");
+ case 0b1000: // FPEXC
+ throw new NotImplementedException("Supervisor Only");
+ default:
+ throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
+ }
+
+ context.Call(dlg, GetIntA32(context, op.Rt));
+ }
+
+ private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
+ {
+ Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
+ v = context.BitwiseAnd(v, Const(1));
+
+ Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
+ c = context.BitwiseAnd(c, Const(1));
+
+ Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
+ z = context.BitwiseAnd(z, Const(1));
+
+ Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
+ n = context.BitwiseAnd(n, Const(1));
+
+ SetFlag(context, PState.VFlag, v);
+ SetFlag(context, PState.CFlag, c);
+ SetFlag(context, PState.ZFlag, z);
+ SetFlag(context, PState.NFlag, n);
+ }
+ }
+}
diff --git a/ARMeilleure/Instructions/InstName.cs b/ARMeilleure/Instructions/InstName.cs
index c81484a6..0c2dd18d 100644
--- a/ARMeilleure/Instructions/InstName.cs
+++ b/ARMeilleure/Instructions/InstName.cs
@@ -82,6 +82,7 @@ namespace ARMeilleure.Instructions
Smaddl,
Smsubl,
Smulh,
+ Smull,
Stlr,
Stlxp,
Stlxr,
@@ -92,6 +93,8 @@ namespace ARMeilleure.Instructions
Sub,
Subs,
Svc,
+ Sxtb,
+ Sxth,
Sys,
Tbnz,
Tbz,
@@ -445,19 +448,140 @@ namespace ARMeilleure.Instructions
Zip2_V,
// Base (AArch32)
+ Bfc,
+ Bfi,
Blx,
Bx,
Cmp,
+ Cmn,
+ Movt,
+ Mul,
+ Lda,
+ Ldab,
+ Ldaex,
+ Ldaexb,
+ Ldaexd,
+ Ldaexh,
+ Ldah,
Ldm,
Ldrb,
Ldrd,
+ Ldrex,
+ Ldrexb,
+ Ldrexd,
+ Ldrexh,
Ldrh,
Ldrsb,
Ldrsh,
+ Mcr,
+ Mla,
+ Mls,
Mov,
+ Mrc,
+ Mrrc,
+ Mvn,
+ Pkh,
+ Pld,
+ Rev,
+ Revsh,
+ Rsb,
+ Rsc,
+ Sbfx,
+ Smlab,
+ Smlal,
+ Smlalh,
+ Smmla,
+ Smmls,
+ Smmul,
+ Stl,
+ Stlb,
+ Stlex,
+ Stlexb,
+ Stlexd,
+ Stlexh,
+ Stlh,
Stm,
Strb,
Strd,
- Strh
+ Strex,
+ Strexb,
+ Strexd,
+ Strexh,
+ Strh,
+ Sxtb16,
+ Teq,
+ Trap,
+ Tst,
+ Ubfx,
+ Umlal,
+ Umull,
+ Uxtb,
+ Uxtb16,
+ Uxth,
+
+ // FP & SIMD (AArch32)
+ Vabs,
+ Vadd,
+ Vand,
+ Vbif,
+ Vbit,
+ Vbsl,
+ Vceq,
+ Vcge,
+ Vcgt,
+ Vcle,
+ Vclt,
+ Vcmp,
+ Vcmpe,
+ Vcvt,
+ Vdiv,
+ Vdup,
+ Vext,
+ Vld1,
+ Vld2,
+ Vld3,
+ Vld4,
+ Vldm,
+ Vldr,
+ Vmax,
+ Vmaxnm,
+ Vmin,
+ Vminnm,
+ Vmla,
+ Vmls,
+ Vmov,
+ Vmovn,
+ Vmrs,
+ Vmsr,
+ Vmul,
+ Vmvn,
+ Vneg,
+ Vnmul,
+ Vnmla,
+ Vnmls,
+ Vorr,
+ Vpadd,
+ Vrev,
+ Vrint,
+ Vsel,
+ Vshl,
+ Vshr,
+ Vshrn,
+ Vst1,
+ Vst2,
+ Vst3,
+ Vst4,
+ Vstm,
+ Vstr,
+ Vsqrt,
+ Vrecpe,
+ Vrecps,
+ Vrsqrte,
+ Vrsqrts,
+ Vsub,
+ Vtbl,
+ Vtrn,
+ Vuzp,
+ Vzip,
}
}
diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs
index 3a1e91c8..988e86bd 100644
--- a/ARMeilleure/Instructions/NativeInterface.cs
+++ b/ARMeilleure/Instructions/NativeInterface.cs
@@ -87,16 +87,39 @@ namespace ARMeilleure.Instructions
return (ulong)GetContext().Fpsr;
}
+ public static uint GetFpscr()
+ {
+ ExecutionContext context = GetContext();
+ uint result = (uint)(context.Fpsr & FPSR.A32Mask) | (uint)(context.Fpcr & FPCR.A32Mask);
+
+ result |= context.GetFPstateFlag(FPState.NFlag) ? (1u << 31) : 0;
+ result |= context.GetFPstateFlag(FPState.ZFlag) ? (1u << 30) : 0;
+ result |= context.GetFPstateFlag(FPState.CFlag) ? (1u << 29) : 0;
+ result |= context.GetFPstateFlag(FPState.VFlag) ? (1u << 28) : 0;
+
+ return result;
+ }
+
public static ulong GetTpidrEl0()
{
return (ulong)GetContext().TpidrEl0;
}
+ public static uint GetTpidrEl032()
+ {
+ return (uint)GetContext().TpidrEl0;
+ }
+
public static ulong GetTpidr()
{
return (ulong)GetContext().Tpidr;
}
+ public static uint GetTpidr32()
+ {
+ return (uint)GetContext().Tpidr;
+ }
+
public static ulong GetCntfrqEl0()
{
return GetContext().CntfrqEl0;
@@ -117,13 +140,31 @@ namespace ARMeilleure.Instructions
GetContext().Fpsr = (FPSR)value;
}
+ public static void SetFpscr(uint value)
+ {
+ ExecutionContext context = GetContext();
+
+ context.SetFPstateFlag(FPState.NFlag, (value & (1u << 31)) != 0);
+ context.SetFPstateFlag(FPState.ZFlag, (value & (1u << 30)) != 0);
+ context.SetFPstateFlag(FPState.CFlag, (value & (1u << 29)) != 0);
+ context.SetFPstateFlag(FPState.VFlag, (value & (1u << 28)) != 0);
+
+ context.Fpsr = FPSR.A32Mask & (FPSR)value;
+ context.Fpcr = FPCR.A32Mask & (FPCR)value;
+ }
+
public static void SetTpidrEl0(ulong value)
{
GetContext().TpidrEl0 = (long)value;
}
-#endregion
-#region "Read"
+ public static void SetTpidrEl032(uint value)
+ {
+ GetContext().TpidrEl0 = (long)value;
+ }
+ #endregion
+
+ #region "Read"
public static byte ReadByte(ulong address)
{
return GetMemoryManager().ReadByte((long)address);
diff --git a/ARMeilleure/Instructions/SoftFallback.cs b/ARMeilleure/Instructions/SoftFallback.cs
index 10bb47df..611e8d6a 100644
--- a/ARMeilleure/Instructions/SoftFallback.cs
+++ b/ARMeilleure/Instructions/SoftFallback.cs
@@ -420,6 +420,26 @@ namespace ARMeilleure.Instructions
return MathF.Truncate(value);
}
}
+
+ public static int FloatToInt32(float value)
+ {
+ return SatF32ToS32(RoundF(value));
+ }
+
+ public static int DoubleToInt32(double value)
+ {
+ return SatF64ToS32(Round(value));
+ }
+
+ public static uint FloatToUInt32(float value)
+ {
+ return SatF32ToU32(RoundF(value));
+ }
+
+ public static uint DoubleToUInt32(double value)
+ {
+ return SatF64ToU32(Round(value));
+ }
#endregion
#region "Saturation"
diff --git a/ARMeilleure/Instructions/SoftFloat.cs b/ARMeilleure/Instructions/SoftFloat.cs
index 256bc5b9..d3e15a2c 100644
--- a/ARMeilleure/Instructions/SoftFloat.cs
+++ b/ARMeilleure/Instructions/SoftFloat.cs
@@ -121,7 +121,7 @@ namespace ARMeilleure.Instructions
private static float FPDefaultNaN()
{
- return -float.NaN;
+ return BitConverter.Int32BitsToSingle(0x7fc00000);
}
private static float FPInfinity(bool sign)
@@ -623,12 +623,18 @@ namespace ARMeilleure.Instructions
{
public static float FPAdd(float value1, float value2)
{
+ return FPAddFpscr(value1, value2, false);
+ }
+
+ public static float FPAddFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -639,7 +645,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if ((inf1 && !sign1) || (inf2 && !sign2))
{
@@ -657,7 +663,7 @@ namespace ARMeilleure.Instructions
{
result = value1 + value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -672,9 +678,10 @@ namespace ARMeilleure.Instructions
public static int FPCompare(float value1, float value2, bool signalNaNs)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out _, context, fpcr);
int result;
@@ -684,7 +691,7 @@ namespace ARMeilleure.Instructions
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs)
{
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
}
else
@@ -708,10 +715,16 @@ namespace ARMeilleure.Instructions
public static float FPCompareEQ(float value1, float value2)
{
+ return FPCompareEQFpscr(value1, value2, false);
+ }
+
+ public static float FPCompareEQFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out _, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
float result;
@@ -721,7 +734,7 @@ namespace ARMeilleure.Instructions
if (type1 == FPType.SNaN || type2 == FPType.SNaN)
{
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
}
else
@@ -734,10 +747,16 @@ namespace ARMeilleure.Instructions
public static float FPCompareGE(float value1, float value2)
{
+ return FPCompareGEFpscr(value1, value2, false);
+ }
+
+ public static float FPCompareGEFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out _, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
float result;
@@ -745,7 +764,7 @@ namespace ARMeilleure.Instructions
{
result = ZerosOrOnes(false);
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else
{
@@ -757,10 +776,16 @@ namespace ARMeilleure.Instructions
public static float FPCompareGT(float value1, float value2)
{
+ return FPCompareGTFpscr(value1, value2, false);
+ }
+
+ public static float FPCompareGTFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out _, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
float result;
@@ -768,7 +793,7 @@ namespace ARMeilleure.Instructions
{
result = ZerosOrOnes(false);
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else
{
@@ -788,14 +813,25 @@ namespace ARMeilleure.Instructions
return FPCompareGT(value2, value1);
}
+ public static float FPCompareLEFpscr(float value1, float value2, bool standardFpscr)
+ {
+ return FPCompareGEFpscr(value2, value1, standardFpscr);
+ }
+
+ public static float FPCompareLTFpscr(float value1, float value2, bool standardFpscr)
+ {
+ return FPCompareGTFpscr(value2, value1, standardFpscr);
+ }
+
public static float FPDiv(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -806,7 +842,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if (inf1 || zero2)
{
@@ -814,7 +850,7 @@ namespace ARMeilleure.Instructions
if (!inf1)
{
- FPProcessException(FPException.DivideByZero, context);
+ FPProcessException(FPException.DivideByZero, context, fpcr);
}
}
else if (zero1 || inf2)
@@ -825,7 +861,7 @@ namespace ARMeilleure.Instructions
{
result = value1 / value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -839,12 +875,18 @@ namespace ARMeilleure.Instructions
public static float FPMax(float value1, float value2)
{
+ return FPMaxFpscr(value1, value2, false);
+ }
+
+ public static float FPMaxFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -877,7 +919,7 @@ namespace ARMeilleure.Instructions
{
result = value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -892,10 +934,16 @@ namespace ARMeilleure.Instructions
public static float FPMaxNum(float value1, float value2)
{
+ return FPMaxNumFpscr(value1, value2, false);
+ }
+
+ public static float FPMaxNumFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1.FPUnpack(out FPType type1, out _, out _, context);
- value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
if (type1 == FPType.QNaN && type2 != FPType.QNaN)
{
@@ -906,17 +954,23 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(true);
}
- return FPMax(value1, value2);
+ return FPMaxFpscr(value1, value2, standardFpscr);
}
public static float FPMin(float value1, float value2)
{
+ return FPMinFpscr(value1, value2, false);
+ }
+
+ public static float FPMinFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -949,7 +1003,7 @@ namespace ARMeilleure.Instructions
{
result = value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -964,10 +1018,16 @@ namespace ARMeilleure.Instructions
public static float FPMinNum(float value1, float value2)
{
+ return FPMinNumFpscr(value1, value2, false);
+ }
+
+ public static float FPMinNumFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1.FPUnpack(out FPType type1, out _, out _, context);
- value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
if (type1 == FPType.QNaN && type2 != FPType.QNaN)
{
@@ -978,17 +1038,23 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(false);
}
- return FPMin(value1, value2);
+ return FPMinFpscr(value1, value2, standardFpscr);
}
public static float FPMul(float value1, float value2)
{
+ return FPMulFpscr(value1, value2, false);
+ }
+
+ public static float FPMulFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -999,7 +1065,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if (inf1 || inf2)
{
@@ -1013,7 +1079,7 @@ namespace ARMeilleure.Instructions
{
result = value1 * value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1027,22 +1093,28 @@ namespace ARMeilleure.Instructions
public static float FPMulAdd(float valueA, float value1, float value2)
{
+ return FPMulAddFpscr(valueA, value1, value2, false);
+ }
+
+ public static float FPMulAddFpscr(float valueA, float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- valueA = valueA.FPUnpack(out FPType typeA, out bool signA, out uint addend, context);
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ valueA = valueA.FPUnpack(out FPType typeA, out bool signA, out uint addend, context, fpcr);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
- float result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, context);
+ float result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, context, fpcr);
if (typeA == FPType.QNaN && ((inf1 && zero2) || (zero1 && inf2)))
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
if (!done)
@@ -1057,7 +1129,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if ((infA && !signA) || (infP && !signP))
{
@@ -1075,7 +1147,7 @@ namespace ARMeilleure.Instructions
{
result = MathF.FusedMultiplyAdd(value1, value2, valueA);
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1094,14 +1166,22 @@ namespace ARMeilleure.Instructions
return FPMulAdd(valueA, value1, value2);
}
+ public static float FPMulSubFpscr(float valueA, float value1, float value2, bool standardFpscr)
+ {
+ value1 = value1.FPNeg();
+
+ return FPMulAddFpscr(valueA, value1, value2, standardFpscr);
+ }
+
public static float FPMulX(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1124,7 +1204,7 @@ namespace ARMeilleure.Instructions
{
result = value1 * value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1153,15 +1233,21 @@ namespace ARMeilleure.Instructions
public static float FPRecipEstimate(float value)
{
+ return FPRecipEstimateFpscr(value, false);
+ }
+
+ public static float FPRecipEstimateFpscr(float value, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value.FPUnpack(out FPType type, out bool sign, out uint op, context);
+ value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr);
float result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else if (type == FPType.Infinity)
{
@@ -1171,13 +1257,13 @@ namespace ARMeilleure.Instructions
{
result = FPInfinity(sign);
- FPProcessException(FPException.DivideByZero, context);
+ FPProcessException(FPException.DivideByZero, context, fpcr);
}
else if (MathF.Abs(value) < MathF.Pow(2f, -128))
{
bool overflowToInf;
- switch (context.Fpcr.GetRoundingMode())
+ switch (fpcr.GetRoundingMode())
{
default:
case FPRoundingMode.ToNearest: overflowToInf = true; break;
@@ -1188,10 +1274,10 @@ namespace ARMeilleure.Instructions
result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
- FPProcessException(FPException.Overflow, context);
- FPProcessException(FPException.Inexact, context);
+ FPProcessException(FPException.Overflow, context, fpcr);
+ FPProcessException(FPException.Inexact, context, fpcr);
}
- else if ((context.Fpcr & FPCR.Fz) != 0 && (MathF.Abs(value) >= MathF.Pow(2f, 126)))
+ else if ((fpcr & FPCR.Fz) != 0 && (MathF.Abs(value) >= MathF.Pow(2f, 126)))
{
result = FPZero(sign);
@@ -1240,16 +1326,49 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static float FPRecipStep(float value1, float value2)
+ {
+ ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.StandardFpcrValue;
+
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
+
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
+
+ if (!done)
+ {
+ bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
+ bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
+
+ float product;
+
+ if ((inf1 && zero2) || (zero1 && inf2))
+ {
+ product = FPZero(false);
+ }
+ else
+ {
+ product = FPMulFpscr(value1, value2, true);
+ }
+
+ result = FPSubFpscr(FPTwo(false), product, true);
+ }
+
+ return result;
+ }
+
public static float FPRecipStepFused(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
value1 = value1.FPNeg();
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1268,7 +1387,7 @@ namespace ARMeilleure.Instructions
{
result = MathF.FusedMultiplyAdd(value1, value2, 2f);
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1283,14 +1402,15 @@ namespace ARMeilleure.Instructions
public static float FPRecpX(float value)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value.FPUnpack(out FPType type, out bool sign, out uint op, context);
+ value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr);
float result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else
{
@@ -1306,27 +1426,33 @@ namespace ARMeilleure.Instructions
public static float FPRSqrtEstimate(float value)
{
+ return FPRSqrtEstimateFpscr(value, false);
+ }
+
+ public static float FPRSqrtEstimateFpscr(float value, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value.FPUnpack(out FPType type, out bool sign, out uint op, context);
+ value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr);
float result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else if (type == FPType.Zero)
{
result = FPInfinity(sign);
- FPProcessException(FPException.DivideByZero, context);
+ FPProcessException(FPException.DivideByZero, context, fpcr);
}
else if (sign)
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if (type == FPType.Infinity)
{
@@ -1369,16 +1495,95 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static float FPHalvedSub(float value1, float value2, ExecutionContext context, FPCR fpcr)
+ {
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
+
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
+
+ if (!done)
+ {
+ bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
+ bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
+
+ if (inf1 && inf2 && sign1 == sign2)
+ {
+ result = FPDefaultNaN();
+
+ FPProcessException(FPException.InvalidOp, context, fpcr);
+ }
+ else if ((inf1 && !sign1) || (inf2 && sign2))
+ {
+ result = FPInfinity(false);
+ }
+ else if ((inf1 && sign1) || (inf2 && !sign2))
+ {
+ result = FPInfinity(true);
+ }
+ else if (zero1 && zero2 && sign1 == !sign2)
+ {
+ result = FPZero(sign1);
+ }
+ else
+ {
+ result = (value1 - value2) / 2.0f;
+
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ {
+ context.Fpsr |= FPSR.Ufc;
+
+ result = FPZero(result < 0f);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static float FPRSqrtStep(float value1, float value2)
+ {
+ ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.StandardFpcrValue;
+
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
+
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
+
+ if (!done)
+ {
+ bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
+ bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
+
+ float product;
+
+ if ((inf1 && zero2) || (zero1 && inf2))
+ {
+ product = FPZero(false);
+ }
+ else
+ {
+ product = FPMulFpscr(value1, value2, true);
+ }
+
+ result = FPHalvedSub(FPThree(false), product, context, fpcr);
+ }
+
+ return result;
+ }
+
public static float FPRSqrtStepFused(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
value1 = value1.FPNeg();
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1397,7 +1602,7 @@ namespace ARMeilleure.Instructions
{
result = MathF.FusedMultiplyAdd(value1, value2, 3f) / 2f;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1412,14 +1617,15 @@ namespace ARMeilleure.Instructions
public static float FPSqrt(float value)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value = value.FPUnpack(out FPType type, out bool sign, out uint op, context);
+ value = value.FPUnpack(out FPType type, out bool sign, out uint op, context, fpcr);
float result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else if (type == FPType.Zero)
{
@@ -1433,13 +1639,13 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else
{
result = MathF.Sqrt(value);
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1452,12 +1658,18 @@ namespace ARMeilleure.Instructions
public static float FPSub(float value1, float value2)
{
+ return FPSubFpscr(value1, value2, false);
+ }
+
+ public static float FPSubFpscr(float value1, float value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
- float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1468,7 +1680,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if ((inf1 && !sign1) || (inf2 && sign2))
{
@@ -1486,7 +1698,7 @@ namespace ARMeilleure.Instructions
{
result = value1 - value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && float.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1500,7 +1712,7 @@ namespace ARMeilleure.Instructions
private static float FPDefaultNaN()
{
- return -float.NaN;
+ return BitConverter.Int32BitsToSingle(0x7fc00000);
}
private static float FPInfinity(bool sign)
@@ -1523,6 +1735,11 @@ namespace ARMeilleure.Instructions
return sign ? -2f : +2f;
}
+ private static float FPThree(bool sign)
+ {
+ return sign ? -3f : +3f;
+ }
+
private static float FPOnePointFive(bool sign)
{
return sign ? -1.5f : +1.5f;
@@ -1543,7 +1760,8 @@ namespace ARMeilleure.Instructions
out FPType type,
out bool sign,
out uint valueBits,
- ExecutionContext context)
+ ExecutionContext context,
+ FPCR fpcr)
{
valueBits = (uint)BitConverter.SingleToInt32Bits(value);
@@ -1551,14 +1769,14 @@ namespace ARMeilleure.Instructions
if ((valueBits & 0x7F800000u) == 0u)
{
- if ((valueBits & 0x007FFFFFu) == 0u || (context.Fpcr & FPCR.Fz) != 0)
+ if ((valueBits & 0x007FFFFFu) == 0u || (fpcr & FPCR.Fz) != 0)
{
type = FPType.Zero;
value = FPZero(sign);
if ((valueBits & 0x007FFFFFu) != 0u)
{
- FPProcessException(FPException.InputDenorm, context);
+ FPProcessException(FPException.InputDenorm, context, fpcr);
}
}
else
@@ -1592,25 +1810,26 @@ namespace ARMeilleure.Instructions
uint op1,
uint op2,
out bool done,
- ExecutionContext context)
+ ExecutionContext context,
+ FPCR fpcr)
{
done = true;
if (type1 == FPType.SNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.SNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
else if (type1 == FPType.QNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.QNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
done = false;
@@ -1626,33 +1845,34 @@ namespace ARMeilleure.Instructions
uint op2,
uint op3,
out bool done,
- ExecutionContext context)
+ ExecutionContext context,
+ FPCR fpcr)
{
done = true;
if (type1 == FPType.SNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.SNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
else if (type3 == FPType.SNaN)
{
- return FPProcessNaN(type3, op3, context);
+ return FPProcessNaN(type3, op3, context, fpcr);
}
else if (type1 == FPType.QNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.QNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
else if (type3 == FPType.QNaN)
{
- return FPProcessNaN(type3, op3, context);
+ return FPProcessNaN(type3, op3, context, fpcr);
}
done = false;
@@ -1660,16 +1880,16 @@ namespace ARMeilleure.Instructions
return FPZero(false);
}
- private static float FPProcessNaN(FPType type, uint op, ExecutionContext context)
+ private static float FPProcessNaN(FPType type, uint op, ExecutionContext context, FPCR fpcr)
{
if (type == FPType.SNaN)
{
op |= 1u << 22;
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
- if ((context.Fpcr & FPCR.Dn) != 0)
+ if ((fpcr & FPCR.Dn) != 0)
{
return FPDefaultNaN();
}
@@ -1677,11 +1897,11 @@ namespace ARMeilleure.Instructions
return BitConverter.Int32BitsToSingle((int)op);
}
- private static void FPProcessException(FPException exc, ExecutionContext context)
+ private static void FPProcessException(FPException exc, ExecutionContext context, FPCR fpcr)
{
int enable = (int)exc + 8;
- if ((context.Fpcr & (FPCR)(1 << enable)) != 0)
+ if ((fpcr & (FPCR)(1 << enable)) != 0)
{
throw new NotImplementedException("Floating-point trap handling.");
}
@@ -1696,12 +1916,18 @@ namespace ARMeilleure.Instructions
{
public static double FPAdd(double value1, double value2)
{
+ return FPAddFpscr(value1, value2, false);
+ }
+
+ public static double FPAddFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1712,7 +1938,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if ((inf1 && !sign1) || (inf2 && !sign2))
{
@@ -1730,7 +1956,7 @@ namespace ARMeilleure.Instructions
{
result = value1 + value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1745,9 +1971,10 @@ namespace ARMeilleure.Instructions
public static int FPCompare(double value1, double value2, bool signalNaNs)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out _, context, fpcr);
int result;
@@ -1757,7 +1984,7 @@ namespace ARMeilleure.Instructions
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs)
{
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
}
else
@@ -1781,10 +2008,16 @@ namespace ARMeilleure.Instructions
public static double FPCompareEQ(double value1, double value2)
{
+ return FPCompareEQFpscr(value1, value2, false);
+ }
+
+ public static double FPCompareEQFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out _, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
double result;
@@ -1794,7 +2027,7 @@ namespace ARMeilleure.Instructions
if (type1 == FPType.SNaN || type2 == FPType.SNaN)
{
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
}
else
@@ -1807,10 +2040,16 @@ namespace ARMeilleure.Instructions
public static double FPCompareGE(double value1, double value2)
{
+ return FPCompareGEFpscr(value1, value2, false);
+ }
+
+ public static double FPCompareGEFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out _, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
double result;
@@ -1818,7 +2057,7 @@ namespace ARMeilleure.Instructions
{
result = ZerosOrOnes(false);
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else
{
@@ -1830,10 +2069,16 @@ namespace ARMeilleure.Instructions
public static double FPCompareGT(double value1, double value2)
{
+ return FPCompareGTFpscr(value1, value2, false);
+ }
+
+ public static double FPCompareGTFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out _, out _, context);
- value2 = value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
double result;
@@ -1841,7 +2086,7 @@ namespace ARMeilleure.Instructions
{
result = ZerosOrOnes(false);
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else
{
@@ -1861,14 +2106,25 @@ namespace ARMeilleure.Instructions
return FPCompareGT(value2, value1);
}
+ public static double FPCompareLEFpscr(double value1, double value2, bool standardFpscr)
+ {
+ return FPCompareGEFpscr(value2, value1, standardFpscr);
+ }
+
+ public static double FPCompareLTFpscr(double value1, double value2, bool standardFpscr)
+ {
+ return FPCompareGTFpscr(value2, value1, standardFpscr);
+ }
+
public static double FPDiv(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1879,7 +2135,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if (inf1 || zero2)
{
@@ -1887,7 +2143,7 @@ namespace ARMeilleure.Instructions
if (!inf1)
{
- FPProcessException(FPException.DivideByZero, context);
+ FPProcessException(FPException.DivideByZero, context, fpcr);
}
}
else if (zero1 || inf2)
@@ -1898,7 +2154,7 @@ namespace ARMeilleure.Instructions
{
result = value1 / value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1912,12 +2168,18 @@ namespace ARMeilleure.Instructions
public static double FPMax(double value1, double value2)
{
+ return FPMaxFpscr(value1, value2, false);
+ }
+
+ public static double FPMaxFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -1950,7 +2212,7 @@ namespace ARMeilleure.Instructions
{
result = value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -1965,10 +2227,16 @@ namespace ARMeilleure.Instructions
public static double FPMaxNum(double value1, double value2)
{
+ return FPMaxNumFpscr(value1, value2, false);
+ }
+
+ public static double FPMaxNumFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1.FPUnpack(out FPType type1, out _, out _, context);
- value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
if (type1 == FPType.QNaN && type2 != FPType.QNaN)
{
@@ -1979,17 +2247,23 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(true);
}
- return FPMax(value1, value2);
+ return FPMaxFpscr(value1, value2, standardFpscr);
}
public static double FPMin(double value1, double value2)
{
+ return FPMinFpscr(value1, value2, false);
+ }
+
+ public static double FPMinFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -2022,7 +2296,7 @@ namespace ARMeilleure.Instructions
{
result = value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2037,10 +2311,16 @@ namespace ARMeilleure.Instructions
public static double FPMinNum(double value1, double value2)
{
+ return FPMinNumFpscr(value1, value2, false);
+ }
+
+ public static double FPMinNumFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1.FPUnpack(out FPType type1, out _, out _, context);
- value2.FPUnpack(out FPType type2, out _, out _, context);
+ value1.FPUnpack(out FPType type1, out _, out _, context, fpcr);
+ value2.FPUnpack(out FPType type2, out _, out _, context, fpcr);
if (type1 == FPType.QNaN && type2 != FPType.QNaN)
{
@@ -2051,17 +2331,23 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(false);
}
- return FPMin(value1, value2);
+ return FPMinFpscr(value1, value2, standardFpscr);
}
public static double FPMul(double value1, double value2)
{
+ return FPMulFpscr(value1, value2, false);
+ }
+
+ public static double FPMulFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -2072,7 +2358,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if (inf1 || inf2)
{
@@ -2086,7 +2372,7 @@ namespace ARMeilleure.Instructions
{
result = value1 * value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2100,22 +2386,28 @@ namespace ARMeilleure.Instructions
public static double FPMulAdd(double valueA, double value1, double value2)
{
+ return FPMulAddFpscr(valueA, value1, value2, false);
+ }
+
+ public static double FPMulAddFpscr(double valueA, double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- valueA = valueA.FPUnpack(out FPType typeA, out bool signA, out ulong addend, context);
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ valueA = valueA.FPUnpack(out FPType typeA, out bool signA, out ulong addend, context, fpcr);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
- double result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, context);
+ double result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, context, fpcr);
if (typeA == FPType.QNaN && ((inf1 && zero2) || (zero1 && inf2)))
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
if (!done)
@@ -2130,7 +2422,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if ((infA && !signA) || (infP && !signP))
{
@@ -2148,7 +2440,7 @@ namespace ARMeilleure.Instructions
{
result = Math.FusedMultiplyAdd(value1, value2, valueA);
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2167,14 +2459,22 @@ namespace ARMeilleure.Instructions
return FPMulAdd(valueA, value1, value2);
}
+ public static double FPMulSubFpscr(double valueA, double value1, double value2, bool standardFpscr)
+ {
+ value1 = value1.FPNeg();
+
+ return FPMulAddFpscr(valueA, value1, value2, standardFpscr);
+ }
+
public static double FPMulX(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -2197,7 +2497,7 @@ namespace ARMeilleure.Instructions
{
result = value1 * value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2226,15 +2526,21 @@ namespace ARMeilleure.Instructions
public static double FPRecipEstimate(double value)
{
+ return FPRecipEstimateFpscr(value, false);
+ }
+
+ public static double FPRecipEstimateFpscr(double value, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value.FPUnpack(out FPType type, out bool sign, out ulong op, context);
+ value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr);
double result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else if (type == FPType.Infinity)
{
@@ -2244,13 +2550,13 @@ namespace ARMeilleure.Instructions
{
result = FPInfinity(sign);
- FPProcessException(FPException.DivideByZero, context);
+ FPProcessException(FPException.DivideByZero, context, fpcr);
}
else if (Math.Abs(value) < Math.Pow(2d, -1024))
{
bool overflowToInf;
- switch (context.Fpcr.GetRoundingMode())
+ switch (fpcr.GetRoundingMode())
{
default:
case FPRoundingMode.ToNearest: overflowToInf = true; break;
@@ -2261,10 +2567,10 @@ namespace ARMeilleure.Instructions
result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
- FPProcessException(FPException.Overflow, context);
- FPProcessException(FPException.Inexact, context);
+ FPProcessException(FPException.Overflow, context, fpcr);
+ FPProcessException(FPException.Inexact, context, fpcr);
}
- else if ((context.Fpcr & FPCR.Fz) != 0 && (Math.Abs(value) >= Math.Pow(2d, 1022)))
+ else if ((fpcr & FPCR.Fz) != 0 && (Math.Abs(value) >= Math.Pow(2d, 1022)))
{
result = FPZero(sign);
@@ -2313,16 +2619,49 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static double FPRecipStep(double value1, double value2)
+ {
+ ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.StandardFpcrValue;
+
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
+
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
+
+ if (!done)
+ {
+ bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
+ bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
+
+ double product;
+
+ if ((inf1 && zero2) || (zero1 && inf2))
+ {
+ product = FPZero(false);
+ }
+ else
+ {
+ product = FPMulFpscr(value1, value2, true);
+ }
+
+ result = FPSubFpscr(FPTwo(false), product, true);
+ }
+
+ return result;
+ }
+
public static double FPRecipStepFused(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
value1 = value1.FPNeg();
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -2341,7 +2680,7 @@ namespace ARMeilleure.Instructions
{
result = Math.FusedMultiplyAdd(value1, value2, 2d);
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2356,14 +2695,15 @@ namespace ARMeilleure.Instructions
public static double FPRecpX(double value)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value.FPUnpack(out FPType type, out bool sign, out ulong op, context);
+ value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr);
double result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else
{
@@ -2379,27 +2719,33 @@ namespace ARMeilleure.Instructions
public static double FPRSqrtEstimate(double value)
{
+ return FPRSqrtEstimateFpscr(value, false);
+ }
+
+ public static double FPRSqrtEstimateFpscr(double value, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value.FPUnpack(out FPType type, out bool sign, out ulong op, context);
+ value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr);
double result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else if (type == FPType.Zero)
{
result = FPInfinity(sign);
- FPProcessException(FPException.DivideByZero, context);
+ FPProcessException(FPException.DivideByZero, context, fpcr);
}
else if (sign)
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if (type == FPType.Infinity)
{
@@ -2442,16 +2788,95 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static double FPHalvedSub(double value1, double value2, ExecutionContext context, FPCR fpcr)
+ {
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
+
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
+
+ if (!done)
+ {
+ bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
+ bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
+
+ if (inf1 && inf2 && sign1 == sign2)
+ {
+ result = FPDefaultNaN();
+
+ FPProcessException(FPException.InvalidOp, context, fpcr);
+ }
+ else if ((inf1 && !sign1) || (inf2 && sign2))
+ {
+ result = FPInfinity(false);
+ }
+ else if ((inf1 && sign1) || (inf2 && !sign2))
+ {
+ result = FPInfinity(true);
+ }
+ else if (zero1 && zero2 && sign1 == !sign2)
+ {
+ result = FPZero(sign1);
+ }
+ else
+ {
+ result = (value1 - value2) / 2.0;
+
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ {
+ context.Fpsr |= FPSR.Ufc;
+
+ result = FPZero(result < 0d);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static double FPRSqrtStep(double value1, double value2)
+ {
+ ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.StandardFpcrValue;
+
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
+
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
+
+ if (!done)
+ {
+ bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero;
+ bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero;
+
+ double product;
+
+ if ((inf1 && zero2) || (zero1 && inf2))
+ {
+ product = FPZero(false);
+ }
+ else
+ {
+ product = FPMulFpscr(value1, value2, true);
+ }
+
+ result = FPHalvedSub(FPThree(false), product, context, fpcr);
+ }
+
+ return result;
+ }
+
public static double FPRSqrtStepFused(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
value1 = value1.FPNeg();
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -2470,7 +2895,7 @@ namespace ARMeilleure.Instructions
{
result = Math.FusedMultiplyAdd(value1, value2, 3d) / 2d;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2485,14 +2910,15 @@ namespace ARMeilleure.Instructions
public static double FPSqrt(double value)
{
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = context.Fpcr;
- value = value.FPUnpack(out FPType type, out bool sign, out ulong op, context);
+ value = value.FPUnpack(out FPType type, out bool sign, out ulong op, context, fpcr);
double result;
if (type == FPType.SNaN || type == FPType.QNaN)
{
- result = FPProcessNaN(type, op, context);
+ result = FPProcessNaN(type, op, context, fpcr);
}
else if (type == FPType.Zero)
{
@@ -2506,13 +2932,13 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else
{
result = Math.Sqrt(value);
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2525,12 +2951,18 @@ namespace ARMeilleure.Instructions
public static double FPSub(double value1, double value2)
{
+ return FPSubFpscr(value1, value2, false);
+ }
+
+ public static double FPSubFpscr(double value1, double value2, bool standardFpscr)
+ {
ExecutionContext context = NativeInterface.GetContext();
+ FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
- value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context);
- value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context);
+ value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
+ value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
- double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context);
+ double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr);
if (!done)
{
@@ -2541,7 +2973,7 @@ namespace ARMeilleure.Instructions
{
result = FPDefaultNaN();
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
else if ((inf1 && !sign1) || (inf2 && sign2))
{
@@ -2559,7 +2991,7 @@ namespace ARMeilleure.Instructions
{
result = value1 - value2;
- if ((context.Fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
+ if ((fpcr & FPCR.Fz) != 0 && double.IsSubnormal(result))
{
context.Fpsr |= FPSR.Ufc;
@@ -2573,7 +3005,7 @@ namespace ARMeilleure.Instructions
private static double FPDefaultNaN()
{
- return -double.NaN;
+ return BitConverter.Int64BitsToDouble(0x7ff8000000000000);
}
private static double FPInfinity(bool sign)
@@ -2596,6 +3028,11 @@ namespace ARMeilleure.Instructions
return sign ? -2d : +2d;
}
+ private static double FPThree(bool sign)
+ {
+ return sign ? -3d : +3d;
+ }
+
private static double FPOnePointFive(bool sign)
{
return sign ? -1.5d : +1.5d;
@@ -2616,7 +3053,8 @@ namespace ARMeilleure.Instructions
out FPType type,
out bool sign,
out ulong valueBits,
- ExecutionContext context)
+ ExecutionContext context,
+ FPCR fpcr)
{
valueBits = (ulong)BitConverter.DoubleToInt64Bits(value);
@@ -2624,14 +3062,14 @@ namespace ARMeilleure.Instructions
if ((valueBits & 0x7FF0000000000000ul) == 0ul)
{
- if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul || (context.Fpcr & FPCR.Fz) != 0)
+ if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul || (fpcr & FPCR.Fz) != 0)
{
type = FPType.Zero;
value = FPZero(sign);
if ((valueBits & 0x000FFFFFFFFFFFFFul) != 0ul)
{
- FPProcessException(FPException.InputDenorm, context);
+ FPProcessException(FPException.InputDenorm, context, fpcr);
}
}
else
@@ -2665,25 +3103,26 @@ namespace ARMeilleure.Instructions
ulong op1,
ulong op2,
out bool done,
- ExecutionContext context)
+ ExecutionContext context,
+ FPCR fpcr)
{
done = true;
if (type1 == FPType.SNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.SNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
else if (type1 == FPType.QNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.QNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
done = false;
@@ -2699,33 +3138,34 @@ namespace ARMeilleure.Instructions
ulong op2,
ulong op3,
out bool done,
- ExecutionContext context)
+ ExecutionContext context,
+ FPCR fpcr)
{
done = true;
if (type1 == FPType.SNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.SNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
else if (type3 == FPType.SNaN)
{
- return FPProcessNaN(type3, op3, context);
+ return FPProcessNaN(type3, op3, context, fpcr);
}
else if (type1 == FPType.QNaN)
{
- return FPProcessNaN(type1, op1, context);
+ return FPProcessNaN(type1, op1, context, fpcr);
}
else if (type2 == FPType.QNaN)
{
- return FPProcessNaN(type2, op2, context);
+ return FPProcessNaN(type2, op2, context, fpcr);
}
else if (type3 == FPType.QNaN)
{
- return FPProcessNaN(type3, op3, context);
+ return FPProcessNaN(type3, op3, context, fpcr);
}
done = false;
@@ -2733,16 +3173,16 @@ namespace ARMeilleure.Instructions
return FPZero(false);
}
- private static double FPProcessNaN(FPType type, ulong op, ExecutionContext context)
+ private static double FPProcessNaN(FPType type, ulong op, ExecutionContext context, FPCR fpcr)
{
if (type == FPType.SNaN)
{
op |= 1ul << 51;
- FPProcessException(FPException.InvalidOp, context);
+ FPProcessException(FPException.InvalidOp, context, fpcr);
}
- if ((context.Fpcr & FPCR.Dn) != 0)
+ if ((fpcr & FPCR.Dn) != 0)
{
return FPDefaultNaN();
}
@@ -2750,11 +3190,11 @@ namespace ARMeilleure.Instructions
return BitConverter.Int64BitsToDouble((long)op);
}
- private static void FPProcessException(FPException exc, ExecutionContext context)
+ private static void FPProcessException(FPException exc, ExecutionContext context, FPCR fpcr)
{
int enable = (int)exc + 8;
- if ((context.Fpcr & (FPCR)(1 << enable)) != 0)
+ if ((fpcr & (FPCR)(1 << enable)) != 0)
{
throw new NotImplementedException("Floating-point trap handling.");
}