1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-04-10 16:39:59 +00:00

Add original line numbers to generated assembly

This commit is contained in:
Karol Stasiak 2018-12-14 15:42:31 +01:00
parent a985c4f753
commit 406d69c74a
64 changed files with 1280 additions and 1137 deletions

View File

@ -47,6 +47,8 @@ Choose syntax for assembly output on 8080-like targets.
* `--syntax=intel`, `--syntax=zilog` sets both previous options at once
* `-fline-numbers`, `-fno-line-numbers` Whether should show source line numbers in assembly output.
## Verbosity options
* `-q` Suppress all messages except for errors.

View File

@ -303,7 +303,7 @@ object Cpu extends Enumeration {
object CompilationFlag extends Enumeration {
val
// common compilation options:
EmitIllegals, DecimalMode, ReadOnlyArrays, LenientTextEncoding,
EmitIllegals, DecimalMode, ReadOnlyArrays, LenientTextEncoding, LineNumbersInAssembly,
// compilation options for MOS:
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,

View File

@ -298,6 +298,10 @@ object Main {
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v).changeFlag(CompilationFlag.UseIntelSyntaxForOutput, v)
).description("Select syntax for assembly input and output.")
boolean("-fline-numbers", "-fno-line-numbers").action((c,v) =>
c.changeFlag(CompilationFlag.LineNumbersInAssembly, v)
).description("Show source line numbers in assembly.")
endOfFlags("--").description("Marks the end of options.")
fluff("", "Verbosity options:", "")

View File

@ -1,12 +1,46 @@
package millfork.assembly
import millfork.env.Constant
import millfork.node.Position
/**
* @author Karol Stasiak
*/
trait AbstractCode {
def sizeInBytes: Int
def isPrintable: Boolean
def parameter: Constant
def source: Option[SourceLine]
}
object SourceLine {
val MultipleFiles: SourceLine = SourceLine("", 0)
def merge(line: Option[SourceLine], lines: Seq[Option[SourceLine]]): Option[SourceLine] = {
line match {
case Some(l) =>
Some(lines.flatten.foldLeft(l)(_ ~ _))
case None =>
lines.flatten.reduceOption(_ ~ _)
}
}
def merge(lines: Seq[Option[SourceLine]]): Option[SourceLine] = {
lines.flatten.reduceOption(_ ~ _)
}
def of(pos: Option[Position]): Option[SourceLine] = pos.map(p => SourceLine(p.moduleName, p.line))
}
case class SourceLine(moduleName: String, line: Int) {
def ~(that: SourceLine): SourceLine = if (this.moduleName == that.moduleName) SourceLine(moduleName, line min that.line) else SourceLine.MultipleFiles
def ~(that: Option[SourceLine]): SourceLine = that.fold(this)(this ~ _)
}
object Elidability extends Enumeration {
val Elidable, Volatile, Fixed = Value
}

View File

@ -1,10 +1,11 @@
package millfork.assembly.mos
import millfork.assembly.AbstractCode
import millfork.assembly.{AbstractCode, Elidability, SourceLine}
import millfork.assembly.mos.Opcode._
import millfork.compiler.CompilationContext
import millfork.compiler.mos.MosCompiler
import millfork.env._
import millfork.node.Position
import millfork.{CompilationFlag, CompilationOptions}
//noinspection TypeAnnotation
@ -475,7 +476,26 @@ object AssemblyLine {
AssemblyLine(opcode, AddrMode.Stack, NumericConstant(addr & 0xff, 1))
}
case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var parameter: Constant, elidable: Boolean = true) extends AbstractCode {
object AssemblyLine0 {
@inline
def unapply(a: AssemblyLine): Some[(Opcode.Value, AddrMode.Value, Constant)] = Some(a.opcode, a.addrMode, a.parameter)
}
case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var parameter: Constant, elidability: Elidability.Value = Elidability.Elidable, source: Option[SourceLine] = None) extends AbstractCode {
def pos(s: Option[SourceLine]): AssemblyLine = if (s.isEmpty || s == source) this else this.copy(source = s)
def pos(s1: Option[SourceLine], s2: Option[SourceLine]): AssemblyLine = pos(Seq(s1, s2))
def position(s: Option[Position]): AssemblyLine = pos(SourceLine.of(s))
def positionIfEmpty(s: Option[Position]): AssemblyLine = if (s.isEmpty || source.isDefined) this else pos(SourceLine.of(s))
def pos(s: Seq[Option[SourceLine]]): AssemblyLine = pos(SourceLine.merge(s))
def mergePos(s: Seq[Option[SourceLine]]): AssemblyLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s))
def elidable: Boolean = elidability == Elidability.Elidable
import AddrMode._
@ -571,8 +591,8 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
def isPrintable: Boolean = true //addrMode != AddrMode.DoesNotExist || opcode == LABEL
override def toString: String =
if (opcode == LABEL) {
override def toString: String = {
val raw = if (opcode == LABEL) {
parameter.toString
} else if (opcode == BYTE) {
" !byte " + parameter.toString
@ -585,4 +605,9 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
}
s" $op ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
}
source match {
case Some(SourceLine(_, line)) if line > 0 => f"$raw%-30s \t; @ $line%d"
case _ => raw
}
}
}

View File

@ -943,10 +943,10 @@ object AlwaysGoodOptimizations {
val ADDR = ctx.get[Constant](1)
val value = code.foldLeft(oldA) { (prev, line) =>
line match {
case AssemblyLine(INC, AbsoluteX, ADDR, _) => (prev + 1) & 0xff
case AssemblyLine(DEC, AbsoluteX, ADDR, _) => (prev - 1) & 0xff
case AssemblyLine(ASL, AbsoluteX, ADDR, _) => (prev << 1) & 0xff
case AssemblyLine(LSR, AbsoluteX, ADDR, _) => (prev >> 1) & 0xff
case AssemblyLine0(INC, AbsoluteX, ADDR) => (prev + 1) & 0xff
case AssemblyLine0(DEC, AbsoluteX, ADDR) => (prev - 1) & 0xff
case AssemblyLine0(ASL, AbsoluteX, ADDR) => (prev << 1) & 0xff
case AssemblyLine0(LSR, AbsoluteX, ADDR) => (prev >> 1) & 0xff
case _ => prev
}
}
@ -1795,7 +1795,7 @@ object AlwaysGoodOptimizations {
List(code.head, AssemblyLine.implied(ROL))
},
(Elidable & HasOpcode(ADC) & HasImmediate(0) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.V, State.C, State.N, State.Z)) ~~> (_ => Nil),
(Elidable & HasOpcode(ROL) & HasClear(State.C)) ~ DebugMatching ~~> (code => code.map(_.copy(opcode = ASL))),
(Elidable & HasOpcode(ROL) & HasClear(State.C)) ~~> (code => code.map(_.copy(opcode = ASL))),
(Elidable & HasOpcode(ROR) & HasClear(State.C)) ~~> (code => code.map(_.copy(opcode = LSR))),
(HasOpcode(AND) & HasImmediate(1)) ~
(Linear & Not(ChangesNAndZ) & Not(HasOpcode(CLC)) & Not(ChangesA)).* ~

View File

