millfork/src/main/scala/millfork/assembly/opt/ReverseFlowAnalyzer.scala

150 lines
6.9 KiB
Scala

package millfork.assembly.opt
import millfork.assembly.{AssemblyLine, OpcodeClasses, State}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
import scala.collection.immutable
/**
* @author Karol Stasiak
*/
sealed trait Importance {
def ~(that: Importance) = (this, that) match {
case (_, Important) | (Important, _) => Important
case (_, Unimportant) | (Unimportant, _) => Unimportant
case (UnknownImportance, UnknownImportance) => UnknownImportance
}
}
case object Important extends Importance {
override def toString = "!"
}
case object Unimportant extends Importance {
override def toString = "*"
}
case object UnknownImportance extends Importance {
override def toString = "?"
}
//noinspection RedundantNewCaseClass
case class CpuImportance(a: Importance = UnknownImportance,
x: Importance = UnknownImportance,
y: Importance = UnknownImportance,
n: Importance = UnknownImportance,
z: Importance = UnknownImportance,
v: Importance = UnknownImportance,
c: Importance = UnknownImportance,
d: Importance = UnknownImportance,
) {
override def toString: String = s"A=$a,X=$x,Y=$y,Z=$z,N=$n,C=$c,V=$v,D=$d"
def ~(that: CpuImportance) = new CpuImportance(
a = this.a ~ that.a,
x = this.x ~ that.x,
y = this.y ~ that.y,
z = this.z ~ that.z,
n = this.n ~ that.n,
c = this.c ~ that.c,
v = this.v ~ that.v,
d = this.d ~ that.d,
)
def isUnimportant(state: State.Value): Boolean = state match {
case State.A => a == Unimportant
case State.X => x == Unimportant
case State.Y => y == Unimportant
case State.Z => z == Unimportant
case State.N => n == Unimportant
case State.C => c == Unimportant
case State.V => v == Unimportant
case State.D => d == Unimportant
}
}
object ReverseFlowAnalyzer {
//noinspection RedundantNewCaseClass
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuImportance] = {
val importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
val codeArray = code.toArray
val initialStatus = new CpuStatus(d = SingleStatus(false))
var changed = true
val finalImportance = new CpuImportance(a = Important, x = Important, y = Important, c = Important, v = Important, d = Important, z = Important, n = Important)
changed = true
while (changed) {
changed = false
var currentImportance: CpuImportance = finalImportance
for (i <- codeArray.indices.reverse) {
import millfork.assembly.Opcode._
import millfork.assembly.AddrMode._
if (importanceArray(i) != currentImportance) {
changed = true
importanceArray(i) = currentImportance
}
codeArray(i) match {
case AssemblyLine(opcode, Relative, MemoryAddressConstant(Label(l)), _) if OpcodeClasses.ShortBranching(opcode) =>
val L = l
val labelIndex = codeArray.indexWhere {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
case _ => false
}
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance
case _ =>
}
codeArray(i) match {
// TODO: JSR?
case AssemblyLine(JMP, Absolute, MemoryAddressConstant(Label(l)), _) =>
val L = l
val labelIndex = codeArray.indexWhere {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
case _ => false
}
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex)
case AssemblyLine(JMP, Indirect, _, _) =>
currentImportance = finalImportance
case AssemblyLine(BNE | BEQ, _, _, _) =>
currentImportance = currentImportance.copy(z = Important)
case AssemblyLine(BMI | BPL, _, _, _) =>
currentImportance = currentImportance.copy(n = Important)
case AssemblyLine(SED | CLD, _, _, _) =>
currentImportance = currentImportance.copy(d = Unimportant)
case AssemblyLine(RTS, _, _, _) =>
currentImportance = finalImportance
case AssemblyLine(DISCARD_XF, _, _, _) =>
currentImportance = currentImportance.copy(x = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
case AssemblyLine(DISCARD_YF, _, _, _) =>
currentImportance = currentImportance.copy(y = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
case AssemblyLine(DISCARD_AF, _, _, _) =>
currentImportance = currentImportance.copy(a = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
case AssemblyLine(opcode, addrMode, _, _) =>
if (OpcodeClasses.ChangesC(opcode)) currentImportance = currentImportance.copy(c = Unimportant)
if (OpcodeClasses.ChangesV(opcode)) currentImportance = currentImportance.copy(v = Unimportant)
if (OpcodeClasses.ChangesNAndZ(opcode)) currentImportance = currentImportance.copy(n = Unimportant, z = Unimportant)
if (OpcodeClasses.OverwritesA(opcode)) currentImportance = currentImportance.copy(a = Unimportant)
if (OpcodeClasses.OverwritesX(opcode)) currentImportance = currentImportance.copy(x = Unimportant)
if (OpcodeClasses.OverwritesY(opcode)) currentImportance = currentImportance.copy(y = Unimportant)
if (OpcodeClasses.ReadsC(opcode)) currentImportance = currentImportance.copy(c = Important)
if (OpcodeClasses.ReadsD(opcode)) currentImportance = currentImportance.copy(d = Important)
if (OpcodeClasses.ReadsV(opcode)) currentImportance = currentImportance.copy(v = Important)
if (OpcodeClasses.ReadsXAlways(opcode)) currentImportance = currentImportance.copy(x = Important)
if (OpcodeClasses.ReadsYAlways(opcode)) currentImportance = currentImportance.copy(y = Important)
if (OpcodeClasses.ReadsAAlways(opcode)) currentImportance = currentImportance.copy(a = Important)
if (OpcodeClasses.ReadsAIfImplied(opcode) && addrMode == Implied) currentImportance = currentImportance.copy(a = Important)
if (addrMode == AbsoluteX || addrMode == IndexedX || addrMode == ZeroPageX) currentImportance = currentImportance.copy(x = Important)
if (addrMode == AbsoluteY || addrMode == IndexedY || addrMode == ZeroPageY) currentImportance = currentImportance.copy(y = Important)
}
}
}
// importanceArray.zip(codeArray).foreach{
// case (i, y) => if (y.isPrintable) println(f"$y%-32s $i%-32s")
// }
// println("---------------------")
importanceArray.toList
}
}