mirror of https://github.com/KarolS/millfork.git
260 lines
11 KiB
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
|
|
}
|
|
}
|