millfork/src/main/scala/millfork/assembly/opt/UndocumentedOptimizations.s...

341 lines
19 KiB
Scala

package millfork.assembly.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.assembly.{AddrMode, AssemblyLine, Opcode, State}
import millfork.assembly.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.OpcodeClasses._
import millfork.env.{Constant, NormalFunction, NumericConstant}
/**
* @author Karol Stasiak
*/
object UndocumentedOptimizations {
val counter = new AtomicInteger(30000)
def getNextLabel(prefix: String) = f".${prefix}%s__${counter.getAndIncrement()}%05d"
// TODO: test these
private val LaxAddrModeRestriction = Not(HasAddrModeIn(Set(AbsoluteX, ZeroPageX, IndexedX, Immediate)))
//noinspection ScalaUnnecessaryParentheses
val UseLax = new RuleBasedAssemblyOptimization("Using undocumented instruction LAX",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(HasOpcode(LDA) & Elidable & MatchAddrMode(0) & MatchParameter(1) & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsA) & Not(ChangesMemory) & Not(HasOpcode(LDX))).*.capture(2) ~
(HasOpcode(LDX) & Elidable & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](2) :+ code.head.copy(opcode = LAX)
},
(HasOpcode(LDX) & Elidable & MatchAddrMode(0) & MatchParameter(1) & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsX) & Not(ChangesMemory) & Not(HasOpcode(LDA))).*.capture(2) ~
(HasOpcode(LDA) & Elidable & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](2) :+ code.head.copy(opcode = LAX)
},
(HasOpcode(LDA) & Elidable & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsA) & Not(ChangesMemory) & Not(HasOpcode(TAX))).*.capture(2) ~
(HasOpcode(TAX) & Elidable) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](2) :+ code.head.copy(opcode = LAX)
},
(HasOpcode(LDX) & Elidable & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsX) & Not(ChangesMemory) & Not(HasOpcode(TXA))).*.capture(2) ~
(HasOpcode(TXA) & Elidable) ~~> { (code, ctx) =>
ctx.get[List[AssemblyLine]](2) :+ code.head.copy(opcode = LAX)
},
(HasOpcode(LDA) & Elidable & MatchAddrMode(0) & MatchParameter(1) & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsX) & Not(ChangesA) & Not(ChangesMemory) & Not(HasOpcode(LDX))).*.capture(2) ~
(HasOpcode(LDX) & Elidable & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> { (code, ctx) =>
code.head.copy(opcode = LAX) :: ctx.get[List[AssemblyLine]](2)
},
(HasOpcode(LDX) & Elidable & MatchAddrMode(0) & MatchParameter(1) & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsA) & Not(ChangesX) & Not(ChangesMemory) & Not(HasOpcode(LDA))).*.capture(2) ~
(HasOpcode(LDA) & Elidable & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> { (code, ctx) =>
code.head.copy(opcode = LAX) :: ctx.get[List[AssemblyLine]](2)
},
(HasOpcode(LDA) & Elidable & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsX) & Not(ChangesA) & Not(ChangesMemory) & Not(HasOpcode(TAX))).*.capture(2) ~
(HasOpcode(TAX) & Elidable & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> { (code, ctx) =>
code.head.copy(opcode = LAX) :: ctx.get[List[AssemblyLine]](2)
},
(HasOpcode(LDX) & Elidable & LaxAddrModeRestriction) ~
(LinearOrLabel & Not(ConcernsA) & Not(ChangesX) & Not(ChangesMemory) & Not(HasOpcode(TXA))).*.capture(2) ~
(HasOpcode(TXA) & Elidable & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> { (code, ctx) =>
code.head.copy(opcode = LAX) :: ctx.get[List[AssemblyLine]](2)
},
)
val SaxModes: Set[AddrMode.Value] = Set(ZeroPage, IndexedX, ZeroPageY, Absolute)
val UseSax = new RuleBasedAssemblyOptimization("Using undocumented instruction SAX",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsA) & Not(ConcernsX)).?.capture(10) ~
(HasOpcode(AND) & Elidable & MatchAddrMode(2) & MatchParameter(3) & Not(ReadsX)) ~
(Linear & Not(ConcernsA) & Not(ConcernsX)).?.capture(11) ~
(HasOpcode(STA) & Elidable & MatchAddrMode(4) & MatchParameter(5) & HasAddrModeIn(SaxModes) & DontMatchParameter(0)) ~
(Linear & Not(ConcernsA) & Not(ConcernsX) & Not(ChangesMemory)).?.capture(12) ~
(HasOpcode(LDA) & Elidable & MatchAddrMode(0) & MatchParameter(1)) ~
(LinearOrLabel & Not(ConcernsX)).*.capture(13) ~ OverwritesX ~~> { (code, ctx) =>
val lda = code.head
val ldx = AssemblyLine(LDX, ctx.get[AddrMode.Value](2), ctx.get[Constant](3))
val sax = AssemblyLine(SAX, ctx.get[AddrMode.Value](4), ctx.get[Constant](5))
val fragment0 = lda :: ctx.get[List[AssemblyLine]](10)
val fragment1 = ldx :: ctx.get[List[AssemblyLine]](11)
val fragment2 = sax :: ctx.get[List[AssemblyLine]](12)
val fragment3 = ctx.get[List[AssemblyLine]](13)
List(fragment0, fragment1, fragment2, fragment3).flatten
},
)
def andConstant(const: Constant, mask: Int): Option[Long] = const match {
case NumericConstant(n, _) => Some(n & mask)
case _ => None
}
val UseAnc = new RuleBasedAssemblyOptimization("Using undocumented instruction ANC",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LDA) & HasImmediate(0)) ~
(Elidable & HasOpcode(CLC)) ~~> (_ => List(AssemblyLine.immediate(ANC, 0))),
(Elidable & HasOpcode(LDA) & HasImmediate(0) & HasClear(State.C)) ~~> (_ => List(AssemblyLine.immediate(ANC, 0))),
(Elidable & HasOpcode(AND) & MatchImmediate(0)) ~
Where(c => andConstant(c.get[Constant](0), 0x80).contains(0)) ~
(Elidable & HasOpcode(CLC)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
(Elidable & HasOpcode(AND) & MatchImmediate(0)) ~
Where(c => andConstant(c.get[Constant](0), 0x80).contains(0x80)) ~
(Elidable & HasOpcode(SEC)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
(Elidable & HasOpcode(AND) & MatchImmediate(0)) ~
(Elidable & HasOpcode(CMP) & HasImmediate(0x80) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
(Elidable & HasOpcode(AND) & MatchImmediate(0)) ~
(Elidable & HasOpcode(CMP) & HasImmediate(0x80) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
(Elidable & HasOpcode(AND) & MatchImmediate(0) & HasClear(State.C)) ~
Where(c => andConstant(c.get[Constant](0), 0x80).contains(0)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
(Elidable & HasOpcode(AND) & MatchImmediate(0) & HasSet(State.C)) ~
Where(c => andConstant(c.get[Constant](0), 0x80).contains(0)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
(Elidable & HasOpcode(AND) & MatchImmediate(0)) ~
(Elidable & HasOpcodeIn(Set(ROL, ASL)) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~~> ((_, ctx) => List(AssemblyLine.immediate(ANC, ctx.get[Int](0)))),
)
val UseSbx = new RuleBasedAssemblyOptimization("Using undocumented instruction SBX",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.A, State.C)).+.captureLength(0) ~
Where(_.get[Int](0) > 2) ~~> ((_, ctx) => List(
AssemblyLine.implied(TXA),
AssemblyLine.immediate(SBX, ctx.get[Int](0)),
)),
(Elidable & HasOpcode(INX) & DoesntMatterWhatItDoesWith(State.A, State.C)).+.captureLength(0) ~
Where(_.get[Int](0) > 2) ~~> ((_, ctx) => List(
AssemblyLine.implied(TXA),
AssemblyLine.immediate(SBX, 256 - ctx.get[Int](0)),
)),
HasOpcode(TXA) ~
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasClear(State.C) & HasClear(State.D) & HasOpcode(ADC) & MatchImmediate(0)) ~
(Elidable & HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.C, State.A)) ~~> ((code, ctx) => List(
code.head,
AssemblyLine.immediate(SBX, 256 - ctx.get[Int](0)),
)),
HasOpcode(TXA) ~
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasSet(State.C) & HasClear(State.D) & HasOpcode(SBC) & MatchImmediate(0)) ~
(Elidable & HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.C, State.A)) ~~> ((code, ctx) => List(
code.head,
AssemblyLine.immediate(SBX, ctx.get[Int](0)),
)),
)
val UseAlr = new RuleBasedAssemblyOptimization("Using undocumented instruction ALR",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Elidable & HasOpcode(AND) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~~> { (code, ctx) =>
List(AssemblyLine.immediate(ALR, code.head.parameter))
},
(Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~
(Elidable & HasOpcode(CLC)) ~~> { (code, ctx) =>
List(AssemblyLine.immediate(ALR, 0xFE))
},
)
val UseArr = new RuleBasedAssemblyOptimization("Using undocumented instruction ARR",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(HasClear(State.D) & Elidable & HasOpcode(AND) & HasAddrMode(Immediate)) ~
(Elidable & HasOpcode(ROR) & HasAddrMode(Implied) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) =>
List(AssemblyLine.immediate(ARR, code.head.parameter))
},
)
private def trivialSequence1(o1: Opcode.Value, o2: Opcode.Value, extra: AssemblyLinePattern, combined: Opcode.Value) =
(Elidable & HasOpcode(o1) & HasAddrMode(Absolute) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & extra).* ~
(Elidable & HasOpcode(o2) & HasAddrMode(Absolute) & MatchParameter(1)) ~~> { (code, ctx) =>
code.tail.init :+ AssemblyLine(combined, Absolute, ctx.get[Constant](1))
}
private def trivialSequence2(o1: Opcode.Value, o2: Opcode.Value, extra: AssemblyLinePattern, combined: Opcode.Value) =
(Elidable & HasOpcode(o1) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & DoesNotConcernMemoryAt(0, 1) & extra).* ~
(Elidable & HasOpcode(o2) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
code.tail.init :+ AssemblyLine(combined, ctx.get[AddrMode.Value](0), ctx.get[Constant](1))
}
// ROL c LDA c AND d => LDA d RLA c
private def trivialCommutativeSequence(o1: Opcode.Value, o2: Opcode.Value, combined: Opcode.Value) = {
(Elidable & HasOpcode(o1) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(LDA) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(o2) & MatchAddrMode(2) & MatchParameter(3)) ~~> { code =>
List(code(2).copy(opcode = LDA), code(1).copy(opcode = combined))
}
}
val UseSlo = new RuleBasedAssemblyOptimization("Using undocumented instruction SLO",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
trivialSequence1(ASL, ORA, Not(ConcernsC), SLO),
trivialSequence2(ASL, ORA, Not(ConcernsC), SLO),
trivialCommutativeSequence(ASL, ORA, SLO),
(Elidable & HasOpcode(ASL) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsMemory)).* ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
code.tail.init ++ List(AssemblyLine.immediate(LDA, 0), AssemblyLine(SLO, ctx.get[AddrMode.Value](0), ctx.get[Constant](1)))
},
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsMemory) & Not(ChangesA)).*.capture(2) ~
(Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~
(Linear & Not(ConcernsMemory) & Not(ChangesA) & Not(ReadsC) & Not(ReadsNOrZ)).*.capture(3) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
List(AssemblyLine.immediate(LDA, 0), AssemblyLine(SRE, ctx.get[AddrMode.Value](0), ctx.get[Constant](1))) ++
ctx.get[List[AssemblyLine]](2) ++
ctx.get[List[AssemblyLine]](3)
},
)
val UseSre = new RuleBasedAssemblyOptimization("Using undocumented instruction SRE",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
trivialSequence1(LSR, EOR, Not(ConcernsC), SRE),
trivialSequence2(LSR, EOR, Not(ConcernsC), SRE),
trivialCommutativeSequence(LSR, EOR, SRE),
(Elidable & HasOpcode(LSR) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsMemory)).* ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
code.tail.init ++ List(AssemblyLine.immediate(LDA, 0), AssemblyLine(SRE, ctx.get[AddrMode.Value](0), ctx.get[Constant](1)))
},
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ConcernsMemory) & Not(ChangesA)).*.capture(2) ~
(Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~
(Linear & Not(ConcernsMemory) & Not(ChangesA) & Not(ReadsC) & Not(ReadsNOrZ)).*.capture(3) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
List(AssemblyLine.immediate(LDA, 0), AssemblyLine(SRE, ctx.get[AddrMode.Value](0), ctx.get[Constant](1))) ++
ctx.get[List[AssemblyLine]](2) ++
ctx.get[List[AssemblyLine]](3)
},
)
val UseRla = new RuleBasedAssemblyOptimization("Using undocumented instruction RLA",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
trivialSequence1(ROL, AND, Not(ConcernsC), RLA),
trivialSequence2(ROL, AND, Not(ConcernsC), RLA),
trivialCommutativeSequence(ROL, AND, RLA),
)
val UseRra = new RuleBasedAssemblyOptimization("Using undocumented instruction RRA",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
// TODO: is it ok? carry flag and stuff?
trivialSequence1(ROR, ADC, Not(ConcernsC), RRA),
trivialSequence2(ROR, ADC, Not(ConcernsC), RRA),
trivialCommutativeSequence(ROR, ADC, RRA),
)
val UseDcp = new RuleBasedAssemblyOptimization("Using undocumented instruction DCP",
needsFlowInfo = FlowInfoRequirement.BothFlows,
trivialSequence1(DEC, CMP, Not(ConcernsC), DCP),
trivialSequence2(DEC, CMP, Not(ConcernsC), DCP),
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(IndexedX, ZeroPageX, AbsoluteX))) ~
(Elidable & HasOpcode(TAX)) ~
(Elidable & HasOpcode(DEC) & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.X, State.C, State.Z, State.N, State.V)) ~~> { code =>
List(code.head.copy(opcode = LDY), code.last.copy(opcode = DCP, addrMode = AbsoluteY))
},
(Elidable & HasOpcode(DEC) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(LDA) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(CMP) & MatchAddrMode(2) & MatchParameter(3) & DoesntMatterWhatItDoesWith(State.V, State.C, State.N, State.A)) ~~> { code =>
List(code(2).copy(opcode = LDA), code(1).copy(opcode = DCP))
}
)
val UseIsc = new RuleBasedAssemblyOptimization("Using undocumented instruction ISC",
needsFlowInfo = FlowInfoRequirement.BothFlows,
trivialSequence1(INC, SBC, Not(ReadsC), ISC),
trivialSequence2(INC, SBC, Not(ReadsC), ISC),
(Elidable & HasOpcode(LDA) & HasImmediate(0) & HasClear(State.D)) ~
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
val label = getNextLabel("is")
List(
AssemblyLine.relative(BCC, label),
code.last.copy(opcode = ISC),
AssemblyLine.label(label))
},
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(ADC) & HasImmediate(0) & HasClear(State.D)) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
val label = getNextLabel("is")
List(
AssemblyLine.relative(BCC, label),
code.last.copy(opcode = ISC),
AssemblyLine.label(label))
},
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(LDA) & HasImmediate(1) & HasClear(State.D) & HasClear(State.C)) ~
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
List(code.last.copy(opcode = ISC))
},
(Elidable & HasOpcode(CLC)).? ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & HasClear(State.D) & HasClear(State.C) & MatchAddrMode(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(ADC) & HasImmediate(1)) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchAddrMode(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
List(code.last.copy(opcode = ISC))
},
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasOpcode(LDA) & HasImmediate(0) & HasClear(State.D) & HasSet(State.C)) ~
(Elidable & HasOpcode(ADC) & MatchAddrMode(1) & MatchParameter(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchParameter(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
List(code.last.copy(opcode = ISC))
},
(Elidable & HasOpcode(SEC)).? ~
(Elidable & HasOpcode(LDA) & MatchAddrMode(1) & HasClear(State.D) & HasSet(State.C) & MatchAddrMode(2) & HasAddrModeIn(Set(IndexedX, IndexedY, AbsoluteY))) ~
(Elidable & HasOpcode(ADC) & HasImmediate(0)) ~
(Elidable & HasOpcode(STA) & MatchAddrMode(1) & MatchAddrMode(2) & DoesntMatterWhatItDoesWith(State.A, State.C, State.Z, State.N, State.V)) ~~> { code =>
List(code.last.copy(opcode = ISC))
},
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(IndexedX, ZeroPageX, AbsoluteX))) ~
(Elidable & HasOpcode(TAX)) ~
(Elidable & HasOpcode(INC) & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.X, State.C, State.Z, State.N, State.V)) ~~> { code =>
List(code.head.copy(opcode = LDY), code.last.copy(opcode = ISC, addrMode = AbsoluteY))
},
(Elidable & HasOpcode(INC) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(LDA) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(CMP) & HasClear(State.D) & MatchAddrMode(2) & MatchParameter(3) & DoesntMatterWhatItDoesWith(State.V, State.C, State.N, State.A)) ~~> { code =>
List(code(2).copy(opcode = LDA), AssemblyLine.implied(SEC), code(1).copy(opcode = ISC))
}
)
val All: List[AssemblyOptimization] = List(
UseLax,
UseSax,
UseSbx,
UseAnc,
UseSlo,
UseSre,
UseAlr,
UseArr,
UseRla,
UseRra,
UseIsc,
UseDcp,
)
}