@ -1,9 +1,9 @@
package millfork.assembly.mos.opt
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.env.NormalFunction
import millfork.error.{ConsoleLogger, Logger}
import millfork.error.Logger
/**
* @author Karol Stasiak
@ -100,57 +100,57 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
//noinspection OptionEqualsSome
private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = code match {
case AssemblyLine(_, AbsoluteY, _, _) :: xs if loaded != Some(Y) => false
case AssemblyLine(_, ZeroPageY, _, _) :: xs if loaded != Some(Y) => false
case AssemblyLine(_, IndexedY, _, _) :: xs if dir == Y2X || loaded != Some(Y) => false
case AssemblyLine(_, LongIndexedY, _, _) :: xs if dir == Y2X || loaded != Some(Y) => false
case AssemblyLine(_, AbsoluteX, _, _) :: xs if loaded != Some(X) => false
case AssemblyLine(_, LongAbsoluteX, _, _) :: xs if loaded != Some(X) => false
case AssemblyLine(_, ZeroPageX, _, _) :: xs if loaded != Some(X) => false
case AssemblyLine(_, IndexedX, _, _) :: xs if dir == X2Y || loaded != Some(X) => false
case AssemblyLine(_, AbsoluteIndexedX, _, _) :: xs if dir == X2Y => false
case AssemblyLine(SHX | SHY | AHX | TAS | LAS, _, _, _) :: xs => false
case AssemblyLine(TXY, _, _, e) :: xs => e && loaded == Some(X) && canOptimize(xs, dir, Some(Y))
case AssemblyLine(TYX, _, _, e) :: xs => e && loaded == Some(Y) && canOptimize(xs, dir, Some(X))
case AssemblyLine0(_, AbsoluteY, _) :: xs if loaded != Some(Y) => false
case AssemblyLine0(_, ZeroPageY, _) :: xs if loaded != Some(Y) => false
case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
case AssemblyLine0(_, AbsoluteX, _) :: xs if loaded != Some(X) => false
case AssemblyLine0(_, LongAbsoluteX, _) :: xs if loaded != Some(X) => false
case AssemblyLine0(_, ZeroPageX, _) :: xs if loaded != Some(X) => false
case AssemblyLine0(_, IndexedX, _) :: xs if dir == X2Y || loaded != Some(X) => false
case AssemblyLine0(_, AbsoluteIndexedX, _) :: xs if dir == X2Y => false
case AssemblyLine0(SHX | SHY | AHX | TAS | LAS, _, _) :: xs => false
case AssemblyLine(TXY, _, _, e, _) :: xs => (e == Elidability.Elidable || e == Elidability.Volatile) && loaded == Some(X) && canOptimize(xs, dir, Some(Y))
case AssemblyLine(TYX, _, _, e, _) :: xs => (e == Elidability.Elidable || e == Elidability.Volatile) && loaded == Some(Y) && canOptimize(xs, dir, Some(X))
// using a wrong index register for one instruction is fine
case AssemblyLine(LDY | TAY, _, _, _) :: AssemblyLine(_, IndexedY, _, _) :: xs if dir == Y2X =>
case AssemblyLine0(LDY | TAY, _, _) :: AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X =>
canOptimize(xs, dir, None)
case AssemblyLine(LDX | TAX, _, _, _) :: AssemblyLine(_, IndexedX, _, _) :: xs if dir == X2Y =>
case AssemblyLine0(LDX | TAX, _, _) :: AssemblyLine0(_, IndexedX, _) :: xs if dir == X2Y =>
canOptimize(xs, dir, None)
case AssemblyLine(LDX | TAX, _, _, _) :: AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _, _) :: xs if dir == X2Y =>
case AssemblyLine0(LDX | TAX, _, _) :: AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _) :: xs if dir == X2Y =>
canOptimize(xs, dir, None)
case AssemblyLine(LDY | TAY, _, _, _) :: AssemblyLine(INY | DEY, _, _, _) :: AssemblyLine(_, IndexedY, _, _) :: xs if dir == Y2X =>
case AssemblyLine0(LDY | TAY, _, _) :: AssemblyLine0(INY | DEY, _, _) :: AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X =>
canOptimize(xs, dir, None)
case AssemblyLine(LDX | TAX, _, _, _) :: AssemblyLine(INX | DEX, _, _, _) :: AssemblyLine(_, IndexedX, _, _) :: xs if dir == X2Y =>
case AssemblyLine0(LDX | TAX, _, _) :: AssemblyLine0(INX | DEX, _, _) :: AssemblyLine0(_, IndexedX, _) :: xs if dir == X2Y =>
canOptimize(xs, dir, None)
case AssemblyLine(LDX | TAX, _, _, _) :: AssemblyLine(INX | DEX, _, _, _) :: AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _, _) :: xs if dir == X2Y =>
case AssemblyLine0(LDX | TAX, _, _) :: AssemblyLine0(INX | DEX, _, _) :: AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _) :: xs if dir == X2Y =>
canOptimize(xs, dir, None)
case AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | STZ | LDZ | LDY | STY | BIT, AbsoluteX | ZeroPageX, _, _) :: xs if dir == X2Y => false
case AssemblyLine(LDX | STX, AbsoluteY | ZeroPageY, _, _) :: xs if dir == Y2X => false
case AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | STZ | LDZ | LDY | STY | BIT, AbsoluteX | ZeroPageX, _) :: xs if dir == X2Y => false
case AssemblyLine0(LDX | STX, AbsoluteY | ZeroPageY, _) :: xs if dir == Y2X => false
case AssemblyLine(LAX, _, _, _) :: xs => false
case AssemblyLine(JSR | BSR, _, _, _) :: xs => false // TODO
case AssemblyLine(JMP, Absolute, _, _) :: xs => canOptimize(xs, dir, None) // TODO
case AssemblyLine(op, _, _, _) :: xs if OpcodeClasses.ShortBranching(op) => canOptimize(xs, dir, None)
case AssemblyLine(RTS | RTL | BRA | BRL, _, _, _) :: xs => canOptimize(xs, dir, None)
case AssemblyLine(LABEL, _, _, _) :: xs => canOptimize(xs, dir, None)
case AssemblyLine(DISCARD_XF, _, _, _) :: xs => canOptimize(xs, dir, loaded.filter(_ != X))
case AssemblyLine(DISCARD_YF, _, _, _) :: xs => canOptimize(xs, dir, loaded.filter(_ != Y))
case AssemblyLine(_, DoesNotExist, _, _) :: xs => canOptimize(xs, dir, loaded)
case AssemblyLine0(LAX, _, _) :: xs => false
case AssemblyLine0(JSR | BSR, _, _) :: xs => false // TODO
case AssemblyLine0(JMP, Absolute, _) :: xs => canOptimize(xs, dir, None) // TODO
case AssemblyLine0(op, _, _) :: xs if OpcodeClasses.ShortBranching(op) => canOptimize(xs, dir, None)
case AssemblyLine0(RTS | RTL | BRA | BRL, _, _) :: xs => canOptimize(xs, dir, None)
case AssemblyLine0(LABEL, _, _) :: xs => canOptimize(xs, dir, None)
case AssemblyLine0(DISCARD_XF, _, _) :: xs => canOptimize(xs, dir, loaded.filter(_ != X))
case AssemblyLine0(DISCARD_YF, _, _) :: xs => canOptimize(xs, dir, loaded.filter(_ != Y))
case AssemblyLine0(_, DoesNotExist, _) :: xs => canOptimize(xs, dir, loaded)
case AssemblyLine(TAX | LDX | PLX, _, _, e) :: xs =>
(e || dir == Y2X) && canOptimize(xs, dir, Some(X))
case AssemblyLine(TAY | LDY | PLY, _, _, e) :: xs =>
(e || dir == X2Y) && canOptimize(xs, dir, Some(Y))
case AssemblyLine(TXA | STX | PHX | CPX | INX | DEX | HuSAX, _, _, e) :: xs =>
(e || dir == Y2X) && loaded == Some(X) && canOptimize(xs, dir, Some(X))
case AssemblyLine(TYA | STY | PHY | CPY | INY | DEY | SAY, _, _, e) :: xs =>
(e || dir == X2Y) && loaded == Some(Y) && canOptimize(xs, dir, Some(Y))
case AssemblyLine(TAX | LDX | PLX, _, _, e, _) :: xs =>
(e == Elidability.Elidable || e == Elidability.Volatile || dir == Y2X) && canOptimize(xs, dir, Some(X))
case AssemblyLine(TAY | LDY | PLY, _, _, e, _) :: xs =>
(e == Elidability.Elidable || e == Elidability.Volatile || dir == X2Y) && canOptimize(xs, dir, Some(Y))
case AssemblyLine(TXA | STX | PHX | CPX | INX | DEX | HuSAX, _, _, e, _) :: xs =>
(e == Elidability.Elidable || e == Elidability.Volatile || dir == Y2X) && loaded == Some(X) && canOptimize(xs, dir, Some(X))
case AssemblyLine(TYA | STY | PHY | CPY | INY | DEY | SAY, _, _, e, _) :: xs =>
(e == Elidability.Elidable || e == Elidability.Volatile || dir == X2Y) && loaded == Some(Y) && canOptimize(xs, dir, Some(Y))
case AssemblyLine(SAX | TXS | SBX, _, _, _) :: xs => dir == Y2X && loaded == Some(X) && canOptimize(xs, dir, Some(X))
case AssemblyLine(TSX, _, _, _) :: xs => dir == Y2X && loaded != Some(Y) && canOptimize(xs, dir, Some(X))
case AssemblyLine0(SAX | TXS | SBX, _, _) :: xs => dir == Y2X && loaded == Some(X) && canOptimize(xs, dir, Some(X))
case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && loaded != Some(Y) && canOptimize(xs, dir, Some(X))
case _ :: xs => canOptimize(xs, dir, loaded)
@ -158,71 +158,71 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
}
private def switchX2Y(code: List[AssemblyLine])(implicit log: Logger): List[AssemblyLine] = code match {
case (a@AssemblyLine(LDX | TAX, _, _, _))
:: (b@AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _, _))
case (a@AssemblyLine0(LDX | TAX, _, _))
:: (b@AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _))
:: xs => a :: b :: switchX2Y(xs)
case (a@AssemblyLine(LDX | TAX, _, _, _))
:: (b@AssemblyLine(_, IndexedX, _, _))
case (a@AssemblyLine0(LDX | TAX, _, _))
:: (b@AssemblyLine0(_, IndexedX, _))
:: xs => a :: b :: switchX2Y(xs)
case (a@AssemblyLine(LDX | TAX, _, _, _))
:: (i@AssemblyLine(INX | DEX, _, _, _))
:: (b@AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _, _))
case (a@AssemblyLine0(LDX | TAX, _, _))
:: (i@AssemblyLine0(INX | DEX, _, _))
:: (b@AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | LDY | STY | STZ, AbsoluteX | ZeroPageX, _))
:: xs => a :: i :: b :: switchX2Y(xs)
case (a@AssemblyLine(LDX | TAX, _, _, _))
:: (i@AssemblyLine(INX | DEX, _, _, _))
:: (b@AssemblyLine(_, IndexedX, _, _))
case (a@AssemblyLine0(LDX | TAX, _, _))
:: (i@AssemblyLine0(INX | DEX, _, _))
:: (b@AssemblyLine0(_, IndexedX, _))
:: xs => a :: i :: b :: switchX2Y(xs)
case (x@AssemblyLine(TAX, _, _, _)) :: xs => x.copy(opcode = TAY) :: switchX2Y(xs)
case (x@AssemblyLine(TXA, _, _, _)) :: xs => x.copy(opcode = TYA) :: switchX2Y(xs)
case (x@AssemblyLine(TXY | TYX, _, _, _)) :: xs => x.copy(opcode = TYX) :: switchX2Y(xs) // keep the transfer for the flags
case (x@AssemblyLine(STX, _, _, _)) :: xs => x.copy(opcode = STY) :: switchX2Y(xs)
case (x@AssemblyLine(LDX, _, _, _)) :: xs => x.copy(opcode = LDY) :: switchX2Y(xs)
case (x@AssemblyLine(INX, _, _, _)) :: xs => x.copy(opcode = INY) :: switchX2Y(xs)
case (x@AssemblyLine(DEX, _, _, _)) :: xs => x.copy(opcode = DEY) :: switchX2Y(xs)
case (x@AssemblyLine(CPX, _, _, _)) :: xs => x.copy(opcode = CPY) :: switchX2Y(xs)
case (x@AssemblyLine(PHX, _, _, _)) :: xs => x.copy(opcode = PHY) :: switchX2Y(xs)
case (x@AssemblyLine(PLX, _, _, _)) :: xs => x.copy(opcode = PLY) :: switchX2Y(xs)
case (x@AssemblyLine(HuSAX, _, _, _)) :: xs => x.copy(opcode = SAY) :: switchX2Y(xs)
case (x@AssemblyLine0(TAX, _, _)) :: xs => x.copy(opcode = TAY) :: switchX2Y(xs)
case (x@AssemblyLine0(TXA, _, _)) :: xs => x.copy(opcode = TYA) :: switchX2Y(xs)
case (x@AssemblyLine0(TXY | TYX, _, _)) :: xs => x.copy(opcode = TYX) :: switchX2Y(xs) // keep the transfer for the flags
case (x@AssemblyLine0(STX, _, _)) :: xs => x.copy(opcode = STY) :: switchX2Y(xs)
case (x@AssemblyLine0(LDX, _, _)) :: xs => x.copy(opcode = LDY) :: switchX2Y(xs)
case (x@AssemblyLine0(INX, _, _)) :: xs => x.copy(opcode = INY) :: switchX2Y(xs)
case (x@AssemblyLine0(DEX, _, _)) :: xs => x.copy(opcode = DEY) :: switchX2Y(xs)
case (x@AssemblyLine0(CPX, _, _)) :: xs => x.copy(opcode = CPY) :: switchX2Y(xs)
case (x@AssemblyLine0(PHX, _, _)) :: xs => x.copy(opcode = PHY) :: switchX2Y(xs)
case (x@AssemblyLine0(PLX, _, _)) :: xs => x.copy(opcode = PLY) :: switchX2Y(xs)
case (x@AssemblyLine0(HuSAX, _, _)) :: xs => x.copy(opcode = SAY) :: switchX2Y(xs)
case AssemblyLine(LAX, _, _, _) :: xs => log.fatal("Unexpected LAX")
case AssemblyLine(TXS, _, _, _) :: xs => log.fatal("Unexpected TXS")
case AssemblyLine(TSX, _, _, _) :: xs => log.fatal("Unexpected TSX")
case AssemblyLine(SBX, _, _, _) :: xs => log.fatal("Unexpected SBX")
case AssemblyLine(SAX, _, _, _) :: xs => log.fatal("Unexpected SAX")
case AssemblyLine(SXY, _, _, _) :: xs => log.fatal("Unexpected SXY")
case AssemblyLine0(LAX, _, _) :: xs => log.fatal("Unexpected LAX")
case AssemblyLine0(TXS, _, _) :: xs => log.fatal("Unexpected TXS")
case AssemblyLine0(TSX, _, _) :: xs => log.fatal("Unexpected TSX")
case AssemblyLine0(SBX, _, _) :: xs => log.fatal("Unexpected SBX")
case AssemblyLine0(SAX, _, _) :: xs => log.fatal("Unexpected SAX")
case AssemblyLine0(SXY, _, _) :: xs => log.fatal("Unexpected SXY")
case (x@AssemblyLine(_, AbsoluteX, _, _)) :: xs => x.copy(addrMode = AbsoluteY) :: switchX2Y(xs)
case (x@AssemblyLine(_, ZeroPageX, _, _)) :: xs => x.copy(addrMode = ZeroPageY) :: switchX2Y(xs)
case (x@AssemblyLine(_, IndexedX, _, _)) :: xs => log.fatal("Unexpected IndexedX")
case (x@AssemblyLine0(_, AbsoluteX, _)) :: xs => x.copy(addrMode = AbsoluteY) :: switchX2Y(xs)
case (x@AssemblyLine0(_, ZeroPageX, _)) :: xs => x.copy(addrMode = ZeroPageY) :: switchX2Y(xs)
case (x@AssemblyLine0(_, IndexedX, _)) :: xs => log.fatal("Unexpected IndexedX")
case x::xs => x :: switchX2Y(xs)
case Nil => Nil
}
private def switchY2X(code: List[AssemblyLine])(implicit log: Logger): List[AssemblyLine] = code match {
case AssemblyLine(LDY | TAY, _, _, _)
:: AssemblyLine(_, IndexedY, _, _)
case AssemblyLine0(LDY | TAY, _, _)
:: AssemblyLine0(_, IndexedY, _)
:: xs => code.take(2) ++ switchY2X(xs)
case AssemblyLine(LDY | TAY, _, _, _)
:: (i@AssemblyLine(INY | DEY, _, _, _))
:: AssemblyLine(_, IndexedY, _, _)
case AssemblyLine0(LDY | TAY, _, _)
:: (i@AssemblyLine0(INY | DEY, _, _))
:: AssemblyLine0(_, IndexedY, _)
:: xs => code.take(3) ++ switchY2X(xs)
case (x@AssemblyLine(TAY, _, _, _)) :: xs => x.copy(opcode = TAX) :: switchY2X(xs)
case (x@AssemblyLine(TYA, _, _, _)) :: xs => x.copy(opcode = TXA) :: switchY2X(xs)
case (x@AssemblyLine(TYX | TXY, _, _, _)) :: xs => x.copy(opcode = TXY) :: switchY2X(xs) // keep the transfer for the flags
case (x@AssemblyLine(STY, _, _, _)) :: xs => x.copy(opcode = STX) :: switchY2X(xs)
case (x@AssemblyLine(LDY, _, _, _)) :: xs => x.copy(opcode = LDX) :: switchY2X(xs)
case (x@AssemblyLine(INY, _, _, _)) :: xs => x.copy(opcode = INX) :: switchY2X(xs)
case (x@AssemblyLine(DEY, _, _, _)) :: xs => x.copy(opcode = DEX) :: switchY2X(xs)
case (x@AssemblyLine(CPY, _, _, _)) :: xs => x.copy(opcode = CPX) :: switchY2X(xs)
case (x@AssemblyLine(PHY, _, _, _)) :: xs => x.copy(opcode = PHX) :: switchY2X(xs)
case (x@AssemblyLine(PLY, _, _, _)) :: xs => x.copy(opcode = PLX) :: switchY2X(xs)
case (x@AssemblyLine(SAY, _, _, _)) :: xs => x.copy(opcode = HuSAX) :: switchY2X(xs)
case AssemblyLine(SXY, _, _, _) :: xs => log.fatal("Unexpected SXY")
case (x@AssemblyLine0(TAY, _, _)) :: xs => x.copy(opcode = TAX) :: switchY2X(xs)
case (x@AssemblyLine0(TYA, _, _)) :: xs => x.copy(opcode = TXA) :: switchY2X(xs)
case (x@AssemblyLine0(TYX | TXY, _, _)) :: xs => x.copy(opcode = TXY) :: switchY2X(xs) // keep the transfer for the flags
case (x@AssemblyLine0(STY, _, _)) :: xs => x.copy(opcode = STX) :: switchY2X(xs)
case (x@AssemblyLine0(LDY, _, _)) :: xs => x.copy(opcode = LDX) :: switchY2X(xs)
case (x@AssemblyLine0(INY, _, _)) :: xs => x.copy(opcode = INX) :: switchY2X(xs)
case (x@AssemblyLine0(DEY, _, _)) :: xs => x.copy(opcode = DEX) :: switchY2X(xs)
case (x@AssemblyLine0(CPY, _, _)) :: xs => x.copy(opcode = CPX) :: switchY2X(xs)
case (x@AssemblyLine0(PHY, _, _)) :: xs => x.copy(opcode = PHX) :: switchY2X(xs)
case (x@AssemblyLine0(PLY, _, _)) :: xs => x.copy(opcode = PLX) :: switchY2X(xs)
case (x@AssemblyLine0(SAY, _, _)) :: xs => x.copy(opcode = HuSAX) :: switchY2X(xs)
case AssemblyLine0(SXY, _, _) :: xs => log.fatal("Unexpected SXY")
case (x@AssemblyLine(_, AbsoluteY, _, _)) :: xs => x.copy(addrMode = AbsoluteX) :: switchY2X(xs)
case (x@AssemblyLine(_, ZeroPageY, _, _)) :: xs => x.copy(addrMode = ZeroPageX) :: switchY2X(xs)
case AssemblyLine(_, IndexedY, _, _) :: xs => log.fatal("Unexpected IndexedY")
case (x@AssemblyLine0(_, AbsoluteY, _)) :: xs => x.copy(addrMode = AbsoluteX) :: switchY2X(xs)
case (x@AssemblyLine0(_, ZeroPageY, _)) :: xs => x.copy(addrMode = ZeroPageX) :: switchY2X(xs)
case AssemblyLine0(_, IndexedY, _) :: xs => log.fatal("Unexpected IndexedY")
case x::xs => x :: switchY2X(xs)
case Nil => Nil

View File

@ -2,8 +2,7 @@ package millfork.assembly.mos.opt
import millfork.assembly.OptimizationContext
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.OpcodeClasses
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
import millfork.assembly.opt.AnyStatus
import millfork.env._
@ -45,14 +44,14 @@ object CoarseFlowAnalyzer {
flagArray(i) = currentStatus
}
codeArray(i) match {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) =>
val L = l
currentStatus = codeArray.indices.flatMap(j => codeArray(j) match {
case AssemblyLine(_, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j))
case AssemblyLine0(_, _, MemoryAddressConstant(Label(L))) => Some(flagArray(j))
case _ => None
}).fold(currentStatus)(_ ~ _)
case AssemblyLine(JSR, _, MemoryAddressConstant(th), _) =>
case AssemblyLine0(JSR, _, MemoryAddressConstant(th)) =>
currentStatus = initialStatus.copy(
a = if (niceFunctionProperties(DoesntChangeA -> th.name)) currentStatus.a else AnyStatus,
ah = if (niceFunctionProperties(DoesntChangeAH -> th.name)) currentStatus.ah else AnyStatus,
@ -63,27 +62,27 @@ object CoarseFlowAnalyzer {
c = if (niceFunctionProperties(DoesntChangeC -> th.name)) currentStatus.c else AnyStatus
)
case AssemblyLine(JSR | BYTE, _, _, _) =>
case AssemblyLine0(JSR | BYTE, _, _) =>
currentStatus = initialStatus
case AssemblyLine(op, Implied, _, _) if FlowAnalyzerForImplied.hasDefinition(op) =>
case AssemblyLine0(op, Implied, _) if FlowAnalyzerForImplied.hasDefinition(op) =>
currentStatus = FlowAnalyzerForImplied.get(op)(currentStatus)
case AssemblyLine(op, Immediate | WordImmediate, NumericConstant(nn, _), _) if FlowAnalyzerForImmediate.hasDefinition(op) =>
case AssemblyLine0(op, Immediate | WordImmediate, NumericConstant(nn, _)) if FlowAnalyzerForImmediate.hasDefinition(op) =>
currentStatus = FlowAnalyzerForImmediate.get(op)(nn.toInt, currentStatus)
case AssemblyLine(op, _, MemoryAddressConstant(th: Thing), _)
case AssemblyLine0(op, _, MemoryAddressConstant(th: Thing))
if th.name == "__reg" && FlowAnalyzerForTheRest.hasDefinition(op) =>
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, Some(0))
case AssemblyLine(op, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(n, _)), _)
case AssemblyLine0(op, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(n, _)))
if th.name == "__reg" && FlowAnalyzerForTheRest.hasDefinition(op) =>
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, Some(n.toInt))
case AssemblyLine(op, _, _, _) if FlowAnalyzerForTheRest.hasDefinition(op) =>
case AssemblyLine0(op, _, _) if FlowAnalyzerForTheRest.hasDefinition(op) =>
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, None)
case AssemblyLine(opcode, addrMode, _, _) =>
case AssemblyLine0(opcode, addrMode, _) =>
currentStatus = currentStatus.copy(src = AnyStatus)
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.copy(x = AnyStatus, eqSX = false)
if (OpcodeClasses.ChangesY(opcode)) currentStatus = currentStatus.copy(y = AnyStatus)

View File

@ -2,7 +2,7 @@ package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0}
import millfork.env._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
@ -29,12 +29,12 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
return code
}
val stillUsedVariables = code.flatMap {
case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case AssemblyLine(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case AssemblyLine0(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _))) => Some(th.name)
case _ => None
}.toSet
val variablesWithAddressesTaken = code.flatMap {
case AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _) =>
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) =>
Some(th.name)
case _ => None
}.toSet

View File

@ -2,9 +2,9 @@ package millfork.assembly.mos.opt
import millfork.Cpu
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0}
import millfork.assembly.mos.Opcode._
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.env._
import millfork.error.ConsoleLogger
@ -19,8 +19,8 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val usedFunctions = code.flatMap {
case AssemblyLine(JSR | BSR | JMP, _, MemoryAddressConstant(th), _) => Some(th.name)
case AssemblyLine(JSR | BSR | JMP, _, NumericConstant(addr, _), _) => Some("$" + addr.toHexString)
case AssemblyLine0(JSR | BSR | JMP, _, MemoryAddressConstant(th)) => Some(th.name)
case AssemblyLine0(JSR | BSR | JMP, _, NumericConstant(addr, _)) => Some("$" + addr.toHexString)
case _ => None
}.toSet
val foreignVariables = f.environment.root.things.values.flatMap {
@ -50,19 +50,19 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
case _ => Nil
}.toSet
val stillReadOrStoredVariables = code.flatMap {
case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case AssemblyLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name)
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
val stillReadVariables = code.flatMap {
case AssemblyLine(op, am, MemoryAddressConstant(th), true)
case AssemblyLine(op, am, MemoryAddressConstant(th), Elidability.Elidable, _)
if storeInstructions(op) && storeAddrModes(am) => Nil
case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), true)
case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _)
if storeInstructions(op) && storeAddrModes(am) => Nil
case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case AssemblyLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name)
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
@ -73,10 +73,10 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
optimizationContext.log.debug(s"Removing pointless store(s) to foreign variables ${unusedForeignVariables.mkString(", ")}")
code.filterNot {
case AssemblyLine(op, am, MemoryAddressConstant(th), _)
case AssemblyLine0(op, am, MemoryAddressConstant(th))
if storeInstructions(op) && storeAddrModes(am) =>
unusedForeignVariables(th.name)
case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), true)
case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _)
if storeInstructions(op) && storeAddrModes(am) =>
unusedForeignVariables(th.name)
case _ => false

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt
import millfork.assembly.OptimizationContext
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, Opcode, State}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
/**
@ -53,7 +53,7 @@ object FlowAnalyzer {
val labelMap: () => Option[Map[String, Int]] = () => req match {
case FlowInfoRequirement.NoRequirement => None
case _ => Some(code.flatMap {
case AssemblyLine(op, _, MemoryAddressConstant(Label(l)), _) if op != Opcode.LABEL => Some(l)
case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => Some(l)
case _ => None
}.groupBy(identity).mapValues(_.size))
}

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
@ -17,9 +17,9 @@ object HudsonOptimizations {
def removeLoadZero(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
ReverseFlowAnalyzer.analyze(f, code, optimizationContext).zip(code).map {
case (i, l) if i.n != Unimportant || i.z != Unimportant => l
case (i, AssemblyLine(LDA, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLA)
case (_, AssemblyLine(LDX, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLX)
case (_, AssemblyLine(LDY, Immediate, NumericConstant(0, _), true)) => AssemblyLine.implied(CLY)
case (i, AssemblyLine(LDA, Immediate, NumericConstant(0, _), Elidability.Elidable | Elidability.Volatile, s)) => AssemblyLine.implied(CLA).pos(s)
case (_, AssemblyLine(LDX, Immediate, NumericConstant(0, _), Elidability.Elidable | Elidability.Volatile, s)) => AssemblyLine.implied(CLX).pos(s)
case (_, AssemblyLine(LDY, Immediate, NumericConstant(0, _), Elidability.Elidable | Elidability.Volatile, s)) => AssemblyLine.implied(CLY).pos(s)
case (_, l) => l
}
}

View File

@ -2,11 +2,12 @@ package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, Opcode}
import millfork.assembly.mos.Opcode._
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
import millfork.error.ConsoleLogger
import millfork.CompilationOptions
import millfork.assembly.Elidability
/**
* @author Karol Stasiak
@ -41,12 +42,12 @@ object JumpFixing {
o += line.sizeInBytes
}
val labelOffsets = code.zipWithIndex.flatMap {
case (AssemblyLine(LABEL, _, MemoryAddressConstant(Label(label)), _), ix) => Some(label -> offsets(ix))
case (AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(label))), ix) => Some(label -> offsets(ix))
case _ => None
}.toMap
var changed = false
val result = code.zipWithIndex.flatMap {
case (line@AssemblyLine(op, AddrMode.Relative, MemoryAddressConstant(Label(label)), true), ix) =>
case (line@AssemblyLine(op, AddrMode.Relative, MemoryAddressConstant(Label(label)), Elidability.Elidable, _), ix) =>
labelOffsets.get(label).fold(List(line)) { labelOffset =>
val thisOffset = offsets(ix)
if (invalidShortJump(thisOffset, labelOffset)) {

View File

@ -1,7 +1,8 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
import millfork.assembly.Elidability
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
import millfork.env.{Label, MemoryAddressConstant}
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
@ -21,20 +22,20 @@ object JumpFollowing {
val currentLabels = mutable.Set[String]()
for (line <- code) {
line match {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(label)), _) =>
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(label))) =>
currentLabels += label
case AssemblyLine(op, _, _, _) if OpcodeClasses.NoopDiscardsFlags(op) =>
case AssemblyLine(LABEL, _, _, _) =>
case AssemblyLine(RTS, _, _, _) =>
case AssemblyLine0(op, _, _) if OpcodeClasses.NoopDiscardsFlags(op) =>
case AssemblyLine0(LABEL, _, _) =>
case AssemblyLine0(RTS, _, _) =>
labelsToRts ++= currentLabels
currentLabels.clear()
case AssemblyLine(RTI, _, _, _) =>
case AssemblyLine0(RTI, _, _) =>
labelsToRti ++= currentLabels
currentLabels.clear()
case AssemblyLine(RTL, _, _, _) =>
case AssemblyLine0(RTL, _, _) =>
labelsToRtl ++= currentLabels
currentLabels.clear()
case AssemblyLine(JMP | BRA, Absolute | Relative, MemoryAddressConstant(Label(label)), _) =>
case AssemblyLine0(JMP | BRA, Absolute | Relative, MemoryAddressConstant(Label(label))) =>
labelsToJumps ++= currentLabels.map(_ -> label)
currentLabels.clear()
case _ =>
@ -42,19 +43,19 @@ object JumpFollowing {
}
}
code.map {
case jump@AssemblyLine(JMP | BRA, Absolute | Relative | LongRelative | LongAbsolute, MemoryAddressConstant(Label(label)), true) =>
case jump@AssemblyLine(JMP | BRA, Absolute | Relative | LongRelative | LongAbsolute, MemoryAddressConstant(Label(label)), Elidability.Elidable, s) =>
if (labelsToRts(label)) {
options.log.debug(s"Optimizing ${jump.opcode} straight into RTS")
AssemblyLine.implied(RTS)
AssemblyLine.implied(RTS).pos(s)
} else if (labelsToRti(label)) {
options.log.debug(s"Optimizing ${jump.opcode} straight into RTI")
AssemblyLine.implied(RTI)
AssemblyLine.implied(RTI).pos(s)
} else if (labelsToRtl(label)) {
options.log.debug(s"Optimizing ${jump.opcode} straight into RTL")
AssemblyLine.implied(RTL)
AssemblyLine.implied(RTL).pos(s)
} else if (labelsToJumps.contains(label)) {
options.log.debug(s"Optimizing ${jump.opcode} straight into a jump")
AssemblyLine.absolute(JMP, Label(labelsToJumps(label)))
AssemblyLine.absolute(JMP, Label(labelsToJumps(label))).pos(s)
} else jump
case x => x
}

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt
import millfork.assembly.OptimizationContext
import millfork.assembly.mos.{AddrMode, AssemblyLine}
import millfork.assembly.{Elidability, OptimizationContext}
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0}
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.Opcode._
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
@ -27,13 +27,13 @@ object JumpShortening {
o += line.sizeInBytes
}
val labelOffsets = code.zipWithIndex.flatMap {
case (AssemblyLine(LABEL, _, MemoryAddressConstant(Label(label)), _), ix) => Some(label -> offsets(ix))
case (AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(label))), ix) => Some(label -> offsets(ix))
case _ => None
}.toMap
val cmos = options.flags(CompilationFlag.EmitCmosOpcodes)
if (cmos) {
code.zipWithIndex.map {
case (line@AssemblyLine(JMP, AddrMode.Absolute, MemoryAddressConstant(Label(label)), true), ix) =>
case (line@AssemblyLine(JMP, AddrMode.Absolute, MemoryAddressConstant(Label(label)), Elidability.Elidable, _), ix) =>
labelOffsets.get(label).fold(line) { labelOffset =>
val thisOffset = offsets(ix)
if (validShortJump(thisOffset, labelOffset)) {
@ -51,7 +51,7 @@ object JumpShortening {
}
} else {
FlowAnalyzer.analyze(f, code, optimizationContext, FlowInfoRequirement.ForwardFlow).zipWithIndex.map {
case ((info, line@AssemblyLine(JMP, AddrMode.Absolute, MemoryAddressConstant(Label(label)), _)), ix) =>
case ((info, line@AssemblyLine0(JMP, AddrMode.Absolute, MemoryAddressConstant(Label(label)))), ix) =>
labelOffsets.get(label).fold(line) { labelOffset =>
val thisOffset = offsets(ix)
if (validShortJump(thisOffset, labelOffset)) {

View File

@ -1,10 +1,10 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
import millfork.assembly.opt.SingleStatus
import millfork.env._
import millfork.error.{ConsoleLogger, Logger}
@ -19,7 +19,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val stillUsedVariables = code.flatMap {
case AssemblyLine(_, _, MemoryAddressConstant(th: MemoryVariable), _) => th match {
case AssemblyLine0(_, _, MemoryAddressConstant(th: MemoryVariable)) => th match {
case MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register)
if typ.size == 1 => Some(name)
case _ => None
@ -27,7 +27,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
case _ => None
}.toSet
val variablesWithAddressesTaken = code.flatMap {
case AssemblyLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
val eligibleVariables = (stillUsedVariables -- variablesWithAddressesTaken).filterNot(_.startsWith("__"))
@ -56,11 +56,11 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
case (AssemblyLine(op@(
LDA | LDX | LDY | LDZ |
ADC | ORA | EOR | AND | SBC |
CMP | CPX | CPY | CPZ), Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
CMP | CPX | CPY | CPZ), Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: xs
if variables(th.name) && map.contains(th.name) =>
true -> (AssemblyLine.immediate(op, map(th.name)) :: optimizeImpl(xs, variables, map)._2)
true -> (AssemblyLine.immediate(op, map(th.name)).pos(s) :: optimizeImpl(xs, variables, map)._2)
case (x@AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), status) :: xs
case (x@AssemblyLine0(STA, Absolute | ZeroPage, MemoryAddressConstant(th)), status) :: xs
if variables(th.name) =>
val newMap = status.a match {
case SingleStatus(n) => map + (th.name -> n)
@ -68,7 +68,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
}
x :: optimizeImpl(xs, variables, newMap)
case (x@AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _), status) :: xs
case (x@AssemblyLine0(STX, Absolute | ZeroPage, MemoryAddressConstant(th)), status) :: xs
if variables(th.name) =>
val newMap = status.x match {
case SingleStatus(n) => map + (th.name -> n)
@ -76,7 +76,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
}
x :: optimizeImpl(xs, variables, newMap)
case (x@AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th: ThingInMemory), _), status) :: xs
case (x@AssemblyLine0(STY, Absolute | ZeroPage, MemoryAddressConstant(th: ThingInMemory)), status) :: xs
if variables(th.name) =>
val newMap = status.y match {
case SingleStatus(n) => map + (th.name -> n)
@ -84,7 +84,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
}
x :: optimizeImpl(xs, variables, newMap)
case (x@AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), status) :: xs
case (x@AssemblyLine0(STZ, Absolute | ZeroPage, MemoryAddressConstant(th)), status) :: xs
if variables(th.name) =>
val newMap = status.iz match {
case SingleStatus(n) => map + (th.name -> n)
@ -92,7 +92,7 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
}
x :: optimizeImpl(xs, variables, newMap)
case (x@AssemblyLine(SAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), status) :: xs
case (x@AssemblyLine0(SAX, Absolute | ZeroPage, MemoryAddressConstant(th)), status) :: xs
if variables(th.name) =>
val newMap = (status.a, status.x) match {
case (SingleStatus(m), SingleStatus(n)) => map + (th.name -> (m & n))
@ -102,28 +102,28 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
}
x :: optimizeImpl(xs, variables, newMap)
case (x@AssemblyLine(INC | ISC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (x@AssemblyLine0(INC | ISC, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if map.contains(th.name) =>
x :: optimizeImpl(xs, variables, map + (th.name -> map(th.name).+(1).&(0xff)))
case (x@AssemblyLine(DEC | DCP, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (x@AssemblyLine0(DEC | DCP, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if map.contains(th.name) =>
x :: optimizeImpl(xs, variables, map + (th.name -> map(th.name).-(1).&(0xff)))
case (x@AssemblyLine(ASL | SLO, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (x@AssemblyLine0(ASL | SLO, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if map.contains(th.name) =>
x :: optimizeImpl(xs, variables, map + (th.name -> map(th.name).<<(1).&(0xff)))
case (x@AssemblyLine(LSR | SRE, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (x@AssemblyLine0(LSR | SRE, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if map.contains(th.name) =>
x :: optimizeImpl(xs, variables, map + (th.name -> map(th.name).&(0xff).>>(1)))
// TODO: consider handling some more opcodes
case (x@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (x@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if OpcodeClasses.ChangesMemoryAlways(op) && map.contains(th.name) =>
x :: optimizeImpl(xs, variables, map - th.name)
case (x@AssemblyLine(LABEL, _, _, _), _) :: xs => x :: optimizeImpl(xs, variables, Map())
case (x@AssemblyLine0(LABEL, _, _), _) :: xs => x :: optimizeImpl(xs, variables, Map())
case (x, _) :: xs => x :: optimizeImpl(xs, variables, map)
case Nil => (false, Nil)
}

View File

@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.{AssemblyLine, State}
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, State}
import millfork.assembly.mos.OpcodeClasses._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
@ -71,12 +71,12 @@ object LoopUnrolling {
private def fixLabels(code: List[AssemblyLine]) = {
val localLabels = code.flatMap {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) => Some(l)
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l)
case _ => None
}.toSet
val labelPrefix = getNextLabel("ur")
code.map {
case s@AssemblyLine(_, _, MemoryAddressConstant(Label(l)), _) if localLabels(l) =>
case s@AssemblyLine0(_, _, MemoryAddressConstant(Label(l))) if localLabels(l) =>
s.copy(parameter = MemoryAddressConstant(Label(labelPrefix + l)))
case s => s
}

View File

@ -147,10 +147,10 @@ object ReverseFlowAnalyzer {
}
val currentLine = codeArray(i)
currentLine match {
case AssemblyLine(opcode, Relative | LongRelative, MemoryAddressConstant(Label(l)), _) if OpcodeClasses.ShortConditionalBranching(opcode) =>
case AssemblyLine0(opcode, Relative | LongRelative, MemoryAddressConstant(Label(l))) if OpcodeClasses.ShortConditionalBranching(opcode) =>
val L = l
val labelIndex = codeArray.indexWhere {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
case _ => false
}
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance
@ -158,7 +158,7 @@ object ReverseFlowAnalyzer {
}
currentLine match {
case AssemblyLine(JSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(fun: FunctionInMemory), _) =>
case AssemblyLine0(JSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(fun: FunctionInMemory)) =>
// this case has to be handled first, because the generic JSR importance handler is too conservative
var result = importanceBeforeJsr
fun.params match {
@ -218,15 +218,15 @@ object ReverseFlowAnalyzer {
else result.r3
)
case AssemblyLine(ANC, _, NumericConstant(0, _), _) =>
case AssemblyLine0(ANC, _, NumericConstant(0, _)) =>
currentImportance = currentImportance.copy(c = Unimportant, n = Unimportant, z = Unimportant, a = Unimportant)
case AssemblyLine(AND, _, NumericConstant(0, _), _) =>
case AssemblyLine0(AND, _, NumericConstant(0, _)) =>
currentImportance = currentImportance.copy(n = Unimportant, z = Unimportant, a = Unimportant)
case AssemblyLine(opcode, Implied, _, _) if ReverseFlowAnalyzerPerImpiedOpcode.hasDefinition(opcode) =>
case AssemblyLine0(opcode, Implied, _) if ReverseFlowAnalyzerPerImpiedOpcode.hasDefinition(opcode) =>
currentImportance = ReverseFlowAnalyzerPerImpiedOpcode.get(opcode)(currentImportance)
case AssemblyLine(opcode, addrMode, _, _) if addrMode != Implied && ReverseFlowAnalyzerPerOpcode.hasDefinition(opcode) =>
case AssemblyLine0(opcode, addrMode, _) if addrMode != Implied && ReverseFlowAnalyzerPerOpcode.hasDefinition(opcode) =>
currentImportance = ReverseFlowAnalyzerPerOpcode.get(opcode)(currentImportance)
if (addrMode == AbsoluteX || addrMode == LongAbsoluteX || addrMode == IndexedX || addrMode == ZeroPageX || addrMode == AbsoluteIndexedX)
currentImportance = currentImportance.copy(x = Important)
@ -235,18 +235,18 @@ object ReverseFlowAnalyzer {
else if (addrMode == IndexedZ /*|| addrMode == LongIndexedZ*/ )
currentImportance = currentImportance.copy(iz = Important)
case AssemblyLine(JMP | BRA, Absolute | Relative | LongAbsolute | LongRelative, MemoryAddressConstant(Label(l)), _) =>
case AssemblyLine0(JMP | BRA, Absolute | Relative | LongAbsolute | LongRelative, MemoryAddressConstant(Label(l))) =>
val L = l
val labelIndex = codeArray.indexWhere {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
case _ => false
}
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex)
case AssemblyLine(JMP, Indirect | AbsoluteIndexedX | LongIndirect, _, _) =>
case AssemblyLine0(JMP, Indirect | AbsoluteIndexedX | LongIndirect, _) =>
currentImportance = finalImportance
case AssemblyLine(REP | SEP, _, NumericConstant(n, _), _) =>
case AssemblyLine0(REP | SEP, _, NumericConstant(n, _)) =>
if ((n & 1) != 0) currentImportance = currentImportance.copy(c = Unimportant)
if ((n & 2) != 0) currentImportance = currentImportance.copy(z = Unimportant)
if ((n & 8) != 0) currentImportance = currentImportance.copy(d = Unimportant)
@ -255,7 +255,7 @@ object ReverseFlowAnalyzer {
if ((n & 0x40) != 0) currentImportance = currentImportance.copy(v = Unimportant)
if ((n & 0x80) != 0) currentImportance = currentImportance.copy(n = Unimportant)
case AssemblyLine(opcode, addrMode, _, _) =>
case AssemblyLine0(opcode, addrMode, _) =>
val reallyIgnoreC =
currentImportance.c == Unimportant &&
currentImportance.v == Unimportant &&

View File

@ -63,6 +63,12 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2)
val optimizedChunk: List[AssemblyLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource =
if (optimizedChunk.isEmpty) optimizedChunk
else if (matchedChunkToOptimize.size == 1) optimizedChunk.map(_.pos(matchedChunkToOptimize.head.source))
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk
log.debug(s"Applied $name ($index)")
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
val before = code.head._1.statusBefore
@ -73,12 +79,12 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
if (log.traceEnabled) {
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
log.trace(" ↓")
optimizedChunk.filter(_.isPrintable).foreach(l => log.trace(l.toString))
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
}
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunk ++ optimizeImpl(f, rest, optimizationContext)
return optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)
} else {
return optimize(f, optimizedChunk ++ rest.map(_._2), optimizationContext)
return optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
}
case None => ()
}
@ -191,19 +197,19 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
val jumps = mutable.Set[String]()
get[List[AssemblyLine]](i).foreach {
// JSR and BSR are allowed
case AssemblyLine(Opcode.RTS | Opcode.RTI | Opcode.RTL | Opcode.BRK, _, _, _) =>
case AssemblyLine0(Opcode.RTS | Opcode.RTI | Opcode.RTL | Opcode.BRK, _, _) =>
return false
case AssemblyLine(Opcode.JMP, AddrMode.Indirect | AddrMode.AbsoluteIndexedX | AddrMode.LongIndirect, _, _) =>
case AssemblyLine0(Opcode.JMP, AddrMode.Indirect | AddrMode.AbsoluteIndexedX | AddrMode.LongIndirect, _) =>
return false
case AssemblyLine(Opcode.LABEL, _, MemoryAddressConstant(Label(l)), _) =>
case AssemblyLine0(Opcode.LABEL, _, MemoryAddressConstant(Label(l))) =>
labels += l
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, MemoryAddressConstant(Label(l)), _) =>
case AssemblyLine0(Opcode.JMP, AddrMode.Absolute, MemoryAddressConstant(Label(l))) =>
jumps += l
case AssemblyLine(Opcode.JMP, AddrMode.Absolute | AddrMode.LongAbsolute, _, _) =>
case AssemblyLine0(Opcode.JMP, AddrMode.Absolute | AddrMode.LongAbsolute, _) =>
return false
case AssemblyLine(_, AddrMode.Relative, MemoryAddressConstant(Label(l)), _) =>
case AssemblyLine0(_, AddrMode.Relative, MemoryAddressConstant(Label(l))) =>
jumps += l
case AssemblyLine(br, _, _, _) if OpcodeClasses.ShortBranching(br) =>
case AssemblyLine0(br, _, _) if OpcodeClasses.ShortBranching(br) =>
return false
case _ => ()
}
@ -721,7 +727,7 @@ case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends
case object Elidable extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.elidable
line.elidability == Elidability.Elidable
}
case object DebugMatching extends AssemblyPattern {
@ -849,8 +855,8 @@ case object ChangesA extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesA(th.name)
case AssemblyLine(_, Implied, _, _) => OpcodeClasses.ChangesAIfImplied(line.opcode) || OpcodeClasses.ChangesAAlways(line.opcode)
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesA(th.name)
case AssemblyLine0(_, Implied, _) => OpcodeClasses.ChangesAIfImplied(line.opcode) || OpcodeClasses.ChangesAAlways(line.opcode)
case _ => OpcodeClasses.ChangesAAlways(line.opcode)
}
}
@ -861,7 +867,7 @@ case object ChangesX extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesX(th.name)
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesX(th.name)
case _ => OpcodeClasses.ChangesX(line.opcode)
}
}
@ -872,7 +878,7 @@ case object ChangesY extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesY(th.name)
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesY(th.name)
case _ => OpcodeClasses.ChangesY(line.opcode)
}
}
@ -885,7 +891,7 @@ case object ReadsC extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionReadsC(th.name)
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionReadsC(th.name)
case _ => OpcodeClasses.ReadsC(line.opcode)
}
}
@ -896,7 +902,7 @@ case object ReadsD extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionReadsD(th.name)
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionReadsD(th.name)
case _ => OpcodeClasses.ReadsD(line.opcode)
}
}
@ -911,7 +917,7 @@ case object ChangesC extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesC(th.name)
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesC(th.name)
case _ => OpcodeClasses.ChangesC(line.opcode)
}
}
@ -950,8 +956,8 @@ case object ChangesAH extends AssemblyLinePattern {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesAH(th.name)
case AssemblyLine(_, Implied, _, _) => OpcodeClasses.ChangesAHIfImplied(line.opcode) || OpcodeClasses.ChangesAHAlways(line.opcode)
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesAH(th.name)
case AssemblyLine0(_, Implied, _) => OpcodeClasses.ChangesAHIfImplied(line.opcode) || OpcodeClasses.ChangesAHAlways(line.opcode)
case _ => OpcodeClasses.ChangesAHAlways(line.opcode)
}
}
@ -959,15 +965,15 @@ case object ChangesAH extends AssemblyLinePattern {
case object ChangesM extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = line match {
case AssemblyLine(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _), _) => (n & 0x20) != 0
case AssemblyLine(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _, _) => true
case AssemblyLine0(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _)) => (n & 0x20) != 0
case AssemblyLine0(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _) => true
case _ => false
}
}
case object ChangesW extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = line match {
case AssemblyLine(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _), _) => (n & 0x10) != 0
case AssemblyLine(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _, _) => true
case AssemblyLine0(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _)) => (n & 0x10) != 0
case AssemblyLine0(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _) => true
case _ => false
}
}
@ -977,9 +983,9 @@ case object ChangesMemory extends AssemblyLinePattern {
import AddrMode._
import Opcode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionChangesMemory(th.name)
case AssemblyLine(op, Implied, _, _) => OpcodeClasses.ChangesMemoryAlways(op)
case AssemblyLine(op, _, _, _) => OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op)
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesMemory(th.name)
case AssemblyLine0(op, Implied, _) => OpcodeClasses.ChangesMemoryAlways(op)
case AssemblyLine0(op, _, _) => OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op)
case _ => false
}
}
@ -990,7 +996,7 @@ case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: Opcode.Valu
import AddrMode._
import Opcode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => !ctx.functionChangesMemory(th.name)
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[AddrMode.Value](addrMode1)
@ -1013,7 +1019,7 @@ case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyL
import AddrMode._
import Opcode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => !ctx.functionReadsMemory(th.name) && !ctx.functionChangesMemory(th.name)
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => !ctx.functionReadsMemory(th.name) && !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[AddrMode.Value](addrMode1)
@ -1273,7 +1279,7 @@ case class Before(pattern: AssemblyPattern) extends AssemblyLinePattern {
case class HasCallerCount(count: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line match {
case AssemblyLine(Opcode.LABEL, _, MemoryAddressConstant(Label(l)), _) => flowInfo.labelUseCount(l) == count
case AssemblyLine0(Opcode.LABEL, _, MemoryAddressConstant(Label(l))) => flowInfo.labelUseCount(l) == count
case _ => false
}
}
@ -1300,7 +1306,7 @@ case object IsZeroPage extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
line match {
case AssemblyLine(_, AddrMode.ZeroPage, _, _) => true
case AssemblyLine0(_, AddrMode.ZeroPage, _) => true
case l@AssemblyLine(LDA | STA | CMP |
LDX | STX | CPX |
LDY | STY | CPY |
@ -1308,7 +1314,7 @@ case object IsZeroPage extends AssemblyLinePattern {
BIT |
ADC | SBC | AND | ORA | EOR |
INC | DEC | ROL | ROR | ASL | LSR |
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, true) =>
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
p match {
case NumericConstant(n, _) => n <= 255
case MemoryAddressConstant(th) => ctx.labelMap.getOrElse(th.name, 0x800) < 0x100

View File

@ -1,10 +1,9 @@
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
import millfork.assembly._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, Opcode}
import millfork.env._
import millfork.error.{ConsoleLogger, Logger}
@ -44,11 +43,11 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
return code
}
val stillUsedVariables = code.flatMap {
case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case _ => None
}.toSet
val variablesWithAddressesTaken = code.flatMap {
case AssemblyLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
val localVariables = f.environment.getAllLocalVariables.filter {
@ -77,16 +76,16 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
private def findSourceForVariable(variable: String, code:List[AssemblyLine]): Either[Boolean, List[AssemblyLine]] = code match {
case Nil => Left(true)
case (load@AssemblyLine(LDA, _, _, _)) :: AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(v), true) :: xs
case (load@AssemblyLine0(LDA, _, _)) :: AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(v), Elidability.Elidable, _) :: xs
if v.name == variable =>
findSourceForVariable(variable, xs) match {
case Left(true) => Right(List(load))
case _ => Left(false)
}
case AssemblyLine(LAX | LDX | LDY | ADC | SBC | CMP | CPX | CPY | AND | EOR | ORA, Absolute | ZeroPage, MemoryAddressConstant(v), _) :: xs
case AssemblyLine0(LAX | LDX | LDY | ADC | SBC | CMP | CPX | CPY | AND | EOR | ORA, Absolute | ZeroPage, MemoryAddressConstant(v)) :: xs
if v.name == variable =>
findSourceForVariable(variable, xs)
case AssemblyLine(_, _, MemoryAddressConstant(v), _) :: _
case AssemblyLine0(_, _, MemoryAddressConstant(v)) :: _
if v.name == variable => Left(false)
case x :: xs => findSourceForVariable(variable, xs)
}
@ -106,17 +105,17 @@ object SingleAssignmentVariableOptimization extends AssemblyOptimization[Assembl
private def replaceVariable(variable: String, replacement: List[AssemblyLine], code: List[(AssemblyLine, CpuImportance)]): Option[List[AssemblyLine]] = code match {
case Nil => Some(Nil)
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(v), true), _) :: xs
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(v), Elidability.Elidable, _), _) :: xs
if v.name == variable =>
replaceVariable(variable, replacement, xs)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(v), true), imp) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(v), Elidability.Elidable, _), imp) :: xs
if v.name == variable =>
replaceVariable(variable, replacement, xs).map(replacement ++ _)
case (AssemblyLine(op@(LAX | LDX | LDY | ADC | SBC | CMP | CPX | CPY | AND | EOR | ORA), Absolute | ZeroPage, MemoryAddressConstant(v), true), imp) :: xs
case (AssemblyLine(op@(LAX | LDX | LDY | ADC | SBC | CMP | CPX | CPY | AND | EOR | ORA), Absolute | ZeroPage, MemoryAddressConstant(v), Elidability.Elidable, _), imp) :: xs
if v.name == variable =>
if (isSingleLda(replacement, getAddrModes(op))) replaceVariable(variable, replacement, xs).map(replacement.map(_.copy(opcode = op)) ++ _)
else None
case (AssemblyLine(_, _, MemoryAddressConstant(v), _), _) :: xs if v.name == variable => None
case (AssemblyLine0(_, _, MemoryAddressConstant(v)), _) :: xs if v.name == variable => None
case x :: xs => replaceVariable(variable, replacement, xs).map(x._1 :: _)
}

View File

