mirror of
https://github.com/lscharen/iigs-sprite-compiler.git
synced 2024-08-31 18:29:06 +00:00
Fix solution printing and add actions to alow stack to advance to new lines. Added 8-bit PHA actions
This commit is contained in:
parent
ac9850998b
commit
21058f3e06
@ -64,7 +64,7 @@ namespace SpriteCompiler.Test
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
|
var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
|
||||||
var search = SpriteGeneratorSearchProblem.Create();
|
var search = SpriteGeneratorSearchProblem.Create(80); // max budget of 80 cycles
|
||||||
var sprite = new List<SpriteByte>
|
var sprite = new List<SpriteByte>
|
||||||
{
|
{
|
||||||
new SpriteByte(0x11, 0x00, 3),
|
new SpriteByte(0x11, 0x00, 3),
|
||||||
|
@ -219,11 +219,66 @@ namespace SpriteCompiler.Test
|
|||||||
Assert.AreEqual(31, (int)solution.Last().PathCost);
|
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)
|
private void WriteOutSolution(IEnumerable<SpriteGeneratorSearchNode> solution)
|
||||||
{
|
{
|
||||||
foreach (var step in solution.Skip(1))
|
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));
|
Trace.WriteLine(string.Format("; Total Cost = {0} cycles", (int)solution.Last().PathCost));
|
||||||
|
@ -44,8 +44,8 @@
|
|||||||
public IEnumerable<T> Solution(T node)
|
public IEnumerable<T> Solution(T node)
|
||||||
{
|
{
|
||||||
var sequence = new List<T>();
|
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);
|
sequence.Add(curr);
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,10 @@ namespace SpriteCompiler.AI
|
|||||||
next.StepCost = problem.StepCost(node.State, action, state);
|
next.StepCost = problem.StepCost(node.State, action, state);
|
||||||
next.Heuristic = problem.Heuristic(state);
|
next.Heuristic = problem.Heuristic(state);
|
||||||
|
|
||||||
#if False
|
#if VERBOSE_DEBUG
|
||||||
Console.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic);
|
System.Diagnostics.Trace.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
yield return next;
|
yield return next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 sealed class SHORT_M : CodeSequence
|
||||||
{
|
{
|
||||||
public SHORT_M() : base(3) { }
|
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
|
public sealed class STACK_REL_8_BIT_IMMEDIATE_STORE : CodeSequence
|
||||||
{
|
{
|
||||||
private readonly byte value;
|
private readonly byte value;
|
||||||
@ -177,10 +226,9 @@ namespace SpriteCompiler.Problem
|
|||||||
|
|
||||||
public sealed class STACK_REL_16_BIT_STORE : CodeSequence
|
public sealed class STACK_REL_16_BIT_STORE : CodeSequence
|
||||||
{
|
{
|
||||||
private readonly ushort value;
|
|
||||||
private readonly byte offset;
|
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)
|
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
|
public sealed class PEA : CodeSequence
|
||||||
{
|
{
|
||||||
private readonly ushort value;
|
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)
|
public override SpriteGeneratorState Apply(SpriteGeneratorState state)
|
||||||
{
|
{
|
||||||
@ -343,4 +422,29 @@ namespace SpriteCompiler.Problem
|
|||||||
return FormatLine("", "PHA", "", "4 cycles");
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
{
|
{
|
||||||
if (state.A.IsLiteral && state.A.Value == topWord.Value.Data)
|
if (state.A.IsLiteral && state.A.Value == topWord.Value.Data)
|
||||||
{
|
{
|
||||||
yield return state.Apply(new PHA());
|
yield return state.Apply(new PHA_16());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -140,7 +140,14 @@
|
|||||||
{
|
{
|
||||||
if (!state.LongA)
|
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
|
// 2. Set the stack to the start of a contiguous segment
|
||||||
// 3. Set the stack to the end 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 (state.A.IsScreenOffset && !state.S.IsScreenOffset && state.LongA)
|
||||||
{
|
{
|
||||||
// If any of the open bytes are within 255 bytes of the accumulator, consider just
|
// If any of the open bytes are within 255 bytes of the accumulator, consider just
|
||||||
@ -211,7 +218,7 @@
|
|||||||
{
|
{
|
||||||
if (data == state.A.Value)
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -239,14 +246,25 @@
|
|||||||
// We can LDA #$XX / STA X,s for any values within 256 bytes of the current address
|
// 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)))
|
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)
|
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
|
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
|
// If the accumulator and stack are both initialized, only propose moves to locations
|
||||||
// before and after the current 256 byte stack-relative window
|
// 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;
|
// If the accumulator already has a screen offset value, just calculate it
|
||||||
foreach (var datum in open.Where(x => (x.Offset - addr) > 255 || (x.Offset - addr) < 0))
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,27 +179,9 @@
|
|||||||
initialState = SpriteGeneratorState.Init(data);
|
initialState = SpriteGeneratorState.Init(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
var solution = search.Search(problem, initialState);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteOutSolution(solution);
|
WriteOutSolution(solution);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user