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

260 lines
11 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 Status[T] {
def contains(value: T): Boolean
def ~(that: Status[T]): Status[T] = {
(this, that) match {
case (AnyStatus(), _) => AnyStatus()
case (_, AnyStatus()) => AnyStatus()
case (SingleStatus(x), SingleStatus(y)) => if (x == y) SingleStatus(x) else AnyStatus()
case (SingleStatus(x), UnknownStatus()) => SingleStatus(x)
case (UnknownStatus(), SingleStatus(x)) => SingleStatus(x)
case (UnknownStatus(), UnknownStatus()) => UnknownStatus()
}
}
}
object Status {
implicit class IntStatusOps(val inner: Status[Int]) extends AnyVal {
def map[T](f: Int => T): Status[T] = inner match {
case SingleStatus(x) => SingleStatus(f(x))
case _ => AnyStatus()
}
def z(f: Int => Int = identity): Status[Boolean] = inner match {
case SingleStatus(x) =>
val y = f(x) & 0xff
SingleStatus(y == 0)
case _ => AnyStatus()
}
def n(f: Int => Int = identity): Status[Boolean] = inner match {
case SingleStatus(x) =>
val y = f(x) & 0xff
SingleStatus(y >= 0x80)
case _ => AnyStatus()
}
}
}
case class SingleStatus[T](t: T) extends Status[T] {
override def contains(value: T): Boolean = t == value
override def toString: String = t match {
case true => "1"
case false => "0"
case _ => t.toString
}
}
case class UnknownStatus[T]() extends Status[T] {
override def contains(value: T) = false
override def toString: String = "_"
}
case class AnyStatus[T]() extends Status[T] {
override def contains(value: T) = false
override def toString: String = "#"
}
//noinspection RedundantNewCaseClass
case class CpuStatus(a: Status[Int] = UnknownStatus(),
x: Status[Int] = UnknownStatus(),
y: Status[Int] = UnknownStatus(),
z: Status[Boolean] = UnknownStatus(),
n: Status[Boolean] = UnknownStatus(),
c: Status[Boolean] = UnknownStatus(),
v: Status[Boolean] = UnknownStatus(),
d: Status[Boolean] = UnknownStatus(),
) {
override def toString: String = s"A=$a,X=$x,Y=$y,Z=$z,N=$n,C=$c,V=$v,D=$d"
def nz: CpuStatus =
this.copy(n = AnyStatus(), z = AnyStatus())
def nz(i: Long): CpuStatus =
this.copy(n = SingleStatus((i & 0x80) != 0), z = SingleStatus((i & 0xff) == 0))
def ~(that: CpuStatus) = new CpuStatus(
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 hasClear(state: State.Value): Boolean = state match {
case State.A => a.contains(0)
case State.X => x.contains(0)
case State.Y => y.contains(0)
case State.Z => z.contains(false)
case State.N => n.contains(false)
case State.C => c.contains(false)
case State.V => v.contains(false)
case State.D => d.contains(false)
}
def hasSet(state: State.Value): Boolean = state match {
case State.A => false
case State.X => false
case State.Y => false
case State.Z => z.contains(true)
case State.N => n.contains(true)
case State.C => c.contains(true)
case State.V => v.contains(true)
case State.D => d.contains(true)
}
}
object CoarseFlowAnalyzer {
//noinspection RedundantNewCaseClass
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuStatus] = {
val flagArray = Array.fill[CpuStatus](code.length)(CpuStatus())
val codeArray = code.toArray
val initialStatus = new CpuStatus(d = SingleStatus(false))
var changed = true
while (changed) {
changed = false
var currentStatus: CpuStatus = if (f.interrupt) CpuStatus() else initialStatus
for (i <- codeArray.indices) {
import millfork.assembly.Opcode._
import millfork.assembly.AddrMode._
if (flagArray(i) != currentStatus) {
changed = true
flagArray(i) = currentStatus
}
codeArray(i) match {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
val L = l
currentStatus = codeArray.indices.flatMap(j => codeArray(j) match {
case AssemblyLine(_, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j))
case _ => None
}).fold(CpuStatus())(_ ~ _)
case AssemblyLine(BCC, _, _, _) =>
currentStatus = currentStatus.copy(c = currentStatus.c ~ SingleStatus(true))
case AssemblyLine(BCS, _, _, _) =>
currentStatus = currentStatus.copy(c = currentStatus.c ~ SingleStatus(false))
case AssemblyLine(BVS, _, _, _) =>
currentStatus = currentStatus.copy(v = currentStatus.v ~ SingleStatus(false))
case AssemblyLine(BVC, _, _, _) =>
currentStatus = currentStatus.copy(v = currentStatus.v ~ SingleStatus(true))
case AssemblyLine(BMI, _, _, _) =>
currentStatus = currentStatus.copy(n = currentStatus.n ~ SingleStatus(false))
case AssemblyLine(BPL, _, _, _) =>
currentStatus = currentStatus.copy(n = currentStatus.n ~ SingleStatus(true))
case AssemblyLine(BEQ, _, _, _) =>
currentStatus = currentStatus.copy(z = currentStatus.z ~ SingleStatus(false))
case AssemblyLine(BNE, _, _, _) =>
currentStatus = currentStatus.copy(z = currentStatus.z ~ SingleStatus(true))
case AssemblyLine(SED, _, _, _) =>
currentStatus = currentStatus.copy(d = SingleStatus(true))
case AssemblyLine(SEC, _, _, _) =>
currentStatus = currentStatus.copy(c = SingleStatus(true))
case AssemblyLine(CLD, _, _, _) =>
currentStatus = currentStatus.copy(d = SingleStatus(false))
case AssemblyLine(CLC, _, _, _) =>
currentStatus = currentStatus.copy(c = SingleStatus(false))
case AssemblyLine(CLV, _, _, _) =>
currentStatus = currentStatus.copy(v = SingleStatus(false))
case AssemblyLine(JSR, _, _, _) =>
currentStatus = initialStatus
case AssemblyLine(LDX, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.nz(n).copy(x = SingleStatus(n))
case AssemblyLine(LDY, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.nz(n).copy(y = SingleStatus(n))
case AssemblyLine(LDA, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.nz(n).copy(a = SingleStatus(n))
case AssemblyLine(LAX, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.nz(n).copy(a = SingleStatus(n), x = SingleStatus(n))
case AssemblyLine(EOR, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ ^ n), z = currentStatus.a.z(_ ^ n), a = currentStatus.a.map(_ ^ n))
case AssemblyLine(AND, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ & n), z = currentStatus.a.z(_ & n), a = currentStatus.a.map(_ & n))
case AssemblyLine(ANC, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ & n), c = currentStatus.a.n(_ & n), z = currentStatus.x.z(_ & n), a = currentStatus.a.map(_ & n))
case AssemblyLine(ORA, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ | n), z = currentStatus.a.z(_ | n), a = currentStatus.a.map(_ | n))
case AssemblyLine(ALR, Immediate, NumericConstant(nn, _), _) =>
val n = nn.toInt
currentStatus = currentStatus.copy(
n = currentStatus.a.n(i => (i & n & 0xff) >> 1),
z = currentStatus.a.z(i => (i & n & 0xff) >> 1),
c = currentStatus.a.map(i => (i & n & 1) == 0),
a = currentStatus.a.map(i => (i & n & 0xff) >> 1))
case AssemblyLine(INX, Implied, _, _) =>
currentStatus = currentStatus.copy(n = currentStatus.x.n(_ + 1), z = currentStatus.x.z(_ + 1), x = currentStatus.x.map(_ + 1))
case AssemblyLine(DEX, Implied, _, _) =>
currentStatus = currentStatus.copy(n = currentStatus.x.n(_ - 1), z = currentStatus.x.z(_ - 1), x = currentStatus.x.map(_ - 1))
case AssemblyLine(INY, Implied, _, _) =>
currentStatus = currentStatus.copy(n = currentStatus.y.n(_ + 1), z = currentStatus.y.z(_ + 1), y = currentStatus.y.map(_ + 1))
case AssemblyLine(DEY, Implied, _, _) =>
currentStatus = currentStatus.copy(n = currentStatus.y.n(_ - 1), z = currentStatus.y.z(_ - 1), y = currentStatus.y.map(_ - 1))
case AssemblyLine(TAX, _, _, _) =>
currentStatus = currentStatus.copy(x = currentStatus.a, n = currentStatus.a.n(), z = currentStatus.a.z())
case AssemblyLine(TXA, _, _, _) =>
currentStatus = currentStatus.copy(a = currentStatus.x, n = currentStatus.x.n(), z = currentStatus.x.z())
case AssemblyLine(TAY, _, _, _) =>
currentStatus = currentStatus.copy(y = currentStatus.a, n = currentStatus.a.n(), z = currentStatus.a.z())
case AssemblyLine(TYA, _, _, _) =>
currentStatus = currentStatus.copy(a = currentStatus.y, n = currentStatus.y.n(), z = currentStatus.y.z())
case AssemblyLine(opcode, addrMode, parameter, _) =>
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.copy(x = AnyStatus())
if (OpcodeClasses.ChangesY(opcode)) currentStatus = currentStatus.copy(y = AnyStatus())
if (OpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus())
if (addrMode == Implied && OpcodeClasses.ChangesAIfImplied(opcode)) currentStatus = currentStatus.copy(a = AnyStatus())
if (OpcodeClasses.ChangesNAndZ(opcode)) currentStatus = currentStatus.nz
if (OpcodeClasses.ChangesC(opcode)) currentStatus = currentStatus.copy(c = AnyStatus())
if (OpcodeClasses.ChangesV(opcode)) currentStatus = currentStatus.copy(v = AnyStatus())
if (opcode == CMP || opcode == CPX || opcode == CPY) {
if (addrMode == Immediate) parameter match {
case NumericConstant(0, _) => currentStatus = currentStatus.copy(c = SingleStatus(true))
case _ => ()
}
}
}
}
// flagArray.zip(codeArray).foreach{
// case (fl, y) => if (y.isPrintable) println(f"$fl%-32s $y%-32s")
// }
// println("---------------------")
}
flagArray.toList
}
}