@ -1,6 +1,6 @@
package millfork.assembly.mos.opt
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0}
import millfork.assembly.mos.Opcode._
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.env._
@ -13,19 +13,19 @@ object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] {
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val usedLabels = code.flatMap {
case AssemblyLine(LABEL, _, _, _) => None
case AssemblyLine(_, _, MemoryAddressConstant(Label(l)), _) => Some(l)
case AssemblyLine0(LABEL, _, _) => None
case AssemblyLine0(_, _, MemoryAddressConstant(Label(l))) => Some(l)
case _ => None
}.toSet
val definedLabels = code.flatMap {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) => Some(l).filter(_.startsWith("."))
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l).filter(_.startsWith("."))
case _ => None
}.toSet
val toRemove = definedLabels -- usedLabels
if (toRemove.nonEmpty) {
optimizationContext.log.debug("Removing labels: " + toRemove.mkString(", "))
code.filterNot {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) => toRemove(l)
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => toRemove(l)
case _ => false
}
} else {

View File

@ -1,7 +1,7 @@
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, NonOverlappingIntervals}
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._
import AddrMode._
@ -10,7 +10,7 @@ import millfork.error.Logger
import millfork.node.MosNiceFunctionProperty
import scala.collection.mutable.ListBuffer
import scala.util.control.TailCalls.{ TailRec, done, tailcall }
import scala.util.control.TailCalls.{TailRec, done, tailcall}
/**
* @author Karol Stasiak
@ -131,11 +131,11 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
return code
}
val stillUsedVariables = code.flatMap {
case AssemblyLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case _ => None
}.toSet
val variablesWithAddressesTaken = code.flatMap {
case AssemblyLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
val localVariables = f.environment.getAllLocalVariables.filter {
@ -354,17 +354,17 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
val vy = yCandidate.getOrElse("-")
val vz = zCandidate.getOrElse("-")
lines match {
case (AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _), _) :: xs
case (AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)), _) :: xs
if th.name == vx || th.name == vy || th.name == vz =>
// if an address of a variable is used, then that variable cannot be assigned to a register
None
case (AssemblyLine(_, AbsoluteX | AbsoluteY | LongAbsoluteX |
case (AssemblyLine0(_, AbsoluteX | AbsoluteY | LongAbsoluteX |
ZeroPageX | ZeroPageY |
IndexedY | IndexedX | IndexedZ |
LongIndexedY | LongIndexedZ |
Indirect | LongIndirect |
AbsoluteIndexedX, MemoryAddressConstant(th), _), _) :: xs =>
AbsoluteIndexedX, MemoryAddressConstant(th)), _) :: xs =>
// if a variable is used as an array or a pointer, then it cannot be assigned to a register
if (th.name == vx || th.name == vy || th.name == vz) {
None
@ -372,50 +372,50 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(SEP | REP, Immediate, NumericConstant(nn, _), _), _) :: xs =>
case (AssemblyLine0(SEP | REP, Immediate, NumericConstant(nn, _)), _) :: xs =>
if ((nn & 0x10) == 0) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
else None
case (AssemblyLine(SEP | REP, _, _, _), _) :: xs => None
case (AssemblyLine0(SEP | REP, _, _), _) :: xs => None
case (AssemblyLine(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx =>
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vx =>
if (features.indexRegisterTransfers) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
else None
case (AssemblyLine(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy =>
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vy =>
if (features.indexRegisterTransfers) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
else None
case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
if opcodesIdentityTable(op) && features.blastProcessing =>
if (th.name == vx || th.name == vy) {
if (elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 0, cycles = -1))
if (elidability == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 0, cycles = -1))
else None
} else {
if (th.name == vz) None
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if th.name == vx && (opcode == LDY || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
// if a variable is used by some opcodes, then it cannot be assigned to a register
None
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if th.name == vy && (opcode == LDX || opcode == LAX || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
// if a variable is used by some opcodes, then it cannot be assigned to a register
None
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if th.name == vz && (opcode == LDX || opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
// if a variable is used by some opcodes, then it cannot be assigned to a register
None
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), imp) :: xs
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), imp) :: xs
if xCandidate.isDefined =>
// if a register is populated with a different variable, then this variable cannot be assigned to that register
// removing LDX saves 3 cycles
if (elidable && th.name == vx) {
if (elidability == Elidability.Elidable && th.name == vx) {
if (imp.z == Unimportant && imp.n == Unimportant) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
} else {
@ -425,21 +425,21 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
None
}
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
if xCandidate.isDefined =>
// LAX = LDX-LDA, and since LDX simplifies to nothing and LDA simplifies to TXA,
// LAX simplifies to TXA, saving two bytes
if (elidable && th.name == vx) {
if (elidability == Elidability.Elidable && th.name == vx) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
} else {
None
}
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), imp) :: xs if yCandidate.isDefined =>
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), imp) :: xs if yCandidate.isDefined =>
// if a register is populated with a different variable, then this variable cannot be assigned to that register
// removing LDX saves 3 bytes
// sometimes that LDX has to be converted into CPX#0
if (elidable && th.name == vy) {
if (elidability == Elidability.Elidable && th.name == vy) {
if (imp.z == Unimportant && imp.n == Unimportant) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
} else {
@ -449,8 +449,8 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
None
}
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), imp) :: xs if zCandidate.isDefined =>
if (elidable && th.name == vz) {
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), imp) :: xs if zCandidate.isDefined =>
if (elidability == Elidability.Elidable && th.name == vz) {
if (imp.z == Unimportant && imp.n == Unimportant) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
} else {
@ -460,73 +460,73 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
None
}
case (AssemblyLine(LDX, _, _, _), _) :: xs if xCandidate.isDefined =>
case (AssemblyLine0(LDX, _, _), _) :: xs if xCandidate.isDefined =>
// if a register is populated with something else than a variable, then no variable cannot be assigned to that register
None
case (AssemblyLine(LDY, _, _, _), _) :: xs if yCandidate.isDefined =>
case (AssemblyLine0(LDY, _, _), _) :: xs if yCandidate.isDefined =>
// if a register is populated with something else than a variable, then no variable cannot be assigned to that register
None
case (AssemblyLine(LDZ, _, _, _), _) :: xs if zCandidate.isDefined =>
case (AssemblyLine0(LDZ, _, _), _) :: xs if zCandidate.isDefined =>
// if a register is populated with something else than a variable, then no variable cannot be assigned to that register
None
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
case (AssemblyLine(LDA, _, _, elidability, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability2, _), _) :: xs
if opcodesCommutative(op) =>
// LDAw/ANDx -> TXA/ANDw
if (th.name == vx || th.name == vy) {
if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
else None
} else {
if (th.name == vz) None
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(CLC, _, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
case (AssemblyLine(LDA, _, _, elidability, _), _) :: (AssemblyLine0(CLC, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability2, _), _) :: xs
if opcodesCommutative(op) =>
if (th.name == vx || th.name == vy) {
if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
else None
} else {
if (th.name == vz) None
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: (AssemblyLine(TAX, _, _, elidable2), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: (AssemblyLine(TAX, _, _, elidability2, _), _) :: xs
if xCandidate.isDefined =>
// a variable cannot be inlined if there is TAX not after LDA of that variable
// but LDA-TAX can be simplified to TXA
if (elidable && elidable2 && th.name == vx) {
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vx) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
} else {
None
}
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: (AssemblyLine(TAY, _, _, elidable2), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: (AssemblyLine(TAY, _, _, elidability2, _), _) :: xs
if yCandidate.isDefined =>
// a variable cannot be inlined if there is TAY not after LDA of that variable
// but LDA-TAY can be simplified to TYA
if (elidable && elidable2 && th.name == vy) {
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vy) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
} else {
None
}
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: (AssemblyLine(TAZ, _, _, elidable2), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: (AssemblyLine(TAZ, _, _, elidability2, _), _) :: xs
if zCandidate.isDefined =>
// a variable cannot be inlined if there is TAZ not after LDA of that variable
// but LDA-TAZ can be simplified to TZA
if (elidable && elidable2 && th.name == vy) {
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vy) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
} else {
None
}
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs =>
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
if (th.name == vy || th.name == vx || th.name == vz) {
if (elidable) {
if (elidability == Elidability.Elidable) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
} else {
None
@ -535,10 +535,10 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs =>
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
if (th.name == vy || th.name == vx || th.name == vz) {
if (elidable) {
if (elidability == Elidability.Elidable) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
} else {
None
@ -547,10 +547,10 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs =>
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
// changing STZ->LDX saves 1 byte
if (th.name == vy || th.name == vx) {
if (elidable && features.izIsAlwaysZero) {
if (elidability == Elidability.Elidable && features.izIsAlwaysZero) {
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 1, cycles = 2))
} else {
None
@ -559,23 +559,23 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
}
case (AssemblyLine(TAX, _, _, _), _) :: xs if xCandidate.isDefined =>
case (AssemblyLine0(TAX, _, _), _) :: xs if xCandidate.isDefined =>
// a variable cannot be inlined if there is TAX not after LDA of that variable
None
case (AssemblyLine(TAY, _, _, _), _) :: xs if yCandidate.isDefined =>
case (AssemblyLine0(TAY, _, _), _) :: xs if yCandidate.isDefined =>
// a variable cannot be inlined if there is TAY not after LDA of that variable
None
case (AssemblyLine(TAZ, _, _, _), _) :: xs if zCandidate.isDefined =>
case (AssemblyLine0(TAZ, _, _), _) :: xs if zCandidate.isDefined =>
// a variable cannot be inlined if there is TAZ not after LDA of that variable
None
case (AssemblyLine(LABEL, _, _, _), _) :: xs =>
case (AssemblyLine0(LABEL, _, _), _) :: xs =>
// labels always end the initial section
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
case (AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _), _) :: xs =>
case (AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)), _) :: xs =>
if (
xCandidate.isDefined && features.functionsSafeForX(th.name) ||
yCandidate.isDefined && features.functionsSafeForY(th.name) ||
@ -601,75 +601,75 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
def canBeInlinedToAccumulator(features: FeaturesForAccumulator, start: Boolean, synced: Boolean, candidate: String, lines: List[(AssemblyLine, CpuImportance)]): Option[CyclesAndBytes] = {
lines match {
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true),_) :: xs
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, _), _) :: xs
if th.name == candidate && start || synced =>
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
case (AssemblyLine(op, _, _, _),_) :: xs if opcodesThatAlwaysPrecludeAAllocation(op) =>
case (AssemblyLine0(op, _, _),_) :: xs if opcodesThatAlwaysPrecludeAAllocation(op) =>
None
case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _),_) :: xs
case (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if th.name == candidate && opcodesThatCannotBeUsedWithAccumulatorAsParameter(op) =>
// if a variable is used by some opcodes, then it cannot be assigned to a register
None
case (AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _),_) :: xs
case (AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)), _) :: xs
if th.name == candidate =>
// if an address of a variable is used, then that variable cannot be assigned to a register
None
case (AssemblyLine(_, AbsoluteX | AbsoluteY | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | Indirect | AbsoluteIndexedX, MemoryAddressConstant(th), _),_) :: xs
case (AssemblyLine0(_, AbsoluteX | AbsoluteY | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | Indirect | AbsoluteIndexedX, MemoryAddressConstant(th)), _) :: xs
if th.name == candidate =>
// if a variable is used as an array or a pointer, then it cannot be assigned to a register
None
case (AssemblyLine(SEP | REP, Immediate, NumericConstant(nn, _), _), _) :: xs =>
case (AssemblyLine0(SEP | REP, Immediate, NumericConstant(nn, _)), _) :: xs =>
if ((nn & 0x20) == 0) canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
else None
case (AssemblyLine(SEP | REP, _, _, _), _) :: xs => None
case (AssemblyLine0(SEP | REP, _, _), _) :: xs => None
case (AssemblyLine(STA, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name == candidate =>
if (synced && elidable) {
case (AssemblyLine(STA, _, MemoryAddressConstant(th), elidability, _), _) :: xs if th.name == candidate =>
if (synced && elidability == Elidability.Elidable) {
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
} else {
None
}
case (AssemblyLine(DCP, Absolute | ZeroPage, MemoryAddressConstant(th), _) ,_):: xs if th.name == candidate =>
case (AssemblyLine0(DCP, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == candidate =>
if (synced) {
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else {
None
}
case (AssemblyLine(STA | SAX, _, MemoryAddressConstant(th), elidable) ,_):: xs if th.name != candidate =>
case (AssemblyLine(STA | SAX, _, MemoryAddressConstant(th), elidability, _), _) :: xs if th.name != candidate =>
if (synced) {
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else {
None
}
case (AssemblyLine(STA | SAX, _, NumericConstant(_, _), _) ,_):: xs =>
case (AssemblyLine0(STA | SAX, _, NumericConstant(_, _)), _) :: xs =>
if (synced) {
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else {
None
}
case (AssemblyLine(SAX, _, MemoryAddressConstant(th), _) ,_):: xs if th.name == candidate =>
case (AssemblyLine0(SAX, _, MemoryAddressConstant(th)), _) :: xs if th.name == candidate =>
// if XAA had stable magic $FF, then SAXv/LDAv would correspond to XAA#ff
// but there's no point in even thinking about that
None
case (AssemblyLine(TAX | TAY, _, _, _),_) :: xs =>
case (AssemblyLine0(TAX | TAY, _, _),_) :: xs =>
if (synced) {
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs)
} else {
None
}
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), imp) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, _), imp) :: xs
if th.name == candidate =>
// removing LDA saves 3 bytes
if (imp.z == Unimportant && imp.n == Unimportant) {
@ -678,58 +678,58 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 1, cycles = 2))
}
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
case (AssemblyLine(LDA, _, _, elidability, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability2, _), _) :: xs
if opcodesCommutative(op) =>
if (th.name == candidate) {
if (elidable && elidable2) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
else None
} else canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(CLC, _, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
case (AssemblyLine(LDA, _, _, elidability, _), _) :: (AssemblyLine0(CLC, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability2, _), _) :: xs
if opcodesCommutative(op) =>
if (th.name == candidate) {
if (elidable && elidable2) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable) canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
else None
} else canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
case (AssemblyLine(LDX | LDY | LAX, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
case (AssemblyLine(LDX | LDY | LAX, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
if th.name == candidate =>
// converting a load into a transfer saves 2 bytes
if (elidable) {
if (elidability == Elidability.Elidable) {
canBeInlinedToAccumulator(features, start = false, synced = true, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
} else {
None
}
case (AssemblyLine(LDA | LAX, _, _, _),_) :: xs =>
case (AssemblyLine0(LDA | LAX, _, _),_) :: xs =>
// if a register is populated with something else than a variable, then no variable cannot be assigned to that register
None
case (AssemblyLine(ASL | LSR | ROR | ROL, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
case (AssemblyLine(ASL | LSR | ROR | ROL, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
if th.name == candidate =>
if (elidable) {
if (elidability == Elidability.Elidable) {
canBeInlinedToAccumulator(features, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
} else {
None
}
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
if th.name == candidate =>
if (features.cmos && elidable) {
if (features.cmos && elidability == Elidability.Elidable) {
canBeInlinedToAccumulator(features, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
} else {
None
}
case (AssemblyLine(TXA | TYA, _, _, elidable), imp) :: xs =>
if (imp.a == Unimportant && imp.c == Unimportant && imp.v == Unimportant && elidable) {
case (AssemblyLine(TXA | TYA, _, _, elidability, _), imp) :: xs =>
if (imp.a == Unimportant && imp.c == Unimportant && imp.v == Unimportant && elidability == Elidability.Elidable) {
// TYA/TXA has to be converted to CPY#0/CPX#0
canBeInlinedToAccumulator(features, start = false, synced = false, candidate, xs).map(_ + CyclesAndBytes(bytes = -1, cycles = 0))
} else {
None
}
case (AssemblyLine(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _), _) :: xs =>
case (AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)), _) :: xs =>
if (features.safeFunctions(th.name)) canBeInlinedToAccumulator(features, start = false, synced = synced, candidate, xs)
else None
@ -756,249 +756,249 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
val vz = zCandidate.getOrElse("-")
val va = aCandidate.getOrElse("-")
lines match {
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map( AssemblyLine.implied(INX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map( AssemblyLine.implied(INX).pos(s) :: _)
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INY) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INY).pos(s) :: _)
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vz =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INZ) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INZ).pos(s) :: _)
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEX).pos(s) :: _)
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEY) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEY).pos(s) :: _)
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vz =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEZ) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEZ).pos(s) :: _)
case (AssemblyLine(opcode@(DEC | INC | ROL | ROR | ASL | LSR), Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(opcode@(DEC | INC | ROL | ROR | ASL | LSR), Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(opcode) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(opcode).pos(s) :: _)
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
if th.name == vx =>
if (imp.z == Unimportant && imp.n == Unimportant) {
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs))
} else {
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPX, 0) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPX, 0).pos(s) :: _)
}
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
case (l@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesIdentityTable(op) && th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(addrMode = AbsoluteX, parameter = features.identityArray) :: _)
case (l@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesIdentityTable(op) && th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(addrMode = AbsoluteY, parameter = features.identityArray) :: _)
case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine0(LDA, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesCommutative(op) && th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(opcode = op) :: _)
case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine0(LDA, _, _), _) :: (clc@AssemblyLine0(CLC, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesCommutative(op) && th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(clc :: l.copy(opcode = op) :: _)
case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine(LDA, _, _, _, s), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesCommutative(op) && th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: l.copy(opcode = op) :: _)
case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine(LDA, _, _, _, s), _) :: (clc@AssemblyLine0(CLC, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesCommutative(op) && th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: clc :: l.copy(opcode = op) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: clc :: l.copy(opcode = op) :: _)
case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine(LDA, _, _, _, s), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesCommutative(op) && th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: l.copy(opcode = op) :: _)
case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (l@AssemblyLine(LDA, _, _, _, s), _) :: (clc@AssemblyLine0(CLC, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
if opcodesCommutative(op) && th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA) :: clc :: l.copy(opcode = op) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: clc :: l.copy(opcode = op) :: _)
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
if th.name == va =>
if (imp.z == Unimportant && imp.n == Unimportant) {
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
} else {
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CMP, 0) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CMP, 0).pos(s) :: _)
}
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
if th.name == vy =>
if (imp.z == Unimportant && imp.n == Unimportant) {
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
} else {
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPY, 0) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPY, 0).pos(s) :: _)
}
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
if th.name == vz =>
if (imp.z == Unimportant && imp.n == Unimportant) {
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
} else {
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPZ, 0) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPZ, 0).pos(s) :: _)
}
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAX, _, _, true), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAX, _, _, Elidability.Elidable, _), _) :: xs
if th.name == vx =>
// these TXA's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAY, _, _, true), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAY, _, _, Elidability.Elidable, _), _) :: xs
if th.name == vy =>
// these TYA's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAZ, _, _, true), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAZ, _, _, Elidability.Elidable, _), _) :: xs
if th.name == vz =>
// these TZA's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TXA, _, _, true), _) :: xs
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TXA, _, _, Elidability.Elidable, _), _) :: xs
if th.name == va =>
// these TAX's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TYA, _, _, true), _) :: xs
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, _), _) :: (AssemblyLine(TYA, _, _, Elidability.Elidable, _), _) :: xs
if th.name == va =>
// these TAY's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY) :: _)
case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs
if th.name == vx && LdxAddrModes(am) =>
// these TXA's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDX, am, param) :: AssemblyLine.implied(TXA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDX, am, param).pos(s1, s2) :: AssemblyLine.implied(TXA).pos(s1, s2) :: _)
case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs
if th.name == vy && LdyAddrModes(am) =>
// these TYA's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDY, am, param) :: AssemblyLine.implied(TYA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDY, am, param).pos(s1, s2) :: AssemblyLine.implied(TYA).pos(s1, s2) :: _)
case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs
if th.name == vz && LdzAddrModes(am) =>
// these TZA's may get optimized away by a different optimization
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDZ, am, param) :: AssemblyLine.implied(TZA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDZ, am, param).pos(s1, s2) :: AssemblyLine.implied(TZA).pos(s1, s2) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
if th.name == vx && CpxyzAddrModes(am) && isNot(vx, param) =>
// ditto
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: AssemblyLine(CPX, am, param) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s1) :: AssemblyLine(CPX, am, param).pos(s2) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
if th.name == vy && CpxyzAddrModes(am) && isNot(vx, param) =>
// ditto
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA) :: AssemblyLine(CPY, am, param) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s1) :: AssemblyLine(CPY, am, param).pos(s2) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
if th.name == vy && CpxyzAddrModes(am) && isNot(vx, param) =>
// ditto
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA) :: AssemblyLine(CPZ, am, param) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s1) :: AssemblyLine(CPZ, am, param).pos(s2) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY).pos(s) :: _)
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX).pos(s) :: _)
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vz =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _)
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _)
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vz =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAZ) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAZ).pos(s) :: _)
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY).pos(s) :: _)
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX) :: _)
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX).pos(s) :: _)
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vx =>
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDX, 0) :: _)
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDX, 0).pos(s) :: _)
else features.log.fatal("Unexpected STZ")
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == vy =>
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDY, 0) :: _)
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDY, 0).pos(s) :: _)
else features.log.fatal("Unexpected STZ")
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
if th.name == va =>
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDA, 0) :: _)
else tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA) :: _)
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDA, 0).pos(s) :: _)
else tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
case (AssemblyLine(TAX, _, _, _), _) :: xs if xCandidate.isDefined =>
case (AssemblyLine0(TAX, _, _), _) :: xs if xCandidate.isDefined =>
features.log.fatal("Unexpected TAX")
case (AssemblyLine(TAY, _, _, _), _) :: xs if yCandidate.isDefined =>
case (AssemblyLine0(TAY, _, _), _) :: xs if yCandidate.isDefined =>
features.log.fatal("Unexpected TAY")
case (AssemblyLine(TAZ, _, _, _), _) :: xs if zCandidate.isDefined =>
case (AssemblyLine0(TAZ, _, _), _) :: xs if zCandidate.isDefined =>
features.log.fatal("Unexpected TAZ")
case (AssemblyLine(TXA, _, _, _), _) :: xs if aCandidate.isDefined =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPX, 0) :: _)
case (AssemblyLine(TXA, _, _, _, s), _) :: xs if aCandidate.isDefined =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPX, 0).pos(s) :: _)
case (AssemblyLine(TYA, _, _, _), _) :: xs if aCandidate.isDefined =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPY, 0) :: _)
case (AssemblyLine(TYA, _, _, _, s), _) :: xs if aCandidate.isDefined =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPY, 0).pos(s) :: _)
case (AssemblyLine(TZA, _, _, _), _) :: xs if aCandidate.isDefined =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPZ, 0) :: _)
case (AssemblyLine(TZA, _, _, _, s), _) :: xs if aCandidate.isDefined =>
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(CPZ, 0).pos(s) :: _)
case (x, _) :: xs => inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs).map(x :: _)

View File

@ -1,10 +1,10 @@
package millfork.assembly.z80
import millfork.CompilationFlag
import millfork.assembly.AbstractCode
import millfork.assembly.{AbstractCode, Elidability, SourceLine}
import millfork.compiler.CompilationContext
import millfork.env.{Constant, Label, NumericConstant, ThingInMemory}
import millfork.node.ZRegister
import millfork.node.{Position, ZRegister}
/**
* @author Karol Stasiak
@ -177,7 +177,24 @@ object ZLine {
def ldViaIxy(x: Boolean, targetOffset: Int, source: ZRegister.Value): ZLine = if (x) ldViaIx(targetOffset, source) else ldViaIy(targetOffset, source)
}
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
object ZLine0 {
@inline
def unapply(a: ZLine): Some[(ZOpcode.Value, ZRegisters, Constant)] = Some(a.opcode, a.registers, a.parameter)
}
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidability: Elidability.Value = Elidability.Elidable, source: Option[SourceLine] = None) extends AbstractCode {
def pos(s: Option[SourceLine]): ZLine = if (s.isEmpty || s == source) this else this.copy(source = s)
def pos(s1: Option[SourceLine], s2: Option[SourceLine]): ZLine = pos(Seq(s1, s2))
def position(s: Option[Position]): ZLine = pos(SourceLine.of(s))
def pos(s: Seq[Option[SourceLine]]): ZLine = pos(SourceLine.merge(s))
def mergePos(s: Seq[Option[SourceLine]]): ZLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s))
def elidable: Boolean = elidability == Elidability.Elidable
override def sizeInBytes: Int = {
import ZOpcode._
@ -268,7 +285,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
override def toString: String = {
import ZOpcode._
opcode match {
val raw = opcode match {
case DISCARD_A => " ; DISCARD_A"
case DISCARD_HL => " ; DISCARD_HL"
case DISCARD_F => " ; DISCARD_F"
@ -350,6 +367,10 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
}
s" $os$ps"
}
source match {
case Some(SourceLine(_, line)) if line > 0 => f"$raw%-30s \t; @ $line%d"
case _ => raw
}
}
def toIntelString: String = {

View File

@ -16,8 +16,8 @@ object AlwaysGoodI80Optimizations {
def change8BitLoadTarget(line: ZLine, newTarget: ZRegister.Value): ZLine = {
line match {
case ZLine(LD, TwoRegistersOffset(_, s, o), p, _) => ZLine(LD, TwoRegistersOffset(newTarget, s, o), p)
case ZLine(LD, TwoRegisters(_, s), p, _) => ZLine(LD, TwoRegisters(newTarget, s), p)
case ZLine0(LD, TwoRegistersOffset(_, s, o), p) => line.copy(registers = TwoRegistersOffset(newTarget, s, o))
case ZLine0(LD, TwoRegisters(_, s), p) => line.copy(registers = TwoRegisters(newTarget, s))
}
}

View File

@ -15,8 +15,8 @@ object AlwaysGoodZ80Optimizations {
def change8BitLoadTarget(line: ZLine, newTarget: ZRegister.Value): ZLine = {
line match {
case ZLine(LD, TwoRegistersOffset(_, s, o), p, _) => ZLine(LD, TwoRegistersOffset(newTarget, s, o), p)
case ZLine(LD, TwoRegisters(_, s), p, _) => ZLine(LD, TwoRegisters(newTarget, s), p)
case ZLine(LD, TwoRegistersOffset(_, s, o), p, _, sl) => line.copy(registers = TwoRegistersOffset(newTarget, s, o))
case ZLine(LD, TwoRegisters(_, s), p, _, sl) => line.copy(registers = TwoRegisters(newTarget, s))
}
}

View File

@ -35,12 +35,12 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
lazy val savingsForRemovingOneStackVariable = {
val localVariableAreaSize = code.flatMap {
case ZLine(_, OneRegisterOffset(ZRegister.MEM_IX_D, offset), _, _) if useIx => Some(offset)
case ZLine(_, TwoRegistersOffset(_, ZRegister.MEM_IX_D, offset), _, _) if useIx => Some(offset)
case ZLine(_, TwoRegistersOffset(ZRegister.MEM_IX_D, _, offset), _, _) if useIx => Some(offset)
case ZLine(_, OneRegisterOffset(ZRegister.MEM_IY_D, offset), _, _) if useIy => Some(offset)
case ZLine(_, TwoRegistersOffset(_, ZRegister.MEM_IY_D, offset), _, _) if useIy => Some(offset)
case ZLine(_, TwoRegistersOffset(ZRegister.MEM_IY_D, _, offset), _, _) if useIy => Some(offset)
case ZLine0(_, OneRegisterOffset(ZRegister.MEM_IX_D, offset), _) if useIx => Some(offset)
case ZLine0(_, TwoRegistersOffset(_, ZRegister.MEM_IX_D, offset), _) if useIx => Some(offset)
case ZLine0(_, TwoRegistersOffset(ZRegister.MEM_IX_D, _, offset), _) if useIx => Some(offset)
case ZLine0(_, OneRegisterOffset(ZRegister.MEM_IY_D, offset), _) if useIy => Some(offset)
case ZLine0(_, TwoRegistersOffset(_, ZRegister.MEM_IY_D, offset), _) if useIy => Some(offset)
case ZLine0(_, TwoRegistersOffset(ZRegister.MEM_IY_D, _, offset), _) if useIy => Some(offset)
case _ => None
}.toSet.size
val prologueAndEpilogue = if (f.returnType.size == 2) CyclesAndBytes(107, 20) else CyclesAndBytes(95, 17)
@ -239,62 +239,62 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
def unapply(c: Int): Option[Int] = if ("IY+" + c == vname) Some(c) else None
}
code match {
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), ThisVar(_), _)) :: xs =>
case (_, ZLine0(LD, TwoRegisters(A, MEM_ABS_8), ThisVar(_))) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(9, 2)))
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), ThisVar(_), _)) :: xs =>
case (_, ZLine0(LD, TwoRegisters(MEM_ABS_8, A), ThisVar(_))) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(9, 2)))
case (_, ZLine(LD, TwoRegistersOffset(reg, MEM_IX_D, ThisOffsetX(_)), _, _)) :: xs =>
case (_, ZLine0(LD, TwoRegistersOffset(reg, MEM_IX_D, ThisOffsetX(_)), _)) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(reg == target, CyclesAndBytes(19, 3), CyclesAndBytes(15, 2)))
case (_, ZLine(LD, TwoRegistersOffset(MEM_IX_D, reg, ThisOffsetX(_)), _, _)) :: xs =>
case (_, ZLine0(LD, TwoRegistersOffset(MEM_IX_D, reg, ThisOffsetX(_)), _)) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(reg == target, CyclesAndBytes(19, 3), CyclesAndBytes(15, 2)))
case (_, ZLine(_, OneRegisterOffset(MEM_IX_D, ThisOffsetX(_)), _, _)) :: xs =>
case (_, ZLine0(_, OneRegisterOffset(MEM_IX_D, ThisOffsetX(_)), _)) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(15, 2)))
case (_, ZLine(LD, TwoRegistersOffset(reg, MEM_IY_D, ThisOffsetY(_)), _, _)) :: xs =>
case (_, ZLine0(LD, TwoRegistersOffset(reg, MEM_IY_D, ThisOffsetY(_)), _)) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(reg == target, CyclesAndBytes(19, 3), CyclesAndBytes(15, 2)))
case (_, ZLine(LD, TwoRegistersOffset(MEM_IY_D, reg, ThisOffsetY(_)), _, _)) :: xs =>
case (_, ZLine0(LD, TwoRegistersOffset(MEM_IY_D, reg, ThisOffsetY(_)), _)) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(reg == target, CyclesAndBytes(19, 3), CyclesAndBytes(15, 2)))
case (_, ZLine(_, OneRegisterOffset(MEM_IY_D, ThisOffsetY(_)), _, _)) :: xs =>
case (_, ZLine0(_, OneRegisterOffset(MEM_IY_D, ThisOffsetY(_)), _)) :: xs =>
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(15, 2)))
case (_, ZLine(LD_16, TwoRegisters(HL, IMM_16), ThisVar(_), _)) :: xs =>
case (_, ZLine0(LD_16, TwoRegisters(HL, IMM_16), ThisVar(_))) :: xs =>
if (target == H || target == L) fail(61) else
canBeInlined(vname, synced, target, addressInHl = Some(true), addressInBc, addressInDe, xs).map(add(CyclesAndBytes(10, 3)))
case (_, ZLine(LD_16, TwoRegisters(BC, IMM_16), ThisVar(_), _)) :: xs =>
case (_, ZLine0(LD_16, TwoRegisters(BC, IMM_16), ThisVar(_))) :: xs =>
if (target == B || target == C) fail(61) else
canBeInlined(vname, synced, target, addressInHl, addressInBc = Some(true), addressInDe, xs).map(add(CyclesAndBytes(10, 3)))
case (_, ZLine(LD_16, TwoRegisters(DE, IMM_16), ThisVar(_), _)) :: xs =>
case (_, ZLine0(LD_16, TwoRegisters(DE, IMM_16), ThisVar(_))) :: xs =>
if (target == D || target == E) fail(61) else
canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe = Some(true), xs).map(add(CyclesAndBytes(10, 3)))
case (_, ZLine(_, OneRegister(MEM_HL), _, _)) :: xs => addressInHl match {
case (_, ZLine0(_, OneRegister(MEM_HL), _)) :: xs => addressInHl match {
case Some(true) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(3, 0)))
case Some(false) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs)
case None => fail(70)
}
case (_, ZLine(LD, TwoRegisters(MEM_HL, NotTarget(_)) | TwoRegisters(NotTarget(_), MEM_HL), _, _)) :: xs => addressInHl match {
case (_, ZLine0(LD, TwoRegisters(MEM_HL, NotTarget(_)) | TwoRegisters(NotTarget(_), MEM_HL), _)) :: xs => addressInHl match {
case Some(true) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(3, 0)))
case Some(false) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs)
case None => fail(71)
}
case (_, ZLine(LD, TwoRegisters(MEM_BC, NotTarget(_)) | TwoRegisters(NotTarget(_), MEM_BC), _, _)) :: xs => addressInBc match {
case (_, ZLine0(LD, TwoRegisters(MEM_BC, NotTarget(_)) | TwoRegisters(NotTarget(_), MEM_BC), _)) :: xs => addressInBc match {
case Some(true) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(3, 0)))
case Some(false) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs)
case None => fail(72)
}
case (_, ZLine(LD, TwoRegisters(MEM_DE, NotTarget(_)) | TwoRegisters(NotTarget(_), MEM_DE), _, _)) :: xs => addressInDe match {
case (_, ZLine0(LD, TwoRegisters(MEM_DE, NotTarget(_)) | TwoRegisters(NotTarget(_), MEM_DE), _)) :: xs => addressInDe match {
case Some(true) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(3, 0)))
case Some(false) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs)
case None => fail(73)
}
case (_, ZLine(_, _, SubbyteConstant(ThisVar(_), _), _)) :: _ => fail(6)
case (_, ZLine(_, _, ThisVar(_), _)) :: _ => fail(4)
case (_, ZLine(_, _, CompoundConstant(_, ThisVar(_), _), _)) :: _ => fail(5)
case (_, ZLine0(_, _, SubbyteConstant(ThisVar(_), _))) :: _ => fail(6)
case (_, ZLine0(_, _, ThisVar(_))) :: _ => fail(4)
case (_, ZLine0(_, _, CompoundConstant(_, ThisVar(_), _))) :: _ => fail(5)
case (_, ZLine(CALL, _, _, _)) :: xs =>
case (_, ZLine0(CALL, _, _)) :: xs =>
// TODO: check return type and allow HL sometimes
target match {
case ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E =>
@ -307,7 +307,7 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
case (_, x) :: xs if x.readsRegister(target) && !synced => fail(2)
// TODO: tossing addresses around:
// case (_, ZLine(LD, TwoRegisters(H, B), _, _)) :: (_, ZLine(LD, TwoRegisters(L, C), _, _)) :: xs =>
// case (_, ZLine0(LD, TwoRegisters(H, B), _)) :: (_, ZLine0(LD, TwoRegisters(L, C), _)) :: xs =>
case (_, x) :: xs if x.changesRegister(HL) => canBeInlined(vname, synced, target, addressInHl = Some(false), addressInBc, addressInDe, xs)
@ -318,7 +318,7 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
case (_, x) :: xs if x.readsRegister(BC) && addressInBc.contains(true) => fail(82)
case (_, x) :: xs if x.readsRegister(DE) && addressInDe.contains(true) => fail(83)
case (_, ZLine(LABEL, _, _, _)) :: xs => canBeInlined(vname, synced = false, target, addressInHl, addressInBc, addressInDe, xs)
case (_, ZLine0(LABEL, _, _)) :: xs => canBeInlined(vname, synced = false, target, addressInHl, addressInBc, addressInDe, xs)
case _ :: xs => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs)
case _ => Some(CyclesAndBytes.Zero)
}
@ -327,69 +327,69 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
def inlineVars(vname: String, target: ZRegister.Value, addressInHl: Boolean, addressInBc: Boolean, addressInDe: Boolean, code: List[ZLine]): List[ZLine] = {
// if (code.nonEmpty) println(code.head)
code match {
case ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _) :: xs if th.name == vname =>
ZLine.ld8(A, target) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _) :: xs if th.name == vname =>
ZLine.ld8(target, A) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s) :: xs if th.name == vname =>
ZLine.ld8(A, target).pos(s) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s) :: xs if th.name == vname =>
ZLine.ld8(target, A).pos(s) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegistersOffset(reg, MEM_IX_D, off), _, _) :: xs if "IX+" + off == vname =>
case ZLine(LD, TwoRegistersOffset(reg, MEM_IX_D, off), _, _, s) :: xs if "IX+" + off == vname =>
if (reg == target) inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
else ZLine.ld8(reg, target) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegistersOffset(MEM_IX_D, reg, off), _, _) :: xs if "IX+" + off == vname =>
else ZLine.ld8(reg, target).pos(s) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegistersOffset(MEM_IX_D, reg, off), _, _, s) :: xs if "IX+" + off == vname =>
if (reg == target) inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
else ZLine.ld8(target, reg) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (l@ZLine(_, OneRegisterOffset(MEM_IX_D, off), _, _)) :: xs if "IX+" + off == vname =>
else ZLine.ld8(target, reg).pos(s) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (l@ZLine0(_, OneRegisterOffset(MEM_IX_D, off), _)) :: xs if "IX+" + off == vname =>
l.copy(registers = OneRegister(target)) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegistersOffset(reg, MEM_IY_D, off), _, _) :: xs if "IY+" + off == vname =>
case ZLine(LD, TwoRegistersOffset(reg, MEM_IY_D, off), _, _, s) :: xs if "IY+" + off == vname =>
if (reg == target) inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
else ZLine.ld8(reg, target) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegistersOffset(MEM_IY_D, reg, off), _, _) :: xs if "IY+" + off == vname =>
else ZLine.ld8(reg, target).pos(s) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD, TwoRegistersOffset(MEM_IY_D, reg, off), _, _, s) :: xs if "IY+" + off == vname =>
if (reg == target) inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
else ZLine.ld8(target, reg) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (l@ZLine(_, OneRegisterOffset(MEM_IY_D, off), _, _)) :: xs if "IY+" + off == vname =>
else ZLine.ld8(target, reg).pos(s) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (l@ZLine0(_, OneRegisterOffset(MEM_IY_D, off), _)) :: xs if "IY+" + off == vname =>
l.copy(registers = OneRegister(target)) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZLine(LD_16, TwoRegisters(HL, IMM_16), MemoryAddressConstant(th), _) :: xs if th.name == vname =>
case ZLine0(LD_16, TwoRegisters(HL, IMM_16), MemoryAddressConstant(th)) :: xs if th.name == vname =>
inlineVars(vname, target, addressInHl = true, addressInBc, addressInDe, xs)
case ZLine(LD_16, TwoRegisters(BC, IMM_16), MemoryAddressConstant(th), _) :: xs if th.name == vname =>
case ZLine0(LD_16, TwoRegisters(BC, IMM_16), MemoryAddressConstant(th)) :: xs if th.name == vname =>
inlineVars(vname, target, addressInHl, addressInBc = true, addressInDe, xs)
case ZLine(LD_16, TwoRegisters(DE, IMM_16), MemoryAddressConstant(th), _) :: xs if th.name == vname =>
case ZLine0(LD_16, TwoRegisters(DE, IMM_16), MemoryAddressConstant(th)) :: xs if th.name == vname =>
inlineVars(vname, target, addressInHl, addressInBc, addressInDe = true, xs)
case (x@ZLine(_, OneRegister(MEM_HL), _, _)) :: xs if addressInHl =>
case (x@ZLine0(_, OneRegister(MEM_HL), _)) :: xs if addressInHl =>
x.copy(registers = OneRegister(target)) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(LD, TwoRegisters(MEM_HL, reg), p, _)) :: xs if addressInHl =>
case (x@ZLine0(LD, TwoRegisters(MEM_HL, reg), p)) :: xs if addressInHl =>
x.copy(registers = TwoRegisters(target, reg), parameter = p) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(LD, TwoRegisters(reg, MEM_HL), p, _)) :: xs if addressInHl =>
case (x@ZLine0(LD, TwoRegisters(reg, MEM_HL), p)) :: xs if addressInHl =>
x.copy(registers = TwoRegisters(reg, target), parameter = p) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(LD, TwoRegisters(MEM_BC, reg), p, _)) :: xs if addressInBc =>
case (x@ZLine0(LD, TwoRegisters(MEM_BC, reg), p)) :: xs if addressInBc =>
x.copy(registers = TwoRegisters(target, reg), parameter = p) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(LD, TwoRegisters(reg, MEM_BC), p, _)) :: xs if addressInBc =>
case (x@ZLine0(LD, TwoRegisters(reg, MEM_BC), p)) :: xs if addressInBc =>
x.copy(registers = TwoRegisters(reg, target), parameter = p) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(LD, TwoRegisters(MEM_DE, reg), p, _)) :: xs if addressInDe =>
case (x@ZLine0(LD, TwoRegisters(MEM_DE, reg), p)) :: xs if addressInDe =>
x.copy(registers = TwoRegisters(target, reg), parameter = p) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(LD, TwoRegisters(reg, MEM_DE), p, _)) :: xs if addressInDe =>
case (x@ZLine0(LD, TwoRegisters(reg, MEM_DE), p)) :: xs if addressInDe =>
x.copy(registers = TwoRegisters(reg, target), parameter = p) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case (x@ZLine(CALL,_,_,_))::xs =>
case (x@ZLine(CALL,_,_,_,s))::xs =>
// TODO: this push/pull pair shouldn't prevent the inlining to the other register in the pair
target match {
case ZRegister.B | ZRegister.C =>
ZLine.register(PUSH, BC) :: x :: ZLine.register(POP, BC) ::
ZLine.register(PUSH, BC).pos(s) :: x :: ZLine.register(POP, BC).pos(s) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
case ZRegister.D | ZRegister.E =>
ZLine.register(PUSH, DE) :: x :: ZLine.register(POP, DE) ::
ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) ::
inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs)
}

View File

@ -37,15 +37,15 @@ object CoarseFlowAnalyzer {
flagArray(i) = currentStatus
}
codeArray(i) match {
case ZLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
case ZLine0(LABEL, _, MemoryAddressConstant(Label(l))) =>
val L = l
currentStatus = codeArray.indices.flatMap(j => codeArray(j) match {
case ZLine(DJNZ, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j).copy(b = AnyStatus))
case ZLine(_, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j))
case ZLine0(DJNZ, _, MemoryAddressConstant(Label(L))) => Some(flagArray(j).copy(b = AnyStatus))
case ZLine0(_, _, MemoryAddressConstant(Label(L))) => Some(flagArray(j))
case _ => None
}).fold(currentStatus)(_ ~ _)
case ZLine(CALL, _, MemoryAddressConstant(fun: FunctionInMemory), _) =>
case ZLine0(CALL, _, MemoryAddressConstant(fun: FunctionInMemory)) =>
val n = fun.name
val result = initialStatus.copy(memIx = currentStatus.memIx)
currentStatus = result.copy(
@ -57,12 +57,12 @@ object CoarseFlowAnalyzer {
l = if (preservesL(n)) currentStatus.l else result.l
)
case ZLine(CALL, _, _, _) =>
case ZLine0(CALL, _, _) =>
currentStatus = initialStatus.copy(memIx = currentStatus.memIx)
case ZLine(BYTE, _, _, _) =>
case ZLine0(BYTE, _, _) =>
currentStatus = initialStatus
case ZLine(ADD, OneRegister(ZRegister.IMM_8), NumericConstant(0, _), _) =>
case ZLine0(ADD, OneRegister(ZRegister.IMM_8), NumericConstant(0, _)) =>
currentStatus = currentStatus.copy(
nf = Status.SingleFalse,
cf = Status.SingleFalse,
@ -70,7 +70,7 @@ object CoarseFlowAnalyzer {
sf = currentStatus.a.map(_.&(0x80) == 0),
pf = if (z80) Status.SingleFalse else AnyStatus,
hf = Status.SingleFalse)
case ZLine(SUB, OneRegister(ZRegister.IMM_8), NumericConstant(0, _), _) =>
case ZLine0(SUB, OneRegister(ZRegister.IMM_8), NumericConstant(0, _)) =>
currentStatus = currentStatus.copy(
nf = Status.SingleTrue,
cf = Status.SingleFalse,
@ -78,96 +78,96 @@ object CoarseFlowAnalyzer {
sf = currentStatus.a.map(_.&(0x80) == 0),
pf = if (z80) Status.SingleFalse else AnyStatus,
hf = Status.SingleFalse)
case l@ZLine(ADD, OneRegister(s), _, _) =>
case l@ZLine0(ADD, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xff),
nf = Status.SingleFalse, cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(SUB, OneRegister(s), _, _) =>
case ZLine0(SUB, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m - n) & 0xff),
nf = Status.SingleTrue, cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(AND, OneRegister(s), _, _) =>
case ZLine0(AND, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m & n) & 0xff),
nf = Status.SingleFalse, cf = Status.SingleFalse, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(OR, OneRegister(ZRegister.A), _, _) =>
case ZLine0(OR, OneRegister(ZRegister.A), _) =>
currentStatus = currentStatus.copy(nf = Status.SingleFalse, cf = Status.SingleFalse, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(XOR, OneRegister(ZRegister.A), _, _) =>
case ZLine0(XOR, OneRegister(ZRegister.A), _) =>
currentStatus = currentStatus.copy(a = Status.SingleZero, nf = Status.SingleFalse, cf = Status.SingleFalse, zf = Status.SingleTrue, sf = Status.SingleFalse, pf = AnyStatus, hf = AnyStatus)
case ZLine(OR, OneRegister(s), _, _) =>
case ZLine0(OR, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m | n) & 0xff),
nf = Status.SingleFalse, cf = Status.SingleFalse, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(XOR, OneRegister(s), _, _) =>
case ZLine0(XOR, OneRegister(s), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m ^ n) & 0xff),
nf = Status.SingleFalse, cf = Status.SingleFalse, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(INC, OneRegister(r), _, _) =>
case ZLine0(INC, OneRegister(r), _) =>
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, currentStatus.getRegister(r).map(i => i.+(1).&(0xff)))
case ZLine(DEC, OneRegister(r), _, _) =>
case ZLine0(DEC, OneRegister(r), _) =>
currentStatus = currentStatus.
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
setRegister(r, currentStatus.getRegister(r).map(i => i.-(1).&(0xff)))
case ZLine(op, OneRegister(r), _, _) if ZOpcodeClasses.SET(op) =>
case ZLine0(op, OneRegister(r), _) if ZOpcodeClasses.SET(op) =>
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i | 1.<<(ZOpcodeClasses.SET_seq.indexOf(op))))
case ZLine(op, OneRegister(r), _, _) if ZOpcodeClasses.RES(op) =>
case ZLine0(op, OneRegister(r), _) if ZOpcodeClasses.RES(op) =>
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i & ~1.<<(ZOpcodeClasses.RES_seq.indexOf(op))))
case ZLine(ADD, OneRegisterOffset(s, o), _, _) =>
case ZLine0(ADD, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m + n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(SUB, OneRegisterOffset(s, o), _, _) =>
case ZLine0(SUB, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m - n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(AND, OneRegisterOffset(s, o), _, _) =>
case ZLine0(AND, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m & n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(OR, OneRegisterOffset(s, o), _, _) =>
case ZLine0(OR, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m | n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(XOR, OneRegisterOffset(s, o), _, _) =>
case ZLine0(XOR, OneRegisterOffset(s, o), _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m ^ n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(CP, _, _, _) =>
case ZLine0(CP, _, _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(LD_16, TwoRegisters(t, ZRegister.IMM_16), NumericConstant(value, _), _) =>
case ZLine0(LD_16, TwoRegisters(t, ZRegister.IMM_16), NumericConstant(value, _)) =>
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
case ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), xx, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), xx) =>
currentStatus = currentStatus.setHL(SingleStatus(xx))
case ZLine(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _), _) =>
case ZLine0(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _)) =>
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
case ZLine(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _), _) =>
case ZLine0(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _)) =>
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt), o)
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
case ZLine0(LD | LD_16, TwoRegisters(t, s), _) =>
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s))
case ZLine(LD | LD_16, TwoRegistersOffset(t, s, o), _, _) =>
case ZLine0(LD | LD_16, TwoRegistersOffset(t, s, o), _) =>
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s, o), o)
case ZLine(EX_DE_HL, _, _, _) =>
case ZLine0(EX_DE_HL, _, _) =>
currentStatus = currentStatus.copy(d = currentStatus.h, e = currentStatus.l, h = currentStatus.d, l = currentStatus.e)
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
case ZLine0(ADD_16, TwoRegisters(t, s), _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
.setRegister(t, (currentStatus.getRegister(t) <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xffff))
case ZLine(SLA, OneRegister(r), _, _) =>
case ZLine0(SLA, OneRegister(r), _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
.setRegister(r, currentStatus.getRegister(r).map(_.<<(1).&(0xff)))
case ZLine(SRL, OneRegister(r), _, _) =>
case ZLine0(SRL, OneRegister(r), _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
.setRegister(r, currentStatus.getRegister(r).map(_.>>(1).&(0x7f)))
case ZLine(RLA | RRA | RLCA | RRCA, _, _, _) =>
case ZLine0(RLA | RRA | RLCA | RRCA, _, _) =>
currentStatus = currentStatus.copy(
a = AnyStatus, cf = AnyStatus,
zf = AnyStatus,
pf = AnyStatus, hf = Status.SingleFalse)
case ZLine(SCF, _, _, _) =>
case ZLine0(SCF, _, _) =>
currentStatus = currentStatus.copy(cf = Status.SingleTrue, hf = Status.SingleFalse, nf = Status.SingleFalse)
case ZLine(CCF, _, _, _) =>
case ZLine0(CCF, _, _) =>
currentStatus = currentStatus.copy(cf = currentStatus.cf.negate, hf = AnyStatus, nf = AnyStatus)
case ZLine(opcode, registers, _, _) =>
case ZLine0(opcode, registers, _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
if (ZOpcodeClasses.ChangesBCAlways(opcode)) currentStatus = currentStatus.copy(b = AnyStatus, c = AnyStatus)

View File

@ -1,7 +1,7 @@
package millfork.assembly.z80.opt
import millfork.CompilationFlag
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.z80._
import millfork.env.{MemoryAddressConstant, NormalFunction, NumericConstant}
import millfork.node.ZRegister
@ -29,11 +29,11 @@ object CompactStackFrame extends AssemblyOptimization[ZLine] {
import millfork.assembly.z80.ZOpcode._
import millfork.node.ZRegister._
code match {
case (name@ZLine(LABEL, _, _, _)) ::
ZLine(PUSH, OneRegister(Index), _, true) ::
ZLine(LD_16, TwoRegisters(Index, IMM_16), NumericConstant(negativeSize, _), true) ::
ZLine(ADD_16, TwoRegisters(Index, SP), _, true) ::
ZLine(LD_16, TwoRegisters(SP, Index), _, true) :: tail =>
case (name@ZLine0(LABEL, _, _)) ::
ZLine(PUSH, OneRegister(Index), _, Elidability.Elidable, s1) ::
ZLine(LD_16, TwoRegisters(Index, IMM_16), NumericConstant(negativeSize, _), Elidability.Elidable, s2) ::
ZLine(ADD_16, TwoRegisters(Index, SP), _, Elidability.Elidable, s3) ::
ZLine(LD_16, TwoRegisters(SP, Index), _, Elidability.Elidable, s4) :: tail =>
val sourceSize = (-negativeSize).&(0xffff).toInt
val usedOffsets: Set[Int] = findUsedOffsets(tail, Index match {
case IX => MEM_IX_D
@ -42,10 +42,10 @@ object CompactStackFrame extends AssemblyOptimization[ZLine] {
val targetSize = usedOffsets.size + usedOffsets.size.&(1)
if (targetSize == sourceSize) None else {
val prologue = if (targetSize == 0) Nil else List(
ZLine.register(PUSH, Index),
ZLine.ldImm16(Index, 0x10000 - targetSize),
ZLine.registers(ADD_16, Index, SP),
ZLine.ld16(SP, Index))
ZLine.register(PUSH, Index).pos(s1),
ZLine.ldImm16(Index, 0x10000 - targetSize).pos(s2),
ZLine.registers(ADD_16, Index, SP).pos(s3),
ZLine.ld16(SP, Index).pos(s4))
val map = usedOffsets.toSeq.sorted.zipWithIndex.toMap
optimizeContinue(tail, Index, sourceSize, targetSize, map).map { optTail =>
(name :: prologue ++ optTail, sourceSize, targetSize)
@ -59,9 +59,9 @@ object CompactStackFrame extends AssemblyOptimization[ZLine] {
def findUsedOffsets(code: List[ZLine], Mem: ZRegister.Value): Set[Int] = {
code.flatMap {
case ZLine(_, OneRegisterOffset(Mem, offset), _, _) => Some(offset)
case ZLine(_, TwoRegistersOffset(_, Mem, offset), _, _) => Some(offset)
case ZLine(_, TwoRegistersOffset(Mem, _, offset), _, _) => Some(offset)
case ZLine0(_, OneRegisterOffset(Mem, offset), _) => Some(offset)
case ZLine0(_, TwoRegistersOffset(_, Mem, offset), _) => Some(offset)
case ZLine0(_, TwoRegistersOffset(Mem, _, offset), _) => Some(offset)
case _ => None
}.toSet
}
@ -74,42 +74,42 @@ object CompactStackFrame extends AssemblyOptimization[ZLine] {
case IY => MEM_IY_D
}
code match {
case (head@ZLine(_, TwoRegistersOffset(reg, Mem, offset), _, _)) :: tail =>
case (head@ZLine0(_, TwoRegistersOffset(reg, Mem, offset), _)) :: tail =>
optimizeContinue(tail, Index, sourceSize, targetSize, mapping).map(
head.copy(registers = TwoRegistersOffset(reg, Mem, mapping(offset))) :: _)
case (head@ZLine(_, TwoRegistersOffset(Mem, reg, offset), _, _)) :: tail =>
case (head@ZLine0(_, TwoRegistersOffset(Mem, reg, offset), _)) :: tail =>
optimizeContinue(tail, Index, sourceSize, targetSize, mapping).map(
head.copy(registers = TwoRegistersOffset(Mem, reg, mapping(offset))) :: _)
case (head@ZLine(_, OneRegisterOffset(Mem, offset), _, _)) :: tail =>
case (head@ZLine0(_, OneRegisterOffset(Mem, offset), _)) :: tail =>
optimizeContinue(tail, Index, sourceSize, targetSize, mapping).map(
head.copy(registers = OneRegisterOffset(Mem, mapping(offset))) :: _)
case
ZLine(LD_16, TwoRegisters(Index, IMM_16), NumericConstant(size, _), _) ::
ZLine(ADD_16, TwoRegisters(Index, SP), _, _) ::
ZLine(LD_16, TwoRegisters(SP, Index), _, _) ::
ZLine(POP, OneRegister(Index), _, _) :: tail =>
ZLine(LD_16, TwoRegisters(Index, IMM_16), NumericConstant(size, _), _, s1) ::
ZLine(ADD_16, TwoRegisters(Index, SP), _, _, s2) ::
ZLine(LD_16, TwoRegisters(SP, Index), _, _, s3) ::
ZLine(POP, OneRegister(Index), _, _, s4) :: tail =>
if (size != sourceSize) None
else {
stripReturn(tail).flatMap {
case (ret, rest) =>
val epilogue = if (targetSize == 0) Nil else {
List(
ZLine.ldImm16(Index, targetSize),
ZLine.registers(ADD_16, Index, SP),
ZLine.ld16(SP, Index),
ZLine.register(POP, Index))
ZLine.ldImm16(Index, targetSize).pos(s1),
ZLine.registers(ADD_16, Index, SP).pos(s2),
ZLine.ld16(SP, Index).pos(s3),
ZLine.register(POP, Index).pos(s4))
}
optimizeContinue(rest, Index, sourceSize, targetSize, mapping).map(epilogue ++ ret ++ _)
}
}
case
ZLine(LD_16, TwoRegisters(HL, IMM_16), NumericConstant(size, _), _) ::
ZLine(ADD_16, TwoRegisters(HL, SP), _, _) ::
ZLine(LD_16, TwoRegisters(SP, HL), _, _) ::
ZLine(POP, OneRegister(Index), _, _) :: tail =>
ZLine(LD_16, TwoRegisters(HL, IMM_16), NumericConstant(size, _), _, s1) ::
ZLine(ADD_16, TwoRegisters(HL, SP), _, _, s2) ::
ZLine(LD_16, TwoRegisters(SP, HL), _, _, s3) ::
ZLine(POP, OneRegister(Index), _, _, s4) :: tail =>
if (size != sourceSize) {
println("Mismatched stack frame sizes")
None
@ -118,16 +118,16 @@ object CompactStackFrame extends AssemblyOptimization[ZLine] {
case (ret, rest) =>
val epilogue = if (targetSize == 0) Nil else {
List(
ZLine.ldImm16(HL, targetSize),
ZLine.registers(ADD_16, HL, SP),
ZLine.ld16(SP, HL),
ZLine.register(POP, Index))
ZLine.ldImm16(HL, targetSize).pos(s1),
ZLine.registers(ADD_16, HL, SP).pos(s2),
ZLine.ld16(SP, HL).pos(s3),
ZLine.register(POP, Index).pos(s4))
}
optimizeContinue(rest, Index, sourceSize, targetSize, mapping).map(epilogue ++ ret ++ _)
}
}
case ZLine(RET | RETI | RETN | BYTE, _, _, _) :: _ => None
case ZLine(JP, _, MemoryAddressConstant(f: NormalFunction), _) :: _ => None
case ZLine0(RET | RETI | RETN | BYTE, _, _) :: _ => None
case ZLine0(JP, _, MemoryAddressConstant(f: NormalFunction)) :: _ => None
case x :: _ if x.changesRegister(Index) => None
case x :: xs => optimizeContinue(xs, Index, sourceSize, targetSize, mapping).map(x :: _)
case Nil => Some(Nil)
@ -140,8 +140,8 @@ object CompactStackFrame extends AssemblyOptimization[ZLine] {
import millfork.assembly.z80.ZOpcode._
val potentialResult = (discards :+ rest.head) -> rest.tail
rest.head match {
case ZLine(RET | RETI | RETN, _, _, _) => Some(potentialResult)
case ZLine(JP, NoRegisters, MemoryAddressConstant(f: NormalFunction), _) => Some(potentialResult)
case ZLine0(RET | RETI | RETN, _, _) => Some(potentialResult)
case ZLine0(JP, NoRegisters, MemoryAddressConstant(f: NormalFunction)) => Some(potentialResult)
case _ => None
}
}

View File

@ -1,10 +1,10 @@
package millfork.assembly.z80.opt
import millfork.assembly.Elidability
import millfork.assembly.z80.ZOpcode._
import millfork.assembly.z80._
import millfork.env.{Constant, Label, MemoryAddressConstant, NormalFunction}
import millfork.parser.Preprocessor.IfContext
import millfork.{CompilationFlag, CompilationOptions}
import millfork.env.{Constant, Label, MemoryAddressConstant}
import millfork.CompilationOptions
/**
* @author Karol Stasiak
@ -12,18 +12,18 @@ import millfork.{CompilationFlag, CompilationOptions}
object ConditionalInstructions {
def apply(options: CompilationOptions, code: List[ZLine]): List[ZLine] = code match {
case (jump@ZLine(JR | JP, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(label)), true)) ::
(call@ZLine(CALL, NoRegisters, _, _)) ::
case (jump@ZLine(JR | JP, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(label)), Elidability.Elidable, _)) ::
(call@ZLine0(CALL, NoRegisters, _)) ::
xs =>
if (startsWithLabel(label, xs)) {
val condCall = call.copy(registers = jump.registers.negate)
options.log.debug(s"Replacing ${jump.toString.trim} ${call.toString.trim} with ${condCall.toString.trim}")
condCall :: apply(options, xs)
}else jump :: call :: apply(options, xs)
case (jump@ZLine(JR | JP, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(label)), true)) :: xs =>
case (jump@ZLine(JR | JP, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(label)), Elidability.Elidable, sl)) :: xs =>
retPrecededByDiscards(xs) match {
case Some(rest) if startsWithLabel(label, rest) =>
val condRet = ZLine(RET, jump.registers.negate, Constant.Zero)
val condRet = ZLine(RET, jump.registers.negate, Constant.Zero).pos(sl)
options.log.debug(s"Replacing ${jump.toString.trim} RET with ${condRet.toString.trim}")
condRet :: apply(options, rest)
@ -35,17 +35,17 @@ object ConditionalInstructions {
private def retPrecededByDiscards(code: List[ZLine]): Option[List[ZLine]] = {
code match {
case ZLine(op, _, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => retPrecededByDiscards(xs)
case ZLine(RET, NoRegisters, _, _) :: xs => Some(xs)
case ZLine0(op, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => retPrecededByDiscards(xs)
case ZLine0(RET, NoRegisters, _) :: xs => Some(xs)
case _ => None
}
}
private def startsWithLabel(LabelName: String, code: List[ZLine]): Boolean = {
code match {
case ZLine(LABEL, _, MemoryAddressConstant(Label(LabelName)), _) :: xs => true
case ZLine(LABEL, _, _, _) :: xs => startsWithLabel(LabelName, xs)
case ZLine(op, _, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => startsWithLabel(LabelName, xs)
case ZLine0(LABEL, _, MemoryAddressConstant(Label(LabelName))) :: xs => true
case ZLine0(LABEL, _, _) :: xs => startsWithLabel(LabelName, xs)
case ZLine0(op, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => startsWithLabel(LabelName, xs)
case _ => false
}
}

View File

@ -1,7 +1,7 @@
package millfork.assembly.z80.opt
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine}
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.ZRegister
@ -30,12 +30,12 @@ object EmptyMemoryStoreRemoval extends AssemblyOptimization[ZLine] {
val lastVariableAccess = code(lastaccess)
import millfork.assembly.z80.ZOpcode._
if (lastVariableAccess match {
case ZLine(LD, TwoRegisters(MEM_HL, _), _, true) => true
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), _, true) => true
case ZLine(INC | DEC, OneRegister(MEM_HL), _, true) =>
case ZLine(LD, TwoRegisters(MEM_HL, _), _, Elidability.Elidable, _) => true
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), _, Elidability.Elidable, _) => true
case ZLine(INC | DEC, OneRegister(MEM_HL), _, Elidability.Elidable, _) =>
val importances = vs.codeWithFlow(lastaccess)._1.importanceAfter
Seq(importances.sf, importances.zf).forall(_ == Unimportant)
case ZLine(SLA | SLL | SRA | SRL | RL | RR | RLC | RRC, OneRegister(MEM_HL), _, true) =>
case ZLine(SLA | SLL | SRA | SRL | RL | RR | RLC | RRC, OneRegister(MEM_HL), _, Elidability.Elidable, _) =>
val importances = vs.codeWithFlow(lastaccess)._1.importanceAfter
Seq(importances.sf, importances.zf, importances.cf).forall(_ == Unimportant)
case _ => false

View File

@ -2,10 +2,9 @@ package millfork.assembly.z80.opt
import millfork.Cpu
import millfork.assembly.z80.ZOpcode._
import millfork.assembly.z80.{TwoRegisters, ZLine}
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.z80.{TwoRegisters, ZLine, ZLine0}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.env._
import millfork.error.ConsoleLogger
/**
* @author Karol Stasiak
@ -15,8 +14,8 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[ZLine] {
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val usedFunctions = code.flatMap {
case ZLine(CALL | JP | JR, _, MemoryAddressConstant(th), _) => Some(th.name)
case ZLine(CALL | JP | JR, _, NumericConstant(addr, _), _) => Some("$" + addr.toHexString)
case ZLine0(CALL | JP | JR, _, MemoryAddressConstant(th)) => Some(th.name)
case ZLine0(CALL | JP | JR, _, NumericConstant(addr, _)) => Some("$" + addr.toHexString)
case _ => None
}.toSet
val foreignVariables = f.environment.root.things.values.flatMap {
@ -46,17 +45,17 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[ZLine] {
}.toSet
import millfork.node.ZRegister._
val stillReadOrStoredVariables = code.flatMap {
case ZLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case ZLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _) => Some(th.name)
case ZLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case ZLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case ZLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name)
case ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
val stillReadVariables = code.flatMap {
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th), true) => Nil
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), true) => Nil
case ZLine(_, _, MemoryAddressConstant(th), _) => Some(th.name)
case ZLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _) => Some(th.name)
case ZLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _) => Some(th.name)
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th), Elidability.Elidable, _) => Nil
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _) => Nil
case ZLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
case ZLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name)
case ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
case _ => None
}.toSet
@ -67,9 +66,9 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[ZLine] {
optimizationContext.log.debug(s"Removing pointless store(s) to foreign variables ${unusedForeignVariables.mkString(", ")}")
code.filterNot {
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th), _) =>
case ZLine0(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th)) =>
unusedForeignVariables(th.name)
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), true) =>
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _) =>
unusedForeignVariables(th.name)
case _ => false
}

View File

@ -1,7 +1,7 @@
package millfork.assembly.z80.opt
import millfork.CompilationOptions
import millfork.assembly.z80.{ZLine, ZOpcode}
import millfork.assembly.z80.{ZLine, ZLine0, ZOpcode}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
/**
@ -47,7 +47,7 @@ object FlowAnalyzer {
val labelMap: (() => Option[Map[String, Int]]) = () => req match {
case FlowInfoRequirement.NoRequirement => None
case _ => Some(code.flatMap {
case ZLine(op, _, MemoryAddressConstant(Label(l)), _) if op != ZOpcode.LABEL => Some(l)
case ZLine0(op, _, MemoryAddressConstant(Label(l))) if op != ZOpcode.LABEL => Some(l)
case _ => None
}.groupBy(identity).mapValues(_.size))
}

View File

@ -3,7 +3,8 @@ package millfork.assembly.z80.opt
import millfork.assembly.z80.ZOpcode._
import millfork.assembly.z80._
import millfork.env.{Constant, Label, MemoryAddressConstant}
import millfork.{CompilationOptions}
import millfork.CompilationOptions
import millfork.assembly.Elidability
import scala.collection.mutable
@ -18,14 +19,14 @@ object JumpFollowing {
val currentLabels = mutable.Set[String]()
for (line <- code) {
line match {
case ZLine(LABEL, _, MemoryAddressConstant(Label(label)), _) =>
case ZLine0(LABEL, _, MemoryAddressConstant(Label(label))) =>
currentLabels += label
case ZLine(op, _, _, _) if ZOpcodeClasses.NoopDiscards(op) =>
case ZLine(LABEL, _, _, _) =>
case ZLine(RET, NoRegisters, _, _) =>
case ZLine0(op, _, _) if ZOpcodeClasses.NoopDiscards(op) =>
case ZLine0(LABEL, _, _) =>
case ZLine0(RET, NoRegisters, _) =>
labelsToRet ++= currentLabels
currentLabels.clear()
case ZLine(JP | JR, NoRegisters, MemoryAddressConstant(Label(label)), _) =>
case ZLine0(JP | JR, NoRegisters, MemoryAddressConstant(Label(label))) =>
labelsToJumps ++= currentLabels.map(_ -> label)
currentLabels.clear()
case _ =>
@ -33,7 +34,7 @@ object JumpFollowing {
}
}
code.map {
case jump@ZLine(JR | JP, cond, MemoryAddressConstant(Label(label)), true) =>
case jump@ZLine(JR | JP, cond, MemoryAddressConstant(Label(label)), Elidability.Elidable, _) =>
if (labelsToRet(label)) {
options.log.debug(s"Optimizing ${jump.opcode} straight into RET")
ZLine(RET, cond, Constant.Zero)

View File

@ -1,7 +1,7 @@
package millfork.assembly.z80.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.OptimizationContext
import millfork.assembly.{Elidability, OptimizationContext}
import millfork.assembly.z80.ZOpcode._
import millfork.assembly.z80._
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
@ -30,11 +30,11 @@ object JumpShortening {
o += line.sizeInBytes
}
val labelOffsets = code.zipWithIndex.flatMap {
case (ZLine(LABEL, _, MemoryAddressConstant(Label(label)), _), ix) => Some(label -> offsets(ix))
case (ZLine0(LABEL, _, MemoryAddressConstant(Label(label))), ix) => Some(label -> offsets(ix))
case _ => None
}.toMap
code.zipWithIndex.map {
case (line@ZLine(JP, NoRegisters | IfFlagSet(ZFlag.Z | ZFlag.C) | IfFlagClear(ZFlag.Z | ZFlag.C), MemoryAddressConstant(Label(label)), true), ix) =>
case (line@ZLine(JP, NoRegisters | IfFlagSet(ZFlag.Z | ZFlag.C) | IfFlagClear(ZFlag.Z | ZFlag.C), MemoryAddressConstant(Label(label)), Elidability.Elidable, _), ix) =>
labelOffsets.get(label).fold(line) { labelOffset =>
val thisOffset = offsets(ix)
val willBeTaken = line.registers == NoRegisters ||

View File

@ -205,45 +205,45 @@ object ReverseFlowAnalyzer {
}
val currentLine = codeArray(i)
currentLine match {
case ZLine(LABEL | EI | DI | NOP, _, _, _) => ()
case ZLine(DJNZ, _, MemoryAddressConstant(Label(l)), _) =>
case ZLine0(LABEL | EI | DI | NOP, _, _) => ()
case ZLine0(DJNZ, _, MemoryAddressConstant(Label(l))) =>
val labelIndex = getLabelIndex(codeArray, l)
currentImportance = if (labelIndex < 0) finalImportance else (importanceArray(labelIndex) ~ currentImportance).butReadsRegister(ZRegister.B).butReadsFlag(ZFlag.Z)
case ZLine(JP | JR, IfFlagSet(flag), MemoryAddressConstant(Label(l)), _) =>
case ZLine0(JP | JR, IfFlagSet(flag), MemoryAddressConstant(Label(l))) =>
val labelIndex = getLabelIndex(codeArray, l)
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance.butReadsFlag(flag)
case ZLine(JP | JR, IfFlagClear(flag), MemoryAddressConstant(Label(l)), _) =>
case ZLine0(JP | JR, IfFlagClear(flag), MemoryAddressConstant(Label(l))) =>
val labelIndex = getLabelIndex(codeArray, l)
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance.butReadsFlag(flag)
case ZLine(JP | JR, NoRegisters, MemoryAddressConstant(Label(l)), _) =>
case ZLine0(JP | JR, NoRegisters, MemoryAddressConstant(Label(l))) =>
val labelIndex = getLabelIndex(codeArray, l)
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex)
case ZLine(DISCARD_HL, _, _, _) =>
case ZLine0(DISCARD_HL, _, _) =>
currentImportance = currentImportance.copy(h = Unimportant, l = Unimportant)
case ZLine(DISCARD_DE, _, _, _) =>
case ZLine0(DISCARD_DE, _, _) =>
currentImportance = currentImportance.copy(d = Unimportant, e = Unimportant)
case ZLine(DISCARD_BC, _, _, _) =>
case ZLine0(DISCARD_BC, _, _) =>
currentImportance = currentImportance.copy(b = Unimportant, c = Unimportant)
case ZLine(DISCARD_IX, _, _, _) =>
case ZLine0(DISCARD_IX, _, _) =>
currentImportance = currentImportance.copy(ixh = Unimportant, ixl = Unimportant)
case ZLine(DISCARD_IY, _, _, _) =>
case ZLine0(DISCARD_IY, _, _) =>
currentImportance = currentImportance.copy(iyh = Unimportant, iyl = Unimportant)
case ZLine(DISCARD_A, _, _, _) =>
case ZLine0(DISCARD_A, _, _) =>
currentImportance = currentImportance.copy(a = Unimportant)
case ZLine(DISCARD_F, _, _, _) =>
case ZLine0(DISCARD_F, _, _) =>
currentImportance = currentImportance.copy(cf = Unimportant, zf = Unimportant, sf = Unimportant, pf = Unimportant, hf = Unimportant, nf = Unimportant)
case ZLine(LD, TwoRegistersOffset(t, s, o), _, _) =>
case ZLine0(LD, TwoRegistersOffset(t, s, o), _) =>
currentImportance = currentImportance.butWritesRegister(t, o).butReadsRegister(s, o)
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
case ZLine0(LD | LD_16, TwoRegisters(t, s), _) =>
currentImportance = currentImportance.butWritesRegister(t).butReadsRegister(s)
case ZLine(EX_DE_HL, TwoRegisters(t, s), _, _) =>
case ZLine0(EX_DE_HL, TwoRegisters(t, s), _) =>
currentImportance = currentImportance.copy(d = currentImportance.h, e = currentImportance.l, h = currentImportance.d, l = currentImportance.e)
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
case ZLine0(ADD_16, TwoRegisters(t, s), _) =>
currentImportance = currentImportance.butReadsRegister(t).butReadsRegister(s)
case ZLine(ADC_16 | SBC_16, TwoRegisters(t, s), _, _) =>
case ZLine0(ADC_16 | SBC_16, TwoRegisters(t, s), _) =>
currentImportance = currentImportance.butReadsRegister(t).butReadsRegister(s).butReadsFlag(ZFlag.C)
case ZLine(XOR, OneRegister(ZRegister.A), _, _) =>
case ZLine0(XOR, OneRegister(ZRegister.A), _) =>
currentImportance = currentImportance.copy(
a = Unimportant,
cf = Unimportant,
@ -252,7 +252,7 @@ object ReverseFlowAnalyzer {
hf = Unimportant,
pf = Unimportant
)
case ZLine(OR | AND, OneRegister(ZRegister.A), _, _) =>
case ZLine0(OR | AND, OneRegister(ZRegister.A), _) =>
currentImportance = currentImportance.copy(
a = currentImportance.zf ~ currentImportance.sf ~ currentImportance.pf ~ currentImportance.a,
cf = Unimportant,
@ -262,7 +262,7 @@ object ReverseFlowAnalyzer {
pf = Unimportant
)
case ZLine(ADD | SUB | CP, OneRegister(s), _, _) =>
case ZLine0(ADD | SUB | CP, OneRegister(s), _) =>
currentImportance = currentImportance.butReadsRegister(s).copy(
a = Important,
cf = Unimportant,
@ -272,7 +272,7 @@ object ReverseFlowAnalyzer {
pf = Unimportant,
nf = Unimportant
)
case ZLine(ADD | SUB | CP, OneRegisterOffset(s, o), _, _) =>
case ZLine0(ADD | SUB | CP, OneRegisterOffset(s, o), _) =>
currentImportance = currentImportance.butReadsRegister(s, o).copy(
a = Important,
cf = Unimportant,
@ -283,7 +283,7 @@ object ReverseFlowAnalyzer {
nf = Unimportant
)
case ZLine(AND | OR | XOR, OneRegister(s), _, _) =>
case ZLine0(AND | OR | XOR, OneRegister(s), _) =>
currentImportance = currentImportance.butReadsRegister(s).copy(
a = Important,
cf = Unimportant,
@ -292,7 +292,7 @@ object ReverseFlowAnalyzer {
sf = Unimportant
)
case ZLine(AND | OR | XOR, OneRegisterOffset(s, o), _, _) =>
case ZLine0(AND | OR | XOR, OneRegisterOffset(s, o), _) =>
currentImportance = currentImportance.butReadsRegister(s, o).copy(
a = Important,
cf = Unimportant,
@ -300,7 +300,7 @@ object ReverseFlowAnalyzer {
pf = Unimportant,
sf = Unimportant
)
case ZLine(ADC | SBC, OneRegister(s), _, _) =>
case ZLine0(ADC | SBC, OneRegister(s), _) =>
currentImportance = currentImportance.butReadsRegister(s).copy(
a = Important,
cf = Important,
@ -310,7 +310,7 @@ object ReverseFlowAnalyzer {
pf = Unimportant,
nf = Unimportant
)
case ZLine(ADC | SBC, OneRegisterOffset(s, o), _, _) =>
case ZLine0(ADC | SBC, OneRegisterOffset(s, o), _) =>
currentImportance = currentImportance.butReadsRegister(s, o).copy(
a = Important,
cf = Important,
@ -322,28 +322,28 @@ object ReverseFlowAnalyzer {
)
case ZLine(DAA, _, _, _) =>
case ZLine0(DAA, _, _) =>
currentImportance = currentImportance.copy(
a = Important,
hf = Important
)
case ZLine(NEG, _, _, _) =>
case ZLine0(NEG, _, _) =>
currentImportance = currentImportance.copy(
a = Important
)
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegister(s), _, _) =>
case ZLine0(INC | DEC | INC_16 | DEC_16, OneRegister(s), _) =>
currentImportance = currentImportance.butReadsRegister(s)
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegisterOffset(s, o), _, _) =>
case ZLine0(INC | DEC | INC_16 | DEC_16, OneRegisterOffset(s, o), _) =>
currentImportance = currentImportance.butReadsRegister(s, o)
case ZLine(op, OneRegister(s), _, _) if ZOpcodeClasses.AllSingleBit(op)=>
case ZLine0(op, OneRegister(s), _) if ZOpcodeClasses.AllSingleBit(op)=>
currentImportance = currentImportance.butReadsRegister(s)
case ZLine(op, OneRegisterOffset(s, o), _, _) if ZOpcodeClasses.AllSingleBit(op)=>
case ZLine0(op, OneRegisterOffset(s, o), _) if ZOpcodeClasses.AllSingleBit(op)=>
currentImportance = currentImportance.butReadsRegister(s, o)
case ZLine(POP, OneRegister(r), _, _) =>
case ZLine0(POP, OneRegister(r), _) =>
currentImportance = currentImportance.butWritesRegister(r)
case ZLine(PUSH, OneRegister(r), _, _) =>
case ZLine0(PUSH, OneRegister(r), _) =>
currentImportance = currentImportance.butReadsRegister(r)
case ZLine(CALL | JP, NoRegisters, MemoryAddressConstant(fun: FunctionInMemory), _) =>
case ZLine0(CALL | JP, NoRegisters, MemoryAddressConstant(fun: FunctionInMemory)) =>
val n = fun.name
fun.params match {
case NormalParamSignature(List(v)) if v.typ.size == 1 =>
@ -440,15 +440,15 @@ object ReverseFlowAnalyzer {
currentImportance = finalImportance.copy(memIx = currentImportance.memIx)
}
case ZLine(SLA | SRL, OneRegister(r), _, _) =>
case ZLine0(SLA | SRL, OneRegister(r), _) =>
currentImportance = currentImportance.butReadsRegister(r).copy(cf = Unimportant, zf = Unimportant, hf = Unimportant, nf = Unimportant, pf = Unimportant)
case ZLine(RL | RR | RLC | RRC, OneRegister(r), _, _) =>
case ZLine0(RL | RR | RLC | RRC, OneRegister(r), _) =>
currentImportance = currentImportance.butReadsRegister(r).copy(cf = Important, zf = Unimportant, hf = Unimportant, nf = Unimportant, pf = Unimportant)
case ZLine(SWAP, OneRegister(r), _, _) =>
case ZLine0(SWAP, OneRegister(r), _) =>
currentImportance = currentImportance.butReadsRegister(r).copy(cf = Unimportant, zf = Unimportant, hf = Unimportant, nf = Unimportant, pf = Unimportant)
case ZLine(RLA | RRA | RLCA | RRCA, _, _, _) =>
case ZLine0(RLA | RRA | RLCA | RRCA, _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A).copy(cf = Important, hf = Unimportant, nf = Unimportant)
case ZLine(SCF, _, _, _) =>
case ZLine0(SCF, _, _) =>
currentImportance = currentImportance.copy(cf = Unimportant, hf = Unimportant, nf = Unimportant)
case _ =>
currentImportance = finalImportance // TODO
@ -465,7 +465,7 @@ object ReverseFlowAnalyzer {
private def getLabelIndex(codeArray: Array[ZLine], L: String) = {
codeArray.indexWhere {
case ZLine(ZOpcode.LABEL, _, MemoryAddressConstant(Label(L)), _) => true
case ZLine0(ZOpcode.LABEL, _, MemoryAddressConstant(Label(L))) => true
case _ => false
}
}

View File

@ -55,6 +55,12 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
case Some(rest: List[(FlowInfo, ZLine)]) =>
val matchedChunkToOptimize: List[ZLine] = code.take(code.length - rest.length).map(_._2)
val optimizedChunk: List[ZLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource =
if (optimizedChunk.isEmpty) optimizedChunk
else if (matchedChunkToOptimize.size == 1) optimizedChunk.map(_.pos(matchedChunkToOptimize.head.source))
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk
log.debug(s"Applied $name ($index)")
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
val before = code.head._1.statusBefore
@ -67,12 +73,12 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
if (log.traceEnabled) {
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
log.trace(" ↓")
optimizedChunk.filter(_.isPrintable).foreach(l => log.trace(l.toString))
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
}
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunk ++ optimizeImpl(f, rest, optimizationContext)
return optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)
} else {
return optimize(f, optimizedChunk ++ rest.map(_._2), optimizationContext)
return optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
}
case None => ()
}
@ -149,15 +155,15 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
import millfork.assembly.z80.ZOpcode._
get[List[ZLine]](i).foreach {
// JSR and BSR are allowed
case ZLine(RET | RST | RETI | RETN, _, _, _) =>
case ZLine0(RET | RST | RETI | RETN, _, _) =>
return false
case ZLine(JP | JR, OneRegister(_), _, _) =>
case ZLine0(JP | JR, OneRegister(_), _) =>
return false
case ZLine(JP | JR | DJNZ, _, MemoryAddressConstant(Label(l)), _) =>
case ZLine0(JP | JR | DJNZ, _, MemoryAddressConstant(Label(l))) =>
jumps += l
case ZLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
case ZLine0(LABEL, _, MemoryAddressConstant(Label(l))) =>
labels += l
case ZLine(JP | JR | DJNZ, _, _, _) =>
case ZLine0(JP | JR | DJNZ, _, _) =>
return false
case _ => ()
}
@ -998,7 +1004,7 @@ case class Before(pattern: AssemblyPattern) extends AssemblyLinePattern {
case class HasCallerCount(count: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
line match {
case ZLine(ZOpcode.LABEL, _, MemoryAddressConstant(Label(l)), _) => flowInfo.labelUseCount(l) == count
case ZLine0(ZOpcode.LABEL, _, MemoryAddressConstant(Label(l))) => flowInfo.labelUseCount(l) == count
case _ => false
}
}

View File

@ -14,9 +14,9 @@ object StackVariableLifetime {
// TODO: this is also probably very wrong
def apply(variableOffset: Int, codeWithFlow: List[(FlowInfo, ZLine)]): Range = {
val flags = codeWithFlow.map {
case (_, ZLine(_, OneRegisterOffset(ZRegister.MEM_IX_D, i), _, _)) => i == variableOffset
case (_, ZLine(_, TwoRegistersOffset(ZRegister.MEM_IX_D, _, i), _, _)) => i == variableOffset
case (_, ZLine(_, TwoRegistersOffset(_, ZRegister.MEM_IX_D, i), _, _)) => i == variableOffset
case (_, ZLine0(_, OneRegisterOffset(ZRegister.MEM_IX_D, i), _)) => i == variableOffset
case (_, ZLine0(_, TwoRegistersOffset(ZRegister.MEM_IX_D, _, i), _)) => i == variableOffset
case (_, ZLine0(_, TwoRegistersOffset(_, ZRegister.MEM_IX_D, i), _)) => i == variableOffset
case _ => false
}
if (flags.forall(!_)) return Range(0, 0)

View File

@ -1,7 +1,7 @@
package millfork.assembly.z80.opt
import millfork.assembly.opt.SingleStatus
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine}
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZLine0}
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.ZRegister
@ -15,9 +15,9 @@ object VariableLifetime {
// TODO: this is also probably very wrong
def apply(variableName: String, codeWithFlow: List[(FlowInfo, ZLine)]): Range = {
val flags = codeWithFlow.map {
case (_, ZLine(_, _, MemoryAddressConstant(MemoryVariable(n, _, _)), _)) => n == variableName
case (_, ZLine(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)), _)) => n == variableName
case (i, ZLine(_, TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL), _, _)) =>
case (_, ZLine0(_, _, MemoryAddressConstant(MemoryVariable(n, _, _)))) => n == variableName
case (_, ZLine0(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)))) => n == variableName
case (i, ZLine0(_, TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL), _)) =>
i.statusBefore.hl match {
case SingleStatus(MemoryAddressConstant(MemoryVariable(n, _, _))) => n == variableName
case SingleStatus(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1))) => n == variableName

View File

@ -3,7 +3,7 @@ package millfork.assembly.z80.opt
import millfork.CompilationFlag
import millfork.assembly.OptimizationContext
import millfork.assembly.opt.SingleStatus
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine}
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZLine0}
import millfork.env._
import millfork.node.ZRegister
@ -40,22 +40,22 @@ object VariableStatus {
return None
}
val stillUsedVariables = code.flatMap {
case ZLine(_, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th), _) => Some(th.name)
case ZLine(_, TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16), MemoryAddressConstant(th), _) => Some(th.name)
case ZLine(_, TwoRegisters(_, IMM_16), MemoryAddressConstant(th), _) => Some(th.name)
case ZLine(_, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
case ZLine(_, TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
case ZLine(_, TwoRegisters(_, IMM_16), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
case ZLine0(_, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th)) => Some(th.name)
case ZLine0(_, TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16), MemoryAddressConstant(th)) => Some(th.name)
case ZLine0(_, TwoRegisters(_, IMM_16), MemoryAddressConstant(th)) => Some(th.name)
case ZLine0(_, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _))) => Some(th.name)
case ZLine0(_, TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _))) => Some(th.name)
case ZLine0(_, TwoRegisters(_, IMM_16), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _))) => Some(th.name)
case _ => None
}.toSet
val variablesWithAddressesTaken = code.zipWithIndex.flatMap {
case (ZLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _), _) =>
case (ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)), _) =>
Some(th.name)
case (ZLine(_, _, SubbyteConstant(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _), _), _) =>
case (ZLine0(_, _, SubbyteConstant(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _)), _) =>
Some(th.name)
case (ZLine(_,
case (ZLine0(_,
TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL),
_, _), i) =>
_), i) =>
flow(i)._1.statusBefore.hl match {
case SingleStatus(MemoryAddressConstant(th)) =>
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) Some(th.name)

View File

@ -1,8 +1,8 @@
package millfork.assembly.z80.opt
import millfork.{CompilationFlag, NonOverlappingIntervals}
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.z80.{TwoRegisters, ZFlag, ZLine}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.z80.{TwoRegisters, ZFlag, ZLine, ZLine0}
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.ZRegister
@ -192,21 +192,21 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
None
}
code match {
case (_, ZLine(LD_16, TwoRegisters(BC, IMM_16), _, true))::
(i, ZLine(ADD_16, TwoRegisters(HL, BC), _, true)) :: xs if target == BC =>
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), _, true))::
(i, ZLine(ADD_16, TwoRegisters(HL, DE), _, true)) :: xs if target == DE =>
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), true)) ::
(_, ZLine(ADD_16, TwoRegisters(HL, BC | DE), _, _)) ::
(i, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th2), true)) ::
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 &&
@ -220,65 +220,65 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
// 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, _), _, true)) ::
(_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), true)) ::
(i, ZLine(ADD_16, TwoRegisters(HL, t2), _, _)) ::
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, ZLine(ADD_16, TwoRegisters(HL, t2), _, _)) ::
// 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), true)) ::
(_, ZLine(LD_16, TwoRegisters(t, _), _, true)) ::
(i, ZLine(ADD_16, TwoRegisters(HL, t2), _, _)) ::
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), true)) ::
(_, ZLine(LD_16, TwoRegisters(t, _), _, true)) ::
(i, ZLine(ADD_16, TwoRegisters(HL, t2), _, _)) ::
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), true)) :: xs if th.name == vname =>
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), true)) :: xs if th.name == vname =>
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), true)) :: xs if th.name == vname =>
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), true)) :: xs if th.name == vname =>
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), true)) :: xs if th.name == vname =>
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), true)) :: xs if th.name == vname =>
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 (_, x) :: (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, t), MemoryAddressConstant(th), true)) :: xs
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), true)) :: xs if th.name == vname =>
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), true)) :: xs if th.name == vname =>
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,_)), true)) :: xs if th.name == vname =>
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, _)), true)) :: xs if th.name == vname =>
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 (_, ZLine(_, _, MemoryAddressConstant(th), _)) :: _ if th.name == vname => fail(4)
case (_, ZLine(_, _, CompoundConstant(_, MemoryAddressConstant(th), _), _)) :: _ if th.name == vname => fail(5)
case (_, ZLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _)) :: _ if th.name == vname => fail(6)
case (_, ZLine(CALL, _, _, _)) :: xs => target match {
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)))
@ -286,8 +286,8 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
}
case (_, x) :: xs if x.changesRegister(target) => fail(1)
case (_, x) :: xs if x.readsRegister(target) && !synced => fail(2)
case (_, ZLine(LABEL, _, _, _)) :: xs => canBeInlined(vname, synced = false, target, xs)
case (_, ZLine(CALL, _, _, _)) :: xs => fail(3)
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)
}
@ -297,31 +297,31 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
// if (code.nonEmpty) println(code.head)
code match {
case (_, load@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _)) ::
(f, add@ZLine(ADD_16, TwoRegisters(HL, BC), _, _)) ::
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) {
ZLine.register(PUSH, BC) :: load :: add :: ZLine.register(POP, BC) :: inlineVars(hl, bc, de, xs)
ZLine.register(PUSH, BC).pos(s1) :: load :: add :: ZLine.register(POP, BC).pos(s2) :: inlineVars(hl, bc, de, xs)
} else {
load.copy(registers = TwoRegisters(DE, IMM_16)) ::
ZLine.registers(ADD_16, HL, DE) ::
ZLine.registers(ADD_16, HL, DE).pos(s2) ::
inlineVars(hl, bc, de, xs)
}
case (_, load@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _)) ::
(f, add@ZLine(ADD_16, TwoRegisters(HL, DE), _, _)) ::
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) {
ZLine.register(PUSH, DE) :: load :: add :: ZLine.register(POP, DE) :: inlineVars(hl, bc, de, xs)
ZLine.register(PUSH, DE).pos(s1) :: load :: add :: ZLine.register(POP, DE).pos(s2) :: inlineVars(hl, bc, de, xs)
} else {
load.copy(registers = TwoRegisters(BC, IMM_16)) ::
ZLine.registers(ADD_16, HL, BC) ::
ZLine.registers(ADD_16, HL, BC).pos(s2) ::
inlineVars(hl, bc, de, xs)
}
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), a1@MemoryAddressConstant(th1), _)) ::
(_, ZLine(ADD_16, TwoRegisters(HL, reg@(DE | BC)), _, _)) ::
(_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), a2@MemoryAddressConstant(th2), _)) ::
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
@ -331,127 +331,127 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
case BC => (B,C)
case DE => (D, E)
}
ZLine.ldAbs8(A, a1) ::
ZLine.register(ADD, l) ::
ZLine.ldAbs8(a2, A) ::
ZLine.ldAbs8(A, a1 + 1) ::
ZLine.register(ADC, h) ::
ZLine.ldAbs8(a2 + 1, A) ::
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) ::
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _)) ::
(_, loadConst@ZLine(LD_16, TwoRegisters(BC, constSource), _, _)) ::
(_, add@ZLine(ADD_16, TwoRegisters(HL, BC), _, _)) :: xs if th.name == bc =>
ZLine.ld8(B, H) :: ZLine.ld8(C, L) ::
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 =>
ZLine.ld8(B, H).pos(s1) :: ZLine.ld8(C, L).pos(s1) ::
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, BC)) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _)) ::
(_, loadConst@ZLine(LD_16, TwoRegisters(DE, constSource), _, _)) ::
(_, add@ZLine(ADD_16, TwoRegisters(HL, DE), _, _)) :: xs if th.name == de =>
ZLine.ld8(D, H) :: ZLine.ld8(E, L) ::
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 =>
ZLine.ld8(D, H).pos(s1) :: ZLine.ld8(E, L).pos(s1) ::
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, DE)) :: inlineVars(hl, bc, de, xs)
// TODO: above with regs swapped
case (_, loadConst@ZLine(LD_16, TwoRegisters(t, constSource), _, _)) ::
(_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _)) ::
(_, add@ZLine(ADD_16, TwoRegisters(HL, t2), _, _)) :: xs if th.name == bc && t == t2 && t != HL =>
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 =>
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, BC)) :: inlineVars(hl, bc, de, xs)
case (_, loadConst@ZLine(LD_16, TwoRegisters(t, constSource),_,_)) ::
(_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _)) ::
(_, add@ZLine(ADD_16, TwoRegisters(HL, t2), _, _)) :: xs if th.name == de && t == t2 && t != HL =>
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 =>
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
add.copy(registers = TwoRegisters(HL, DE)) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
ZLine.ld8(H, B) :: ZLine.ld8(L, C) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
ZLine.ld8(B, H) :: ZLine.ld8(C, L) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
ZLine.ld8(H, D) :: ZLine.ld8(L, E) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
ZLine.ld8(D, H) :: ZLine.ld8(E, L) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
ZLine.ld8(H, B).pos(s) :: ZLine.ld8(L, C).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
ZLine.ld8(B, H).pos(s) :: ZLine.ld8(C, L).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
ZLine.ld8(H, D).pos(s) :: ZLine.ld8(L, E).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
ZLine.ld8(D, H).pos(s) :: ZLine.ld8(E, L).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
ZLine.ld8(D, B) :: ZLine.ld8(E, C) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
ZLine.ld8(B, D) :: ZLine.ld8(C, E) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
ZLine.ld8(D, H) :: ZLine.ld8(E, L) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
ZLine.ld8(H, D) :: ZLine.ld8(L, E) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
ZLine.ld8(D, B).pos(s) :: ZLine.ld8(E, C).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
ZLine.ld8(B, D).pos(s) :: ZLine.ld8(C, E).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
ZLine.ld8(D, H).pos(s) :: ZLine.ld8(E, L).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
ZLine.ld8(H, D).pos(s) :: ZLine.ld8(L, E).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
ZLine.ld8(B, H) :: ZLine.ld8(C, L) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
ZLine.ld8(H, B) :: ZLine.ld8(L, C) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
ZLine.ld8(B, D) :: ZLine.ld8(C, E) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
ZLine.ld8(D, B) :: ZLine.ld8(E, C) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
ZLine.ld8(B, H).pos(s) :: ZLine.ld8(C, L).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
ZLine.ld8(H, B).pos(s) :: ZLine.ld8(L, C).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
ZLine.ld8(B, D).pos(s) :: ZLine.ld8(C, E).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
ZLine.ld8(D, B).pos(s) :: ZLine.ld8(E, C).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
ZLine.ld8(A, L) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _) ) :: xs if th.name == hl =>
ZLine.ld8(L, A) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
ZLine.ld8(A, C) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _) ) :: xs if th.name == bc =>
ZLine.ld8(C, A) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
ZLine.ld8(A, E) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _) ) :: xs if th.name == de =>
ZLine.ld8(E, A) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
ZLine.ld8(A, L).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
ZLine.ld8(L, A).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
ZLine.ld8(A, C).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
ZLine.ld8(C, A).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
ZLine.ld8(A, E).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
ZLine.ld8(E, A).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) ) :: xs if th.name == hl =>
ZLine.ld8(A, H) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) ) :: xs if th.name == hl =>
ZLine.ld8(H, A) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) ) :: xs if th.name == bc =>
ZLine.ld8(A, B) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) ) :: xs if th.name == bc =>
ZLine.ld8(B, A) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) ) :: xs if th.name == de =>
ZLine.ld8(A, D) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) ) :: xs if th.name == de =>
ZLine.ld8(D, A) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == hl =>
ZLine.ld8(A, H).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == hl =>
ZLine.ld8(H, A).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == bc =>
ZLine.ld8(A, B).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == bc =>
ZLine.ld8(B, A).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == de =>
ZLine.ld8(A, D).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == de =>
ZLine.ld8(D, A).pos(s) :: inlineVars(hl, bc, de, xs)
case (_, l1@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, BC), _, _)) :: xs if bc != "" =>
ZLine.register(PUSH, BC) :: l1 :: l2 :: ZLine.register(POP, BC) :: inlineVars(hl, bc, de, xs)
case (_, l1@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _, s1)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s2)) :: xs if bc != "" =>
ZLine.register(PUSH, BC).pos(s1) :: l1 :: l2 :: ZLine.register(POP, BC).pos(s2) :: inlineVars(hl, bc, de, xs)
case (_, l1@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, DE), _, _)) :: xs if de != "" =>
ZLine.register(PUSH, DE) :: l1 :: l2 :: ZLine.register(POP, DE) :: inlineVars(hl, bc, de, xs)
case (_, l1@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _, s1)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, DE), _, _, s2)) :: xs if de != "" =>
ZLine.register(PUSH, DE).pos(s1) :: l1 :: l2 :: ZLine.register(POP, DE).pos(s2) :: inlineVars(hl, bc, de, xs)
case (_, x@ZLine(CALL, _, _, _)) :: xs =>
case (_, x@ZLine(CALL, _, _, _, s)) :: xs =>
if (bc != "") {
ZLine.register(PUSH, BC) :: x :: ZLine.register(POP, BC) :: inlineVars(hl, bc, de, xs)
ZLine.register(PUSH, BC).pos(s) :: x :: ZLine.register(POP, BC).pos(s) :: inlineVars(hl, bc, de, xs)
} else if (de != "") {
ZLine.register(PUSH, DE) :: x :: ZLine.register(POP, DE) :: inlineVars(hl, bc, de, xs)
ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: inlineVars(hl, bc, de, xs)
} else {
throw new IllegalStateException()
}
case x :: (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _)) :: xs if x._2.changesRegister(HL) && th.name == hl =>
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(HL) && th.name == hl =>
x._2 :: inlineVars(hl, bc, de, xs)
case x :: (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _)) :: xs if x._2.changesRegister(BC) && th.name == bc =>
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(BC) && th.name == bc =>
x._2 :: inlineVars(hl, bc, de, xs)
case x :: (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _)) :: xs if x._2.changesRegister(DE) && th.name == de =>
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(DE) && th.name == de =>
x._2 :: inlineVars(hl, bc, de, xs)
case x :: _ if bc != "" && x._2.changesRegister(BC) => throw new IllegalStateException()

View File

@ -1,6 +1,7 @@
package millfork.compiler.mos
import millfork.CompilationFlag
import millfork.assembly.Elidability
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
@ -314,7 +315,7 @@ object BuiltIns {
case Some(NumericConstant(shift, _)) if shift > 0 =>
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, a1, l, _)), List(AssemblyLine(STA, a2, h, _))) =>
case List(List(AssemblyLine0(STA, a1, l)), List(AssemblyLine0(STA, a2, h))) =>
if (a1 == a2 && l.+(1).quickSimplify == h) {
return List(AssemblyLine.accu16) ++ List.fill(shift.toInt)(AssemblyLine(if (aslRatherThanLsr) ASL_W else LSR_W, a1, l)) ++ List(AssemblyLine.accu8)
}
@ -345,7 +346,7 @@ object BuiltIns {
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, a1, l, _)), List(AssemblyLine(STA, a2, h, _))) =>
case List(List(AssemblyLine0(STA, a1, l)), List(AssemblyLine0(STA, a2, h))) =>
if (a1 == a2 && l.+(1).quickSimplify == h) {
return compileCounter ++ List(
AssemblyLine.relative(BEQ, labelSkip),
@ -428,7 +429,7 @@ object BuiltIns {
case ComparisonType.Equal | ComparisonType.NotEqual | ComparisonType.LessSigned | ComparisonType.GreaterOrEqualSigned =>
val secondParamCompiledUnoptimized = simpleOperation(cmpOp, ctx, rhs, IndexChoice.PreferY, preserveA = true, commutative = false)
secondParamCompiledUnoptimized match {
case List(AssemblyLine(cmpOp, Immediate, NumericConstant(0, _), true)) =>
case List(AssemblyLine(cmpOp, Immediate, NumericConstant(0, _), Elidability.Elidable, _)) =>
if (OpcodeClasses.ChangesAAlways(firstParamCompiled.last.opcode)) {
Nil
} else {
@ -941,8 +942,8 @@ object BuiltIns {
var addendSize = addendType.size
def isRhsComplex(xs: List[AssemblyLine]): Boolean = xs match {
case AssemblyLine(LDA, _, _, _) :: Nil => false
case AssemblyLine(LDA, _, _, _) :: AssemblyLine(LDX, _, _, _) :: Nil => false
case AssemblyLine0(LDA, _, _) :: Nil => false
case AssemblyLine0(LDA, _, _) :: AssemblyLine0(LDX, _, _) :: Nil => false
case _ => true
}
@ -974,7 +975,7 @@ object BuiltIns {
case Some(NumericConstant(1, _)) if canUseIncDec && !subtract =>
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
case List(List(AssemblyLine0(STA, ZeroPage, l)), List(AssemblyLine0(STA, ZeroPage, h))) =>
if (l.+(1).quickSimplify == h) {
return List(AssemblyLine.zeropage(INC_W, l))
}
@ -983,7 +984,7 @@ object BuiltIns {
}
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
case List(List(AssemblyLine0(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l)), List(AssemblyLine0(STA, a2, h))) =>
if (a1 == a2 && l.+(1).quickSimplify == h) {
return List(AssemblyLine.accu16, AssemblyLine(INC_W, a1, l), AssemblyLine.accu8)
}
@ -994,7 +995,7 @@ object BuiltIns {
case Some(NumericConstant(-1, _)) if canUseIncDec && subtract =>
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
case List(List(AssemblyLine0(STA, ZeroPage, l)), List(AssemblyLine0(STA, ZeroPage, h))) =>
if (l.+(1).quickSimplify == h) {
return List(AssemblyLine.zeropage(INC_W, l))
}
@ -1002,7 +1003,7 @@ object BuiltIns {
}
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
case List(List(AssemblyLine0(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l)), List(AssemblyLine0(STA, a2, h))) =>
if (a1 == a2 && l.+(1).quickSimplify == h) {
return List(AssemblyLine.accu16, AssemblyLine(INC_W, a1, l), AssemblyLine.accu8)
}
@ -1013,7 +1014,7 @@ object BuiltIns {
case Some(NumericConstant(1, _)) if canUseIncDec && subtract =>
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
case List(List(AssemblyLine0(STA, ZeroPage, l)), List(AssemblyLine0(STA, ZeroPage, h))) =>
if (l.+(1).quickSimplify == h) {
return List(AssemblyLine.zeropage(DEC_W, l))
}
@ -1021,7 +1022,7 @@ object BuiltIns {
}
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
case List(List(AssemblyLine0(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l)), List(AssemblyLine0(STA, a2, h))) =>
if (a1 == a2 && l.+(1).quickSimplify == h) {
return List(AssemblyLine.accu16, AssemblyLine(DEC_W, a1, l), AssemblyLine.accu8)
}
@ -1032,7 +1033,7 @@ object BuiltIns {
case Some(NumericConstant(-1, _)) if canUseIncDec && !subtract =>
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
case List(List(AssemblyLine0(STA, ZeroPage, l)), List(AssemblyLine0(STA, ZeroPage, h))) =>
if (l.+(1).quickSimplify == h) {
return List(AssemblyLine.zeropage(DEC_W, l))
}
@ -1040,7 +1041,7 @@ object BuiltIns {
}
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
case List(List(AssemblyLine0(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l)), List(AssemblyLine0(STA, a2, h))) =>
if (a1 == a2 && l.+(1).quickSimplify == h) {
return List(AssemblyLine.accu16, AssemblyLine(DEC_W, a1, l), AssemblyLine.accu8)
}
@ -1121,9 +1122,9 @@ object BuiltIns {
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
(removeTsx(targetBytes), calculateRhs, removeTsx(addendByteRead)) match {
case (
List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))),
List(List(AssemblyLine0(STA, ta1, tl)), List(AssemblyLine0(STA, ta2, th))),
Nil,
List(List(AssemblyLine(LDA, Immediate, al, _)), List(AssemblyLine(LDA, Immediate, ah, _)))) =>
List(List(AssemblyLine0(LDA, Immediate, al)), List(AssemblyLine0(LDA, Immediate, ah)))) =>
if (ta1 == ta2 && tl.+(1).quickSimplify == th) {
return wrapInSedCldIfNeeded(decimal, List(
AssemblyLine.implied(if(subtract) SEC else CLC),
@ -1134,9 +1135,9 @@ object BuiltIns {
AssemblyLine.accu8))
}
case (
List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))),
List(List(AssemblyLine0(STA, ta1, tl)), List(AssemblyLine0(STA, ta2, th))),
Nil,
List(List(AssemblyLine(LDA, aa1, al, _)), List(AssemblyLine(LDA, aa2, ah, _)))) =>
List(List(AssemblyLine0(LDA, aa1, al)), List(AssemblyLine0(LDA, aa2, ah)))) =>
if (ta1 == ta2 && aa1 == aa2 && tl.+(1).quickSimplify == th && al.+(1).quickSimplify == ah) {
return wrapInSedCldIfNeeded(decimal, List(
AssemblyLine.accu16,
@ -1147,9 +1148,9 @@ object BuiltIns {
AssemblyLine.accu8))
}
case (
List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))),
List(AssemblyLine(TSX, _, _, _), AssemblyLine(LDA, AbsoluteX, NumericConstant(al, _), _), AssemblyLine(LDY, AbsoluteX, NumericConstant(ah, _), _)),
List(Nil, List(AssemblyLine(TYA, _, _, _)))) =>
List(List(AssemblyLine0(STA, ta1, tl)), List(AssemblyLine0(STA, ta2, th))),
List(AssemblyLine0(TSX, _, _), AssemblyLine0(LDA, AbsoluteX, NumericConstant(al, _)), AssemblyLine0(LDY, AbsoluteX, NumericConstant(ah, _))),
List(Nil, List(AssemblyLine0(TYA, _, _)))) =>
if (ta1 == ta2 && tl.+(1).quickSimplify == th && al + 1 == ah) {
return wrapInSedCldIfNeeded(decimal, List(
AssemblyLine.accu16,
@ -1291,7 +1292,7 @@ object BuiltIns {
}
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
(removeTsx(targetBytes), removeTsx(addendByteRead)) match {
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, Immediate, al, _)), List(AssemblyLine(LDA, Immediate, ah, _)))) =>
case (List(List(AssemblyLine0(STA, ta1, tl)), List(AssemblyLine0(STA, ta2, th))), List(List(AssemblyLine0(LDA, Immediate, al)), List(AssemblyLine0(LDA, Immediate, ah)))) =>
if (ta1 == ta2 && tl.+(1).quickSimplify == th) {
return List(
AssemblyLine.accu16,
@ -1300,7 +1301,7 @@ object BuiltIns {
AssemblyLine(STA_W, ta1, tl),
AssemblyLine.accu8)
}
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, aa1, al, _)), List(AssemblyLine(LDA, aa2, ah, _)))) =>
case (List(List(AssemblyLine0(STA, ta1, tl)), List(AssemblyLine0(STA, ta2, th))), List(List(AssemblyLine0(LDA, aa1, al)), List(AssemblyLine0(LDA, aa2, ah)))) =>
if (ta1 == ta2 && aa1 == aa2 && tl.+(1).quickSimplify == th && al.+(1).quickSimplify == ah) {
return List(
AssemblyLine.accu16,
@ -1416,7 +1417,7 @@ object BuiltIns {
}
private def removeTsx(codes: List[List[AssemblyLine]]): List[List[AssemblyLine]] = codes.map {
case List(AssemblyLine(TSX, _, _, _), AssemblyLine(op, AbsoluteX, NumericConstant(nn, _), _)) if nn >= 0x100 && nn <= 0x1ff =>
case List(AssemblyLine0(TSX, _, _), AssemblyLine0(op, AbsoluteX, NumericConstant(nn, _))) if nn >= 0x100 && nn <= 0x1ff =>
List(AssemblyLine(op, Stack, NumericConstant(nn & 0xff, 1)))
case x => x
}

View File

@ -74,8 +74,8 @@ object DecimalBuiltIns {
}
List(
if (rotate) AssemblyLine.implied(ROR) else AssemblyLine.implied(LSR),
AssemblyLine(LABEL, DoesNotExist, Label(constantLabel).toAddress, elidable = cmos),
AssemblyLine(PHP, Implied, Constant.Zero, elidable = cmos),
AssemblyLine(LABEL, DoesNotExist, Label(constantLabel).toAddress, elidability = if (cmos) Elidability.Elidable else Elidability.Fixed),
AssemblyLine(PHP, Implied, Constant.Zero, elidability = if (cmos) Elidability.Elidable else Elidability.Fixed),
AssemblyLine.relative(BPL, skipHiDigit),
AssemblyLine.implied(SEC),
AssemblyLine.immediate(SBC, 0x30),

View File

@ -1,6 +1,7 @@
package millfork.compiler.mos
import millfork.CompilationFlag
import millfork.assembly.Elidability
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.compiler.{AbstractCompiler, CompilationContext}
@ -18,11 +19,11 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
val chunk = MosStatementCompiler.compile(ctx, new MosStatementPreprocessor(ctx, ctx.function.code)())
val zpRegisterSize = ctx.options.zpRegisterSize
val storeParamsFromRegisters = ctx.function.params match {
val storeParamsFromRegisters = (ctx.function.params match {
case NormalParamSignature(List(param@MemoryVariable(_, typ, _))) if typ.size == 1 =>
List(AssemblyLine.absolute(STA, param))
case _ => Nil
}
}).map(_.position(ctx.function.position))
val phReg =
if (zpRegisterSize > 0) {
val reg = ctx.env.get[VariableInMemory]("__reg")
@ -30,7 +31,7 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
List(
AssemblyLine.zeropage(LDA, reg, i),
AssemblyLine.implied(PHA))
}.toList
}.toList.map(_.position(ctx.function.position))
} else Nil
val prefix = storeParamsFromRegisters ++ (if (ctx.function.interrupt) {
@ -116,8 +117,8 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
}
AssemblyLine.accu16 :: (initialBytes ++ lastByte)
} else phReg
} else Nil) ++ stackPointerFixAtBeginning(ctx)
val label = AssemblyLine.label(Label(ctx.function.name)).copy(elidable = false)
} else Nil).map(_.position(ctx.function.position)) ++ stackPointerFixAtBeginning(ctx)
val label = AssemblyLine.label(Label(ctx.function.name)).copy(elidability = Elidability.Fixed)
label :: (prefix ++ chunk)
}
@ -130,9 +131,9 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
AssemblyLine.implied(TSX),
AssemblyLine.immediate(LDA, 0xff),
AssemblyLine.immediate(SBX, m.stackVariablesSize),
AssemblyLine.implied(TXS)) // this TXS is fine, it won't appear in 65816 code
AssemblyLine.implied(TXS)).map(_.position(m.position)) // this TXS is fine, it won't appear in 65816 code
}
List.fill(m.stackVariablesSize)(AssemblyLine.implied(PHA))
List.fill(m.stackVariablesSize)(AssemblyLine.implied(PHA)).map(_.position(m.position))
}
}

View File

@ -75,8 +75,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
}
def fixTsx(code: List[AssemblyLine]): List[AssemblyLine] = code match {
case (tsx@AssemblyLine(TSX, _, _, _)) :: xs => tsx :: AssemblyLine.implied(INX) :: fixTsx(xs)
case (txs@AssemblyLine(TXS, _, _, _)) :: xs => ???
case (tsx@AssemblyLine0(TSX, _, _)) :: xs => tsx :: AssemblyLine.implied(INX) :: fixTsx(xs)
case (txs@AssemblyLine0(TXS, _, _)) :: xs => ???
case x :: xs => x :: fixTsx(xs)
case Nil => Nil
}
@ -95,22 +95,20 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
def changesZpreg(code: List[AssemblyLine], Offset: Int): Boolean = {
code.exists {
case AssemblyLine(op,
case AssemblyLine0(op,
AddrMode.ZeroPage | AddrMode.Absolute | AddrMode.LongAbsolute,
CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(Offset, _)),
_) if th.name == "__reg" && OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op) => true
case AssemblyLine(op,
CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(Offset, _))) if th.name == "__reg" && OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op) => true
case AssemblyLine0(op,
AddrMode.ZeroPage | AddrMode.Absolute | AddrMode.LongAbsolute,
MemoryAddressConstant(th),
_) if th.name == "__reg" && Offset == 0 && OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op) => true
case AssemblyLine(JSR | BYTE | BSR, _, _, _) => true
MemoryAddressConstant(th)) if th.name == "__reg" && Offset == 0 && OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op) => true
case AssemblyLine0(JSR | BYTE | BSR, _, _) => true
case _ => false
}
}
def preserveCarryIfNeeded(ctx: CompilationContext, code: List[AssemblyLine]): List[AssemblyLine] = {
if (code.exists {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => true
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => true
case x => OpcodeClasses.ChangesC(x.opcode)
}) {
AssemblyLine.implied(PHP) +: fixTsx(code) :+ AssemblyLine.implied(PLP)
@ -751,26 +749,36 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
ctx.log.error("Invalid number of parameters", f.position)
Nil
} else {
assertAllArithmeticBytes("Nonet argument has to be a byte", ctx, params)
params.head match {
case SumExpression(addends, _) =>
if (addends.exists(a => !a._1)) {
ctx.log.warn("Nonet subtraction may not work as expected", expr.position)
env.eval(expr) match {
case Some(c) =>
exprTypeAndVariable match {
case Some((t, v)) =>
compileConstant(ctx, c, v)
case _ =>
Nil
}
if (addends.size > 2) {
ctx.log.warn("Nonet addition works correctly only for two operands", expr.position)
case None =>
assertAllArithmeticBytes("Nonet argument has to be a byte", ctx, params)
params.head match {
case SumExpression(addends, _) =>
if (addends.exists(a => !a._1)) {
ctx.log.warn("Nonet subtraction may not work as expected", expr.position)
}
if (addends.size > 2) {
ctx.log.warn("Nonet addition works correctly only for two operands", expr.position)
}
case FunctionCallExpression("+" | "+'" | "<<" | "<<'" | "nonet", _) => // ok
case _ =>
ctx.log.warn("Unspecified nonet operation, results might be unpredictable", expr.position)
}
case FunctionCallExpression("+" | "+'" | "<<" | "<<'" | "nonet", _) => // ok
case _ =>
ctx.log.warn("Unspecified nonet operation, results might be unpredictable", expr.position)
val label = ctx.nextLabel("no")
compile(ctx, params.head, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None) ++ List(
AssemblyLine.immediate(LDX, 0),
AssemblyLine.relative(BCC, label),
AssemblyLine.implied(INX),
AssemblyLine.label(label)
)
}
val label = ctx.nextLabel("no")
compile(ctx, params.head, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None) ++ List(
AssemblyLine.immediate(LDX, 0),
AssemblyLine.relative(BCC, label),
AssemblyLine.implied(INX),
AssemblyLine.label(label)
)
}
case "&&" =>
assertBool(ctx, "&&", params)
@ -1076,12 +1084,12 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
case function: MacroFunction =>
val (paramPreparation, statements) = MosMacroExpander.inlineFunction(ctx, function, params, expr.position)
paramPreparation ++ statements.map {
case MosAssemblyStatement(opcode, addrMode, expression, elidable) =>
case MosAssemblyStatement(opcode, addrMode, expression, elidability) =>
val param = env.evalForAsm(expression).getOrElse {
ctx.log.error("Inlining failed due to non-constant things", expression.position)
Constant.Zero
}
AssemblyLine(opcode, addrMode, param, elidable)
AssemblyLine(opcode, addrMode, param, elidability)
}
case function: EmptyFunction =>
??? // TODO: type conversion?

View File

@ -1,7 +1,7 @@
package millfork.compiler.mos
import millfork.CompilationFlag
import millfork.assembly.BranchingOpcodeMapping
import millfork.assembly.{BranchingOpcodeMapping, Elidability}
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
@ -138,7 +138,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
List(AssemblyLine.implied(RTS))
})
}
statement match {
(statement match {
case EmptyStatement(stmts) =>
stmts.foreach(s => compile(ctx, s))
Nil
@ -165,10 +165,10 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
case RawBytesStatement(contents) =>
env.extractArrayContents(contents).map { expr =>
env.eval(expr) match {
case Some(c) => AssemblyLine(BYTE, RawByte, c, elidable = false)
case Some(c) => AssemblyLine(BYTE, RawByte, c, elidability = Elidability.Fixed)
case None =>
ctx.log.error("Non-constant raw byte", position = statement.position)
AssemblyLine(BYTE, RawByte, Constant.Zero, elidable = false)
AssemblyLine(BYTE, RawByte, Constant.Zero, elidability = Elidability.Fixed)
}
}
case Assignment(dest, source) =>
@ -273,7 +273,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
compileBreakStatement(ctx, s)
case s:ContinueStatement =>
compileContinueStatement(ctx, s)
}
}).map(_.positionIfEmpty(statement.position))
}
def stackPointerFixBeforeReturn(ctx: CompilationContext): List[AssemblyLine] = {

View File

@ -76,10 +76,10 @@ object PseudoregisterBuiltIns {
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(rightType -> ctx.env.get[VariableInMemory]("__reg.b2b3")), BranchSpec.None)
compileRight match {
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _)) => Nil
AssemblyLine0(LDA, Immediate, NumericConstant(0, _)),
AssemblyLine0(STA, ZeroPage, _),
AssemblyLine0(LDX | LDA, Immediate, NumericConstant(0, _)),
AssemblyLine0(STA | STX, ZeroPage, _)) => Nil
case _ =>
compileRight ++
List(
@ -108,16 +108,16 @@ object PseudoregisterBuiltIns {
compileRight match {
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _)) => Nil
AssemblyLine0(LDA, Immediate, NumericConstant(0, _)),
AssemblyLine0(STA, ZeroPage, _),
AssemblyLine0(LDX | LDA, Immediate, NumericConstant(0, _)),
AssemblyLine0(STA | STX, ZeroPage, _)) => Nil
case List(
l@AssemblyLine(LDA, _, _, _),
AssemblyLine(STA, ZeroPage, _, _),
h@AssemblyLine(LDX | LDA, addrMode, _, _),
AssemblyLine(STA | STX, ZeroPage, _, _)) if addrMode != ZeroPageY => BuiltIns.wrapInSedCldIfNeeded(decimal,
l@AssemblyLine0(LDA, _, _),
AssemblyLine0(STA, ZeroPage, _),
h@AssemblyLine0(LDX | LDA, addrMode, _),
AssemblyLine0(STA | STX, ZeroPage, _)) if addrMode != ZeroPageY => BuiltIns.wrapInSedCldIfNeeded(decimal,
List(prepareCarry,
AssemblyLine.zeropage(LDA, reg),
l.copy(opcode = op),
@ -176,24 +176,24 @@ object PseudoregisterBuiltIns {
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(MosExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
compileRight match {
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _))
AssemblyLine0(LDA, Immediate, NumericConstant(0, _)),
AssemblyLine0(STA, ZeroPage, _),
AssemblyLine0(LDX | LDA, Immediate, NumericConstant(0, _)),
AssemblyLine0(STA | STX, ZeroPage, _))
if op != AND => Nil
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0xff, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0xff, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _))
AssemblyLine0(LDA, Immediate, NumericConstant(0xff, _)),
AssemblyLine0(STA, ZeroPage, _),
AssemblyLine0(LDX | LDA, Immediate, NumericConstant(0xff, _)),
AssemblyLine0(STA | STX, ZeroPage, _))
if op == AND => Nil
case List(
l@AssemblyLine(LDA, _, _, _),
AssemblyLine(STA, ZeroPage, _, _),
h@AssemblyLine(LDX | LDA, addrMode, _, _),
AssemblyLine(STA | STX, ZeroPage, _, _)) if addrMode != ZeroPageY =>
l@AssemblyLine0(LDA, _, _),
AssemblyLine0(STA, ZeroPage, _),
h@AssemblyLine0(LDX | LDA, addrMode, _),
AssemblyLine0(STA | STX, ZeroPage, _)) if addrMode != ZeroPageY =>
List(
AssemblyLine.zeropage(LDA, reg),
l.copy(opcode = op),
@ -245,8 +245,8 @@ object PseudoregisterBuiltIns {
case _ =>
val compileCounter = MosExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(MosRegister.X, b)), NoBranching)
val compileCounterAndPrepareFirstParam = compileCounter match {
case List(AssemblyLine(LDX, _, _, _)) => firstParamCompiled ++ compileCounter
case List(AssemblyLine(LDY, _, _, _), AssemblyLine(LDX, _, _, _)) => firstParamCompiled ++ compileCounter
case List(AssemblyLine0(LDX, _, _)) => firstParamCompiled ++ compileCounter
case List(AssemblyLine0(LDY, _, _), AssemblyLine0(LDX, _, _)) => firstParamCompiled ++ compileCounter
case _ =>
MosExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching) ++
List(AssemblyLine.implied(PHA)) ++
@ -343,14 +343,14 @@ object PseudoregisterBuiltIns {
}
def usesRegLo(code: List[AssemblyLine]): Boolean = code.forall{
case AssemblyLine(JSR | BSR | TCD | TDC, _, _, _) => true
case AssemblyLine(_, _, MemoryAddressConstant(th), _) if th.name == "__reg" => true
case AssemblyLine0(JSR | BSR | TCD | TDC, _, _) => true
case AssemblyLine0(_, _, MemoryAddressConstant(th)) if th.name == "__reg" => true
case _ => false
}
def usesRegHi(code: List[AssemblyLine]): Boolean = code.forall{
case AssemblyLine(JSR | BSR | TCD | TDC, _, _, _) => true
case AssemblyLine(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _) if th.name == "__reg" => true
case AssemblyLine0(JSR | BSR | TCD | TDC, _, _) => true
case AssemblyLine0(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _))) if th.name == "__reg" => true
case _ => false
}
}

View File

@ -1,6 +1,7 @@
package millfork.compiler.z80
import millfork.CompilationFlag
import millfork.assembly.Elidability
import millfork.assembly.z80._
import millfork.compiler.CompilationContext
import millfork.env._
@ -68,7 +69,7 @@ object Z80BulkMemoryOperations {
case _ => Z80ExpressionCompiler.stashBCIfChanged(ctx, loadA)
}
val loadDE = calculateAddress match {
case List(ZLine(ZOpcode.LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), c, _)) =>
case List(ZLine0(ZOpcode.LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), c)) =>
if (incOp == DEC_16) List(ZLine.ldImm16(ZRegister.DE, (c - 1).quickSimplify))
else List(ZLine.ldImm16(ZRegister.DE, (c + 1).quickSimplify))
case _ => List(
@ -465,7 +466,7 @@ object Z80BulkMemoryOperations {
}
Z80StatementCompiler.compile(ctx, IfStatement(
FunctionCallExpression(operator, List(f.start, f.end)),
List(Z80AssemblyStatement(ZOpcode.NOP, NoRegisters, None, LiteralExpression(0, 1), elidable = false)),
List(Z80AssemblyStatement(ZOpcode.NOP, NoRegisters, None, LiteralExpression(0, 1), elidability = Elidability.Fixed)),
Nil))
}

View File

@ -233,13 +233,13 @@ object Z80Comparisons {
import ZRegister._
val sub = if (i == 0) SUB else SBC
var compareBytes = (lb, rb) match {
case (List(ZLine(LD, TwoRegisters(A, _), _, _)),
List(ZLine(LD, TwoRegisters(A, IMM_8), param, _))) =>
case (List(ZLine0(LD, TwoRegisters(A, _), _)),
List(ZLine0(LD, TwoRegisters(A, IMM_8), param))) =>
lb :+ ZLine.imm8(sub, param)
case (List(ZLine(LD, TwoRegisters(A, _), _, _)),
List(ZLine(LD, TwoRegisters(A, reg), _, _))) if reg != MEM_ABS_8 =>
case (List(ZLine0(LD, TwoRegisters(A, _), _)),
List(ZLine0(LD, TwoRegisters(A, reg), _))) if reg != MEM_ABS_8 =>
lb :+ ZLine.register(sub, reg)
case (List(ZLine(LD, TwoRegisters(A, _), _, _)), _) =>
case (List(ZLine0(LD, TwoRegisters(A, _), _)), _) =>
Z80ExpressionCompiler.stashAFIfChangedF(ctx, rb :+ ZLine.ld8(E, A)) ++ lb :+ ZLine.register(sub, E)
case _ =>
if (preserveBc || preserveHl) ??? // TODO: preserve HL/BC for the next round of comparisons
@ -293,17 +293,17 @@ object Z80Comparisons {
import ZOpcode._
import ZRegister._
(lb, rb) match {
case (_, List(ZLine(LD, TwoRegisters(A, IMM_8), param, _))) =>
case (_, List(ZLine0(LD, TwoRegisters(A, IMM_8), param))) =>
lb :+ ZLine.imm8(CP, param)
case (List(ZLine(LD, TwoRegisters(A, IMM_8), param, _)), _) =>
case (List(ZLine0(LD, TwoRegisters(A, IMM_8), param)), _) =>
rb :+ ZLine.imm8(CP, param)
case (List(ZLine(LD, TwoRegisters(A, _), _, _)),
List(ZLine(LD, TwoRegisters(A, reg), _, _))) if reg != MEM_ABS_8 =>
case (List(ZLine0(LD, TwoRegisters(A, _), _)),
List(ZLine0(LD, TwoRegisters(A, reg), _))) if reg != MEM_ABS_8 =>
lb :+ ZLine.register(CP, reg)
case (List(ZLine(LD, TwoRegisters(A, reg), _, _)),
List(ZLine(LD, TwoRegisters(A, _), _, _))) if reg != MEM_ABS_8 =>
case (List(ZLine0(LD, TwoRegisters(A, reg), _)),
List(ZLine0(LD, TwoRegisters(A, _), _))) if reg != MEM_ABS_8 =>
rb :+ ZLine.register(CP, reg)
case (List(ZLine(LD, TwoRegisters(A, _), _, _)), _) =>
case (List(ZLine0(LD, TwoRegisters(A, _), _)), _) =>
(rb :+ ZLine.ld8(E, A)) ++ lb :+ ZLine.register(CP, E)
case _ =>
var actualLb = lb
@ -322,14 +322,14 @@ object Z80Comparisons {
private def isBytesFromHL(calculateLeft: List[List[ZLine]]) = {
calculateLeft(1) match {
case List(ZLine(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.H), _, _)) => true
case List(ZLine0(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.H), _)) => true
case _ => false
}
}
private def isBytesFromBC(calculateLeft: List[List[ZLine]]) = {
calculateLeft(1) match {
case List(ZLine(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.B), _, _)) => true
case List(ZLine0(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.B), _)) => true
case _ => false
}
}

View File

@ -1,6 +1,7 @@
package millfork.compiler.z80
import millfork.CompilationFlag
import millfork.assembly.Elidability
import millfork.assembly.z80.ZLine
import millfork.compiler.{AbstractCompiler, CompilationContext}
import millfork.env.{Label, NormalParamSignature}
@ -14,7 +15,7 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
override def compile(ctx: CompilationContext): List[ZLine] = {
ctx.env.nameCheck(ctx.function.code)
val chunk = Z80StatementCompiler.compile(ctx, new Z80StatementPreprocessor(ctx, ctx.function.code)())
val label = ZLine.label(Label(ctx.function.name)).copy(elidable = false)
val label = ZLine.label(Label(ctx.function.name)).copy(elidability = Elidability.Fixed)
val storeParamsFromRegisters = ctx.function.params match {
case NormalParamSignature(List(param)) if param.typ.size == 1 =>
List(ZLine.ldAbs8(param.toAddress, ZRegister.A))
@ -61,7 +62,7 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
}
case _ => Nil
}
label :: (preserveRegisters(ctx) ++ stackPointerFixAtBeginning(ctx) ++ storeParamsFromRegisters ++ chunk)
label :: (preserveRegisters(ctx) ++ stackPointerFixAtBeginning(ctx) ++ storeParamsFromRegisters).map(_.position(ctx.function.position)) ++ chunk
}
def preserveRegisters(ctx: CompilationContext): List[ZLine] = {
@ -103,7 +104,7 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
def restoreRegistersAndReturn(ctx: CompilationContext): List[ZLine] = {
import millfork.assembly.z80.ZOpcode._
import ZRegister._
if (ctx.function.interrupt) {
(if (ctx.function.interrupt) {
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
if (ctx.options.flag(CompilationFlag.UseShadowRegistersForInterrupts)) {
List(
@ -155,7 +156,7 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
ZLine.register(POP, IX),
ZLine.implied(RET))
} else List(ZLine.implied(RET))
} else List(ZLine.implied(RET))
} else List(ZLine.implied(RET))).map(_.position(ctx.function.position))
}
def stackPointerFixAtBeginning(ctx: CompilationContext): List[ZLine] = {
@ -172,7 +173,7 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
import millfork.assembly.z80.ZOpcode._
import ZRegister._
val localVariableArea = ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)
if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
(if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
List(
ZLine.register(PUSH, IX),
ZLine.ldImm16(IX, 0x10000 - localVariableArea),
@ -228,6 +229,6 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
ZLine.registers(ADD_16, HL, SP),
ZLine.ld16(SP, HL))
}
}
}).map(_.position(ctx.function.position))
}
}

View File

@ -25,13 +25,13 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val toA = compileToA(ctx, expression)
if (toA.isEmpty) Nil else {
toA.last match {
case ZLine(ZOpcode.LD, TwoRegisters(ZRegister.A, source), _, _) if source == register =>
case ZLine0(ZOpcode.LD, TwoRegisters(ZRegister.A, source), _) if source == register =>
toA.init
case ZLine(ZOpcode.LD, TwoRegisters(ZRegister.A, source@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E | ZRegister.MEM_HL)), _, _) =>
case ZLine0(ZOpcode.LD, TwoRegisters(ZRegister.A, source@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E | ZRegister.MEM_HL)), _) =>
toA.init :+ ZLine.ld8(register, source)
case ZLine(ZOpcode.LD, TwoRegistersOffset(ZRegister.A, ZRegister.MEM_IX_D, offset), _, _) =>
case ZLine0(ZOpcode.LD, TwoRegistersOffset(ZRegister.A, ZRegister.MEM_IX_D, offset), _) =>
toA.init :+ ZLine.ldViaIx(register, offset)
case ZLine(ZOpcode.LD, TwoRegistersOffset(ZRegister.A, ZRegister.MEM_IY_D, offset), _, _) =>
case ZLine0(ZOpcode.LD, TwoRegistersOffset(ZRegister.A, ZRegister.MEM_IY_D, offset), _) =>
toA.init :+ ZLine.ldViaIy(register, offset)
case _ =>
toA :+ ZLine.ld8(register, ZRegister.A)
@ -126,15 +126,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
def fixTsx(ctx: CompilationContext, code: List[ZLine]): List[ZLine] = code match {
case (ldhlsp@ZLine(LD_HLSP, _, param, _)) :: xs => ldhlsp.copy(parameter = param + 2) :: fixTsx(ctx, xs)
case (ldhl@ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), param, _)) ::
(addhlsp@ZLine(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _, _)) ::
(ldsphl@ZLine(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _, _)) ::
case (ldhlsp@ZLine0(LD_HLSP, _, param)) :: xs => ldhlsp.copy(parameter = param + 2) :: fixTsx(ctx, xs)
case (ldhl@ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), param)) ::
(addhlsp@ZLine0(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _)) ::
(ldsphl@ZLine0(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _)) ::
xs => ??? // TODO: ldhl :: addhlsp :: ldsphl :: fixTsx(ctx, xs)
case (ldhl@ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), param, _)) ::
(addhlsp@ZLine(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _, _)) ::
case (ldhl@ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), param)) ::
(addhlsp@ZLine0(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _)) ::
xs => ldhl.copy(parameter = param + 2) :: addhlsp :: fixTsx(ctx, xs)
case (x@ZLine(EX_SP, _, _, _)) :: xs =>
case (x@ZLine0(EX_SP, _, _)) :: xs =>
// EX_SP should be only generated by the optimizer
ctx.log.warn("Stray EX (SP) encountered, generated code might be invalid")
x :: fixTsx(ctx, xs)
@ -438,7 +438,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
case i: IndexedExpression =>
calculateAddressToHL(ctx, i) match {
case List(ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr, _)) => loadByte(addr, target)
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(addr, target)
case code => code ++ loadByteViaHL(target)
}
case SumExpression(params, decimal) =>
@ -1188,7 +1188,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
case i:IndexedExpression =>
calculateAddressToHL(ctx, i) match {
case List(ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr, _)) => storeA(ctx, addr, 1, signedSource)
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => storeA(ctx, addr, 1, signedSource)
case code => if (code.exists(changesA)) {
List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(ctx, code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E)
} else code :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)

View File

@ -19,7 +19,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
val options = ctx.options
val env = ctx.env
val ret = Z80Compiler.restoreRegistersAndReturn(ctx)
statement match {
(statement match {
case EmptyStatement(stmts) =>
stmts.foreach(s => compile(ctx, s))
Nil
@ -158,7 +158,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
}
case ExpressionStatement(e) =>
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING)
case Z80AssemblyStatement(op, reg, offset, expression, elidable) =>
case Z80AssemblyStatement(op, reg, offset, expression, elidability) =>
val param: Constant = expression match {
// TODO: hmmm
case VariableExpression(name) =>
@ -191,8 +191,8 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
}
case _ => reg
}
List(ZLine(op, registers, param, elidable))
}
List(ZLine(op, registers, param, elidability))
}).map(_.position(statement.position))
}
private def fixStackOnReturn(ctx: CompilationContext): List[ZLine] = {

View File

@ -1,5 +1,6 @@
package millfork.node
import millfork.assembly.Elidability
import millfork.assembly.mos.{AddrMode, Opcode}
import millfork.assembly.z80.{ZOpcode, ZRegisters}
import millfork.env.{Constant, ParamPassingConvention, Type}
@ -345,11 +346,11 @@ case class Assignment(destination: LhsExpression, source: Expression) extends Ex
override def getAllExpressions: List[Expression] = List(destination, source)
}
case class MosAssemblyStatement(opcode: Opcode.Value, addrMode: AddrMode.Value, expression: Expression, elidable: Boolean) extends ExecutableStatement {
case class MosAssemblyStatement(opcode: Opcode.Value, addrMode: AddrMode.Value, expression: Expression, elidability: Elidability.Value) extends ExecutableStatement {
override def getAllExpressions: List[Expression] = List(expression)
}
case class Z80AssemblyStatement(opcode: ZOpcode.Value, registers: ZRegisters, offsetExpression: Option[Expression], expression: Expression, elidable: Boolean) extends ExecutableStatement {
case class Z80AssemblyStatement(opcode: ZOpcode.Value, registers: ZRegisters, offsetExpression: Option[Expression], expression: Expression, elidability: Elidability.Value) extends ExecutableStatement {
override def getAllExpressions: List[Expression] = List(expression)
}
@ -390,7 +391,7 @@ case class ContinueStatement(label: String) extends ExecutableStatement {
}
object MosAssemblyStatement {
def implied(opcode: Opcode.Value, elidable: Boolean) = MosAssemblyStatement(opcode, AddrMode.Implied, LiteralExpression(0, 1), elidable)
def implied(opcode: Opcode.Value, elidability: Elidability.Value) = MosAssemblyStatement(opcode, AddrMode.Implied, LiteralExpression(0, 1), elidability)
def nonexistent(opcode: Opcode.Value) = MosAssemblyStatement(opcode, AddrMode.DoesNotExist, LiteralExpression(0, 1), elidable = true)
def nonexistent(opcode: Opcode.Value) = MosAssemblyStatement(opcode, AddrMode.DoesNotExist, LiteralExpression(0, 1), elidability = Elidability.Elidable)
}

View File

@ -3,7 +3,7 @@ package millfork.output
import millfork.assembly._
import millfork.compiler.{AbstractCompiler, CompilationContext}
import millfork.env._
import millfork.error.{ConsoleLogger, Logger}
import millfork.error.Logger
import millfork.node.{CallGraph, NiceFunctionProperty, Program}
import millfork._
import millfork.assembly.z80.ZLine
@ -323,14 +323,14 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
assembly.append("* = $" + index.toHexString)
assembly.append(" !byte $2c")
assembly.append(" " + bytePseudoopcode + " $2c")
assembly.append(name)
val c = thing.toAddress
writeByte(bank, index, 0x2c.toByte) // BIT abs
index += 1
for (i <- 0 until typ.size) {
writeByte(bank, index, c.subbyte(i))
assembly.append(" !byte " + c.subbyte(i).quickSimplify)
assembly.append(" " + bytePseudoopcode + " " + c.subbyte(i).quickSimplify)
index += 1
}
initializedVariablesSize += typ.size
@ -341,7 +341,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
val index = codeAllocators("default").allocateBytes(mem.banks("default"), options, 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment)
writeByte("default", index, 2.toByte) // BIT abs
assembly.append("* = $" + index.toHexString)
assembly.append(" !byte 2 ;; end of LUnix relocatable segment")
assembly.append(" " + bytePseudoopcode + " 2 ;; end of LUnix relocatable segment")
justAfterCode += "default" -> (index + 1)
}
env.allPreallocatables.foreach {
@ -360,7 +360,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
index += 1
}
items.grouped(16).foreach { group =>
assembly.append(" !byte " + group.map(expr => env.eval(expr) match {
assembly.append(" " + bytePseudoopcode + " " + group.map(expr => env.eval(expr) match {
case Some(c) => c.quickSimplify.toString
case None => "<? unknown constant ?>"
}).mkString(", "))
@ -380,7 +380,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
case Some(c) =>
for (i <- 0 until typ.size) {
writeByte(bank, index, c.subbyte(i))
assembly.append(" !byte " + c.subbyte(i).quickSimplify)
assembly.append(" " + bytePseudoopcode + " " + c.subbyte(i).quickSimplify)
index += 1
}
case None =>
@ -475,24 +475,47 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[T]): List[T]
private val FinalWhitespace = "\\s+$".r
private def outputFunction(bank: String, code: List[T], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {
val printLineNumbers = options.flag(CompilationFlag.LineNumbersInAssembly)
var index = startFrom
assOut.append("* = $" + startFrom.toHexString)
var lastSource = Option.empty[SourceLine]
for (instr <- code) {
if (instr.isPrintable) {
if (options.flag(CompilationFlag.UseIntelSyntaxForOutput)) {
if(lastSource != instr.source) {
lastSource = instr.source
if (printLineNumbers) {
lastSource match {
case Some(SourceLine(moduleName, line)) if line > 0 =>
assOut.append(s";line:$line:$moduleName")
case _ =>
assOut.append(s";line")
}
}
}
val line = if (options.flag(CompilationFlag.UseIntelSyntaxForOutput)) {
instr match {
case zline: ZLine =>
assOut.append(zline.toIntelString)
zline.toIntelString
case _ =>
assOut.append(instr.toString)
instr.toString
}
} else {
assOut.append(instr.toString)
instr.toString
}
if (line.contains("; @")) {
assOut.append(FinalWhitespace.replaceAllIn(line.substring(0, line.lastIndexOf("; @")), ""))
} else {
assOut.append(line)
}
}
index = emitInstruction(bank, options, index, instr)
}
if (printLineNumbers && lastSource.isDefined){
assOut.append(s";line")
}
index
}

View File

@ -1,7 +1,7 @@
package millfork.output
import millfork.JobContext
import millfork.assembly.AbstractCode
import millfork.assembly.{AbstractCode, Elidability}
import millfork.assembly.mos.Opcode
import millfork.assembly.z80.ZOpcode
import millfork.compiler.AbstractCompiler
@ -72,8 +72,8 @@ abstract class AbstractInliningCalculator[T <: AbstractCode] {
case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions)
case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil))
case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil)
case MosAssemblyStatement(Opcode.JSR, _, VariableExpression(name), true) => (name -> false) :: Nil
case Z80AssemblyStatement(ZOpcode.CALL, _, _, VariableExpression(name), true) => (name -> false) :: Nil
case MosAssemblyStatement(Opcode.JSR, _, VariableExpression(name), Elidability.Elidable) => (name -> false) :: Nil
case Z80AssemblyStatement(ZOpcode.CALL, _, _, VariableExpression(name), Elidability.Elidable) => (name -> false) :: Nil
case s: Statement => getAllCalledFunctions(s.getAllExpressions)
case s: VariableExpression => Set(
s.name,

View File

@ -6,7 +6,7 @@ import millfork.env._
import millfork.error.{ConsoleLogger, FatalErrorReporting}
import millfork.node.{MosNiceFunctionProperty, NiceFunctionProperty, Program}
import millfork._
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode, OpcodeClasses}
import millfork.assembly.mos._
import millfork.compiler.mos.MosCompiler
import scala.annotation.tailrec
@ -33,32 +33,32 @@ class MosAssembler(program: Program,
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.Opcode._
instr match {
case AssemblyLine(BYTE, RawByte, c, _) =>
case AssemblyLine0(BYTE, RawByte, c) =>
writeByte(bank, index, c)
index + 1
case AssemblyLine(BYTE, _, _, _) => log.fatal("BYTE opcode failure")
case AssemblyLine(_, RawByte, _, _) => log.fatal("BYTE opcode failure")
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) =>
case AssemblyLine0(BYTE, _, _) => log.fatal("BYTE opcode failure")
case AssemblyLine0(_, RawByte, _) => log.fatal("BYTE opcode failure")
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(labelName))) =>
labelMap(labelName) = index
index
case AssemblyLine(_, DoesNotExist, _, _) =>
case AssemblyLine0(_, DoesNotExist, _) =>
index
case AssemblyLine(op, Implied, _, _) =>
case AssemblyLine0(op, Implied, _) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, Implied, options))
index + 1
case AssemblyLine(op, Relative, param, _) =>
case AssemblyLine0(op, Relative, param) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, Relative, options))
writeByte(bank, index + 1, AssertByte(param - (index + 2)))
index + 2
case AssemblyLine(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | LongIndexedY | LongIndexedZ | Stack), param, _) =>
case AssemblyLine0(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | LongIndexedY | LongIndexedZ | Stack), param) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeByte(bank, index + 1, param)
index + 2
case AssemblyLine(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param, _) =>
case AssemblyLine0(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
index + 3
case AssemblyLine(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param, _) =>
case AssemblyLine0(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
writeByte(bank, index + 3, extractBank(param, options))
@ -76,7 +76,7 @@ class MosAssembler(program: Program,
BIT |
ADC | SBC | AND | ORA | EOR |
INC | DEC | ROL | ROR | ASL | LSR |
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, true) =>
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
p match {
case NumericConstant(n, _) => if (n <= 255) l.copy(addrMode = AddrMode.ZeroPage) else l
case MemoryAddressConstant(th) => if (labelMap.getOrElse(th.name, 0x800) < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
@ -95,8 +95,8 @@ class MosAssembler(program: Program,
import Opcode._
import AddrMode._
code match {
case AssemblyLine(JMP | JSR | BSR, Indirect | LongIndirect | AbsoluteIndexedX, _, _) :: _ => true
case AssemblyLine(PHA, _, _, _) :: AssemblyLine(RTS | RTL, _, _, _) :: _ => true
case AssemblyLine0(JMP | JSR | BSR, Indirect | LongIndirect | AbsoluteIndexedX, _) :: _ => true
case AssemblyLine0(PHA, _, _) :: AssemblyLine0(RTS | RTL, _, _) :: _ => true
case _ :: xs => isNaughty(xs)
case Nil => false
}
@ -109,15 +109,15 @@ class MosAssembler(program: Program,
import NiceFunctionProperty._
if (isNaughty(code)) return
val localLabels = code.flatMap {
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) => Some(l)
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l)
case _ => None
}.toSet
def genericPropertyScan(niceFunctionProperty: NiceFunctionProperty)(predicate: AssemblyLine => Boolean): Unit = {
val preserved = code.forall {
case AssemblyLine(JSR | BSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => niceFunctionProperties(niceFunctionProperty -> th.name)
case AssemblyLine(JSR | BSR, _, _, _) => false
case AssemblyLine(op, _, MemoryAddressConstant(Label(label)), _) if OpcodeClasses.AllDirectJumps(op) => localLabels(label)
case AssemblyLine(op, _, _, _) if OpcodeClasses.AllDirectJumps(op) => false
case AssemblyLine0(JSR | BSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(th)) => niceFunctionProperties(niceFunctionProperty -> th.name)
case AssemblyLine0(JSR | BSR, _, _) => false
case AssemblyLine0(op, _, MemoryAddressConstant(Label(label))) if OpcodeClasses.AllDirectJumps(op) => localLabels(label)
case AssemblyLine0(op, _, _) if OpcodeClasses.AllDirectJumps(op) => false
case l => predicate(l)
}
if (preserved) {
@ -125,47 +125,47 @@ class MosAssembler(program: Program,
}
}
genericPropertyScan(DoesntChangeX) {
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesX(op)
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesX(op)
}
genericPropertyScan(DoesntChangeY) {
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesY(op)
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesY(op)
}
genericPropertyScan(DoesntChangeA) {
case AssemblyLine(op, _, _, _) if OpcodeClasses.ChangesAAlways(op) => false
case AssemblyLine(op, _, Implied, _) if OpcodeClasses.ChangesAIfImplied(op) => false
case AssemblyLine0(op, _, _) if OpcodeClasses.ChangesAAlways(op) => false
case AssemblyLine0(op, _, Implied) if OpcodeClasses.ChangesAIfImplied(op) => false
case _ => true
}
genericPropertyScan(DoesntChangeIZ) {
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesIZ(op)
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesIZ(op)
}
genericPropertyScan(DoesntChangeAH) {
case AssemblyLine(op, _, _, _) if OpcodeClasses.ChangesAHAlways(op) => false
case AssemblyLine(op, _, Implied, _) if OpcodeClasses.ChangesAHIfImplied(op) => false
case AssemblyLine0(op, _, _) if OpcodeClasses.ChangesAHAlways(op) => false
case AssemblyLine0(op, _, Implied) if OpcodeClasses.ChangesAHIfImplied(op) => false
case _ => true
}
genericPropertyScan(DoesntChangeC) {
case AssemblyLine(SEP | REP, Immediate, NumericConstant(imm, _), _) => (imm & 1) == 0
case AssemblyLine(SEP | REP, _, _, _) => false
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ChangesC(op)
case AssemblyLine0(SEP | REP, Immediate, NumericConstant(imm, _)) => (imm & 1) == 0
case AssemblyLine0(SEP | REP, _, _) => false
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesC(op)
}
genericPropertyScan(DoesntConcernD) {
case AssemblyLine(SEP | REP, Immediate, NumericConstant(imm, _), _) => (imm & 8) == 0
case AssemblyLine(SEP | REP, _, _, _) => false
case AssemblyLine(op, _, _, _) => !OpcodeClasses.ReadsD(op) && !OpcodeClasses.OverwritesD(op)
case AssemblyLine0(SEP | REP, Immediate, NumericConstant(imm, _)) => (imm & 8) == 0
case AssemblyLine0(SEP | REP, _, _) => false
case AssemblyLine0(op, _, _) => !OpcodeClasses.ReadsD(op) && !OpcodeClasses.OverwritesD(op)
}
genericPropertyScan(DoesntReadMemory) {
case AssemblyLine(op, Implied | Immediate | WordImmediate, _, _) => true
case AssemblyLine(op, _, _, _) if OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(op) => false
case AssemblyLine0(op, Implied | Immediate | WordImmediate, _) => true
case AssemblyLine0(op, _, _) if OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(op) => false
case _ => true
}
genericPropertyScan(DoesntWriteMemory) {
case AssemblyLine(op, Implied | Immediate | WordImmediate, _, _) => true
case AssemblyLine(op, _, _, _) if OpcodeClasses.ChangesMemoryIfNotImplied(op) || OpcodeClasses.ChangesMemoryAlways(op) => false
case AssemblyLine0(op, Implied | Immediate | WordImmediate, _) => true
case AssemblyLine0(op, _, _) if OpcodeClasses.ChangesMemoryIfNotImplied(op) || OpcodeClasses.ChangesMemoryAlways(op) => false
case _ => true
}
genericPropertyScan(IsLeaf) {
case AssemblyLine(JSR | BSR, Implied | Immediate | WordImmediate, _, _) => false
case AssemblyLine(JMP, Absolute, th:Thing, _) => th.name.startsWith(".")
case AssemblyLine0(JSR | BSR, Implied | Immediate | WordImmediate, _) => false
case AssemblyLine0(JMP, Absolute, th:Thing) => th.name.startsWith(".")
case _ => true
}
}

View File

@ -1,7 +1,7 @@
package millfork.output
import millfork.CompilationOptions
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode, OpcodeClasses}
import millfork.assembly.mos._
import millfork.env.{Environment, Label, MemoryAddressConstant}
import Opcode._
import millfork.assembly.mos.AddrMode._
@ -13,7 +13,7 @@ import scala.collection.mutable
*/
class MosDeduplicate(env: Environment, options: CompilationOptions) extends Deduplicate[AssemblyLine](env, options) {
override def getJump(line: AssemblyLine): Option[String] = line match {
case AssemblyLine(Opcode.JMP, Absolute, MemoryAddressConstant(thing), _) => Some(thing.name)
case AssemblyLine0(Opcode.JMP, Absolute, MemoryAddressConstant(thing)) => Some(thing.name)
case _ => None
}
@ -21,7 +21,7 @@ class MosDeduplicate(env: Environment, options: CompilationOptions) extends Dedu
override def actualCode(FunctionName: String, functionCode: List[AssemblyLine]): List[AssemblyLine] = {
functionCode match {
case AssemblyLine(Opcode.LABEL, _, MemoryAddressConstant(Label(FunctionName)), _) :: xs => xs
case AssemblyLine0(Opcode.LABEL, _, MemoryAddressConstant(Label(FunctionName))) :: xs => xs
case xs => xs
}
}
@ -63,14 +63,14 @@ class MosDeduplicate(env: Environment, options: CompilationOptions) extends Dedu
def rtsPrecededByDiscards(xs: List[AssemblyLine]): Option[List[AssemblyLine]] = {
xs match {
case AssemblyLine(op, _, _, _) :: xs if OpcodeClasses.NoopDiscardsFlags(op) => rtsPrecededByDiscards(xs)
case AssemblyLine(RTS, _, _, _) :: xs => Some(xs)
case AssemblyLine0(op, _, _) :: xs if OpcodeClasses.NoopDiscardsFlags(op) => rtsPrecededByDiscards(xs)
case AssemblyLine0(RTS, _, _) :: xs => Some(xs)
case _ => None
}
}
override def tco(code: List[AssemblyLine]): List[AssemblyLine] = code match {
case (call@AssemblyLine(JSR, Absolute | LongAbsolute, _, _)) :: xs => rtsPrecededByDiscards(xs) match {
case (call@AssemblyLine0(JSR, Absolute | LongAbsolute, _)) :: xs => rtsPrecededByDiscards(xs) match {
case Some(rest) => call.copy(opcode = JMP) :: tco(rest)
case _ => call :: tco(xs)
}
@ -82,13 +82,13 @@ class MosDeduplicate(env: Environment, options: CompilationOptions) extends Dedu
val map = mutable.Map[String, String]()
var counter = 0
code.foreach{
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(x)), _) if x.startsWith(".") =>
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(x))) if x.startsWith(".") =>
map(x) = if (temporary) ".ddtmp__" + counter else env.nextLabel("dd")
counter += 1
case _ =>
}
code.map{
case l@AssemblyLine(_, _, MemoryAddressConstant(Label(x)), _) if map.contains(x) =>
case l@AssemblyLine0(_, _, MemoryAddressConstant(Label(x))) if map.contains(x) =>
l.copy(parameter = MemoryAddressConstant(Label(map(x))))
case l => l
}
@ -98,14 +98,14 @@ class MosDeduplicate(env: Environment, options: CompilationOptions) extends Dedu
val myLabels = mutable.Set[String]()
val useCount = mutable.Map[String, Int]()
snippet.foreach{
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(x)), _) =>
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(x))) =>
myLabels += x
case AssemblyLine(_, _, MemoryAddressConstant(Label(x)), _) =>
case AssemblyLine0(_, _, MemoryAddressConstant(Label(x))) =>
useCount(x) = useCount.getOrElse(x, 0) - 1
case _ =>
}
wholeCode.foreach {
case AssemblyLine(op, _, MemoryAddressConstant(Label(x)), _) if op != LABEL && myLabels(x) =>
case AssemblyLine0(op, _, MemoryAddressConstant(Label(x))) if op != LABEL && myLabels(x) =>
useCount(x) = useCount.getOrElse(x, 0) + 1
case _ =>
}

