mirror of
https://github.com/lscharen/iigs-sprite-compiler.git
synced 2025-02-21 05:29:17 +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
|
||||
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),
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user