1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-09 16:29:34 +00:00
millfork/src/main/scala/millfork/assembly/z80/opt/WordVariableToRegisterOptimization.scala
2023-01-27 18:13:21 +01:00

590 lines
32 KiB
Scala

package millfork.assembly.z80.opt
import millfork.{CompilationFlag, NonOverlappingIntervals}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZFlag, ZLine, ZLine0, ZOpcode, ZOpcodeClasses}
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.ZRegister
import scala.collection.mutable.ListBuffer
import scala.util.control.TailCalls.{TailRec, done, tailcall}
/**
* @author Karol Stasiak
*/
object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
override def name = "Allocating variables to register pairs"
override def minimumRequiredLines: Int = 3
object CyclesAndBytes {
val Zero = CyclesAndBytes(0, 0)
}
case class CyclesAndBytes(bytes: Int, cycles: Int) {
def +(that: CyclesAndBytes) = CyclesAndBytes(this.bytes + that.bytes, this.cycles + that.cycles)
}
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val vs = VariableStatus(f, code, optimizationContext, _.size == 2, allowParams = true).getOrElse(return code)
val options = optimizationContext.options
val log = options.log
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
val z80 = optimizationContext.options.flag(CompilationFlag.EmitZ80Opcodes)
val exdehl = optimizationContext.options.flag(CompilationFlag.EmitIntel8080Opcodes)
val hlCandidates = vs.variablesWithLifetimes.filter {
case (v, range) =>
val tuple = vs.codeWithFlow(range.start)
okPrefix(vs, v, range, ZRegister.HL, allowDirectLoad = true, allowIndirectLoad = false) && tuple._1.importanceAfter.h != Important &&
tuple._1.importanceAfter.l != Important || {
// println(s"Cannot inline ${v.name} to HL because of early $tuple")
false
}
}.flatMap {
case (v, range) =>
canBeInlined(v.name, synced = false, ZRegister.HL, vs.codeWithFlow.slice(range.start, range.end)).map { score =>
(v.name, range, score +
(if (vs.variablesWithRegisterHint(v.name)) CyclesAndBytes(16, 16) else CyclesAndBytes.Zero) +
(if (vs.paramVariables(v.name)) CyclesAndBytes(-16, -3) else CyclesAndBytes.Zero)
)
}
}
val bcCandidates = vs.variablesWithLifetimes.filter {
case (v, range) =>
val tuple = vs.codeWithFlow(range.start)
okPrefix(vs, v, range, ZRegister.BC, z80, allowIndirectLoad = false) && tuple._1.importanceAfter.b != Important &&
tuple._1.importanceAfter.c != Important || {
// println(s"Cannot inline ${v.name} to BC because of early $tuple")
false
}
}.flatMap {
case (v, range) =>
canBeInlined(v.name, synced = false, ZRegister.BC, vs.codeWithFlow.slice(range.start, range.end)).map { score =>
(v.name, range, score +
(if (vs.variablesWithRegisterHint(v.name)) CyclesAndBytes(16, 16) else CyclesAndBytes.Zero) +
(if (vs.paramVariables(v.name)) {
if (z80) CyclesAndBytes(-20, -4)
else CyclesAndBytes(-24, -5)
} else CyclesAndBytes.Zero)
)
}
}
val deCandidates = vs.variablesWithLifetimes.filter {
case (v, range) =>
val tuple = vs.codeWithFlow(range.start)
okPrefix(vs, v, range, ZRegister.DE, z80, exdehl) && tuple._1.importanceAfter.d != Important &&
tuple._1.importanceAfter.e != Important || {
// println(s"Cannot inline ${v.name} to DE because of early $tuple")
false
}
}.flatMap {
case (v, range) =>
canBeInlined(v.name, synced = false, ZRegister.DE, vs.codeWithFlow.slice(range.start, range.end)).map { score =>
(v.name, range, score +
(if (vs.variablesWithRegisterHint(v.name)) CyclesAndBytes(16, 16) else CyclesAndBytes.Zero) +
(if (vs.paramVariables(v.name)) {
if (z80 || exdehl) CyclesAndBytes(-20, -4)
else CyclesAndBytes(-24, -5)
} else CyclesAndBytes.Zero)
)
}
}
val hlCandidateSets = NonOverlappingIntervals.apply[(String, Range, CyclesAndBytes)](hlCandidates, _._2.start, _._2.end)
val bcCandidateSets = NonOverlappingIntervals.apply[(String, Range, CyclesAndBytes)](bcCandidates, _._2.start, _._2.end)
val deCandidateSets = NonOverlappingIntervals.apply[(String, Range, CyclesAndBytes)](deCandidates, _._2.start, _._2.end)
val variants = for {
vhl <- if (options.flag(CompilationFlag.SingleThreaded)) hlCandidateSets else hlCandidateSets.par
nhl = vhl.map(_._1)
vbc <- bcCandidateSets
nbc = vbc.map(_._1)
if (nhl & nbc).isEmpty
vde <- deCandidateSets
nde = vde.map(_._1)
if (nhl & nde).isEmpty
if (nhl & nde).isEmpty
score = vhl.toSeq.map(x => costFunction(x._3)).sum +
vbc.toSeq.map(x => costFunction(x._3)).sum +
vde.toSeq.map(x => costFunction(x._3)).sum
} yield (score, vhl, vbc, vde)
if (variants.isEmpty) {
return code
}
// variants.foreach(println)
val (_, bestHLs, bestBCs, bestDEs) = variants.maxBy(_._1)
def reportOptimizedBlock[T](oldCode: List[(T, ZLine)], newCode: List[ZLine]): Unit = {
if (log.traceEnabled) {
oldCode.foreach(l => log.trace(l._2.toString))
log.trace(" ↓")
newCode.foreach(l => log.trace(l.toString))
}
}
if (bestHLs.nonEmpty || bestBCs.nonEmpty || bestDEs.nonEmpty) {
val output = ListBuffer[ZLine]()
var i = 0
while (i < code.length) {
var done = false
bestHLs.find(_._2.start == i).foreach {
case (v, range, _) =>
log.debug(s"Inlining $v to register pair HL")
val oldCode = vs.codeWithFlow.slice(range.start, range.end)
val newCode = inlineVars(v, "", "", oldCode).result
reportOptimizedBlock(oldCode, newCode)
if (vs.paramVariables(v)) {
val addr = vs.localVariables.find(_.name.==(v)).get.asInstanceOf[MemoryVariable].toAddress
output += ZLine.ldAbs16(ZRegister.HL, addr)
}
output ++= newCode
i = range.end
if (removeVariablesForReal && !vs.paramVariables(v) && contains(range, vs.variablesWithLifetimesMap(v))) {
f.environment.removeVariable(v)
}
done = true
}
if (!done) {
bestBCs.find(_._2.start == i).foreach {
case (v, range, _) =>
log.debug(s"Inlining $v to register pair BC")
val oldCode = vs.codeWithFlow.slice(range.start, range.end)
val newCode = inlineVars("", v, "", oldCode).result
reportOptimizedBlock(oldCode, newCode)
if (vs.paramVariables(v)) {
val addr = vs.localVariables.find(_.name.==(v)).get.asInstanceOf[MemoryVariable].toAddress
if (z80) {
output += ZLine.ldAbs16(ZRegister.BC, addr)
} else {
output += ZLine.ldAbs16(ZRegister.HL, addr)
output += ZLine.ld8(ZRegister.B, ZRegister.H)
output += ZLine.ld8(ZRegister.C, ZRegister.L)
}
}
output ++= newCode
i = range.end
if (removeVariablesForReal && !vs.paramVariables(v) && contains(range, vs.variablesWithLifetimesMap(v))) {
f.environment.removeVariable(v)
}
done = true
}
}
if (!done) {
bestDEs.find(_._2.start == i).foreach {
case (v, range, _) =>
log.debug(s"Inlining $v to register pair DE")
val oldCode = vs.codeWithFlow.slice(range.start, range.end)
val newCode = inlineVars("", "", v, oldCode).result
reportOptimizedBlock(oldCode, newCode)
if (vs.paramVariables(v)) {
val addr = vs.localVariables.find(_.name.==(v)).get.asInstanceOf[MemoryVariable].toAddress
if (z80) {
output += ZLine.ldAbs16(ZRegister.DE, addr)
} else if (exdehl) {
output += ZLine.ldAbs16(ZRegister.HL, addr)
output += ZLine.implied(ZOpcode.EX_DE_HL)
} else {
output += ZLine.ldAbs16(ZRegister.HL, addr)
output += ZLine.ld8(ZRegister.D, ZRegister.H)
output += ZLine.ld8(ZRegister.E, ZRegister.L)
}
}
output ++= newCode
i = range.end
if (removeVariablesForReal && !vs.paramVariables(v) && contains(range, vs.variablesWithLifetimesMap(v))) {
f.environment.removeVariable(v)
}
done = true
}
}
if (!done) {
output += code(i)
i += 1
}
}
output.toList
} else {
code
}
}
def okPrefix(vs: VariableStatus, v: Variable, range: Range, reg: ZRegister.Value, allowDirectLoad: Boolean, allowIndirectLoad: Boolean): Boolean = {
if (!vs.paramVariables(v.name)) return true
if (v.isVolatile) return false
if (!allowDirectLoad && !allowIndirectLoad) {
// println(s"okPrefix $v false: better cpu required for $reg")
return false
}
if (vs.paramRegs.contains(reg) || vs.paramRegs.contains(ZRegister.A)) {
// println(s"okPrefix $v false: reg $reg in use")
return false
}
if (range.size < 2) {
// println(s"okPrefix $v false: range $range too small")
return false
}
if (range.start < 1) {
// println(s"okPrefix $v true: range $range early enough")
return true
}
import ZOpcode._
vs.codeWithFlow.take(range.start) match {
case Nil =>
case (_, ZLine0(LABEL, _, _)) :: xs =>
if (xs.exists { case (_, l) => l.opcode == LABEL || ZOpcodeClasses.NonLinear(l.opcode) }) {
// println(s"okPrefix false: LABEL in prefix")
return false
}
case _ => return false
}
val importance = vs.codeWithFlow(range.start - 1)._1.importanceAfter
if (allowDirectLoad) {
importance.getRegister(reg) == Unimportant && importance.a == Unimportant
} else {
importance.getRegister(reg) == Unimportant && importance.a == Unimportant && importance.h == Unimportant && importance.l == Unimportant
}
}
def contains(outer: Range, inner: Range): Boolean = {
outer.contains(inner.start) && outer.contains(inner.end - 1)
}
import millfork.assembly.z80.ZOpcode._
import millfork.node.ZRegister._
def add(first: Boolean, ifTrue: CyclesAndBytes, ifFalse: CyclesAndBytes): CyclesAndBytes=>CyclesAndBytes = { c =>
if (first) c + ifTrue else c + ifFalse
}
def add(value: CyclesAndBytes): CyclesAndBytes=>CyclesAndBytes = _ + value
def canBeInlined(vname: String, synced: Boolean, target: ZRegister.Value, code: List[(FlowInfo, ZLine)]): Option[CyclesAndBytes] = {
def fail(reason: Int): None.type = {
// println(s"Cannot inline $vname to $target because of [[$reason]] ${code.head}")
None
}
code match {
case (_, ZLine(LD_16, TwoRegisters(BC, IMM_16), _, Elidability.Elidable, _))::
(i, ZLine(ADD_16, TwoRegisters(HL, BC), _, Elidability.Elidable, _)) :: xs if target == BC =>
if (i.importanceAfter.getRegister(BC) == Important) fail(22)
else if(i.importanceAfter.getRegister(DE) == Important) canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(-21, -2)))
else canBeInlined(vname, synced = true, target, xs)
case (_, ZLine(LD_16, TwoRegisters(DE, IMM_16), _, Elidability.Elidable, _))::
(i, ZLine(ADD_16, TwoRegisters(HL, DE), _, Elidability.Elidable, _)) :: xs if target == DE =>
if (i.importanceAfter.getRegister(DE) == Important) fail(23)
else if (i.importanceAfter.getRegister(BC) == Important) canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(-21, -2)))
else canBeInlined(vname, synced = true, target, xs)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th1), Elidability.Elidable, _)) ::
(_, ZLine0(ADD_16, TwoRegisters(HL, BC | DE), _)) ::
(i, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th2), Elidability.Elidable, _)) ::
xs if target == HL && th1.name != vname && th2.name != vname &&
i.importanceAfter.getFlag(ZFlag.Z) != Important &&
i.importanceAfter.getFlag(ZFlag.H) != Important &&
i.importanceAfter.getFlag(ZFlag.P) != Important &&
i.importanceAfter.getRegister(A) != Important &&
i.importanceAfter.getRegister(HL) != Important &&
i.importanceAfter.getFlag(ZFlag.Z) != Important =>
// bytes before: 3 + 1 + 3 = 7
// cycles before: 16 + 11 + 16 = 43
// bytes after: 3 + 1 + 3 + 3 + 1 + 3 = 14
// cycles after: 13 + 4 + 13 + 13 + 4 + 13 = 60
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(-17, -7)))
case (_, ZLine(LD_16, TwoRegisters(t, _), _, Elidability.Elidable, _)) ::
(_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) ::
(i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
xs if th.name == vname && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
// LD PP ; LD HL,(qq) ; ADD HL,PP → LD H,P ; LD L,P ; ADD HL,QQ
canBeInlined(vname, synced = true, target, xs).map(add(target == t, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
// case (_, ZLine(LD_16, TwoRegisters(t, _), _, true, _)) ::
// (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), true, _)) ::
// (i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
// xs if th.name == vname && t == target && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
// canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(16, 3)))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), Elidability.Elidable, _)) ::
(_, ZLine(LD_16, TwoRegisters(t, _), _, Elidability.Elidable, _)) ::
(i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
xs if th.name == vname && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
// LD (vv),HL ; LD QQ,__ ; ADD HL,QQ (vv@QQ)→ LD QQ,HL ; LD HL,__, ADD HL,QQ
canBeInlined(vname, synced = true, target, xs).map(add(target == t, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), Elidability.Elidable, _)) ::
(_, ZLine(LD_16, TwoRegisters(t, _), _, Elidability.Elidable, _)) ::
(i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
xs if t == target && th.name == vname && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
// LD (vv),HL ; LD QQ,__ ; ADD HL,QQ (vv@QQ)→ LD QQ,HL ; LD PP,__, ADD HL,PP
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(16, 3)))
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced = true, target, xs).map(add(target == HL, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced = true, target, xs).map(add(target == HL, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced = true, target, xs).map(add(target == DE, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced = true, target, xs).map(add(target == DE, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced = true, target, xs).map(add(target == BC, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced = true, target, xs).map(add(target == BC, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
case (_, ZLine(OR, OneRegister(A), _, Elidability.Elidable, _)) ::
(f, ZLine(SBC_16, TwoRegisters(HL, BC | DE), _, Elidability.Elidable, _)) :: xs if target == HL =>
val i = f.importanceAfter
if (i.h == Unimportant &&
i.l == Unimportant &&
i.a == Unimportant &&
i.hf == Unimportant &&
i.nf == Unimportant &&
i.pf == Unimportant &&
i.sf == Unimportant &&
i.zf == Unimportant) {
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(3, -1)))
} else {
None
}
case (_, x) :: (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, t), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs
if th.name == vname && t == target && x.changesRegister(t) =>
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(16, 3)))
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), Elidability.Elidable, _)) :: xs if th.name == vname =>
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
case (_, ZLine0(_, _, MemoryAddressConstant(th))) :: _ if th.name == vname => fail(4)
case (_, ZLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _))) :: _ if th.name == vname => fail(5)
case (_, ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _))) :: _ if th.name == vname => fail(6)
case (_, ZLine0(CALL, _, _)) :: xs => target match {
// TODO: check return type and allow HL sometimes
case BC | DE =>
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(-21, -2)))
case _ => fail(3)
}
case (_, x) :: xs if x.changesRegister(target) => fail(1)
case (_, x) :: xs if x.readsRegister(target) && !synced => fail(2)
case (_, ZLine0(LABEL, _, _)) :: xs => canBeInlined(vname, synced = false, target, xs)
case (_, ZLine0(CALL, _, _)) :: xs => fail(3)
case _ :: xs => canBeInlined(vname, synced, target, xs)
case _ => Some(CyclesAndBytes.Zero)
}
}
def inlineVars(hl: String, bc: String, de: String, code: List[(FlowInfo, ZLine)]): TailRec[List[ZLine]] = {
// if (code.nonEmpty) println(code.head)
code match {
case (_, ZLine(OR, OneRegister(A), _, _, _)) ::
(_, l@ZLine(SBC_16, TwoRegisters(HL, BC), _, _, _)) ::
xs if hl != "" =>
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.ld8(A, L).pos(l.source) ::
ZLine.register(SUB, C).pos(l.source) ::
ZLine.ld8(A, H).pos(l.source) ::
ZLine.register(SBC, B).pos(l.source) :: _)
case (_, ZLine(OR, OneRegister(A), _, _, _)) ::
(_, l@ZLine(SBC_16, TwoRegisters(HL, DE), _, _, _)) ::
xs if hl != "" =>
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.ld8(A, L).pos(l.source) ::
ZLine.register(SUB, E).pos(l.source) ::
ZLine.ld8(A, H).pos(l.source) ::
ZLine.register(SBC, D).pos(l.source) :: _)
case (_, load@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _, s1)) ::
(f, add@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s2)) ::
xs if bc != "" =>
if (f.importanceAfter.getRegister(DE) == Important) {
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.register(PUSH, BC).pos(s1) :: load :: add :: ZLine.register(POP, BC).pos(s2) :: _)
} else {
tailcall(inlineVars(hl, bc, de, xs)).map(
load.copy(registers = TwoRegisters(DE, IMM_16)) :: ZLine.registers(ADD_16, HL, DE).pos(s2) :: _)
}
case (_, load@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _, s1)) ::
(f, add@ZLine(ADD_16, TwoRegisters(HL, DE), _, _, s2)) ::
xs if de != "" =>
if (f.importanceAfter.getRegister(BC) == Important) {
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.register(PUSH, DE).pos(s1) :: load :: add :: ZLine.register(POP, DE).pos(s2) :: _)
} else {
tailcall(inlineVars(hl, bc, de, xs)).map(
load.copy(registers = TwoRegisters(BC, IMM_16)) :: ZLine.registers(ADD_16, HL, BC).pos(s2) :: _)
}
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), a1@MemoryAddressConstant(th1), _, s1)) ::
(_, ZLine(ADD_16, TwoRegisters(HL, reg@(DE | BC)), _, _, s2)) ::
(_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), a2@MemoryAddressConstant(th2), _, s3)) ::
xs if hl != "" && th1.name != hl && th2.name != hl =>
// bytes before: 3 + 1 + 3 = 7
// cycles before: 16 + 11 + 16 = 43
// bytes after: 3 + 1 + 3 + 3 + 1 + 3 = 14
// cycles after: 13 + 4 + 13 + 13 + 4 + 13 = 60
val (h, l) = reg match {
case BC => (B, C)
case DE => (D, E)
}
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.ldAbs8(A, a1).pos(s1) ::
ZLine.register(ADD, l).pos(s2) ::
ZLine.ldAbs8(a2, A).pos(s3) ::
ZLine.ldAbs8(A, a1 + 1).pos(s1) ::
ZLine.register(ADC, h).pos(s2) ::
ZLine.ldAbs8(a2 + 1, A).pos(s3) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s1)) ::
(_, loadConst@ZLine(LD_16, TwoRegisters(BC, constSource), _, _, s2)) ::
(_, add@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s3)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.ld8(B, H).pos(s1) :: ZLine.ld8(C, L).pos(s1) ::
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, BC)) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s1)) ::
(_, loadConst@ZLine0(LD_16, TwoRegisters(DE, constSource), _)) ::
(_, add@ZLine0(ADD_16, TwoRegisters(HL, DE), _)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(
ZLine.ld8(D, H).pos(s1) :: ZLine.ld8(E, L).pos(s1) ::
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, DE)) :: _)
// TODO: above with regs swapped
case (_, loadConst@ZLine0(LD_16, TwoRegisters(t, constSource), _)) ::
(_, ZLine0(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th))) ::
(_, add@ZLine0(ADD_16, TwoRegisters(HL, t2), _)) :: xs if th.name == bc && t == t2 && t != HL =>
tailcall(inlineVars(hl, bc, de, xs)).map(
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, BC)) :: _)
case (_, loadConst@ZLine0(LD_16, TwoRegisters(t, constSource), _)) ::
(_, ZLine0(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th))) ::
(_, add@ZLine0(ADD_16, TwoRegisters(HL, t2), _)) :: xs if th.name == de && t == t2 && t != HL =>
tailcall(inlineVars(hl, bc, de, xs)).map(
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, DE)) :: _)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs))
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, B).pos(s) :: ZLine.ld8(L, C).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, H).pos(s) :: ZLine.ld8(C, L).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, D).pos(s) :: ZLine.ld8(L, E).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, H).pos(s) :: ZLine.ld8(E, L).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs))
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, B).pos(s) :: ZLine.ld8(E, C).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, D).pos(s) :: ZLine.ld8(C, E).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, H).pos(s) :: ZLine.ld8(E, L).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, D).pos(s) :: ZLine.ld8(L, E).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs))
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs))
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, H).pos(s) :: ZLine.ld8(C, L).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, B).pos(s) :: ZLine.ld8(L, C).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, D).pos(s) :: ZLine.ld8(C, E).pos(s) :: _)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, B).pos(s) :: ZLine.ld8(E, C).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, L).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(L, A).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, C).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(C, A).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, E).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(E, A).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, H).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, A).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, B).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, A).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, D).pos(s) :: _)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, A).pos(s) :: _)
case (_, l1@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _, s1)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s2)) :: xs if bc != "" =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, BC).pos(s1) :: l1 :: l2 :: ZLine.register(POP, BC).pos(s2) :: _)
case (_, l1@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _, s1)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, DE), _, _, s2)) :: xs if de != "" =>
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s1) :: l1 :: l2 :: ZLine.register(POP, DE).pos(s2) :: _)
case (_, x@ZLine(CALL, _, _, _, s)) :: xs =>
if (bc != "") {
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, BC).pos(s) :: x :: ZLine.register(POP, BC).pos(s) :: _)
} else if (de != "") {
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: _)
} else {
tailcall(inlineVars(hl, bc, de, xs)).map(x :: _)
}
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(HL) && th.name == hl =>
tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(BC) && th.name == bc =>
tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(DE) && th.name == de =>
tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
case x :: _ if bc != "" && x._2.changesRegister(BC) => throw new IllegalStateException()
case x :: _ if de != "" && x._2.changesRegister(DE) => throw new IllegalStateException()
case x :: _ if hl != "" && x._2.changesRegister(HL) => throw new IllegalStateException()
case x :: xs => tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
case Nil => done(Nil)
}
}
}