diff --git a/Source/Data/FieldSize.cs b/Source/Data/FieldSize.cs
index 58928235..bf869b54 100644
--- a/Source/Data/FieldSize.cs
+++ b/Source/Data/FieldSize.cs
@@ -288,5 +288,29 @@ public static bool IsBigEndian(this FieldSize size)
return false;
}
}
+
+ ///
+ /// Gets the big-endian size corresponding to the provided little-endian size or
+ /// the little-endian size corresponding to the provided big-endian size.
+ ///
+ public static FieldSize ToggleEndianness(this FieldSize size)
+ {
+ switch (size)
+ {
+ case FieldSize.Word: return FieldSize.BigEndianWord;
+ case FieldSize.TByte: return FieldSize.BigEndianTByte;
+ case FieldSize.DWord: return FieldSize.BigEndianDWord;
+ case FieldSize.Float: return FieldSize.BigEndianFloat;
+ case FieldSize.Double32: return FieldSize.BigEndianDouble32;
+ case FieldSize.BigEndianWord: return FieldSize.Word;
+ case FieldSize.BigEndianTByte: return FieldSize.TByte;
+ case FieldSize.BigEndianDWord: return FieldSize.DWord;
+ case FieldSize.BigEndianFloat: return FieldSize.Float;
+ case FieldSize.BigEndianDouble32: return FieldSize.Double32;
+ case FieldSize.MBF32: return FieldSize.LittleEndianMBF32;
+ case FieldSize.LittleEndianMBF32: return FieldSize.MBF32;
+ default: return size;
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Source/Parser/Expressions/ComparisonExpression.cs b/Source/Parser/Expressions/ComparisonExpression.cs
index 1ed59f33..01c20366 100644
--- a/Source/Parser/Expressions/ComparisonExpression.cs
+++ b/Source/Parser/Expressions/ComparisonExpression.cs
@@ -166,7 +166,7 @@ public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase
var comparisonNormalize = left as IComparisonNormalizeExpression;
if (comparisonNormalize != null)
{
- var normalized = comparisonNormalize.NormalizeComparison(right, Operation, true);
+ var normalized = comparisonNormalize.NormalizeComparison(right, Operation, false);
if (normalized != null && normalized is not ComparisonExpression)
{
result = normalized;
@@ -257,11 +257,11 @@ public static ExpressionBase NormalizeFloatComparisonForInteger(ExpressionBase l
{
case ComparisonOperation.Equal:
// integer a == 4.2 can never be true
- return new ErrorExpression("Result can never be true using integer math");
+ return new ErrorExpression("Result can never be true using integer math") { Location = left.Location.Union(right.Location) };
case ComparisonOperation.NotEqual:
// integer a != 4.2 is always true
- return new ErrorExpression("Result is always true using integer math");
+ return new ErrorExpression("Result is always true using integer math") { Location = left.Location.Union(right.Location) };
case ComparisonOperation.LessThan:
// integer a < 4.2 becomes integer a <= 4
diff --git a/Source/Parser/Expressions/Trigger/ErrorRequirementExpression.cs b/Source/Parser/Expressions/Trigger/ErrorRequirementExpression.cs
new file mode 100644
index 00000000..8f6a5379
--- /dev/null
+++ b/Source/Parser/Expressions/Trigger/ErrorRequirementExpression.cs
@@ -0,0 +1,31 @@
+using RATools.Parser.Internal;
+using System.Text;
+
+namespace RATools.Parser.Expressions.Trigger
+{
+ internal class ErrorRequirementExpression : RequirementExpressionBase
+ {
+ public ErrorRequirementExpression(ErrorExpression error)
+ {
+ _error = error;
+ }
+
+ private readonly ErrorExpression _error;
+
+ public override ErrorExpression BuildTrigger(TriggerBuilderContext context)
+ {
+ return _error;
+ }
+
+ internal override void AppendString(StringBuilder builder)
+ {
+ _error.AppendString(builder);
+ }
+
+ protected override bool Equals(ExpressionBase obj)
+ {
+ var that = obj as ErrorRequirementExpression;
+ return (that != null && that._error == _error);
+ }
+ }
+}
diff --git a/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs b/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs
index a88546e4..529caa5c 100644
--- a/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs
+++ b/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs
@@ -323,6 +323,7 @@ private ExpressionBase ApplyAddition(ExpressionBase right, RequirementType combi
if (_memoryAccessors == null)
_memoryAccessors = new List();
_memoryAccessors.Add(modifiedMemoryAccessor);
+
break;
default:
@@ -1543,8 +1544,16 @@ public ErrorExpression BuildTrigger(TriggerBuilderContext context, ExpressionBas
}
if (_memoryAccessors != null)
+ {
memoryAccessors.AddRange(_memoryAccessors);
+ if (_memoryAccessors.Any(a => a.ModifyingOperator == RequirementOperator.Multiply) &&
+ _memoryAccessors.Any(a => a.ModifyingOperator == RequirementOperator.None))
+ {
+ CombineNeighboringMemoryReads(memoryAccessors);
+ }
+ }
+
if (appendConstantAccessor)
memoryAccessors.Add(constantAccessor);
@@ -1658,6 +1667,229 @@ public ErrorExpression BuildTrigger(TriggerBuilderContext context, ExpressionBas
return null;
}
+ internal MemoryValueExpression NormalizeMemoryReads()
+ {
+ if (_memoryAccessors.Any(a => a.ModifyingOperator == RequirementOperator.Multiply) &&
+ _memoryAccessors.Any(a => a.ModifyingOperator == RequirementOperator.None))
+ {
+ var memoryAccessors = new List(_memoryAccessors);
+ CombineNeighboringMemoryReads(memoryAccessors);
+ if (memoryAccessors.Count != _memoryAccessors.Count)
+ {
+ var newMemoryValue = Clone();
+ newMemoryValue._memoryAccessors = memoryAccessors;
+ return newMemoryValue;
+ }
+ }
+
+ return this;
+ }
+
+ private static void CombineNeighboringMemoryReads(List memoryAccessors)
+ {
+ int initialCount;
+ do
+ {
+ initialCount = memoryAccessors.Count;
+
+ for (int i = memoryAccessors.Count - 1; i >= 0; --i)
+ {
+ var modifiedMemoryAccessor = memoryAccessors[i];
+ if (modifiedMemoryAccessor.ModifyingOperator != RequirementOperator.Multiply)
+ continue;
+
+ if (modifiedMemoryAccessor.Modifier.Type != FieldType.Value)
+ continue;
+ if (!modifiedMemoryAccessor.MemoryAccessor.Field.IsMemoryReference)
+ continue;
+ if (modifiedMemoryAccessor.MemoryAccessor.Field.IsFloat)
+ continue;
+ if (modifiedMemoryAccessor.MemoryAccessor.Field.Size.GetByteSize() == 4)
+ continue;
+
+ switch (modifiedMemoryAccessor.Modifier.Value)
+ {
+ case 0x10:
+ MergeLowNibble(memoryAccessors, i, modifiedMemoryAccessor);
+ break;
+
+ case 0x100:
+ MergeLowByte(memoryAccessors, i, modifiedMemoryAccessor, false);
+ break;
+
+ case 0x10000:
+ MergeLowWord(memoryAccessors, i, modifiedMemoryAccessor, false);
+ break;
+
+ case 0x1000000:
+ MergeLowTByte(memoryAccessors, i, modifiedMemoryAccessor, false);
+ break;
+
+ case 100:
+ MergeLowByte(memoryAccessors, i, modifiedMemoryAccessor, true);
+ break;
+
+ case 10000:
+ MergeLowWord(memoryAccessors, i, modifiedMemoryAccessor, true);
+ break;
+
+ case 1000000:
+ MergeLowTByte(memoryAccessors, i, modifiedMemoryAccessor, true);
+ break;
+ }
+ }
+ } while (memoryAccessors.Count != initialCount && memoryAccessors.Count > 1);
+ }
+
+ private static void MergeLowNibble(List memoryAccessors, int modifiedIndex, ModifiedMemoryAccessorExpression modifiedMemoryAccessor)
+ {
+ if (modifiedMemoryAccessor.MemoryAccessor.Field.Size == FieldSize.HighNibble)
+ {
+ var lowNibbleIndex = FindMemoryRead(memoryAccessors, FieldSize.LowNibble,
+ modifiedMemoryAccessor, modifiedMemoryAccessor.MemoryAccessor.Field.Value);
+ if (lowNibbleIndex != -1)
+ MergeMemoryReads(memoryAccessors, modifiedIndex, lowNibbleIndex, FieldSize.Byte);
+ }
+ }
+
+ private static void MergeLowByte(List memoryAccessors, int modifiedIndex, ModifiedMemoryAccessorExpression modifiedMemoryAccessor, bool matchBcd)
+ {
+ bool isBcd = modifiedMemoryAccessor.MemoryAccessor is BinaryCodedDecimalExpression;
+ if (isBcd != matchBcd)
+ return;
+
+ FieldSize targetSize;
+ var fieldSize = modifiedMemoryAccessor.MemoryAccessor.Field.Size;
+ switch (fieldSize)
+ {
+ case FieldSize.Byte:
+ targetSize = FieldSize.Word;
+ break;
+ case FieldSize.Word:
+ targetSize = FieldSize.TByte;
+ break;
+ case FieldSize.TByte:
+ targetSize = FieldSize.DWord;
+ break;
+ case FieldSize.BigEndianWord:
+ targetSize = FieldSize.BigEndianTByte;
+ break;
+ case FieldSize.BigEndianTByte:
+ targetSize = FieldSize.BigEndianDWord;
+ break;
+ default:
+ return;
+ }
+
+ MergeAccessors(memoryAccessors, modifiedIndex, modifiedMemoryAccessor,
+ FieldSize.Byte, fieldSize, targetSize, isBcd);
+ }
+
+ private static void MergeLowWord(List memoryAccessors, int modifiedIndex, ModifiedMemoryAccessorExpression modifiedMemoryAccessor, bool matchBcd)
+ {
+ bool isBcd = modifiedMemoryAccessor.MemoryAccessor is BinaryCodedDecimalExpression;
+ if (isBcd != matchBcd)
+ return;
+
+ FieldSize targetSize;
+ var fieldSize = modifiedMemoryAccessor.MemoryAccessor.Field.Size;
+ switch (fieldSize)
+ {
+ case FieldSize.Byte:
+ targetSize = FieldSize.TByte;
+ break;
+ case FieldSize.Word:
+ targetSize = FieldSize.DWord;
+ break;
+ case FieldSize.BigEndianWord:
+ targetSize = FieldSize.BigEndianDWord;
+ break;
+ default:
+ return;
+ }
+
+ MergeAccessors(memoryAccessors, modifiedIndex, modifiedMemoryAccessor,
+ FieldSize.Word, fieldSize, targetSize, isBcd);
+ }
+
+ private static void MergeLowTByte(List memoryAccessors, int modifiedIndex, ModifiedMemoryAccessorExpression modifiedMemoryAccessor, bool matchBcd)
+ {
+ bool isBcd = modifiedMemoryAccessor.MemoryAccessor is BinaryCodedDecimalExpression;
+ if (isBcd != matchBcd)
+ return;
+
+ if (modifiedMemoryAccessor.MemoryAccessor.Field.Size == FieldSize.Byte)
+ {
+ MergeAccessors(memoryAccessors, modifiedIndex, modifiedMemoryAccessor,
+ FieldSize.TByte, FieldSize.Byte, FieldSize.DWord, isBcd);
+ }
+ }
+
+ private static void MergeAccessors(List memoryAccessors,
+ int modifiedIndex, ModifiedMemoryAccessorExpression modifiedMemoryAccessor,
+ FieldSize mergeSize, FieldSize fieldSize, FieldSize targetSize, bool isBcd)
+ {
+ var address = modifiedMemoryAccessor.MemoryAccessor.Field.Value;
+
+ if (targetSize.IsBigEndian())
+ {
+ var lowIndex = FindMemoryRead(memoryAccessors, mergeSize.ToggleEndianness(),
+ modifiedMemoryAccessor,
+ address + fieldSize.GetByteSize(), isBcd);
+ if (lowIndex != -1)
+ MergeMemoryReads(memoryAccessors, lowIndex, modifiedIndex, targetSize, isBcd);
+ }
+ else
+ {
+ var lowIndex = FindMemoryRead(memoryAccessors, mergeSize,
+ modifiedMemoryAccessor, address - mergeSize.GetByteSize(), isBcd);
+ if (lowIndex != -1)
+ {
+ MergeMemoryReads(memoryAccessors, modifiedIndex, lowIndex, targetSize, isBcd);
+ }
+ else if (fieldSize == FieldSize.Byte)
+ {
+ lowIndex = FindMemoryRead(memoryAccessors, mergeSize.ToggleEndianness(),
+ modifiedMemoryAccessor, address + 1, isBcd);
+ if (lowIndex != -1)
+ MergeMemoryReads(memoryAccessors, lowIndex, modifiedIndex, targetSize.ToggleEndianness(), isBcd);
+ }
+ }
+ }
+
+ private static void MergeMemoryReads(List memoryAccessors, int fromIndex, int toIndex, FieldSize newSize, bool makeBcd = false)
+ {
+ var newAccessor = memoryAccessors[toIndex].MemoryAccessor.ChangeFieldSize(newSize);
+ if (makeBcd)
+ newAccessor = new BinaryCodedDecimalExpression(newAccessor);
+
+ var newModifiedAccessor = new ModifiedMemoryAccessorExpression(newAccessor) { CombiningOperator = memoryAccessors[toIndex].CombiningOperator };
+ newModifiedAccessor.Location = newAccessor.Location.Union(memoryAccessors[fromIndex].Location);
+ memoryAccessors[toIndex] = newModifiedAccessor;
+ memoryAccessors.RemoveAt(fromIndex);
+ }
+
+ private static int FindMemoryRead(List memoryAccessors, FieldSize size, ModifiedMemoryAccessorExpression matchMemoryAccessor, uint address, bool matchBcd = false)
+ {
+ for (int i = 0; i < memoryAccessors.Count; ++i)
+ {
+ var modifiedMemoryAccessor = memoryAccessors[i];
+ if (modifiedMemoryAccessor.ModifyingOperator == RequirementOperator.None &&
+ modifiedMemoryAccessor.MemoryAccessor.Field.Value == address &&
+ modifiedMemoryAccessor.MemoryAccessor.Field.Size == size &&
+ modifiedMemoryAccessor.CombiningOperator == matchMemoryAccessor.CombiningOperator &&
+ modifiedMemoryAccessor.MemoryAccessor.Field.Type == matchMemoryAccessor.MemoryAccessor.Field.Type &&
+ modifiedMemoryAccessor.MemoryAccessor.PointerChainMatches(matchMemoryAccessor.MemoryAccessor))
+ {
+ bool isBcd = (modifiedMemoryAccessor.MemoryAccessor is BinaryCodedDecimalExpression);
+ if (isBcd == matchBcd)
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
public ErrorExpression Execute(InterpreterScope scope)
{
if (_memoryAccessors == null || _memoryAccessors.Count == 0)
diff --git a/Source/Parser/Expressions/Trigger/RequirementConditionExpression.cs b/Source/Parser/Expressions/Trigger/RequirementConditionExpression.cs
index 76f36e2f..3c9e2ee7 100644
--- a/Source/Parser/Expressions/Trigger/RequirementConditionExpression.cs
+++ b/Source/Parser/Expressions/Trigger/RequirementConditionExpression.cs
@@ -368,22 +368,52 @@ private static bool ConvertToBCD(ExpressionBase expression, out ExpressionBase n
return false;
}
- private ErrorExpression NormalizeBCD(TriggerBuilderContext context, out RequirementExpressionBase result)
+ private void NormalizeMemoryReads(ref RequirementExpressionBase result)
{
+ var condition = result as RequirementConditionExpression;
+ if (condition == null)
+ return;
+
+ ExpressionBase newLeft = condition.Left;
+ ExpressionBase newRight = condition.Right;
+
+ var memoryValue = Left as MemoryValueExpression;
+ if (memoryValue != null)
+ newLeft = memoryValue.NormalizeMemoryReads();
+
+ memoryValue = Right as MemoryValueExpression;
+ if (memoryValue != null)
+ newRight = memoryValue.NormalizeMemoryReads();
+
+ if (!ReferenceEquals(Left, newLeft) || !ReferenceEquals(Right, newRight))
+ {
+ result = new RequirementConditionExpression
+ {
+ Left = newLeft,
+ Comparison = condition.Comparison,
+ Right = newRight,
+ Location = condition.Location,
+ };
+ }
+ }
+
+ private static ErrorExpression NormalizeBCD(TriggerBuilderContext context, ref RequirementExpressionBase result)
+ {
+ var condition = result as RequirementConditionExpression;
+ if (condition == null)
+ return null;
+
ExpressionBase newLeft;
ExpressionBase newRight;
- bool leftHasBCD = ExtractBCD(Left, out newLeft);
- bool rightHasBCD = ExtractBCD(Right, out newRight);
+ bool leftHasBCD = ExtractBCD(condition.Left, out newLeft);
+ bool rightHasBCD = ExtractBCD(condition.Right, out newRight);
if (!rightHasBCD)
{
if (!leftHasBCD)
- {
- result = this;
return null;
- }
- newRight = Right;
+ newRight = condition.Right;
var rightConstant = newRight as IntegerConstantExpression;
if (rightConstant != null)
{
@@ -399,7 +429,7 @@ private ErrorExpression NormalizeBCD(TriggerBuilderContext context, out Requirem
if (newRight == null)
{
// right value cannot be decoded into 32-bits
- switch (Comparison)
+ switch (condition.Comparison)
{
case ComparisonOperation.NotEqual:
case ComparisonOperation.LessThan:
@@ -415,11 +445,11 @@ private ErrorExpression NormalizeBCD(TriggerBuilderContext context, out Requirem
}
else if (!leftHasBCD)
{
- leftHasBCD = ConvertToBCD(Right, out newLeft);
+ leftHasBCD = ConvertToBCD(condition.Right, out newLeft);
if (newLeft == null)
{
// left value cannot be decoded into 32-bits
- switch (Comparison)
+ switch (condition.Comparison)
{
case ComparisonOperation.NotEqual:
case ComparisonOperation.GreaterThan:
@@ -436,7 +466,7 @@ private ErrorExpression NormalizeBCD(TriggerBuilderContext context, out Requirem
if (leftHasBCD && rightHasBCD && context.CanModifyComparison)
{
- if (Comparison == ComparisonOperation.Equal || Comparison == ComparisonOperation.NotEqual)
+ if (condition.Comparison == ComparisonOperation.Equal || condition.Comparison == ComparisonOperation.NotEqual)
{
var leftMemoryValue = newLeft as MemoryValueExpression;
if (leftMemoryValue != null && leftMemoryValue.HasConstant)
@@ -456,15 +486,14 @@ private ErrorExpression NormalizeBCD(TriggerBuilderContext context, out Requirem
result = new RequirementConditionExpression()
{
Left = newLeft,
- Comparison = Comparison,
+ Comparison = condition.Comparison,
Right = newRight,
- Location = Location,
+ Location = condition.Location,
};
return null;
}
- result = this;
return null;
}
@@ -756,8 +785,10 @@ public ExpressionBase Normalize(TriggerBuilderContext context)
Left = new MemoryValueExpression(modifiedMemoryAccessor);
}
- RequirementExpressionBase result;
- var error = NormalizeBCD(context, out result);
+ RequirementExpressionBase result = this;
+ NormalizeMemoryReads(ref result);
+
+ var error = NormalizeBCD(context, ref result);
if (error != null)
return error;
diff --git a/Source/Parser/Expressions/Trigger/RequirementExpressionBase.cs b/Source/Parser/Expressions/Trigger/RequirementExpressionBase.cs
index 2be887f1..922f6ea2 100644
--- a/Source/Parser/Expressions/Trigger/RequirementExpressionBase.cs
+++ b/Source/Parser/Expressions/Trigger/RequirementExpressionBase.cs
@@ -180,6 +180,10 @@ internal static RequirementExpressionBase ConvertToRequirementExpression(Express
if (memoryValue != null)
return new MemoryValueExpression.MemoryValueRequirementExpression(memoryValue) { Location = expression.Location };
+ var error = expression as ErrorExpression;
+ if (error != null)
+ return new ErrorRequirementExpression(error);
+
return null;
}
}
diff --git a/Tests/Parser/Expressions/Trigger/MemoryValueExpressionTests.cs b/Tests/Parser/Expressions/Trigger/MemoryValueExpressionTests.cs
index b9db58e9..3d574049 100644
--- a/Tests/Parser/Expressions/Trigger/MemoryValueExpressionTests.cs
+++ b/Tests/Parser/Expressions/Trigger/MemoryValueExpressionTests.cs
@@ -446,9 +446,9 @@ public void TestUnderflowAdjustmentImpossible()
scope.AddFunction(new MemoryAccessorFunction("byte", FieldSize.Byte));
ExpressionBase result;
- Assert.That(expr.ReplaceVariables(scope, out result), Is.False);
- Assert.That(result, Is.InstanceOf());
- Assert.That(((ErrorExpression)result).Message, Is.EqualTo("Expression can never be true"));
+ Assert.That(expr.ReplaceVariables(scope, out result), Is.True);
+ result = ((RequirementConditionExpression)result).Normalize(new TriggerBuilderContext());
+ Assert.That(result, Is.InstanceOf());
}
[Test]
@@ -571,5 +571,91 @@ public void TestMergeBitCountLimits(ComparisonOperation comparisonOperation, int
ExpressionTests.AssertAppendString(comparison, expected);
}
}
+
+
+ [Test]
+ // low4 + high4 * 16
+ [TestCase("low4(0x1234) + high4(0x1234) * 16", "0xH001234")]
+ [TestCase("high4(0x1234) * 16 + low4(0x1234)", "0xH001234")]
+ [TestCase("low4(0x1234) + high4(0x1235) * 16", "A:0xU001235*16_0xL001234")]
+ [TestCase("low4(0x1234) - high4(0x1234) * 16", "B:0xU001234*16_0xL001234")]
+ // byte + byte * 256
+ [TestCase("byte(0x1234) + byte(0x1235) * 256", "0x 001234")]
+ [TestCase("byte(0x1235) * 256 + byte(0x1234)", "0x 001234")]
+ [TestCase("byte(0x1234) + byte(0x1234) * 256", "A:0xH001234*256_0xH001234")]
+ [TestCase("byte(0x1235) + byte(0x1234) * 256", "0xI001234")]
+ [TestCase("byte(0x1234) * 256 + byte(0x1235)", "0xI001234")]
+ // byte + word * 256
+ [TestCase("byte(0x1234) + word(0x1235) * 256", "0xW001234")]
+ [TestCase("word(0x1235) * 256 + byte(0x1234)", "0xW001234")]
+ [TestCase("byte(0x1235) + word(0x1234) * 256", "A:0x 001234*256_0xH001235")]
+ // byte + tbyte * 256
+ [TestCase("byte(0x1234) + tbyte(0x1235) * 256", "0xX001234")]
+ [TestCase("tbyte(0x1235) * 256 + byte(0x1234)", "0xX001234")]
+ // byte + word_be * 256
+ [TestCase("byte(0x1236) + word_be(0x1234) * 256", "0xJ001234")]
+ [TestCase("word_be(0x1234) * 256 + byte(0x1236)", "0xJ001234")]
+ [TestCase("byte(0x1234) + word_be(0x1235) * 256", "A:0xI001235*256_0xH001234")]
+ // word + word * 65536
+ [TestCase("word(0x1234) + word(0x1236) * 65536", "0xX001234")]
+ [TestCase("word(0x1236) * 65536 + word(0x1234)", "0xX001234")]
+ [TestCase("word(0x1234) + word(0x1235) * 65536", "A:0x 001235*65536_0x 001234")]
+ // word_be + byte * 65536
+ [TestCase("word_be(0x1235) + byte(0x1234) * 65536", "0xJ001234")]
+ [TestCase("byte(0x1234) * 65536 + word_be(0x1235)", "0xJ001234")]
+ [TestCase("word_be(0x1234) + byte(0x1236) * 65536", "A:0xH001236*65536_0xI001234")]
+ // word_be + word_be * 65536
+ [TestCase("word_be(0x1236) + word_be(0x1234) * 65536", "0xG001234")]
+ [TestCase("word_be(0x1234) * 65536 + word_be(0x1236)", "0xG001234")]
+ [TestCase("word_be(0x1234) + word_be(0x1236) * 65536", "A:0xI001236*65536_0xI001234")]
+ // tbyte + byte * 16777216
+ [TestCase("tbyte(0x1234) + byte(0x1237) * 16777216", "0xX001234")]
+ [TestCase("byte(0x1237) * 16777216 + tbyte(0x1234)", "0xX001234")]
+ [TestCase("tbyte(0x1234) + byte(0x1235) * 16777216", "A:0xH001235*16777216_0xW001234")]
+ // tbyte_be + byte * 16777216
+ [TestCase("tbyte_be(0x1235) + byte(0x1234) * 16777216", "0xG001234")]
+ [TestCase("byte(0x1234) * 16777216 + tbyte_be(0x1235)", "0xG001234")]
+ [TestCase("tbyte_be(0x1234) + byte(0x1237) * 16777216", "A:0xH001237*16777216_0xJ001234")]
+ // prev interference
+ [TestCase("byte(0x1234) + prev(byte(0x1235)) * 256", "A:d0xH001235*256_0xH001234")]
+ [TestCase("prev(byte(0x1234)) + byte(0x1235) * 256", "A:0xH001235*256_d0xH001234")]
+ [TestCase("prev(byte(0x1234)) + prev(byte(0x1235) * 256)", "d0x 001234")]
+ [TestCase("byte(0x1234) + byte(0x1235) * 256 - prev(byte(0x1234)) - prev(byte(0x1235) * 256)", "B:d0x 001234=0_0x 001234")]
+ // pointer
+ [TestCase("byte(dword(0x1000) + 0x1234) + byte(dword(0x1000) + 0x1235) * 256", "I:0xX001000_0x 001234")]
+ [TestCase("byte(dword(0x1000) + 0x1234) + byte(dword(0x1004) + 0x1235) * 256", "I:0xX001004_A:0xH001235*256_I:0xX001000_0xH001234")]
+ [TestCase("byte(dword(0x1000) + 0x1234) + byte(0x1235) * 256", "A:0xH001235*256_I:0xX001000_0xH001234")]
+ // bcd byte + bcd byte * 100
+ [TestCase("bcd(byte(0x1234)) + bcd(byte(0x1235)) * 100", "b0x 001234")]
+ [TestCase("bcd(byte(0x1235)) * 100 + bcd(byte(0x1234))", "b0x 001234")]
+ [TestCase("bcd(byte(0x1234)) + byte(0x1235) * 100", "A:0xH001235*100_b0xH001234")]
+ // bcd word + bcd byte * 10000
+ [TestCase("bcd(word(0x1234)) + bcd(byte(0x1236)) * 10000", "b0xW001234")]
+ [TestCase("bcd(byte(0x1236)) * 10000 + bcd(word(0x1234))", "b0xW001234")]
+ [TestCase("bcd(word(0x1234)) + byte(0x1236) * 10000", "A:0xH001236*10000_b0x 001234")]
+ // bcd word + bcd word * 10000
+ [TestCase("bcd(word(0x1234)) + bcd(word(0x1236)) * 10000", "b0xX001234")]
+ [TestCase("bcd(word(0x1236)) * 10000 + bcd(word(0x1234))", "b0xX001234")]
+ [TestCase("bcd(word(0x1234)) + word(0x1236) * 10000", "A:0x 001236*10000_b0x 001234")]
+ // bcd tbyte + bcd byte * 1000000
+ [TestCase("bcd(tbyte(0x1234)) + bcd(byte(0x1237)) * 1000000", "b0xX001234")]
+ [TestCase("bcd(byte(0x1237)) * 1000000 + bcd(tbyte(0x1234))", "b0xX001234")]
+ // bcd word_be + bcd byte * 10000
+ [TestCase("bcd(word_be(0x1235)) + bcd(byte(0x1234)) * 10000", "b0xJ001234")]
+ [TestCase("bcd(byte(0x1234)) * 10000 + bcd(word_be(0x1235))", "b0xJ001234")]
+ // bcd word_be + bcd word_be * 10000
+ [TestCase("bcd(word_be(0x1236)) + bcd(word_be(0x1234)) * 10000", "b0xG001234")]
+ [TestCase("bcd(word_be(0x1234)) * 10000 + bcd(word_be(0x1236))", "b0xG001234")]
+ // longer chains
+ [TestCase("byte(0x1234) + byte(0x1235)*256 + byte(0x1236)*65536", "0xW001234")]
+ [TestCase("byte(0x1236)*65536 + byte(0x1235)*256 + byte(0x1234)", "0xW001234")]
+ [TestCase("bcd(byte(0x1234)) + bcd(byte(0x1235))*100 + bcd(byte(0x1236))*10000", "b0xW001234")]
+ [TestCase("bcd(byte(0x1236))*10000 + bcd(byte(0x1235))*100 + bcd(byte(0x1234))", "b0xW001234")]
+ public void TestCombineNeighboringMemoryReads(string input, string expected)
+ {
+ var expr = TriggerExpressionTests.Parse(input);
+ Assert.That(expr, Is.InstanceOf());
+ TriggerExpressionTests.AssertSerialize((ITriggerExpression)expr, expected);
+ }
}
}
\ No newline at end of file