View File

@ -1,6 +1,7 @@
package millfork.output
import millfork.JobContext
import millfork.assembly.Elidability
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.{AddrMode, _}
import millfork.compiler.AbstractCompiler
@ -23,13 +24,13 @@ object MosInliningCalculator extends AbstractInliningCalculator[AssemblyLine] {
def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[AssemblyLine]): Option[List[AssemblyLine]] = {
if (code.isEmpty) return None
val localLabels = code.flatMap{
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(l)), _) => Some(l)
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l)
case _ => None
}
val lastLineOfCode = code.last
lastLineOfCode match {
case AssemblyLine(RTS | RTL, _, _, _) =>
case AssemblyLine(JMP, AddrMode.Absolute, _, _) =>
case AssemblyLine0(RTS | RTL, _, _) =>
case AssemblyLine0(JMP, AddrMode.Absolute, _) =>
case _ => return None
}
var result = code.init
@ -41,20 +42,20 @@ object MosInliningCalculator extends AbstractInliningCalculator[AssemblyLine] {
}
if (result.head.opcode == LABEL && result.head.parameter == Label(fname).toAddress) result = result.tail
if (result.exists{
case AssemblyLine(op, AddrMode.Absolute | AddrMode.Relative | AddrMode.DoesNotExist, MemoryAddressConstant(Label(l)), _) if jumpingRelatedOpcodes(op) =>
case AssemblyLine0(op, AddrMode.Absolute | AddrMode.Relative | AddrMode.DoesNotExist, MemoryAddressConstant(Label(l))) if jumpingRelatedOpcodes(op) =>
if (!localLabels.contains(l) && !l.startsWith(".")) {
println("Bad jump " + l)
// println("Bad jump " + l)
true
} else false
case AssemblyLine(JSR, AddrMode.Absolute, MemoryAddressConstant(th:ExternFunction), _) =>
case AssemblyLine0(JSR, AddrMode.Absolute, MemoryAddressConstant(th:ExternFunction)) =>
false
case AssemblyLine(JSR, AddrMode.Absolute, MemoryAddressConstant(th:NormalFunction), _) =>
case AssemblyLine0(JSR, AddrMode.Absolute, MemoryAddressConstant(th:NormalFunction)) =>
if(!functionsThatCanBeCalledFromInlinedFunctions(th.name)){
println("Bad call " + th)
// println("Bad call " + th)
true
} else false
case AssemblyLine(op, _, _, _) if jumpingRelatedOpcodes(op) || badOpcodes(op) =>
println("Bad opcode " + op)
case AssemblyLine0(op, _, _) if jumpingRelatedOpcodes(op) || badOpcodes(op) =>
// println("Bad opcode " + op)
true
case _ => false
}) return None
@ -63,18 +64,18 @@ object MosInliningCalculator extends AbstractInliningCalculator[AssemblyLine] {
def inline(code: List[AssemblyLine], inlinedFunctions: Map[String, List[AssemblyLine]], jobContext: JobContext): List[AssemblyLine] = {
code.flatMap {
case AssemblyLine(Opcode.JSR, AddrMode.Absolute | AddrMode.LongAbsolute, p, true) if inlinedFunctions.contains(p.toString) =>
case AssemblyLine(Opcode.JSR, AddrMode.Absolute | AddrMode.LongAbsolute, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = jobContext.nextLabel("ai")
inlinedFunctions(p.toString).map {
case line@AssemblyLine(_, _, MemoryAddressConstant(Label(label)), _) =>
case line@AssemblyLine0(_, _, MemoryAddressConstant(Label(label))) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l
}
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, p, true) if inlinedFunctions.contains(p.toString) =>
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = jobContext.nextLabel("ai")
inlinedFunctions(p.toString).map {
case line@AssemblyLine(_, _, MemoryAddressConstant(Label(label)), _) =>
case line@AssemblyLine0(_, _, MemoryAddressConstant(Label(label))) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l

View File

@ -68,24 +68,24 @@ class Z80Assembler(program: Program,
}
try { instr match {
case ZLine(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName)), _) =>
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
labelMap(labelName) = index
index
case ZLine(BYTE, NoRegisters, param, _) =>
case ZLine0(BYTE, NoRegisters, param) =>
writeByte(bank, index, param)
index + 1
case ZLine(DISCARD_F | DISCARD_HL | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_A, NoRegisters, _, _) =>
case ZLine0(DISCARD_F | DISCARD_HL | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_A, NoRegisters, _) =>
index
case ZLine(LABEL | BYTE | DISCARD_F | DISCARD_HL | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_A, _, _, _) =>
case ZLine0(LABEL | BYTE | DISCARD_F | DISCARD_HL | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_A, _, _) =>
???
case ZLine(RST, NoRegisters, param, _) =>
case ZLine0(RST, NoRegisters, param) =>
val opcode = param.quickSimplify match {
case NumericConstant(n, _) if n >=0 && n <= 0x38 && n % 8 == 0 => 0xc7 + n.toInt
case _ => log.error("Invalid param for RST"); 0xc7
}
writeByte(bank, index, opcode)
index + 1
case ZLine(IM, NoRegisters, param, _) =>
case ZLine0(IM, NoRegisters, param) =>
requireZ80()
val opcode = param.quickSimplify match {
case NumericConstant(0, _) => 0x46
@ -96,23 +96,23 @@ class Z80Assembler(program: Program,
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, opcode)
index + 2
case ZLine(op, OneRegister(reg), _, _) if ZOpcodeClasses.AllSingleBit(op) =>
case ZLine0(op, OneRegister(reg), _) if ZOpcodeClasses.AllSingleBit(op) =>
requireExtended80()
writeByte(bank, index, 0xcb)
writeByte(bank, index + 1, ZOpcodeClasses.singleBitOpcode(op) + internalRegisterIndex(reg))
index + 2
case ZLine(op, NoRegisters, _, _) if implieds.contains(op) =>
case ZLine0(op, NoRegisters, _) if implieds.contains(op) =>
writeByte(bank, index, implieds(op))
index + 1
case ZLine(EXX, NoRegisters, _, _)=>
case ZLine0(EXX, NoRegisters, _)=>
requireZ80()
writeByte(bank, index, 0xd9)
index + 1
case ZLine(EX_AF_AF, NoRegisters, _, _)=>
case ZLine0(EX_AF_AF, NoRegisters, _)=>
requireZ80()
writeByte(bank, index, 8)
index + 1
case ZLine(RETI, NoRegisters, _, _) =>
case ZLine0(RETI, NoRegisters, _) =>
requireExtended80()
if (useSharpOpcodes()) {
writeByte(bank, index, 0xd9)
@ -122,15 +122,15 @@ class Z80Assembler(program: Program,
writeByte(bank, index + 1, 0x4d)
index + 2
}
case ZLine(op, NoRegisters, _, _) if edImplieds.contains(op) =>
case ZLine0(op, NoRegisters, _) if edImplieds.contains(op) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, edImplieds(op))
index + 2
case ZLine(ADD_16, TwoRegisters(ZRegister.HL, source), _, _) =>
case ZLine0(ADD_16, TwoRegisters(ZRegister.HL, source), _) =>
writeByte(bank, index, 9 + 16 * internalRegisterIndex(source))
index + 1
case ZLine(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source@(ZRegister.IX | ZRegister.IY)), _, _)=>
case ZLine0(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source@(ZRegister.IX | ZRegister.IY)), _)=>
requireZ80()
if (ix == source) {
writeByte(bank, index, prefixByte(ix))
@ -140,60 +140,60 @@ class Z80Assembler(program: Program,
log.fatal("Cannot assemble " + instr)
index
}
case ZLine(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source), _, _) =>
case ZLine0(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source), _) =>
requireZ80()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 9 + 16 * internalRegisterIndex(source))
index + 2
case ZLine(ADC_16, TwoRegisters(ZRegister.HL, reg), _, _) =>
case ZLine0(ADC_16, TwoRegisters(ZRegister.HL, reg), _) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x4a + 0x10 * internalRegisterIndex(reg))
index + 2
case ZLine(SBC_16, TwoRegisters(ZRegister.HL, reg), _, _) =>
case ZLine0(SBC_16, TwoRegisters(ZRegister.HL, reg), _) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x42 + 0x10 * internalRegisterIndex(reg))
index + 2
case ZLine(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.IMM_16), param, _) =>
case ZLine0(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.IMM_16), param) =>
requireZ80()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x21)
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(target, ZRegister.IMM_16), param, _) =>
case ZLine0(LD_16, TwoRegisters(target, ZRegister.IMM_16), param) =>
writeByte(bank, index, 1 + 16 * internalRegisterIndex(target))
writeWord(bank, index + 1, param)
index + 3
case ZLine(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.MEM_ABS_16), param, _) =>
case ZLine0(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.MEM_ABS_16), param) =>
requireZ80()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x2a)
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ix@(ZRegister.IX | ZRegister.IY)), param, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ix@(ZRegister.IX | ZRegister.IY)), param) =>
requireZ80()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x22)
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16), param, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16), param) =>
requireIntel8080()
writeByte(bank, index, 0x2a)
writeWord(bank, index + 1, param)
index + 3
case ZLine(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ZRegister.HL), param, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ZRegister.HL), param) =>
requireIntel8080()
writeByte(bank, index, 0x22)
writeWord(bank, index + 1, param)
index + 3
case ZLine(LD_16, TwoRegisters(reg@(ZRegister.BC | ZRegister.DE | ZRegister.SP), ZRegister.MEM_ABS_16), param, _) =>
case ZLine0(LD_16, TwoRegisters(reg@(ZRegister.BC | ZRegister.DE | ZRegister.SP), ZRegister.MEM_ABS_16), param) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index+1, 0x4b + 0x10 * internalRegisterIndex(reg))
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ZRegister.SP), param, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ZRegister.SP), param) =>
requireExtended80()
if (useSharpOpcodes()) {
writeByte(bank, index, 8)
@ -205,74 +205,74 @@ class Z80Assembler(program: Program,
writeWord(bank, index + 2, param)
index + 4
}
case ZLine(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, reg@(ZRegister.BC | ZRegister.DE)), param, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, reg@(ZRegister.BC | ZRegister.DE)), param) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index+1, 0x43 + 0x10 * internalRegisterIndex(reg))
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _) =>
writeByte(bank, index, 0xF9)
index + 1
case ZLine(LD_16, TwoRegisters(ZRegister.SP, ix@(ZRegister.IX | ZRegister.IY)), _, _) =>
case ZLine0(LD_16, TwoRegisters(ZRegister.SP, ix@(ZRegister.IX | ZRegister.IY)), _) =>
requireZ80()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0xF9)
index + 2
case ZLine(op, OneRegister(ZRegister.IMM_8), param, _) if immediates.contains(op) =>
case ZLine0(op, OneRegister(ZRegister.IMM_8), param) if immediates.contains(op) =>
val o = immediates(op)
writeByte(bank, index, o)
writeByte(bank, index + 1, param)
index + 2
case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if ZOpcodeClasses.AllSingleBit(op) =>
case ZLine0(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) if ZOpcodeClasses.AllSingleBit(op) =>
requireZ80()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0xcb)
writeByte(bank, index + 2, offset)
writeByte(bank, index + 3, ZOpcodeClasses.singleBitOpcode(op) + internalRegisterIndex(ZRegister.MEM_HL))
index + 4
case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if oneRegister.contains(op) =>
case ZLine0(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) if oneRegister.contains(op) =>
requireZ80()
val o = oneRegister(op)
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.MEM_HL) * o.multiplier)
writeByte(bank, index + 2, offset)
index + 3
case ZLine(op, OneRegister(ix@(ZRegister.IX | ZRegister.IY)), _, _) if oneRegister.contains(op) =>
case ZLine0(op, OneRegister(ix@(ZRegister.IX | ZRegister.IY)), _) if oneRegister.contains(op) =>
requireZ80()
val o = oneRegister(op)
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier)
writeByte(bank, index + 2, instr.parameter)
index + 3
case ZLine(op, OneRegister(reg), _, _) if reg != ZRegister.IMM_8 && oneRegister.contains(op) =>
case ZLine0(op, OneRegister(reg), _) if reg != ZRegister.IMM_8 && oneRegister.contains(op) =>
val o = oneRegister(op)
writeByte(bank, index, o.opcode + internalRegisterIndex(reg) * o.multiplier)
index + 1
case ZLine(SLL, OneRegister(reg), _, _) =>
case ZLine0(SLL, OneRegister(reg), _) =>
requireZ80Illegals()
writeByte(bank, index, 0xcb)
writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg))
index + 2
case ZLine(SWAP, OneRegister(reg), _, _) =>
case ZLine0(SWAP, OneRegister(reg), _) =>
requireSharp()
writeByte(bank, index, 0xcb)
writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg))
index + 2
case ZLine(SLL, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) =>
case ZLine0(SLL, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) =>
requireZ80Illegals()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0xcb)
writeByte(bank, index + 2, offset)
writeByte(bank, index + 3, 0x30 + internalRegisterIndex(MEM_HL))
index + 4
case ZLine(op, OneRegister(reg), _, _) if cbOneRegister.contains(op) =>
case ZLine0(op, OneRegister(reg), _) if cbOneRegister.contains(op) =>
requireExtended80()
val o = cbOneRegister(op)
writeByte(bank, index, 0xcb)
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(reg) * o.multiplier)
index + 2
case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if cbOneRegister.contains(op) =>
case ZLine0(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) if cbOneRegister.contains(op) =>
requireZ80()
val o = cbOneRegister(op)
writeByte(bank, index, prefixByte(ix))
@ -280,7 +280,7 @@ class Z80Assembler(program: Program,
writeByte(bank, index + 2, offset)
writeByte(bank, index + 3, o.opcode + internalRegisterIndex(MEM_HL) * o.multiplier)
index + 4
case ZLine(LD, registers, _, _) =>
case ZLine0(LD, registers, _) =>
registers match {
case TwoRegisters(I, A) =>
requireZ80()
@ -363,254 +363,254 @@ class Z80Assembler(program: Program,
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 1
}
case ZLine(IN_IMM, OneRegister(ZRegister.A), param, _) =>
case ZLine0(IN_IMM, OneRegister(ZRegister.A), param) =>
requireIntel8080()
writeByte(bank, index, 0xdb)
writeByte(bank, index + 1, param)
index + 2
case ZLine(IN_C, OneRegister(reg), param, _) =>
case ZLine0(IN_C, OneRegister(reg), param) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x40 + 8 * internalRegisterIndex(reg))
index + 2
case ZLine(OUT_IMM, OneRegister(ZRegister.A), param, _) =>
case ZLine0(OUT_IMM, OneRegister(ZRegister.A), param) =>
requireIntel8080()
writeByte(bank, index, 0xd3)
writeByte(bank, index + 1, param)
index + 2
case ZLine(OUT_C, OneRegister(reg), param, _) =>
case ZLine0(OUT_C, OneRegister(reg), param) =>
requireZ80()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x41 + 8 * internalRegisterIndex(reg))
index + 2
case ZLine(CALL, NoRegisters, param, _) =>
case ZLine0(CALL, NoRegisters, param) =>
writeByte(bank, index, 0xcd)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagClear(ZFlag.Z), param, _) =>
case ZLine0(CALL, IfFlagClear(ZFlag.Z), param) =>
writeByte(bank, index, 0xc4)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagClear(ZFlag.C), param, _) =>
case ZLine0(CALL, IfFlagClear(ZFlag.C), param) =>
writeByte(bank, index, 0xd4)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagClear(ZFlag.P), param, _) =>
case ZLine0(CALL, IfFlagClear(ZFlag.P), param) =>
requireIntel8080()
writeByte(bank, index, 0xe4)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagClear(ZFlag.S), param, _) =>
case ZLine0(CALL, IfFlagClear(ZFlag.S), param) =>
requireIntel8080()
writeByte(bank, index, 0xf4)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagSet(ZFlag.Z), param, _) =>
case ZLine0(CALL, IfFlagSet(ZFlag.Z), param) =>
writeByte(bank, index, 0xcc)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagSet(ZFlag.C), param, _) =>
case ZLine0(CALL, IfFlagSet(ZFlag.C), param) =>
writeByte(bank, index, 0xdc)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagSet(ZFlag.P), param, _) =>
case ZLine0(CALL, IfFlagSet(ZFlag.P), param) =>
requireIntel8080()
writeByte(bank, index, 0xec)
writeWord(bank, index + 1, param)
index + 3
case ZLine(CALL, IfFlagSet(ZFlag.S), param, _) =>
case ZLine0(CALL, IfFlagSet(ZFlag.S), param) =>
requireIntel8080()
writeByte(bank, index, 0xfc)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, NoRegisters, param, _) =>
case ZLine0(JP, NoRegisters, param) =>
writeByte(bank, index, 0xc3)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagClear(ZFlag.Z), param, _) =>
case ZLine0(JP, IfFlagClear(ZFlag.Z), param) =>
writeByte(bank, index, 0xc2)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagClear(ZFlag.C), param, _) =>
case ZLine0(JP, IfFlagClear(ZFlag.C), param) =>
writeByte(bank, index, 0xd2)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagClear(ZFlag.P), param, _) =>
case ZLine0(JP, IfFlagClear(ZFlag.P), param) =>
requireIntel8080()
writeByte(bank, index, 0xe2)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagClear(ZFlag.S), param, _) =>
case ZLine0(JP, IfFlagClear(ZFlag.S), param) =>
requireIntel8080()
writeByte(bank, index, 0xf2)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagSet(ZFlag.Z), param, _) =>
case ZLine0(JP, IfFlagSet(ZFlag.Z), param) =>
writeByte(bank, index, 0xca)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagSet(ZFlag.C), param, _) =>
case ZLine0(JP, IfFlagSet(ZFlag.C), param) =>
writeByte(bank, index, 0xda)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagSet(ZFlag.P), param, _) =>
case ZLine0(JP, IfFlagSet(ZFlag.P), param) =>
requireIntel8080()
writeByte(bank, index, 0xea)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, IfFlagSet(ZFlag.S), param, _) =>
case ZLine0(JP, IfFlagSet(ZFlag.S), param) =>
requireIntel8080()
writeByte(bank, index, 0xfa)
writeWord(bank, index + 1, param)
index + 3
case ZLine(JP, OneRegister(HL), _, _) =>
case ZLine0(JP, OneRegister(HL), _) =>
writeByte(bank, index, 0xe9)
index + 1
case ZLine(JP, OneRegister(IX), _, _) =>
case ZLine0(JP, OneRegister(IX), _) =>
requireZ80()
writeByte(bank, index, 0xdd)
writeByte(bank, index, 0xe9)
index + 2
case ZLine(JP, OneRegister(IY), _, _) =>
case ZLine0(JP, OneRegister(IY), _) =>
requireZ80()
writeByte(bank, index, 0xfd)
writeByte(bank, index, 0xe9)
index + 2
case ZLine(RET, IfFlagClear(ZFlag.Z), _, _) =>
case ZLine0(RET, IfFlagClear(ZFlag.Z), _) =>
writeByte(bank, index, 0xc0)
index + 1
case ZLine(RET, IfFlagClear(ZFlag.C), _, _) =>
case ZLine0(RET, IfFlagClear(ZFlag.C), _) =>
writeByte(bank, index, 0xd0)
index + 1
case ZLine(RET, IfFlagClear(ZFlag.P), _, _) =>
case ZLine0(RET, IfFlagClear(ZFlag.P), _) =>
requireIntel8080()
writeByte(bank, index, 0xe0)
index + 1
case ZLine(RET, IfFlagClear(ZFlag.S), _, _) =>
case ZLine0(RET, IfFlagClear(ZFlag.S), _) =>
requireIntel8080()
writeByte(bank, index, 0xf0)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.Z), _, _) =>
case ZLine0(RET, IfFlagSet(ZFlag.Z), _) =>
writeByte(bank, index, 0xc8)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.C), _, _) =>
case ZLine0(RET, IfFlagSet(ZFlag.C), _) =>
writeByte(bank, index, 0xd8)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.P), _, _) =>
case ZLine0(RET, IfFlagSet(ZFlag.P), _) =>
requireIntel8080()
writeByte(bank, index, 0xe8)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.S), _, _) =>
case ZLine0(RET, IfFlagSet(ZFlag.S), _) =>
requireIntel8080()
writeByte(bank, index, 0xf8)
index + 1
case ZLine(JR, IfFlagClear(ZFlag.Z), param, _) =>
case ZLine0(JR, IfFlagClear(ZFlag.Z), param) =>
requireExtended80()
writeByte(bank, index, 0x20)
writeByte(bank, index + 1, AssertByte(param - index - 2))
index + 2
case ZLine(JR, IfFlagClear(ZFlag.C), param, _) =>
case ZLine0(JR, IfFlagClear(ZFlag.C), param) =>
requireExtended80()
writeByte(bank, index, 0x30)
writeByte(bank, index + 1, AssertByte(param - index - 2))
index + 2
case ZLine(JR, IfFlagSet(ZFlag.Z), param, _) =>
case ZLine0(JR, IfFlagSet(ZFlag.Z), param) =>
requireExtended80()
writeByte(bank, index, 0x28)
writeByte(bank, index + 1, AssertByte(param - index - 2))
index + 2
case ZLine(JR, IfFlagSet(ZFlag.C), param, _) =>
case ZLine0(JR, IfFlagSet(ZFlag.C), param) =>
requireExtended80()
writeByte(bank, index, 0x38)
writeByte(bank, index + 1, AssertByte(param - index - 2))
index + 2
case ZLine(JR, NoRegisters, param, _) =>
case ZLine0(JR, NoRegisters, param) =>
requireExtended80()
writeByte(bank, index, 0x18)
writeByte(bank, index + 1, AssertByte(param - index - 2))
index + 2
case ZLine(DJNZ, NoRegisters, param, _) =>
case ZLine0(DJNZ, NoRegisters, param) =>
requireZ80()
writeByte(bank, index, 0x10)
writeByte(bank, index + 1, AssertByte(param - index - 2))
index + 2
case ZLine(EX_SP, OneRegister(HL), _, _) =>
case ZLine0(EX_SP, OneRegister(HL), _) =>
requireIntel8080()
writeByte(bank, index, 0xe3)
index + 1
case ZLine(EX_SP, OneRegister(IX), _, _) =>
case ZLine0(EX_SP, OneRegister(IX), _) =>
requireZ80()
writeByte(bank, index, 0xdd)
writeByte(bank, index + 1, 0xe3)
index + 2
case ZLine(EX_SP, OneRegister(IY), _, _) =>
case ZLine0(EX_SP, OneRegister(IY), _) =>
requireZ80()
writeByte(bank, index, 0xfd)
writeByte(bank, index + 1, 0xe3)
index + 2
case ZLine(EX_DE_HL, _, _, _) =>
case ZLine0(EX_DE_HL, _, _) =>
requireIntel8080()
writeByte(bank, index, 0xeb)
index + 1
case ZLine(ADD_SP, _, param, _) =>
case ZLine0(ADD_SP, _, param) =>
requireSharp()
writeByte(bank, index, 0xe8)
writeByte(bank, index + 1, param)
index + 2
case ZLine(LD_HLSP, _, param, _) =>
case ZLine0(LD_HLSP, _, param) =>
requireSharp()
writeByte(bank, index, 0xf8)
writeByte(bank, index + 1, param)
index + 2
case ZLine(LD_AHLI, _, _, _) =>
case ZLine0(LD_AHLI, _, _) =>
requireSharp()
writeByte(bank, index, 0x2a)
index + 1
case ZLine(LD_AHLD, _, _, _) =>
case ZLine0(LD_AHLD, _, _) =>
requireSharp()
writeByte(bank, index, 0x3a)
index + 1
case ZLine(LD_HLIA, _, _, _) =>
case ZLine0(LD_HLIA, _, _) =>
requireSharp()
writeByte(bank, index, 0x22)
index + 1
case ZLine(LD_HLDA, _, _, _) =>
case ZLine0(LD_HLDA, _, _) =>
requireSharp()
writeByte(bank, index, 0x32)
index + 1
case ZLine(LDH_AD, _, param, _) =>
case ZLine0(LDH_AD, _, param) =>
requireSharp()
writeByte(bank, index, 0xf0)
writeByte(bank, index + 1, param.loByte)
index + 2
case ZLine(LDH_DA, _, param, _) =>
case ZLine0(LDH_DA, _, param) =>
requireSharp()
writeByte(bank, index, 0xe0)
writeByte(bank, index + 1, param.loByte)
index + 2
case ZLine(LDH_AC, _, _, _) =>
case ZLine0(LDH_AC, _, _) =>
requireSharp()
writeByte(bank, index, 0xf2)
index + 1
case ZLine(LDH_CA, _, _, _) =>
case ZLine0(LDH_CA, _, _) =>
requireSharp()
writeByte(bank, index, 0xe2)
index + 1
case ZLine(STOP, _, _, _) =>
case ZLine0(STOP, _, _) =>
requireSharp()
writeByte(bank, index, 0x10)
index + 1

