Fix solution printing and add actions to alow stack to advance to new lines. Added 8-bit PHA actions

This commit is contained in:
Lucas Scharenbroich 2016-12-14 00:04:10 -06:00
parent ac9850998b
commit 21058f3e06
7 changed files with 210 additions and 41 deletions

View File

@ -64,7 +64,7 @@ namespace SpriteCompiler.Test
{
// Arrange
var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
var search = SpriteGeneratorSearchProblem.Create();
var search = SpriteGeneratorSearchProblem.Create(80); // max budget of 80 cycles
var sprite = new List<SpriteByte>
{
new SpriteByte(0x11, 0x00, 3),

View File

@ -219,11 +219,66 @@ namespace SpriteCompiler.Test
Assert.AreEqual(31, (int)solution.Last().PathCost);
}
[TestMethod]
public void TestThreeLineSprite()
{
// Arrange
var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
var search = SpriteGeneratorSearchProblem.Create();
// Act : solve the problem
var data = new[]
{
new SpriteByte(0x11, 0),
new SpriteByte(0x11, 160),
new SpriteByte(0x11, 320)
};
var solution = search.Search(problem, SpriteGeneratorState.Init(data));
// Current best solution
//
// TCS ; 2 cycles
// SEP #$10 ; 3 cycles
// LDA #$11 ; 2 cycles
// PHA ; 3 cycles
// STA A1,s ; 4 cycles
// REP #$10 ; 3 cycles
// TSC ; 2 cycles
// ADC #321 ; 3 cycles
// TCS ; 2 cycles
// SEP #$10 ; 3 cycles
// LDA #$11 ; 2 cycles
// PHA ; 3 cycles
// REP #$10 ; 3 cycles
//; Total Cost = 35 cycles
//
// Once other register caching becomes available, this should be able to be improved to
//
// TCS ; 2 cycles
// SEP #$20 ; 3 cycles
// LDX #$11 ; 2 cycles
// PHX ; 3 cycles
// ADC #160 ; 3 cycles
// TCS ; 2 cycles
// PHX ; 3 cycles
// ADC #161 ; 3 cycles
// TCS ; 2 cycles
// PHX ; 3 cycles
// REP #$20 ; 3 cycles
//; Total Cost = 29 cycles
// Write out the solution
WriteOutSolution(solution);
Assert.AreEqual(35, (int)solution.Last().PathCost);
}
private void WriteOutSolution(IEnumerable<SpriteGeneratorSearchNode> solution)
{
foreach (var step in solution.Skip(1))
{
Trace.WriteLine(step.Action.ToString());
Trace.WriteLine(step.Action.Emit());
}
Trace.WriteLine(string.Format("; Total Cost = {0} cycles", (int)solution.Last().PathCost));

View File

@ -44,8 +44,8 @@
public IEnumerable<T> Solution(T node)
{
var sequence = new List<T>();
for (var curr = node; node != null; node = node.Parent)
for (var curr = node; curr != null; curr = curr.Parent)
{
sequence.Add(curr);
}

View File

@ -32,9 +32,10 @@ namespace SpriteCompiler.AI
next.StepCost = problem.StepCost(node.State, action, state);
next.Heuristic = problem.Heuristic(state);
#if False
Console.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic);
#endif
#if VERBOSE_DEBUG
System.Diagnostics.Trace.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic);
#endif
yield return next;
}
}

View File

@ -72,6 +72,30 @@ namespace SpriteCompiler.Problem
}
}
public sealed class TSC : CodeSequence
{
public TSC() : base(2) { }
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
{
return state.Clone(_ =>
{
_.A = _.S;
});
}
public override string ToString()
{
return "TSC";
}
public override string Emit()
{
return FormatLine("", "TSC", "", "2 cycles");
}
}
public sealed class SHORT_M : CodeSequence
{
public SHORT_M() : base(3) { }
@ -112,6 +136,31 @@ namespace SpriteCompiler.Problem
}
}
public sealed class STACK_REL_8_BIT_STORE : CodeSequence
{
private readonly byte offset;
public STACK_REL_8_BIT_STORE(byte offset) : base(4) { this.offset = offset; }
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
{
return state.Clone(_ =>
{
_.RemoveByte((ushort)(offset + _.S.Value));
});
}
public override string ToString()
{
return "STA " + offset.ToString("X2") + ",s";
}
public override string Emit()
{
return FormatLine("", "STA", offset.ToString("X2") + ",s", "4 cycles");
}
}
public sealed class STACK_REL_8_BIT_IMMEDIATE_STORE : CodeSequence
{
private readonly byte value;
@ -177,10 +226,9 @@ namespace SpriteCompiler.Problem
public sealed class STACK_REL_16_BIT_STORE : CodeSequence
{
private readonly ushort value;
private readonly byte offset;
public STACK_REL_16_BIT_STORE(ushort value, byte offset) : base(5) { this.value = value; this.offset = offset; }
public STACK_REL_16_BIT_STORE(byte offset) : base(5) { this.offset = offset; }
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
{
@ -294,6 +342,37 @@ namespace SpriteCompiler.Problem
}
}
public sealed class LOAD_8_BIT_IMMEDIATE_AND_PUSH : CodeSequence
{
private readonly byte value;
public LOAD_8_BIT_IMMEDIATE_AND_PUSH(byte value) : base(5) { this.value = value; }
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
{
return state.Clone(_ =>
{
_.A = _.A.LoadConstant(value); // Need to be able to track high / low bytes independently...
_.RemoveByte((ushort)(_.S.Value));
_.S = _.S.Add(-1);
});
}
public override string ToString()
{
return "LDA #$" + value.ToString("X2") + " / PHA";
}
public override string Emit()
{
return String.Join("\n",
FormatLine("", "LDA", "#$" + value.ToString("X2"), "2 cycles"),
FormatLine("", "PHA", "", "3 cycles")
);
}
}
public sealed class PEA : CodeSequence
{
private readonly ushort value;
@ -320,9 +399,9 @@ namespace SpriteCompiler.Problem
}
}
public sealed class PHA : CodeSequence
public sealed class PHA_16 : CodeSequence
{
public PHA() : base(4) { }
public PHA_16() : base(4) { }
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
{
@ -343,4 +422,29 @@ namespace SpriteCompiler.Problem
return FormatLine("", "PHA", "", "4 cycles");
}
}
public sealed class PHA_8 : CodeSequence
{
public PHA_8() : base(3) { }
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
{
return state.Clone(_ =>
{
_.RemoveByte((ushort)(_.S.Value));
_.S = _.S.Add(-1);
});
}
public override string ToString()
{
return "PHA";
}
public override string Emit()
{
return FormatLine("", "PHA", "", "3 cycles");
}
}
}

