From de3754e91ef98eff9c4d3c73ee293e34fb3f6381 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 30 Nov 2016 00:43:54 -0600 Subject: [PATCH] Add a 16-bit word operation and unit test --- SpriteCompiler.Test/Tests.cs | 35 +++++++++++++----- SpriteCompiler/Problem/CodeSequence.cs | 27 ++++++++++++++ .../Problem/SpriteGeneratorSearchProblem.cs | 37 ++++++++++++++++++- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/SpriteCompiler.Test/Tests.cs b/SpriteCompiler.Test/Tests.cs index 30a1cef..777a41b 100644 --- a/SpriteCompiler.Test/Tests.cs +++ b/SpriteCompiler.Test/Tests.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using SpriteCompiler.Problem; using SpriteCompiler.AI; using System.Diagnostics; +using System.Collections.Generic; namespace SpriteCompiler.Test { @@ -50,13 +51,9 @@ namespace SpriteCompiler.Test Assert.AreEqual(14, (int)solution.Last().PathCost); // Write out the solution - foreach (var step in solution.Skip(1)) - { - Trace.WriteLine(step.Action.ToString()); - } + WriteOutSolution(solution); } - /* [TestMethod] public void TestSingleWordSprite() { @@ -65,11 +62,31 @@ namespace SpriteCompiler.Test var search = SpriteGeneratorSearchProblem.Create(); // Act : solve the problem - var solution = search.Search(problem, new SpriteGeneratorState()); + var solution = search.Search(problem, new SpriteGeneratorState(new byte[] { 0xAA, 0x55 })); - // Assert : The initial state IS the goal state - Assert.AreEqual(1, solution.Count()); + // Assert + // + // The fastest way to draw a single word at the current location should be + // + // TCS + // LDA #$55AA + // STA 0,s = 10 cycles + + Assert.AreEqual(3, solution.Count()); + Assert.AreEqual(10, (int)solution.Last().PathCost); + + // Write out the solution + WriteOutSolution(solution); + } + + private void WriteOutSolution(IEnumerable solution) + { + foreach (var step in solution.Skip(1)) + { + Trace.WriteLine(step.Action.ToString()); + } + + Trace.WriteLine(string.Format("; Total Cost = {0} cycles", (int)solution.Last().PathCost)); } - */ } } diff --git a/SpriteCompiler/Problem/CodeSequence.cs b/SpriteCompiler/Problem/CodeSequence.cs index bc58162..95691e1 100644 --- a/SpriteCompiler/Problem/CodeSequence.cs +++ b/SpriteCompiler/Problem/CodeSequence.cs @@ -118,4 +118,31 @@ namespace SpriteCompiler.Problem ); } } + + public sealed class STACK_REL_16_BIT_IMMEDIATE_STORE : CodeSequence + { + private readonly ushort value; + private readonly byte offset; + + public STACK_REL_16_BIT_IMMEDIATE_STORE(ushort value, byte offset) : base(8) { this.value = value; this.offset = offset; } + + public override SpriteGeneratorState Apply(SpriteGeneratorState state) + { + return state.Clone(_ => + { + _.A = _.A.LoadConstant(value); + _.RemoveByte((ushort)(offset + _.S.Value)); + _.RemoveByte((ushort)(offset + _.S.Value + 1)); + }); + } + + public override string ToString() + { + return String.Join("\n", + FormatLine("", "LDA", "#$" + value.ToString("X4"), "3 cycles"), + FormatLine("", "STA", offset.ToString("X2") + ",s", "5 cycles") + ); + } + } + } diff --git a/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs b/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs index 5353fc3..815e933 100644 --- a/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs +++ b/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs @@ -242,6 +242,34 @@ namespace SpriteCompiler.Problem actions.Add(new SHORT_M()); // Add any possible 16-bit data manipulations + if (state.S.IsScreenOffset) + { + var addr = state.S.Value; + + // Look for consecutive bytes + var local = state.Bytes.Where(WithinRangeOf(addr, 257)).ToList(); // 16-bit value can extend to the 256th byte + var words = local + .Skip(1) + .Select((x, i) => new { High = x, Low = local[i] }) + .Where(p => p.Low.Offset == (p.High.Offset - 1)) + .ToList(); + + foreach (var word in words) + { + var offset = (byte)(word.Low.Offset - addr); + var data = (ushort)(word.Low.Data + (word.High.Data << 8)); + actions.Add(new STACK_REL_16_BIT_IMMEDIATE_STORE(data, offset)); + + } + + // We can LDA #$XXXX / STA X,s for any values within 256 bytes of the current address + foreach (var datum in state.Bytes.Where(WithinRangeOf(addr, 256))) + { + var offset = (byte)(datum.Offset - addr); + actions.Add(new STACK_REL_8_BIT_IMMEDIATE_STORE(datum.Data, offset)); + } + } + } else { @@ -252,8 +280,8 @@ namespace SpriteCompiler.Problem { var addr = state.S.Value; - // We can LDA #$XX / STA X,s for any values within 256 bytes of the curren address - foreach (var datum in state.Bytes.Where(x => (x.Offset - addr) < 255)) + // We can LDA #$XX / STA X,s for any values within 256 bytes of the current address + foreach (var datum in state.Bytes.Where(WithinRangeOf(addr, 256))) { var offset = datum.Offset - addr; actions.Add(new STACK_REL_8_BIT_IMMEDIATE_STORE(datum.Data, (byte)offset)); @@ -264,6 +292,11 @@ namespace SpriteCompiler.Problem // Run through the actions to create a dictionary return actions.ToDictionary(x => x, x => x.Apply(state)); } + + private Func WithinRangeOf(int addr, int range) + { + return x => (x.Offset >= addr) && ((x.Offset - addr) < range); + } } public class SpriteGeneratorHeuristicFunction : IHeuristicFunction