diff --git a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala index 4553f2f1..81cffd9f 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -188,9 +188,10 @@ object AlwaysGoodOptimizations { val PointlessOperationPairRemoval = new RuleBasedAssemblyOptimization("Pointless operation pair", needsFlowInfo = FlowInfoRequirement.NoRequirement, - operationPairBuilder(PHA, PLA, Not(ConcernsA) & Not(ConcernsStack)), - operationPairBuilder(PHX, PLX, Not(ConcernsX) & Not(ConcernsStack)), - operationPairBuilder(PHY, PLY, Not(ConcernsY) & Not(ConcernsStack)), + operationPairBuilder(PHA, PLA, Not(ChangesA) & Not(ConcernsStack)), + operationPairBuilder(PHX, PLX, Not(ChangesX) & Not(ConcernsStack)), + operationPairBuilder(PHY, PLY, Not(ChangesY) & Not(ConcernsStack)), + operationPairBuilder(PHZ, PLZ, Not(ChangesIZ) & Not(ConcernsStack)), operationPairBuilder(INX, DEX, Not(ConcernsX) & Not(ReadsNOrZ)), operationPairBuilder(DEX, INX, Not(ConcernsX) & Not(ReadsNOrZ)), operationPairBuilder(INY, DEY, Not(ConcernsX) & Not(ReadsNOrZ)), @@ -206,6 +207,15 @@ object AlwaysGoodOptimizations { } } + private def operationPairBuilder3(op1: Opcode.Value, op1extra: AssemblyLinePattern, op2: Opcode.Value, middle: AssemblyLinePattern) = { + (HasOpcode(op1) & Elidable & op1extra) ~ + middle.*.capture(1) ~ + Where(_.isExternallyLinearBlock(1)) ~ + (HasOpcode(op2) & Elidable) ~~> { (_, ctx) => + ctx.get[List[AssemblyLine]](1) + } + } + val PointlessOperationPairRemoval2 = new RuleBasedAssemblyOptimization("Pointless operation pair 2", needsFlowInfo = FlowInfoRequirement.BackwardFlow, operationPairBuilder2( @@ -236,6 +246,16 @@ object AlwaysGoodOptimizations { DEY, DoesntMatterWhatItDoesWith(State.Y, State.N, State.Z), Anything, INY, DoesntMatterWhatItDoesWith(State.Y, State.N, State.Z)), + operationPairBuilder3(PHA, Anything, PLA, Not(ChangesA) & Not(ConcernsStack)), + operationPairBuilder3(PHX, Anything, PLX, Not(ChangesX) & Not(ConcernsStack)), + operationPairBuilder3(PHY, Anything, PLY, Not(ChangesY) & Not(ConcernsStack)), + operationPairBuilder3(PHZ, Anything, PLZ, Not(ChangesIZ) & Not(ConcernsStack)), + operationPairBuilder3(PHD, Anything, PHD, Not(ChangesDirectPageRegister)), + operationPairBuilder3(PHB, Anything, PHB, Not(ChangesDataBankRegister)), + operationPairBuilder3(INX, DoesntMatterWhatItDoesWith(State.N, State.Z), DEX, Not(ConcernsX) & Not(ReadsNOrZ)), + operationPairBuilder3(DEX, DoesntMatterWhatItDoesWith(State.N, State.Z), INX, Not(ConcernsX) & Not(ReadsNOrZ)), + operationPairBuilder3(INY, DoesntMatterWhatItDoesWith(State.N, State.Z), DEY, Not(ConcernsX) & Not(ReadsNOrZ)), + operationPairBuilder3(DEY, DoesntMatterWhatItDoesWith(State.N, State.Z), INY, Not(ConcernsX) & Not(ReadsNOrZ)), ) val PointlessStackStashing = new RuleBasedAssemblyOptimization("Pointless stack stashing", diff --git a/src/main/scala/millfork/assembly/opt/CoarseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/opt/CoarseFlowAnalyzer.scala index 93a37d11..f4e43e74 100644 --- a/src/main/scala/millfork/assembly/opt/CoarseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/opt/CoarseFlowAnalyzer.scala @@ -54,11 +54,47 @@ object Status { case _ => AnyStatus() } + def zw(f: Int => Int = identity): Status[Boolean] = inner match { + case SingleStatus(x) => + val y = f(x) & 0xffff + SingleStatus(y == 0) + case _ => AnyStatus() + } + + def nw(f: Int => Int = identity): Status[Boolean] = inner match { + case SingleStatus(x) => + val y = f(x) & 0xffff + SingleStatus(y >= 0x8000) + case _ => AnyStatus() + } + + def lo: Status[Int] = inner match { + case SingleStatus(x) => SingleStatus(x & 0xff) + case _ => AnyStatus() + } + + def hi: Status[Int] = inner match { + case SingleStatus(x) => SingleStatus(x.&(0xff00).>>(8)) + case _ => AnyStatus() + } + def adc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match { case SingleStatus(x) => decimal match { case SingleStatus(false) => carry match { - case SingleStatus(true) => SingleStatus(x + value + 1) - case SingleStatus(false) => SingleStatus(x + value) + case SingleStatus(true) => SingleStatus((x + value + 1) & 0xff) + case SingleStatus(false) => SingleStatus((x + value) & 0xff) + case _ => AnyStatus() + } + case _ => AnyStatus() + } + case _ => AnyStatus() + } + + def adc_w(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match { + case SingleStatus(x) => decimal match { + case SingleStatus(false) => carry match { + case SingleStatus(true) => SingleStatus((x + value + 1) & 0xffff) + case SingleStatus(false) => SingleStatus((x + value) & 0xffff) case _ => AnyStatus() } case _ => AnyStatus() @@ -93,6 +129,7 @@ case class AnyStatus[T]() extends Status[T] { } //noinspection RedundantNewCaseClass case class CpuStatus(a: Status[Int] = UnknownStatus(), + ah: Status[Int] = UnknownStatus(), x: Status[Int] = UnknownStatus(), y: Status[Int] = UnknownStatus(), iz: Status[Int] = UnknownStatus(), @@ -107,14 +144,24 @@ case class CpuStatus(a: Status[Int] = UnknownStatus(), override def toString: String = s"A=$a,X=$x,Y=$y,Z=$z,N=$n,C=$c,V=$v,D=$d" + def aw: Status[Int] = (ah, a) match { + case (SingleStatus(h), SingleStatus(l)) => SingleStatus(h.&(0xff).<<(8).+(l&0xff)) + case (UnknownStatus(), UnknownStatus()) => UnknownStatus() + case _ => AnyStatus() + } + def nz: CpuStatus = this.copy(n = AnyStatus(), z = AnyStatus()) def nz(i: Long): CpuStatus = this.copy(n = SingleStatus((i & 0x80) != 0), z = SingleStatus((i & 0xff) == 0)) + def nzw(i: Long): CpuStatus = + this.copy(n = SingleStatus((i & 0x8000) != 0), z = SingleStatus((i & 0xffff) == 0)) + def ~(that: CpuStatus) = new CpuStatus( a = this.a ~ that.a, + ah = this.ah ~ that.ah, x = this.x ~ that.x, y = this.y ~ that.y, iz = this.iz ~ that.iz, @@ -129,6 +176,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus(), def hasClear(state: State.Value): Boolean = state match { case State.A => a.contains(0) + case State.AH => ah.contains(0) case State.X => x.contains(0) case State.Y => y.contains(0) case State.IZ => iz.contains(0) @@ -144,6 +192,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus(), def hasSet(state: State.Value): Boolean = state match { case State.A => false + case State.AH => false case State.X => false case State.Y => false case State.IZ => false @@ -254,24 +303,38 @@ object CoarseFlowAnalyzer { val n = nn.toInt & 0xff currentStatus = currentStatus.nz(n).copy(iz = SingleStatus(n)) + case AssemblyLine(LDX_W, WordImmediate, NumericConstant(nn, _), _) => + val n = nn.toInt & 0xff + currentStatus = currentStatus.nzw(nn).copy(x = SingleStatus(n)) + case AssemblyLine(LDY_W, WordImmediate, NumericConstant(nn, _), _) => + val n = nn.toInt & 0xff + currentStatus = currentStatus.nzw(nn).copy(y = SingleStatus(n)) + case AssemblyLine(LDA_W, WordImmediate, NumericConstant(nn, _), _) => + val n = nn.toInt & 0xff + val nh = (nn.toInt >> 8) & 0xff + currentStatus = currentStatus.nzw(nn).copy(a = SingleStatus(n), ah = SingleStatus(nh)) + + case AssemblyLine(XBA, _, _, _) => + currentStatus = currentStatus.copy(a = currentStatus.ah, n = currentStatus.ah.n(), z = currentStatus.ah.z(), ah = currentStatus.a) + case AssemblyLine(ADC, Immediate, NumericConstant(nn, _), _) => - val n = nn.toInt + val n = nn.toInt & 0xff val newA = currentStatus.a.adc(n, currentStatus.c, currentStatus.d) currentStatus = currentStatus.copy(n = newA.n(), z = newA.z(), a = newA, c = AnyStatus(), v = AnyStatus()) case AssemblyLine(EOR, Immediate, NumericConstant(nn, _), _) => - val n = nn.toInt + val n = nn.toInt & 0xff currentStatus = currentStatus.copy(n = currentStatus.a.n(_ ^ n), z = currentStatus.a.z(_ ^ n), a = currentStatus.a.map(_ ^ n)) case AssemblyLine(AND, Immediate, NumericConstant(nn, _), _) => - val n = nn.toInt + val n = nn.toInt & 0xff currentStatus = currentStatus.copy(n = currentStatus.a.n(_ & n), z = currentStatus.a.z(_ & n), a = currentStatus.a.map(_ & n)) case AssemblyLine(ANC, Immediate, NumericConstant(nn, _), _) => - val n = nn.toInt + val n = nn.toInt & 0xff currentStatus = currentStatus.copy(n = currentStatus.a.n(_ & n), c = currentStatus.a.n(_ & n), z = currentStatus.x.z(_ & n), a = currentStatus.a.map(_ & n)) case AssemblyLine(ORA, Immediate, NumericConstant(nn, _), _) => - val n = nn.toInt + val n = nn.toInt & 0xff currentStatus = currentStatus.copy(n = currentStatus.a.n(_ | n), z = currentStatus.a.z(_ | n), a = currentStatus.a.map(_ | n)) case AssemblyLine(ALR, Immediate, NumericConstant(nn, _), _) => - val n = nn.toInt + val n = nn.toInt & 0xff currentStatus = currentStatus.copy( n = currentStatus.a.n(i => (i & n & 0xff) >> 1), z = currentStatus.a.z(i => (i & n & 0xff) >> 1), @@ -279,19 +342,23 @@ object CoarseFlowAnalyzer { a = currentStatus.a.map(i => (i & n & 0xff) >> 1)) + case AssemblyLine(ADC_W, WordImmediate, NumericConstant(nn, _), _) => - val n = nn.toInt & 0xff - val newA = currentStatus.a.adc(n, currentStatus.c, currentStatus.d) - currentStatus = currentStatus.copy(n = AnyStatus(), z = newA.z().withHiddenHi, a = newA, c = AnyStatus(), v = AnyStatus()) - case AssemblyLine(EOR_W, WordImmediate, NumericConstant(nn, _), _) => - val n = nn.toInt & 0xff - currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ ^ n).withHiddenHi, a = currentStatus.a.map(_ ^ n)) + val n = nn.toInt & 0xffff + val newA = currentStatus.aw.adc_w(n, currentStatus.c, currentStatus.d) + currentStatus = currentStatus.copy(n = newA.nw(), z = newA.zw(), a = newA.lo, ah = newA.hi, c = AnyStatus(), v = AnyStatus()) case AssemblyLine(AND_W, WordImmediate, NumericConstant(nn, _), _) => - val n = nn.toInt & 0xff - currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ & n).withHiddenHi, a = currentStatus.a.map(_ & n)) + val n = nn.toInt & 0xffff + val newA = currentStatus.aw.map(_ & 0xffff) + currentStatus = currentStatus.copy(n = newA.nw(), z = newA.zw(), a = newA.lo, ah = newA.hi) + case AssemblyLine(EOR_W, WordImmediate, NumericConstant(nn, _), _) => + val n = nn.toInt & 0xffff + val newA = currentStatus.aw.map(_ ^ 0xffff) + currentStatus = currentStatus.copy(n = newA.nw(), z = newA.zw(), a = newA.lo, ah = newA.hi) case AssemblyLine(ORA_W, WordImmediate, NumericConstant(nn, _), _) => - val n = nn.toInt & 0xff - currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ | n).withHiddenHi, a = currentStatus.a.map(_ | n)) + val n = nn.toInt & 0xffff + val newA = currentStatus.aw.map(_ | 0xffff) + currentStatus = currentStatus.copy(n = newA.nw(), z = newA.zw(), a = newA.lo, ah = newA.hi) case AssemblyLine(INX, Implied, _, _) => currentStatus = currentStatus.copy(n = currentStatus.x.n(_ + 1), z = currentStatus.x.z(_ + 1), x = currentStatus.x.map(v => (v + 1) & 0xff)) @@ -317,9 +384,11 @@ object CoarseFlowAnalyzer { case AssemblyLine(DEY_W, Implied, _, _) => currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.y.z(_ - 1).withHiddenHi, y = currentStatus.y.map(v => (v - 1) & 0xff)) case AssemblyLine(INC_W, Implied, _, _) => - currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ + 1).withHiddenHi, a = currentStatus.a.map(v => (v + 1) & 0xff)) + val newA = currentStatus.aw.map(v => (v + 1) & 0xffff) + currentStatus = currentStatus.copy(n = newA.nw(), z = newA.zw(), a = newA.lo, ah = newA.hi) case AssemblyLine(DEC_W, Implied, _, _) => - currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ - 1).withHiddenHi, a = currentStatus.a.map(v => (v - 1) & 0xff)) + val newA = currentStatus.aw.map(v => (v - 1) & 0xffff) + currentStatus = currentStatus.copy(n = newA.nw(), z = newA.zw(), a = newA.lo, ah = newA.hi) case AssemblyLine(TAX, _, _, _) => currentStatus = currentStatus.copy(x = currentStatus.a, n = currentStatus.a.n(), z = currentStatus.a.z()) @@ -351,6 +420,8 @@ object CoarseFlowAnalyzer { if (OpcodeClasses.ChangesY(opcode)) currentStatus = currentStatus.copy(y = AnyStatus()) if (OpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus()) if (addrMode == Implied && OpcodeClasses.ChangesAIfImplied(opcode)) currentStatus = currentStatus.copy(a = AnyStatus()) + if (OpcodeClasses.ChangesAHAlways(opcode)) currentStatus = currentStatus.copy(ah = AnyStatus()) + if (addrMode == Implied && OpcodeClasses.ChangesAHIfImplied(opcode)) currentStatus = currentStatus.copy(ah = AnyStatus()) if (OpcodeClasses.ChangesNAndZ(opcode)) currentStatus = currentStatus.nz if (OpcodeClasses.ChangesC(opcode)) currentStatus = currentStatus.copy(c = AnyStatus()) if (OpcodeClasses.ChangesV(opcode)) currentStatus = currentStatus.copy(v = AnyStatus()) diff --git a/src/main/scala/millfork/assembly/opt/SixteenOptimizations.scala b/src/main/scala/millfork/assembly/opt/SixteenOptimizations.scala index 785448cf..64416225 100644 --- a/src/main/scala/millfork/assembly/opt/SixteenOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/SixteenOptimizations.scala @@ -185,11 +185,33 @@ object SixteenOptimizations { }, ) + val PointlessIndexTransfers = new RuleBasedAssemblyOptimization("Pointless index transfer", + needsFlowInfo = FlowInfoRequirement.BackwardFlow, + (Elidable & HasOpcode(TXY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Not(ChangesX) & Not(ChangesY) & Linear & (Not(ConcernsY) | Elidable & HasAddrMode(AbsoluteY) & SupportsAbsoluteX)).* ~ + (Not(ReadsY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> (_.tail.map { l => + if (l.addrMode == AbsoluteY) l.copy(addrMode = AbsoluteX) else l + }), + (Elidable & HasOpcode(TYX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Not(ChangesX) & Not(ChangesY) & Linear & (Not(ConcernsX) | Elidable & HasAddrMode(AbsoluteX) & SupportsAbsoluteY)).* ~ + (Not(ReadsX) & DoesntMatterWhatItDoesWith(State.X)) ~~> (_.tail.map { l => + if (l.addrMode == AbsoluteX) l.copy(addrMode = AbsoluteY) else l + }), + ) + // TODO: rewrite most 8-bit optimizations that are applicable to 16-bit code - val AllForEmulation: List[AssemblyOptimization] = List(AccumulatorSwapping, OptimizeZeroIndex, RepSepWeakening, OptimizeStackRelative) + val AllForEmulation: List[AssemblyOptimization] = List( + AccumulatorSwapping, + OptimizeStackRelative, + OptimizeZeroIndex, + PointlessIndexTransfers, + RepSepWeakening, + ) - val AllForNative: List[AssemblyOptimization] = List(PointlessLoadAfterLoadOrStore) + val AllForNative: List[AssemblyOptimization] = List( + PointlessLoadAfterLoadOrStore + ) val All: List[AssemblyOptimization] = AllForEmulation ++ AllForNative }