View File

@ -117,7 +117,7 @@
{
if (state.A.IsLiteral && state.A.Value == topWord.Value.Data)
{
yield return state.Apply(new PHA());
yield return state.Apply(new PHA_16());
}
else
{
@ -140,7 +140,14 @@
{
if (!state.LongA)
{
yield return state.Apply(new STACK_REL_8_BIT_IMMEDIATE_STORE(topByte.Value.Data, 0));
if (state.A.IsLiteral && ((state.A.Value & 0xFF) == topByte.Value.Data))
{
yield return state.Apply(new PHA_8());
}
else
{
yield return state.Apply(new LOAD_8_BIT_IMMEDIATE_AND_PUSH(topByte.Value.Data));
}
}
}
}
@ -152,7 +159,7 @@
// 2. Set the stack to the start of a contiguous segment
// 3. Set the stack to the end of a contiguous segment
// move to the first or last byte of each span. So , take the first byte and then look for any
// move to the first or last byte of each span. So, take the first byte and then look for any
if (state.A.IsScreenOffset && !state.S.IsScreenOffset && state.LongA)
{
// If any of the open bytes are within 255 bytes of the accumulator, consider just
@ -211,7 +218,7 @@
{
if (data == state.A.Value)
{
yield return state.Apply(new STACK_REL_16_BIT_STORE(data, offset));
yield return state.Apply(new STACK_REL_16_BIT_STORE(offset));
}
else
{
@ -239,14 +246,25 @@
// We can LDA #$XX / STA X,s for any values within 256 bytes of the current address
foreach (var datum in open.Where(WithinRangeOf(addr, 256)))
{
var offset = datum.Offset - addr;
var offset = (byte)(datum.Offset - addr);
// Easy case when mask is empty
if (datum.Mask == 0x00)
{
yield return state.Apply(new STACK_REL_8_BIT_IMMEDIATE_STORE(datum.Data, (byte)offset));
if (datum.Data == (state.A.Value & 0xFF))
{
yield return state.Apply(new STACK_REL_8_BIT_STORE(offset));
}
else
{
yield return state.Apply(new STACK_REL_8_BIT_IMMEDIATE_STORE(datum.Data, offset));
}
}
// Otherwise there is really only one choice LDA / AND / ORA / STA sequence
else
{
yield return state.Apply(new STACK_REL_8_BIT_READ_MODIFY_WRITE(datum.Data, datum.Mask, (byte)offset));
yield return state.Apply(new STACK_REL_8_BIT_READ_MODIFY_WRITE(datum.Data, datum.Mask, offset));
}
}
}
@ -254,12 +272,21 @@
// If the accumulator and stack are both initialized, only propose moves to locations
// before and after the current 256 byte stack-relative window
if (state.A.IsScreenOffset && state.S.IsScreenOffset && state.LongA)
if (state.S.IsScreenOffset && state.LongA)
{
var addr = state.S.Value;
foreach (var datum in open.Where(x => (x.Offset - addr) > 255 || (x.Offset - addr) < 0))
// If the accumulator already has a screen offset value, just calculate it
if (state.A.IsScreenOffset)
{
yield return state.Apply(new MOVE_STACK(datum.Offset - state.A.Value));
var addr = state.S.Value;
foreach (var datum in open.Where(x => (x.Offset - addr) > 255 || (x.Offset - addr) < 0))
{
yield return state.Apply(new MOVE_STACK(datum.Offset - state.A.Value));
}
}
// Otherwise, put the stack in the accumulator
else
{
yield return state.Apply(new TSC());
}
}
}

View File

@ -179,27 +179,9 @@
initialState = SpriteGeneratorState.Init(data);
}
/*
IEnumerable<SpriteGeneratorSearchNode> solution = null;
if (verbose)
{
search.InitializeSearch(initialState);
while (true)
{
var step = search.SearchStep(problem);
if (step.IsGoal)
{
break;
}
}
}
else
{
solution = search.Search(problem, fringe, initialState);
}
var solution = search.Search(problem, initialState);
WriteOutSolution(solution);
*/
}
}
}