View File

@ -4,6 +4,7 @@ import millfork.CompilationOptions
import millfork.assembly.z80.{ZOpcode, _}
import millfork.env.{Environment, Label, MemoryAddressConstant}
import ZOpcode._
import millfork.assembly.Elidability
import millfork.node.ZRegister.SP
import scala.collection.mutable
@ -13,7 +14,7 @@ import scala.collection.mutable
*/
class Z80Deduplicate(env: Environment, options: CompilationOptions) extends Deduplicate[ZLine](env, options) {
override def getJump(line: ZLine): Option[String] = line match {
case ZLine(JP, NoRegisters, MemoryAddressConstant(thing), _) => Some(thing.name)
case ZLine0(JP, NoRegisters, MemoryAddressConstant(thing)) => Some(thing.name)
case _ => None
}
@ -21,7 +22,7 @@ class Z80Deduplicate(env: Environment, options: CompilationOptions) extends Dedu
override def actualCode(FunctionName: String, functionCode: List[ZLine]): List[ZLine] = {
functionCode match {
case ZLine(LABEL, _, MemoryAddressConstant(Label(FunctionName)), _) :: xs => xs
case ZLine0(LABEL, _, MemoryAddressConstant(Label(FunctionName))) :: xs => xs
case xs => xs
}
}
@ -65,20 +66,20 @@ class Z80Deduplicate(env: Environment, options: CompilationOptions) extends Dedu
case _ => false
}
override def createCall(functionName: String): ZLine = ZLine(CALL, NoRegisters, MemoryAddressConstant(Label(functionName)), elidable = false)
override def createCall(functionName: String): ZLine = ZLine(CALL, NoRegisters, MemoryAddressConstant(Label(functionName)), elidability = Elidability.Fixed)
override def createReturn(): ZLine = ZLine.implied(RET)
def retPrecededByDiscards(xs: List[ZLine]): Option[List[ZLine]] = {
xs match {
case ZLine(op, _, _, _) :: xs if ZOpcodeClasses.NoopDiscards(op) => retPrecededByDiscards(xs)
case ZLine(RET, _, _, _) :: xs => Some(xs)
case ZLine0(op, _, _) :: ys if ZOpcodeClasses.NoopDiscards(op) => retPrecededByDiscards(ys)
case ZLine0(RET, _, _) :: ys => Some(ys)
case _ => None
}
}
override def tco(code: List[ZLine]): List[ZLine] = code match {
case (call@ZLine(CALL, _, _, _)) :: xs => retPrecededByDiscards(xs) match {
case (call@ZLine0(CALL, _, _)) :: xs => retPrecededByDiscards(xs) match {
case Some(rest) =>call.copy(opcode = JP) :: tco(rest)
case _ => call :: tco(xs)
}
@ -90,13 +91,13 @@ class Z80Deduplicate(env: Environment, options: CompilationOptions) extends Dedu
val map = mutable.Map[String, String]()
var counter = 0
code.foreach{
case ZLine(LABEL, _, MemoryAddressConstant(Label(x)), _) if x.startsWith(".") =>
case ZLine0(LABEL, _, MemoryAddressConstant(Label(x))) if x.startsWith(".") =>
map(x) = if (temporary) ".ddtmp__" + counter else env.nextLabel("dd")
counter += 1
case _ =>
}
code.map{
case l@ZLine(_, _, MemoryAddressConstant(Label(x)), _) if map.contains(x) =>
case l@ZLine0(_, _, MemoryAddressConstant(Label(x))) if map.contains(x) =>
l.copy(parameter = MemoryAddressConstant(Label(map(x))))
case l => l
}
@ -106,14 +107,14 @@ class Z80Deduplicate(env: Environment, options: CompilationOptions) extends Dedu
val myLabels = mutable.Set[String]()
val useCount = mutable.Map[String, Int]()
snippet.foreach{
case ZLine(LABEL, _, MemoryAddressConstant(Label(x)), _) =>
case ZLine0(LABEL, _, MemoryAddressConstant(Label(x))) =>
myLabels += x
case ZLine(_, _, MemoryAddressConstant(Label(x)), _) =>
case ZLine0(_, _, MemoryAddressConstant(Label(x))) =>
useCount(x) = useCount.getOrElse(x, 0) - 1
case _ =>
}
wholeCode.foreach {
case ZLine(op, _, MemoryAddressConstant(Label(x)), _) if op != LABEL && myLabels(x) =>
case ZLine0(op, _, MemoryAddressConstant(Label(x))) if op != LABEL && myLabels(x) =>
useCount(x) = useCount.getOrElse(x, 0) + 1
case _ =>
}

View File

@ -1,6 +1,7 @@
package millfork.output
import millfork.JobContext
import millfork.assembly.Elidability
import millfork.assembly.z80._
import millfork.compiler.AbstractCompiler
import millfork.env._
@ -20,7 +21,7 @@ object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
override def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[ZLine]): Option[List[ZLine]] = {
if (code.isEmpty) return None
code.last match {
case ZLine(RET, NoRegisters, _, _) =>
case ZLine0(RET, NoRegisters, _) =>
case _ => return None
}
var result = code.init
@ -29,14 +30,14 @@ object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
}
if (result.head.opcode == LABEL && result.head.parameter == Label(fname).toAddress) result = result.tail
if (result.exists {
case ZLine(op, _, MemoryAddressConstant(Label(l)), _) if jumpingRelatedOpcodes(op) =>
case ZLine0(op, _, MemoryAddressConstant(Label(l))) if jumpingRelatedOpcodes(op) =>
!l.startsWith(".")
case ZLine(CALL, _, MemoryAddressConstant(th: ExternFunction), _) => false
case ZLine(CALL, _, NumericConstant(_, _), _) => false
case ZLine(JP, OneRegister(_), _, _) => false
case ZLine(CALL, _, MemoryAddressConstant(th: NormalFunction), _) =>
case ZLine0(CALL, _, MemoryAddressConstant(th: ExternFunction)) => false
case ZLine0(CALL, _, NumericConstant(_, _)) => false
case ZLine0(JP, OneRegister(_), _) => false
case ZLine0(CALL, _, MemoryAddressConstant(th: NormalFunction)) =>
!functionsThatCanBeCalledFromInlinedFunctions(th.name)
case ZLine(op, _, _, _) if jumpingRelatedOpcodes(op) || badOpcodes(op) => true
case ZLine0(op, _, _) if jumpingRelatedOpcodes(op) || badOpcodes(op) => true
case _ => false
}) return None
Some(result)
@ -55,20 +56,20 @@ object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
override def inline(code: List[ZLine], inlinedFunctions: Map[String, List[ZLine]], jobContext: JobContext): List[ZLine] = {
code.flatMap {
case ZLine(CALL, registers, p, true) if inlinedFunctions.contains(p.toString) =>
case ZLine(CALL, registers, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = jobContext.nextLabel("ai")
wrap(registers, jobContext,
inlinedFunctions(p.toString).map {
case line@ZLine(_, _, MemoryAddressConstant(Label(label)), _) =>
case line@ZLine0(_, _, MemoryAddressConstant(Label(label))) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l
})
case ZLine(JP | JR, registers, p, true) if inlinedFunctions.contains(p.toString) =>
case ZLine(JP | JR, registers, p, Elidability.Elidable, _) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = jobContext.nextLabel("ai")
wrap(registers, jobContext,
inlinedFunctions(p.toString).map {
case line@ZLine(_, _, MemoryAddressConstant(Label(label)), _) =>
case line@ZLine0(_, _, MemoryAddressConstant(Label(label))) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l

View File

@ -5,6 +5,7 @@ import java.nio.file.{Files, Paths}
import java.util
import fastparse.all._
import millfork.assembly.Elidability
import millfork.env._
import millfork.error.{ConsoleLogger, Logger}
import millfork.node._
@ -589,7 +590,10 @@ object MfParser {
val nonStatementLevel = 1 // everything but not `=`
val mathLevel = 4 // the `:` operator
val elidable: P[Boolean] = ("?".! ~/ HWS).?.map(_.isDefined)
val elidable: P[Elidability.Value] = ("?".! ~/ HWS).?.map{
case Some(_) => Elidability.Elidable
case _ => Elidability.Fixed
}
val externFunctionBody: P[Option[List[Statement]]] = P("extern" ~/ PassWith(None))

View File

@ -6,6 +6,7 @@ import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node._
import millfork.CompilationOptions
import millfork.assembly.Elidability
import millfork.output.{MemoryAlignment, NoAlignment, WithinPageAlignment}
/**
@ -21,7 +22,7 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
def fastAlignmentForFunctions: MemoryAlignment = WithinPageAlignment
// TODO: label and instruction in one line
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), elidable = true))
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), Elidability.Elidable))
// def zeropageAddrModeHint: P[Option[Boolean]] = Pass
@ -55,7 +56,7 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
}
val asmInstruction: P[ExecutableStatement] = {
val lineParser: P[(Boolean, Opcode.Value, (AddrMode.Value, Expression))] = !"}" ~ elidable ~/ asmOpcode ~/ asmParameter
val lineParser: P[(Elidability.Value, Opcode.Value, (AddrMode.Value, Expression))] = !"}" ~ elidable ~/ asmOpcode ~/ asmParameter
lineParser.map { case (elid, op, param) =>
(op, param._1) match {
case (Opcode.SAX, AddrMode.Implied) => MosAssemblyStatement(Opcode.HuSAX, param._1, param._2, elid)

View File

@ -4,7 +4,7 @@ import java.util.Locale
import fastparse.all.{parserApi, _}
import fastparse.core
import millfork.assembly.z80
import millfork.assembly.{Elidability, z80}
import millfork.{CompilationFlag, CompilationOptions, node}
import millfork.assembly.z80.{ZOpcode, _}
import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
@ -52,7 +52,7 @@ case class Z80Parser(filename: String,
} yield ParameterDeclaration(typ, appc).pos(p)
// TODO: label and instruction in one line
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidable = true))
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidability = Elidability.Elidable))
val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement)

View File

@ -1,9 +1,10 @@
package millfork.test
import millfork.assembly.Elidability
import millfork.{CompilationOptions, Cpu, CpuFamily, JobContext}
import millfork.assembly.z80.{LocalVariableAddressViaIX, NoRegisters, ZLine}
import millfork.compiler.LabelGenerator
import millfork.env.{Constant, Environment, NumericConstant}
import millfork.env.{Environment, NumericConstant}
import millfork.output.Z80Assembler
import millfork.test.emu._
import org.scalatest.{FunSuite, Matchers}
@ -43,7 +44,7 @@ class ZLineSizeSuite extends FunSuite with Matchers {
runCase(ZLine.register(BIT0, E))
runCase(ZLine.register(JP, IX))
runCase(ZLine.register(JP, HL))
runCase(ZLine(IM, NoRegisters, NumericConstant(1, 1), true))
runCase(ZLine(IM, NoRegisters, NumericConstant(1, 1), Elidability.Elidable))
runCase(ZLine.register(DEC, HL))
runCase(ZLine.register(DEC, LocalVariableAddressViaIX(7)))
runCase(ZLine.ld8(A, MEM_HL))