package millfork.assembly.mos.opt

import millfork.assembly._
import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._
import AddrMode._
import millfork.env._

/**
  * @author Karol Stasiak
  */
object DangerousOptimizations {

  val ConstantIndexOffsetPropagation = new RuleBasedAssemblyOptimization("Constant index offset propagation",
    // TODO: try to guess when overflow can happen
    needsFlowInfo = FlowInfoRequirement.BothFlows,
    (Elidable & HasOpcode(CLC)).? ~
      (Elidable & HasClear(State.C) & HasOpcode(ADC) & MatchImmediate(0) & DoesntMatterWhatItDoesWith(State.V, State.C)) ~
      (
        (HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
        (Linear & Not(ConcernsY)).*
        ).capture(1) ~
      (Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
      val last = code.last
      ctx.get[List[AssemblyLine]](1) :+ last.copy(parameter = last.parameter.+(ctx.get[Constant](0)).quickSimplify)
    },
    (Elidable & HasOpcode(CLC)).? ~
      (Elidable & HasClear(State.C) & HasOpcode(ADC) & MatchImmediate(0) & DoesntMatterWhatItDoesWith(State.V, State.C)) ~
      (
        (HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
        (Linear & Not(ConcernsX)).*
        ).capture(1) ~
      (Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
      val last = code.last
      ctx.get[List[AssemblyLine]](1) :+ last.copy(parameter = last.parameter.+(ctx.get[Constant](0)).quickSimplify)
    },
    (Elidable & HasOpcode(INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
      (Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
      val last = code.last
      List(last.copy(parameter = last.parameter.+(1).quickSimplify))
    },
    (Elidable & HasOpcode(DEY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
      (Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
      val last = code.last
      List(last.copy(parameter = last.parameter.+(-1).quickSimplify))
    },
    (Elidable & HasOpcode(INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
      (Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
      val last = code.last
      List(last.copy(parameter = last.parameter.+(1).quickSimplify))
    },
    (Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
      (Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
      val last = code.last
      List(last.copy(parameter = last.parameter.+(-1).quickSimplify))
    },
  )

  val All: List[AssemblyOptimization[AssemblyLine]] = List(ConstantIndexOffsetPropagation)
}
