From d562ba37a0bc603e9719bb36dc9e7e9bf4406687 Mon Sep 17 00:00:00 2001
From: jduncanator <1518948+jduncanator@users.noreply.github.com>
Date: Tue, 3 Dec 2019 15:00:56 +1100
Subject: [PATCH 1/3] Fully reverse swkbd configuration structure and follow-up
to swkbd implementation (#839)
* am: Fully reverse swkbd configuration structure
* Add documentation
* Remove explicit access modifiers
* Fix formatting
* Fix further formatting issues
---
.../SoftwareKeyboard/InitialCursorPosition.cs | 18 +++
.../Applets/SoftwareKeyboard/InputFormMode.cs | 18 +++
.../SoftwareKeyboard/InvalidCharFlags.cs | 56 ++++++++
.../Applets/SoftwareKeyboard/KeyboardMode.cs | 28 ++++
.../Applets/SoftwareKeyboard/PasswordMode.cs | 18 +++
.../SoftwareKeyboardApplet.cs | 56 +++++---
.../SoftwareKeyboardConfig.cs | 129 ++++++++++++++++--
.../SoftwareKeyboard/SoftwareKeyboardState.cs | 5 +-
.../SoftwareKeyboard/SoftwareKeyboardType.cs | 20 ---
9 files changed, 295 insertions(+), 53 deletions(-)
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs
delete mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs
new file mode 100644
index 0000000000..727b6d27b1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the initial position of the cursor displayed in the area.
+ ///
+ enum InitialCursorPosition : uint
+ {
+ ///
+ /// Position the cursor at the beginning of the text
+ ///
+ Start,
+
+ ///
+ /// Position the cursor at the end of the text
+ ///
+ End
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs
new file mode 100644
index 0000000000..c3ce2c1251
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the text entry mode.
+ ///
+ enum InputFormMode : uint
+ {
+ ///
+ /// Displays the text entry area as a single-line field.
+ ///
+ SingleLine,
+
+ ///
+ /// Displays the text entry area as a multi-line field.
+ ///
+ MultiLine
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs
new file mode 100644
index 0000000000..f3fd8ac85d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs
@@ -0,0 +1,56 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies prohibited character sets.
+ ///
+ [Flags]
+ enum InvalidCharFlags : uint
+ {
+ ///
+ /// No characters are prohibited.
+ ///
+ None = 0 << 1,
+
+ ///
+ /// Prohibits spaces.
+ ///
+ Space = 1 << 1,
+
+ ///
+ /// Prohibits the at (@) symbol.
+ ///
+ AtSymbol = 1 << 2,
+
+ ///
+ /// Prohibits the percent (%) symbol.
+ ///
+ Percent = 1 << 3,
+
+ ///
+ /// Prohibits the forward slash (/) symbol.
+ ///
+ ForwardSlash = 1 << 4,
+
+ ///
+ /// Prohibits the backward slash (\) symbol.
+ ///
+ BackSlash = 1 << 5,
+
+ ///
+ /// Prohibits numbers.
+ ///
+ Numbers = 1 << 6,
+
+ ///
+ /// Prohibits characters outside of those allowed in download codes.
+ ///
+ DownloadCode = 1 << 7,
+
+ ///
+ /// Prohibits characters outside of those allowed in Mii Nicknames.
+ ///
+ Username = 1 << 8
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs
new file mode 100644
index 0000000000..e5418a6f34
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs
@@ -0,0 +1,28 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the variant of keyboard displayed on screen.
+ ///
+ enum KeyboardMode : uint
+ {
+ ///
+ /// A full alpha-numeric keyboard.
+ ///
+ Default,
+
+ ///
+ /// Number pad.
+ ///
+ NumbersOnly,
+
+ ///
+ /// QWERTY (and variants) keyboard only.
+ ///
+ LettersOnly,
+
+ ///
+ /// Unknown keyboard variant.
+ ///
+ Unknown
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs
new file mode 100644
index 0000000000..fc9e1ff8e2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the display mode of text in a password field.
+ ///
+ enum PasswordMode : uint
+ {
+ ///
+ /// Display input characters.
+ ///
+ Disabled,
+
+ ///
+ /// Hide input characters.
+ ///
+ Enabled
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
index 22fbe8d0a8..2780446a8b 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
@@ -9,11 +9,11 @@ namespace Ryujinx.HLE.HOS.Applets
{
internal class SoftwareKeyboardApplet : IApplet
{
- private const string DEFAULT_NUMB = "1";
- private const string DEFAULT_TEXT = "Ryujinx";
+ private const string DefaultNumb = "1";
+ private const string DefaultText = "Ryujinx";
- private const int STANDARD_BUFFER_SIZE = 0x7D8;
- private const int INTERACTIVE_BUFFER_SIZE = 0x7D4;
+ private const int StandardBufferSize = 0x7D8;
+ private const int InteractiveBufferSize = 0x7D4;
private SoftwareKeyboardState _state = SoftwareKeyboardState.Uninitialized;
@@ -22,7 +22,8 @@ namespace Ryujinx.HLE.HOS.Applets
private SoftwareKeyboardConfig _keyboardConfig;
- private string _textValue = DEFAULT_TEXT;
+ private string _textValue = DefaultText;
+ private Encoding _encoding = Encoding.Unicode;
public event EventHandler AppletStateChanged;
@@ -42,6 +43,11 @@ namespace Ryujinx.HLE.HOS.Applets
_keyboardConfig = ReadStruct(keyboardConfig);
+ if (_keyboardConfig.UseUtf8)
+ {
+ _encoding = Encoding.UTF8;
+ }
+
_state = SoftwareKeyboardState.Ready;
Execute();
@@ -58,9 +64,9 @@ namespace Ryujinx.HLE.HOS.Applets
{
// If the keyboard type is numbers only, we swap to a default
// text that only contains numbers.
- if (_keyboardConfig.Type == SoftwareKeyboardType.NumbersOnly)
+ if (_keyboardConfig.Mode == KeyboardMode.NumbersOnly)
{
- _textValue = DEFAULT_NUMB;
+ _textValue = DefaultNumb;
}
// If the max string length is 0, we set it to a large default
@@ -70,6 +76,15 @@ namespace Ryujinx.HLE.HOS.Applets
_keyboardConfig.StringLengthMax = 100;
}
+ // If the game requests a string with a minimum length less
+ // than our default text, repeat our default text until we meet
+ // the minimum length requirement.
+ // This should always be done before the text truncation step.
+ while (_textValue.Length < _keyboardConfig.StringLengthMin)
+ {
+ _textValue = String.Join(" ", _textValue, _textValue);
+ }
+
// If our default text is longer than the allowed length,
// we truncate it.
if (_textValue.Length > _keyboardConfig.StringLengthMax)
@@ -77,7 +92,18 @@ namespace Ryujinx.HLE.HOS.Applets
_textValue = _textValue.Substring(0, (int)_keyboardConfig.StringLengthMax);
}
- if (!_keyboardConfig.CheckText)
+ // Does the application want to validate the text itself?
+ if (_keyboardConfig.CheckText)
+ {
+ // The application needs to validate the response, so we
+ // submit it to the interactive output buffer, and poll it
+ // for validation. Once validated, the application will submit
+ // back a validation status, which is handled in OnInteractiveDataPushIn.
+ _state = SoftwareKeyboardState.ValidationPending;
+
+ _interactiveSession.Push(BuildResponse(_textValue, true));
+ }
+ else
{
// If the application doesn't need to validate the response,
// we push the data to the non-interactive output buffer
@@ -88,16 +114,6 @@ namespace Ryujinx.HLE.HOS.Applets
AppletStateChanged?.Invoke(this, null);
}
- else
- {
- // The application needs to validate the response, so we
- // submit it to the interactive output buffer, and poll it
- // for validation. Once validated, the application will submit
- // back a validation status, which is handled in OnInteractiveDataPushIn.
- _state = SoftwareKeyboardState.ValidationPending;
-
- _interactiveSession.Push(BuildResponse(_textValue, true));
- }
}
private void OnInteractiveData(object sender, EventArgs e)
@@ -136,12 +152,12 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse(string text, bool interactive)
{
- int bufferSize = !interactive ? STANDARD_BUFFER_SIZE : INTERACTIVE_BUFFER_SIZE;
+ int bufferSize = interactive ? InteractiveBufferSize : StandardBufferSize;
using (MemoryStream stream = new MemoryStream(new byte[bufferSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
- byte[] output = Encoding.Unicode.GetBytes(text);
+ byte[] output = _encoding.GetBytes(text);
if (!interactive)
{
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs
index 183da774ce..fd462382bc 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs
@@ -2,32 +2,137 @@
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
- // TODO(jduncanator): Define all fields
- [StructLayout(LayoutKind.Explicit)]
+ ///
+ /// A structure that defines the configuration options of the software keyboard.
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct SoftwareKeyboardConfig
{
+ private const int SubmitTextLength = 8;
+ private const int HeaderTextLength = 64;
+ private const int SubtitleTextLength = 128;
+ private const int GuideTextLength = 256;
+
///
/// Type of keyboard.
///
- [FieldOffset(0x0)]
- public SoftwareKeyboardType Type;
+ public KeyboardMode Mode;
///
- /// When non-zero, specifies the max string length. When the input is too long, swkbd will stop accepting more input until text is deleted via the B button (Backspace).
+ /// The string displayed in the Submit button.
///
- [FieldOffset(0x3AC)]
- public uint StringLengthMax;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SubmitTextLength + 1)]
+ public string SubmitText;
///
- /// When non-zero, specifies the max string length. When the input is too long, swkbd will display an icon and disable the ok-button.
+ /// The character displayed in the left button of the numeric keyboard.
+ /// This is ignored when Mode is not set to NumbersOnly.
///
- [FieldOffset(0x3B0)]
- public uint StringLengthMaxExtended;
+ public char LeftOptionalSymbolKey;
///
- /// When set, the application will validate the entered text whilst the swkbd is still on screen.
+ /// The character displayed in the right button of the numeric keyboard.
+ /// This is ignored when Mode is not set to NumbersOnly.
///
- [FieldOffset(0x3D0), MarshalAs(UnmanagedType.I1)]
+ public char RightOptionalSymbolKey;
+
+ ///
+ /// When set, predictive typing is enabled making use of the system dictionary,
+ /// and any custom user dictionary.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool PredictionEnabled;
+
+ ///
+ /// Specifies prohibited characters that cannot be input into the text entry area.
+ ///
+ public InvalidCharFlags InvalidCharFlag;
+
+ ///
+ /// The initial position of the text cursor displayed in the text entry area.
+ ///
+ public InitialCursorPosition InitialCursorPosition;
+
+ ///
+ /// The string displayed in the header area of the keyboard.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HeaderTextLength + 1)]
+ public string HeaderText;
+
+ ///
+ /// The string displayed in the subtitle area of the keyboard.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SubtitleTextLength + 1)]
+ public string SubtitleText;
+
+ ///
+ /// The placeholder string displayed in the text entry area when no text is entered.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = GuideTextLength + 1)]
+ public string GuideText;
+
+ ///
+ /// When non-zero, specifies the maximum allowed length of the string entered into the text entry area.
+ ///
+ public int StringLengthMax;
+
+ ///
+ /// When non-zero, specifies the minimum allowed length of the string entered into the text entry area.
+ ///
+ public int StringLengthMin;
+
+ ///
+ /// When enabled, hides input characters as dots in the text entry area.
+ ///
+ public PasswordMode PasswordMode;
+
+ ///
+ /// Specifies whether the text entry area is displayed as a single-line entry, or a multi-line entry field.
+ ///
+ public InputFormMode InputFormMode;
+
+ ///
+ /// When set, enables or disables the return key. This value is ignored when single-line entry is specified as the InputFormMode.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool UseNewLine;
+
+ ///
+ /// When set, the software keyboard will return a UTF-8 encoded string, rather than UTF-16.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool UseUtf8;
+
+ ///
+ /// When set, the software keyboard will blur the game application rendered behind the keyboard.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool UseBlurBackground;
+
+ ///
+ /// Offset into the work buffer of the initial text when the keyboard is first displayed.
+ ///
+ public int InitialStringOffset;
+
+ ///
+ /// Length of the initial text.
+ ///
+ public int InitialStringLength;
+
+ ///
+ /// Offset into the work buffer of the custom user dictionary.
+ ///
+ public int CustomDictionaryOffset;
+
+ ///
+ /// Number of entries in the custom user dictionary.
+ ///
+ public int CustomDictionaryCount;
+
+ ///
+ /// When set, the text entered will be validated on the application side after the keyboard has been submitted.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
public bool CheckText;
}
}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs
index 42a2831ec9..0f66fc9bac 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs
@@ -1,6 +1,9 @@
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
- internal enum SoftwareKeyboardState
+ ///
+ /// Identifies the software keyboard state.
+ ///
+ enum SoftwareKeyboardState
{
///
/// swkbd is uninitialized.
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs
deleted file mode 100644
index 4875da8098..0000000000
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
-{
- internal enum SoftwareKeyboardType : uint
- {
- ///
- /// Normal keyboard.
- ///
- Default = 0,
-
- ///
- /// Number pad. The buttons at the bottom left/right are only available when they're set in the config by leftButtonText / rightButtonText.
- ///
- NumbersOnly = 1,
-
- ///
- /// QWERTY (and variants) keyboard only.
- ///
- LettersOnly = 2
- }
-}
\ No newline at end of file
From 8c85bdf2edf5ebd7965fbbd08106f2e8d877d73e Mon Sep 17 00:00:00 2001
From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>
Date: Sat, 7 Dec 2019 13:45:32 +0100
Subject: [PATCH 2/3] Implemented fast paths for: (#841)
* cpu-misc_opt
* B = ~b
* ;
---
ARMeilleure/CodeGen/X86/Assembler.cs | 6 +
ARMeilleure/CodeGen/X86/CodeGenerator.cs | 12 +-
ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 4 +
ARMeilleure/CodeGen/X86/PreAllocator.cs | 7 +-
ARMeilleure/CodeGen/X86/X86Instruction.cs | 6 +
ARMeilleure/Common/BitUtils.cs | 62 +++--
ARMeilleure/Decoders/DecoderHelper.cs | 97 +++++--
ARMeilleure/Decoders/OpCodeSimdFmov.cs | 17 +-
ARMeilleure/Decoders/OpCodeSimdImm.cs | 17 +-
ARMeilleure/Instructions/InstEmitAluHelper.cs | 2 +-
.../Instructions/InstEmitSimdArithmetic.cs | 261 ++++++++++++++----
ARMeilleure/Instructions/InstEmitSimdCmp.cs | 22 +-
.../Instructions/InstEmitSimdHelper.cs | 5 +-
ARMeilleure/Instructions/InstEmitSimdMove.cs | 59 ++--
ARMeilleure/Instructions/SoftFloat.cs | 32 ++-
.../IntermediateRepresentation/Intrinsic.cs | 4 +
16 files changed, 450 insertions(+), 163 deletions(-)
diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs
index ee80d892b0..24a122c33b 100644
--- a/ARMeilleure/CodeGen/X86/Assembler.cs
+++ b/ARMeilleure/CodeGen/X86/Assembler.cs
@@ -75,6 +75,10 @@ namespace ARMeilleure.CodeGen.X86
Add(X86Instruction.And, new InstructionInfo(0x00000021, 0x04000083, 0x04000081, BadOp, 0x00000023, InstructionFlags.None));
Add(X86Instruction.Andnpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Andnps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex));
+ Add(X86Instruction.Andpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex | InstructionFlags.Prefix66));
+ Add(X86Instruction.Andps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex));
+ Add(X86Instruction.Blendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3815, InstructionFlags.Prefix66));
+ Add(X86Instruction.Blendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3814, InstructionFlags.Prefix66));
Add(X86Instruction.Bsr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbd, InstructionFlags.None));
Add(X86Instruction.Bswap, new InstructionInfo(0x00000fc8, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RegOnly));
Add(X86Instruction.Call, new InstructionInfo(0x020000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
@@ -245,6 +249,8 @@ namespace ARMeilleure.CodeGen.X86
Add(X86Instruction.Unpckhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex));
Add(X86Instruction.Unpcklpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Unpcklps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex));
+ Add(X86Instruction.Vblendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4b, InstructionFlags.Vex | InstructionFlags.Prefix66));
+ Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs
index 33fc2aee3e..d1224363c0 100644
--- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs
+++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs
@@ -336,7 +336,15 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(!dest.Type.IsInteger());
- if (info.Inst == X86Instruction.Pblendvb && HardwareCapabilities.SupportsVexEncoding)
+ if (info.Inst == X86Instruction.Blendvpd && HardwareCapabilities.SupportsVexEncoding)
+ {
+ context.Assembler.WriteInstruction(X86Instruction.Vblendvpd, dest, src1, src2, src3);
+ }
+ else if (info.Inst == X86Instruction.Blendvps && HardwareCapabilities.SupportsVexEncoding)
+ {
+ context.Assembler.WriteInstruction(X86Instruction.Vblendvps, dest, src1, src2, src3);
+ }
+ else if (info.Inst == X86Instruction.Pblendvb && HardwareCapabilities.SupportsVexEncoding)
{
context.Assembler.WriteInstruction(X86Instruction.Vpblendvb, dest, src1, src2, src3);
}
@@ -1646,7 +1654,7 @@ namespace ARMeilleure.CodeGen.X86
for (int offset = PageSize; offset < size; offset += PageSize)
{
- Operand memOp = new MemoryOperand(OperandType.I32, rsp, null, Multiplier.x1, -offset);;
+ Operand memOp = new MemoryOperand(OperandType.I32, rsp, null, Multiplier.x1, -offset);
context.Assembler.Mov(temp, memOp, OperandType.I32);
}
diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs
index 73fb5fd1b6..e87de03509 100644
--- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs
+++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs
@@ -19,6 +19,10 @@ namespace ARMeilleure.CodeGen.X86
Add(Intrinsic.X86Addss, new IntrinsicInfo(X86Instruction.Addss, IntrinsicType.Binary));
Add(Intrinsic.X86Andnpd, new IntrinsicInfo(X86Instruction.Andnpd, IntrinsicType.Binary));
Add(Intrinsic.X86Andnps, new IntrinsicInfo(X86Instruction.Andnps, IntrinsicType.Binary));
+ Add(Intrinsic.X86Andpd, new IntrinsicInfo(X86Instruction.Andpd, IntrinsicType.Binary));
+ Add(Intrinsic.X86Andps, new IntrinsicInfo(X86Instruction.Andps, IntrinsicType.Binary));
+ Add(Intrinsic.X86Blendvpd, new IntrinsicInfo(X86Instruction.Blendvpd, IntrinsicType.Ternary));
+ Add(Intrinsic.X86Blendvps, new IntrinsicInfo(X86Instruction.Blendvps, IntrinsicType.Ternary));
Add(Intrinsic.X86Cmppd, new IntrinsicInfo(X86Instruction.Cmppd, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpps, new IntrinsicInfo(X86Instruction.Cmpps, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpsd, new IntrinsicInfo(X86Instruction.Cmpsd, IntrinsicType.TernaryImm));
diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/ARMeilleure/CodeGen/X86/PreAllocator.cs
index a149013115..034a87ac25 100644
--- a/ARMeilleure/CodeGen/X86/PreAllocator.cs
+++ b/ARMeilleure/CodeGen/X86/PreAllocator.cs
@@ -298,8 +298,11 @@ namespace ARMeilleure.CodeGen.X86
{
IntrinsicOperation intrinOp = (IntrinsicOperation)operation;
- // PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
- if (intrinOp.Intrinsic == Intrinsic.X86Pblendvb && !HardwareCapabilities.SupportsVexEncoding)
+ // BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
+ if ((intrinOp.Intrinsic == Intrinsic.X86Blendvpd ||
+ intrinOp.Intrinsic == Intrinsic.X86Blendvps ||
+ intrinOp.Intrinsic == Intrinsic.X86Pblendvb) &&
+ !HardwareCapabilities.SupportsVexEncoding)
{
Operand xmm0 = Xmm(X86Register.Xmm0, OperandType.V128);
diff --git a/ARMeilleure/CodeGen/X86/X86Instruction.cs b/ARMeilleure/CodeGen/X86/X86Instruction.cs
index 10ba891aa5..a29e68fb6f 100644
--- a/ARMeilleure/CodeGen/X86/X86Instruction.cs
+++ b/ARMeilleure/CodeGen/X86/X86Instruction.cs
@@ -10,6 +10,10 @@ namespace ARMeilleure.CodeGen.X86
And,
Andnpd,
Andnps,
+ Andpd,
+ Andps,
+ Blendvpd,
+ Blendvps,
Bsr,
Bswap,
Call,
@@ -180,6 +184,8 @@ namespace ARMeilleure.CodeGen.X86
Unpckhps,
Unpcklpd,
Unpcklps,
+ Vblendvpd,
+ Vblendvps,
Vpblendvb,
Xor,
Xorpd,
diff --git a/ARMeilleure/Common/BitUtils.cs b/ARMeilleure/Common/BitUtils.cs
index 55344608cc..7a29dcff7f 100644
--- a/ARMeilleure/Common/BitUtils.cs
+++ b/ARMeilleure/Common/BitUtils.cs
@@ -1,12 +1,12 @@
-using System.Runtime.CompilerServices;
-
namespace ARMeilleure.Common
{
static class BitUtils
{
private const int DeBrujinSequence = 0x77cb531;
- private static int[] DeBrujinLbsLut;
+ private static readonly int[] DeBrujinLbsLut;
+
+ private static readonly sbyte[] HbsNibbleLut;
static BitUtils()
{
@@ -18,19 +18,27 @@ namespace ARMeilleure.Common
DeBrujinLbsLut[lutIndex] = index;
}
+
+ HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int LowestBitSet(int value)
+ public static int CountBits(int value)
{
- if (value == 0)
+ int count = 0;
+
+ while (value != 0)
{
- return -1;
+ value &= ~(value & -value);
+
+ count++;
}
- int lsb = value & -value;
+ return count;
+ }
- return DeBrujinLbsLut[(uint)(DeBrujinSequence * lsb) >> 27];
+ public static long FillWithOnes(int bits)
+ {
+ return bits == 64 ? -1L : (1L << bits) - 1;
}
public static int HighestBitSet(int value)
@@ -51,9 +59,22 @@ namespace ARMeilleure.Common
return -1;
}
- private static readonly sbyte[] HbsNibbleLut = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
+ public static int HighestBitSetNibble(int value)
+ {
+ return HbsNibbleLut[value];
+ }
- public static int HighestBitSetNibble(int value) => HbsNibbleLut[value & 0b1111];
+ public static int LowestBitSet(int value)
+ {
+ if (value == 0)
+ {
+ return -1;
+ }
+
+ int lsb = value & -value;
+
+ return DeBrujinLbsLut[(uint)(DeBrujinSequence * lsb) >> 27];
+ }
public static long Replicate(long bits, int size)
{
@@ -67,25 +88,6 @@ namespace ARMeilleure.Common
return output;
}
- public static int CountBits(int value)
- {
- int count = 0;
-
- while (value != 0)
- {
- value &= ~(value & -value);
-
- count++;
- }
-
- return count;
- }
-
- public static long FillWithOnes(int bits)
- {
- return bits == 64 ? -1L : (1L << bits) - 1;
- }
-
public static int RotateRight(int bits, int shift, int size)
{
return (int)RotateRight((uint)bits, shift, size);
diff --git a/ARMeilleure/Decoders/DecoderHelper.cs b/ARMeilleure/Decoders/DecoderHelper.cs
index 3cbd49123d..bc41c61c6a 100644
--- a/ARMeilleure/Decoders/DecoderHelper.cs
+++ b/ARMeilleure/Decoders/DecoderHelper.cs
@@ -1,10 +1,77 @@
using ARMeilleure.Common;
-using System;
namespace ARMeilleure.Decoders
{
static class DecoderHelper
{
+ static DecoderHelper()
+ {
+ Imm8ToFP32Table = BuildImm8ToFP32Table();
+ Imm8ToFP64Table = BuildImm8ToFP64Table();
+ }
+
+ public static readonly uint[] Imm8ToFP32Table;
+ public static readonly ulong[] Imm8ToFP64Table;
+
+ private static uint[] BuildImm8ToFP32Table()
+ {
+ uint[] tbl = new uint[256];
+
+ for (int idx = 0; idx < 256; idx++)
+ {
+ tbl[idx] = ExpandImm8ToFP32((uint)idx);
+ }
+
+ return tbl;
+ }
+
+ private static ulong[] BuildImm8ToFP64Table()
+ {
+ ulong[] tbl = new ulong[256];
+
+ for (int idx = 0; idx < 256; idx++)
+ {
+ tbl[idx] = ExpandImm8ToFP64((ulong)idx);
+ }
+
+ return tbl;
+ }
+
+ // abcdefgh -> aBbbbbbc defgh000 00000000 00000000 (B = ~b)
+ private static uint ExpandImm8ToFP32(uint imm)
+ {
+ uint MoveBit(uint bits, int from, int to)
+ {
+ return ((bits >> from) & 1U) << to;
+ }
+
+ return MoveBit(imm, 7, 31) | MoveBit(~imm, 6, 30) |
+ MoveBit(imm, 6, 29) | MoveBit( imm, 6, 28) |
+ MoveBit(imm, 6, 27) | MoveBit( imm, 6, 26) |
+ MoveBit(imm, 6, 25) | MoveBit( imm, 5, 24) |
+ MoveBit(imm, 4, 23) | MoveBit( imm, 3, 22) |
+ MoveBit(imm, 2, 21) | MoveBit( imm, 1, 20) |
+ MoveBit(imm, 0, 19);
+ }
+
+ // abcdefgh -> aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000 (B = ~b)
+ private static ulong ExpandImm8ToFP64(ulong imm)
+ {
+ ulong MoveBit(ulong bits, int from, int to)
+ {
+ return ((bits >> from) & 1UL) << to;
+ }
+
+ return MoveBit(imm, 7, 63) | MoveBit(~imm, 6, 62) |
+ MoveBit(imm, 6, 61) | MoveBit( imm, 6, 60) |
+ MoveBit(imm, 6, 59) | MoveBit( imm, 6, 58) |
+ MoveBit(imm, 6, 57) | MoveBit( imm, 6, 56) |
+ MoveBit(imm, 6, 55) | MoveBit( imm, 6, 54) |
+ MoveBit(imm, 5, 53) | MoveBit( imm, 4, 52) |
+ MoveBit(imm, 3, 51) | MoveBit( imm, 2, 50) |
+ MoveBit(imm, 1, 49) | MoveBit( imm, 0, 48);
+ }
+
public struct BitMask
{
public long WMask;
@@ -62,34 +129,6 @@ namespace ARMeilleure.Decoders
};
}
- public static long DecodeImm8Float(long imm, int size)
- {
- int e = 0, f = 0;
-
- switch (size)
- {
- case 0: e = 8; f = 23; break;
- case 1: e = 11; f = 52; break;
-
- default: throw new ArgumentOutOfRangeException(nameof(size));
- }
-
- long value = (imm & 0x3f) << f - 4;
-
- long eBit = (imm >> 6) & 1;
- long sBit = (imm >> 7) & 1;
-
- if (eBit != 0)
- {
- value |= (1L << e - 3) - 1 << f + 2;
- }
-
- value |= (eBit ^ 1) << f + e - 1;
- value |= sBit << f + e;
-
- return value;
- }
-
public static long DecodeImm24_2(int opCode)
{
return ((long)opCode << 40) >> 38;
diff --git a/ARMeilleure/Decoders/OpCodeSimdFmov.cs b/ARMeilleure/Decoders/OpCodeSimdFmov.cs
index 61a3f077d9..f0da03968d 100644
--- a/ARMeilleure/Decoders/OpCodeSimdFmov.cs
+++ b/ARMeilleure/Decoders/OpCodeSimdFmov.cs
@@ -8,16 +8,8 @@ namespace ARMeilleure.Decoders
public OpCodeSimdFmov(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
- int imm5 = (opCode >> 5) & 0x1f;
int type = (opCode >> 22) & 0x3;
- if (imm5 != 0b00000 || type > 1)
- {
- Instruction = InstDescriptor.Undefined;
-
- return;
- }
-
Size = type;
long imm;
@@ -25,7 +17,14 @@ namespace ARMeilleure.Decoders
Rd = (opCode >> 0) & 0x1f;
imm = (opCode >> 13) & 0xff;
- Immediate = DecoderHelper.DecodeImm8Float(imm, type);
+ if (type == 0)
+ {
+ Immediate = (long)DecoderHelper.Imm8ToFP32Table[(int)imm];
+ }
+ else /* if (type == 1) */
+ {
+ Immediate = (long)DecoderHelper.Imm8ToFP64Table[(int)imm];
+ }
}
}
}
\ No newline at end of file
diff --git a/ARMeilleure/Decoders/OpCodeSimdImm.cs b/ARMeilleure/Decoders/OpCodeSimdImm.cs
index ecad906d91..a88e360ee4 100644
--- a/ARMeilleure/Decoders/OpCodeSimdImm.cs
+++ b/ARMeilleure/Decoders/OpCodeSimdImm.cs
@@ -23,19 +23,19 @@ namespace ARMeilleure.Decoders
if (modeHigh == 0b111)
{
- Size = modeLow != 0 ? op : 3;
-
switch (op | (modeLow << 1))
{
case 0:
// 64-bits Immediate.
// Transform abcd efgh into abcd efgh abcd efgh ...
+ Size = 3;
imm = (long)((ulong)imm * 0x0101010101010101);
break;
case 1:
// 64-bits Immediate.
// Transform abcd efgh into aaaa aaaa bbbb bbbb ...
+ Size = 3;
imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4;
imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2;
imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1;
@@ -49,9 +49,16 @@ namespace ARMeilleure.Decoders
break;
case 2:
+ // 2 x 32-bits floating point Immediate.
+ Size = 0;
+ imm = (long)DecoderHelper.Imm8ToFP32Table[(int)imm];
+ imm |= imm << 32;
+ break;
+
case 3:
- // Floating point Immediate.
- imm = DecoderHelper.DecodeImm8Float(imm, Size);
+ // 64-bits floating point Immediate.
+ Size = 1;
+ imm = (long)DecoderHelper.Imm8ToFP64Table[(int)imm];
break;
}
}
@@ -72,7 +79,7 @@ namespace ARMeilleure.Decoders
}
else
{
- // 8 bits without shift.
+ // 8-bits without shift.
Size = 0;
}
diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/ARMeilleure/Instructions/InstEmitAluHelper.cs
index 81d5c9eb34..d032b32e87 100644
--- a/ARMeilleure/Instructions/InstEmitAluHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitAluHelper.cs
@@ -268,7 +268,7 @@ namespace ARMeilleure.Instructions
{
if (setCarry)
{
- SetFlag(context, PState.CFlag, Const(0));;
+ SetFlag(context, PState.CFlag, Const(0));
}
return Const(0);
diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
index c411a6d3fc..1a9e01c8da 100644
--- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
@@ -384,8 +384,7 @@ namespace ARMeilleure.Instructions
}
else
{
- OperandType type = sizeF != 0 ? OperandType.FP64
- : OperandType.FP32;
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
Operand ne0 = context.VectorExtract(type, GetVec(op.Rn), 0);
Operand ne1 = context.VectorExtract(type, GetVec(op.Rn), 1);
@@ -455,6 +454,7 @@ namespace ARMeilleure.Instructions
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ Operand d = GetVec(op.Rd);
Operand a = GetVec(op.Ra);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
@@ -462,18 +462,16 @@ namespace ARMeilleure.Instructions
if (op.Size == 0)
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
+ context.Copy(d, context.VectorZeroUpper96(res));
}
else /* if (op.Size == 1) */
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
+ context.Copy(d, context.VectorZeroUpper64(res));
}
}
else
@@ -517,18 +515,32 @@ namespace ARMeilleure.Instructions
public static void Fmaxnm_S(ArmEmitterContext context)
{
- EmitScalarBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: true, scalar: true);
+ }
+ else
+ {
+ EmitScalarBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
+ });
+ }
}
public static void Fmaxnm_V(ArmEmitterContext context)
{
- EmitVectorBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: true, scalar: false);
+ }
+ else
+ {
+ EmitVectorBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
+ });
+ }
}
public static void Fmaxp_V(ArmEmitterContext context)
@@ -578,18 +590,32 @@ namespace ARMeilleure.Instructions
public static void Fminnm_S(ArmEmitterContext context)
{
- EmitScalarBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: false, scalar: true);
+ }
+ else
+ {
+ EmitScalarBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
+ });
+ }
}
public static void Fminnm_V(ArmEmitterContext context)
{
- EmitVectorBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: false, scalar: false);
+ }
+ else
+ {
+ EmitVectorBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
+ });
+ }
}
public static void Fminp_V(ArmEmitterContext context)
@@ -813,6 +839,7 @@ namespace ARMeilleure.Instructions
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ Operand d = GetVec(op.Rd);
Operand a = GetVec(op.Ra);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
@@ -820,18 +847,16 @@ namespace ARMeilleure.Instructions
if (op.Size == 0)
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
+ context.Copy(d, context.VectorZeroUpper96(res));
}
else /* if (op.Size == 1) */
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
+ context.Copy(d, context.VectorZeroUpper64(res));
}
}
else
@@ -1035,36 +1060,88 @@ namespace ARMeilleure.Instructions
public static void Fnmadd_S(ArmEmitterContext context) // Fused.
{
- OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ if (Optimizations.FastFP && Optimizations.UseSse2)
+ {
+ OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
- int sizeF = op.Size & 1;
+ Operand d = GetVec(op.Rd);
+ Operand a = GetVec(op.Ra);
+ Operand n = GetVec(op.Rn);
+ Operand m = GetVec(op.Rm);
- OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+ if (op.Size == 0)
+ {
+ Operand mask = X86GetScalar(context, -0f);
- Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
- Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
- Operand ae = context.VectorExtract(type, GetVec(op.Ra), 0);
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
- Operand res = context.Subtract(context.Multiply(context.Negate(ne), me), ae);
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
- context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
+ context.Copy(d, context.VectorZeroUpper96(res));
+ }
+ else /* if (op.Size == 1) */
+ {
+ Operand mask = X86GetScalar(context, -0d);
+
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
+
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
+
+ context.Copy(d, context.VectorZeroUpper64(res));
+ }
+ }
+ else
+ {
+ EmitScalarTernaryRaOpF(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPNegMulAdd, SoftFloat64.FPNegMulAdd, op1, op2, op3);
+ });
+ }
}
public static void Fnmsub_S(ArmEmitterContext context) // Fused.
{
- OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ if (Optimizations.FastFP && Optimizations.UseSse2)
+ {
+ OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
- int sizeF = op.Size & 1;
+ Operand d = GetVec(op.Rd);
+ Operand a = GetVec(op.Ra);
+ Operand n = GetVec(op.Rn);
+ Operand m = GetVec(op.Rm);
- OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+ if (op.Size == 0)
+ {
+ Operand mask = X86GetScalar(context, -0f);
- Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
- Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
- Operand ae = context.VectorExtract(type, GetVec(op.Ra), 0);
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
- Operand res = context.Subtract(context.Multiply(ne, me), ae);
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
- context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
+ context.Copy(d, context.VectorZeroUpper96(res));
+ }
+ else /* if (op.Size == 1) */
+ {
+ Operand mask = X86GetScalar(context, -0d);
+
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
+
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
+
+ context.Copy(d, context.VectorZeroUpper64(res));
+ }
+ }
+ else
+ {
+ EmitScalarTernaryRaOpF(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPNegMulSub, SoftFloat64.FPNegMulSub, op1, op2, op3);
+ });
+ }
}
public static void Fnmul_S(ArmEmitterContext context)
@@ -2067,9 +2144,7 @@ namespace ARMeilleure.Instructions
m = context.AddIntrinsic(Intrinsic.X86Psrldq, m, Const(8));
}
- Intrinsic movInst = op.Size == 0
- ? Intrinsic.X86Pmovsxbw
- : Intrinsic.X86Pmovsxwd;
+ Intrinsic movInst = op.Size == 0 ? Intrinsic.X86Pmovsxbw : Intrinsic.X86Pmovsxwd;
n = context.AddIntrinsic(movInst, n);
m = context.AddIntrinsic(movInst, m);
@@ -2694,9 +2769,7 @@ namespace ARMeilleure.Instructions
m = context.AddIntrinsic(Intrinsic.X86Psrldq, m, Const(8));
}
- Intrinsic movInst = op.Size == 0
- ? Intrinsic.X86Pmovzxbw
- : Intrinsic.X86Pmovzxwd;
+ Intrinsic movInst = op.Size == 0 ? Intrinsic.X86Pmovzxbw : Intrinsic.X86Pmovzxwd;
n = context.AddIntrinsic(movInst, n);
m = context.AddIntrinsic(movInst, m);
@@ -3011,6 +3084,98 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res);
}
+ private static Operand EmitSse2VectorIsQNaNOpF(ArmEmitterContext context, Operand opF)
+ {
+ IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
+
+ if ((op.Size & 1) == 0)
+ {
+ const int QBit = 22;
+
+ Operand qMask = X86GetAllElements(context, 1 << QBit);
+
+ Operand mask1 = context.AddIntrinsic(Intrinsic.X86Cmpps, opF, opF, Const((int)CmpCondition.UnorderedQ));
+
+ Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask);
+ mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, mask2, qMask, Const((int)CmpCondition.Equal));
+
+ return context.AddIntrinsic(Intrinsic.X86Andps, mask1, mask2);
+ }
+ else /* if ((op.Size & 1) == 1) */
+ {
+ const int QBit = 51;
+
+ Operand qMask = X86GetAllElements(context, 1L << QBit);
+
+ Operand mask1 = context.AddIntrinsic(Intrinsic.X86Cmppd, opF, opF, Const((int)CmpCondition.UnorderedQ));
+
+ Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask);
+ mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, mask2, qMask, Const((int)CmpCondition.Equal));
+
+ return context.AddIntrinsic(Intrinsic.X86Andpd, mask1, mask2);
+ }
+ }
+
+ private static void EmitSse41MaxMinNumOpF(ArmEmitterContext context, bool isMaxNum, bool scalar)
+ {
+ OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+
+ Operand d = GetVec(op.Rd);
+ Operand n = GetVec(op.Rn);
+ Operand m = GetVec(op.Rm);
+
+ Operand nQNaNMask = EmitSse2VectorIsQNaNOpF(context, n);
+ Operand mQNaNMask = EmitSse2VectorIsQNaNOpF(context, m);
+
+ Operand nNum = context.Copy(n);
+ Operand mNum = context.Copy(m);
+
+ int sizeF = op.Size & 1;
+
+ if (sizeF == 0)
+ {
+ Operand negInfMask = X86GetAllElements(context, isMaxNum ? float.NegativeInfinity : float.PositiveInfinity);
+
+ Operand nMask = context.AddIntrinsic(Intrinsic.X86Andnps, mQNaNMask, nQNaNMask);
+ Operand mMask = context.AddIntrinsic(Intrinsic.X86Andnps, nQNaNMask, mQNaNMask);
+
+ nNum = context.AddIntrinsic(Intrinsic.X86Blendvps, nNum, negInfMask, nMask);
+ mNum = context.AddIntrinsic(Intrinsic.X86Blendvps, mNum, negInfMask, mMask);
+
+ Operand res = context.AddIntrinsic(isMaxNum ? Intrinsic.X86Maxps : Intrinsic.X86Minps, nNum, mNum);
+
+ if (scalar)
+ {
+ res = context.VectorZeroUpper96(res);
+ }
+ else if (op.RegisterSize == RegisterSize.Simd64)
+ {
+ res = context.VectorZeroUpper64(res);
+ }
+
+ context.Copy(d, res);
+ }
+ else /* if (sizeF == 1) */
+ {
+ Operand negInfMask = X86GetAllElements(context, isMaxNum ? double.NegativeInfinity : double.PositiveInfinity);
+
+ Operand nMask = context.AddIntrinsic(Intrinsic.X86Andnpd, mQNaNMask, nQNaNMask);
+ Operand mMask = context.AddIntrinsic(Intrinsic.X86Andnpd, nQNaNMask, mQNaNMask);
+
+ nNum = context.AddIntrinsic(Intrinsic.X86Blendvpd, nNum, negInfMask, nMask);
+ mNum = context.AddIntrinsic(Intrinsic.X86Blendvpd, mNum, negInfMask, mMask);
+
+ Operand res = context.AddIntrinsic(isMaxNum ? Intrinsic.X86Maxpd : Intrinsic.X86Minpd, nNum, mNum);
+
+ if (scalar)
+ {
+ res = context.VectorZeroUpper64(res);
+ }
+
+ context.Copy(d, res);
+ }
+ }
+
private enum AddSub
{
None,
diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/ARMeilleure/Instructions/InstEmitSimdCmp.cs
index ac1bffcb2e..e70f56a0a3 100644
--- a/ARMeilleure/Instructions/InstEmitSimdCmp.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdCmp.cs
@@ -300,7 +300,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.Equal, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: true);
}
else
{
@@ -312,7 +312,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.Equal, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: false);
}
else
{
@@ -324,7 +324,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
}
else
{
@@ -336,7 +336,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
}
else
{
@@ -348,7 +348,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: true);
}
else
{
@@ -360,7 +360,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: false);
}
else
{
@@ -372,7 +372,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true);
}
else
{
@@ -384,7 +384,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false);
}
else
{
@@ -396,7 +396,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: true);
}
else
{
@@ -408,7 +408,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: false);
}
else
{
@@ -673,7 +673,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res);
}
- private static void EmitCmpSseOrSse2OpF(ArmEmitterContext context, CmpCondition cond, bool scalar)
+ private static void EmitSse2CmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs
index f0880079e4..28d075dd3b 100644
--- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs
@@ -907,7 +907,7 @@ namespace ARMeilleure.Instructions
Operand res = context.VectorZero();
- Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);;
+ Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);
int elems = 8 >> op.Size;
@@ -939,7 +939,7 @@ namespace ARMeilleure.Instructions
Operand res = context.VectorZero();
- Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);;
+ Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);
int elems = 8 >> op.Size;
@@ -1114,6 +1114,7 @@ namespace ARMeilleure.Instructions
Equal = 0, // Ordered, non-signaling.
LessThan = 1, // Ordered, signaling.
LessThanOrEqual = 2, // Ordered, signaling.
+ UnorderedQ = 3, // Non-signaling.
NotLessThan = 5, // Unordered, signaling.
NotLessThanOrEqual = 6, // Unordered, signaling.
OrderedQ = 7, // Non-signaling.
diff --git a/ARMeilleure/Instructions/InstEmitSimdMove.cs b/ARMeilleure/Instructions/InstEmitSimdMove.cs
index 9d2aeb3bde..789c8c8712 100644
--- a/ARMeilleure/Instructions/InstEmitSimdMove.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdMove.cs
@@ -177,7 +177,7 @@ namespace ARMeilleure.Instructions
if (op.RegisterSize == RegisterSize.Simd64)
{
- nShifted = context.AddIntrinsic(Intrinsic.X86Movlhps, nShifted, context.VectorZero());
+ nShifted = context.VectorZeroUpper64(nShifted);
}
nShifted = context.AddIntrinsic(Intrinsic.X86Psrldq, nShifted, Const(op.Imm4));
@@ -188,7 +188,7 @@ namespace ARMeilleure.Instructions
if (op.RegisterSize == RegisterSize.Simd64)
{
- mShifted = context.AddIntrinsic(Intrinsic.X86Movlhps, mShifted, context.VectorZero());
+ mShifted = context.VectorZeroUpper64(mShifted);
}
Operand res = context.AddIntrinsic(Intrinsic.X86Por, nShifted, mShifted);
@@ -277,9 +277,10 @@ namespace ARMeilleure.Instructions
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
+ Operand d = GetVec(op.Rd);
Operand n = GetIntOrZR(context, op.Rn);
- context.Copy(GetVec(op.Rd), EmitVectorInsert(context, GetVec(op.Rd), n, 1, 3));
+ context.Copy(d, EmitVectorInsert(context, d, n, 1, 3));
}
public static void Fmov_S(ArmEmitterContext context)
@@ -311,18 +312,32 @@ namespace ARMeilleure.Instructions
{
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
- Operand e = Const(op.Immediate);
-
- Operand res = context.VectorZero();
-
- int elems = op.RegisterSize == RegisterSize.Simd128 ? 4 : 2;
-
- for (int index = 0; index < (elems >> op.Size); index++)
+ if (Optimizations.UseSse2)
{
- res = EmitVectorInsert(context, res, e, index, op.Size + 2);
+ if (op.RegisterSize == RegisterSize.Simd128)
+ {
+ context.Copy(GetVec(op.Rd), X86GetAllElements(context, op.Immediate));
+ }
+ else
+ {
+ context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate));
+ }
}
+ else
+ {
+ Operand e = Const(op.Immediate);
- context.Copy(GetVec(op.Rd), res);
+ Operand res = context.VectorZero();
+
+ int elems = op.RegisterSize == RegisterSize.Simd128 ? 2 : 1;
+
+ for (int index = 0; index < elems; index++)
+ {
+ res = EmitVectorInsert(context, res, e, index, 3);
+ }
+
+ context.Copy(GetVec(op.Rd), res);
+ }
}
public static void Ins_Gp(ArmEmitterContext context)
@@ -349,7 +364,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse2)
{
- EmitMoviMvni(context, not: false);
+ EmitSse2MoviMvni(context, not: false);
}
else
{
@@ -361,7 +376,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse2)
{
- EmitMoviMvni(context, not: true);
+ EmitSse2MoviMvni(context, not: true);
}
else
{
@@ -430,13 +445,11 @@ namespace ARMeilleure.Instructions
{
Operand d = GetVec(op.Rd);
- Operand res = context.AddIntrinsic(Intrinsic.X86Movlhps, d, context.VectorZero());
-
- Operand n = GetVec(op.Rn);
+ Operand res = context.VectorZeroUpper64(d);
Operand mask = X86GetAllElements(context, _masksE0_TrnUzpXtn[op.Size]);
- Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
+ Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, GetVec(op.Rn), mask);
Intrinsic movInst = op.RegisterSize == RegisterSize.Simd128
? Intrinsic.X86Movlhps
@@ -444,7 +457,7 @@ namespace ARMeilleure.Instructions
res = context.AddIntrinsic(movInst, res, res2);
- context.Copy(GetVec(op.Rd), res);
+ context.Copy(d, res);
}
else
{
@@ -452,7 +465,9 @@ namespace ARMeilleure.Instructions
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
- Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
+ Operand d = GetVec(op.Rd);
+
+ Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
for (int index = 0; index < elems; index++)
{
@@ -461,7 +476,7 @@ namespace ARMeilleure.Instructions
res = EmitVectorInsert(context, res, ne, part + index, op.Size);
}
- context.Copy(GetVec(op.Rd), res);
+ context.Copy(d, res);
}
}
@@ -475,7 +490,7 @@ namespace ARMeilleure.Instructions
EmitVectorZip(context, part: 1);
}
- private static void EmitMoviMvni(ArmEmitterContext context, bool not)
+ private static void EmitSse2MoviMvni(ArmEmitterContext context, bool not)
{
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
diff --git a/ARMeilleure/Instructions/SoftFloat.cs b/ARMeilleure/Instructions/SoftFloat.cs
index af22c85d21..256bc5b975 100644
--- a/ARMeilleure/Instructions/SoftFloat.cs
+++ b/ARMeilleure/Instructions/SoftFloat.cs
@@ -1089,8 +1089,6 @@ namespace ARMeilleure.Instructions
public static float FPMulSub(float valueA, float value1, float value2)
{
- ExecutionContext context = NativeInterface.GetContext();
-
value1 = value1.FPNeg();
return FPMulAdd(valueA, value1, value2);
@@ -1138,6 +1136,21 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static float FPNegMulAdd(float valueA, float value1, float value2)
+ {
+ valueA = valueA.FPNeg();
+ value1 = value1.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
+ public static float FPNegMulSub(float valueA, float value1, float value2)
+ {
+ valueA = valueA.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
public static float FPRecipEstimate(float value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -2196,6 +2209,21 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static double FPNegMulAdd(double valueA, double value1, double value2)
+ {
+ valueA = valueA.FPNeg();
+ value1 = value1.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
+ public static double FPNegMulSub(double valueA, double value1, double value2)
+ {
+ valueA = valueA.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
public static double FPRecipEstimate(double value)
{
ExecutionContext context = NativeInterface.GetContext();
diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
index e2d3c6dbaf..57c8914d9c 100644
--- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
+++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
@@ -8,6 +8,10 @@ namespace ARMeilleure.IntermediateRepresentation
X86Addss,
X86Andnpd,
X86Andnps,
+ X86Andpd,
+ X86Andps,
+ X86Blendvpd,
+ X86Blendvps,
X86Cmppd,
X86Cmpps,
X86Cmpsd,
From d925de2d0e7f3a065020c63dc1274644284e4006 Mon Sep 17 00:00:00 2001
From: Thog
Date: Sun, 8 Dec 2019 14:35:25 +0100
Subject: [PATCH 3/3] Fix ILogger type and size decoding (#842)
* Fix ILogger type and size decoding
The type and size are custom encoded integer not byte.
This fix issues on games that send messages longer than 127 characters.
* Address gdk's comments
---
.../HOS/Services/Lm/LogService/ILogger.cs | 24 +++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
index 357a13324a..a269312ebe 100644
--- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
+++ b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
@@ -8,6 +8,26 @@ namespace Ryujinx.HLE.HOS.Services.Lm.LogService
{
public ILogger() { }
+ private static int ReadEncodedInt(BinaryReader reader)
+ {
+ int result = 0;
+ int position = 0;
+
+ byte encoded;
+
+ do
+ {
+ encoded = reader.ReadByte();
+
+ result += (encoded & 0x7F) << (7 * position);
+
+ position++;
+
+ } while ((encoded & 0x80) != 0);
+
+ return result;
+ }
+
[Command(0)]
// Log(buffer)
public ResultCode Log(ServiceCtx context)
@@ -34,8 +54,8 @@ namespace Ryujinx.HLE.HOS.Services.Lm.LogService
while (ms.Position < ms.Length)
{
- byte type = reader.ReadByte();
- byte size = reader.ReadByte();
+ int type = ReadEncodedInt(reader);
+ int size = ReadEncodedInt(reader);
LmLogField field = (LmLogField)type;