diff --git a/src/main/scala/millfork/assembly/z80/ZLine.scala b/src/main/scala/millfork/assembly/z80/ZLine.scala index 61ad06dc..e103fc98 100644 --- a/src/main/scala/millfork/assembly/z80/ZLine.scala +++ b/src/main/scala/millfork/assembly/z80/ZLine.scala @@ -1002,7 +1002,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case DE => changesRegister(D) || changesRegister(E) case IX => changesRegister(IXH) || changesRegister(IXL) case IY => changesRegister(IYH) || changesRegister(IYL) - case AF => ??? + case AF => true case IMM_8 | IMM_16 => false case MEM_ABS_8 | MEM_ABS_16 | MEM_DE | MEM_HL | MEM_BC | MEM_IX_D | MEM_IY_D | SP => ??? case _ => diff --git a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala index 9467d663..4b73c01d 100644 --- a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala +++ b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala @@ -496,6 +496,9 @@ object AlwaysGoodI80Optimizations { shallowerStack(code.tail.init) } }), + //37 + (Elidable & HasOpcode(PUSH) & HasRegisterParam(AF)) ~ + (Elidable & HasOpcode(POP) & HasRegisterParam(AF)) ~~> (_.tail.init), ) private def shallowerStack(lines: List[ZLine]): List[ZLine] = lines match { diff --git a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala index d9460923..919061b1 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala @@ -974,6 +974,11 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { case sot: StackOffsetThing => List(calculateStackAddressToD(ctx, sot.offset), List(MLine.tfr(M6809Register.A, M6809Register.B))) } + case e:DerefExpression => + List.tabulate(targetSize)(i => + if (i == 0) compileAddressToX(ctx, e) :+ MLine.indexedX(LDB, 0) + else List(MLine.indexedX(LDB, i)) + ) case e:FunctionCallExpression => ctx.env.maybeGet[NormalFunction](e.functionName) match { case Some(function) => diff --git a/src/main/scala/millfork/compiler/mos/BuiltIns.scala b/src/main/scala/millfork/compiler/mos/BuiltIns.scala index f543f64f..d4646a59 100644 --- a/src/main/scala/millfork/compiler/mos/BuiltIns.scala +++ b/src/main/scala/millfork/compiler/mos/BuiltIns.scala @@ -446,7 +446,7 @@ object BuiltIns { AssemblyLine.implied(TAX), AssemblyLine.label(labelRepeat)) ++ singleShift ++ List( AssemblyLine.implied(DEX), - AssemblyLine.relative(BEQ, labelRepeat)) + AssemblyLine.relative(BNE, labelRepeat)) case _ => val labelSkip = ctx.nextLabel("ss") val labelRepeat = ctx.nextLabel("sr") @@ -455,7 +455,7 @@ object BuiltIns { AssemblyLine.relative(BEQ, labelSkip), AssemblyLine.label(labelRepeat)) ++ singleShift ++ List( AssemblyLine.implied(DEX), - AssemblyLine.relative(BEQ, labelRepeat), + AssemblyLine.relative(BNE, labelRepeat), AssemblyLine.label(labelSkip)) } })) @@ -1320,8 +1320,8 @@ object BuiltIns { if (empty) { result += adc.copy(opcode = LDA) } else { - result += AssemblyLine.implied(CLC) - result += adc + result += AssemblyLine.implied(CLC) + result += adc } empty = false } diff --git a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala index 1c7e0238..0eabed54 100644 --- a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala @@ -113,17 +113,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { def compileToHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.HL) - def compileDerefPointer(ctx: CompilationContext, expression: DerefExpression): List[ZLine] = { + def compileDerefPointer(ctx: CompilationContext, expression: DerefExpression, extraOffset: Int = 0): List[ZLine] = { import ZRegister._ val innerPart = compileToHL(ctx, expression.inner) innerPart match { case List(ZLine0(LD_16, TwoRegisters(HL, IMM_16), c)) => - List(ZLine(LD_16, TwoRegisters(HL, IMM_16), c + expression.offset)) + List(ZLine(LD_16, TwoRegisters(HL, IMM_16), c + expression.offset + extraOffset)) case _ => - innerPart ++ (expression.offset match { + innerPart ++ ((expression.offset + extraOffset) match { case 0 => Nil - case i if i < 5 => List.fill(i)(ZLine.register(INC_16, ZRegister.HL)) // TODO: a better threshold - case _ => List(ZLine.ldImm8(ZRegister.C, expression.offset), ZLine.ldImm8(ZRegister.B, 0), ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) + case i if i > 0 && i < 5 => List.fill(i)(ZLine.register(INC_16, ZRegister.HL)) // TODO: a better threshold + case n => List(ZLine.ldImm8(ZRegister.C, n & 0xff), ZLine.ldImm8(ZRegister.B, n >> 8), ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) }) } } @@ -1355,12 +1355,12 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { } } - def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression, forWriting: Boolean): Option[(LocalVariableAddressOperand, List[ZLine])] = { + def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression, forWriting: Boolean, extraOffset: Int = 0): Option[(LocalVariableAddressOperand, List[ZLine])] = { val env = ctx.env expr match { case VariableExpression(name) => env.get[Variable](name) match { - case v:VariableInMemory => Some(LocalVariableAddressViaHL -> List(ZLine.ldImm16(ZRegister.HL, v.toAddress))) + case v:VariableInMemory => Some(LocalVariableAddressViaHL -> List(ZLine.ldImm16(ZRegister.HL, v.toAddress + extraOffset))) case v:StackVariable => if (ctx.options.flag(CompilationFlag.UseIxForStack)){ Some(LocalVariableAddressViaIX(v.baseOffset) -> Nil) @@ -1370,8 +1370,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { Some(LocalVariableAddressViaHL -> calculateStackAddressToHL(ctx, v)) } } - case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i, forWriting)) - case i:DerefExpression => Some(LocalVariableAddressViaHL -> compileDerefPointer(ctx, i)) + case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i, forWriting, extraOffset)) + case i:DerefExpression => Some(LocalVariableAddressViaHL -> compileDerefPointer(ctx, i, extraOffset)) case _:SeparateBytesExpression => None case _ => ??? } @@ -1391,11 +1391,11 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { } } - def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression, forWriting: Boolean): List[ZLine] = { + def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression, forWriting: Boolean, extraOffset: Int = 0): List[ZLine] = { val env = ctx.env val pointy = env.getPointy(i.name) AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index) - val elementSize = pointy.elementType.size + val elementSize = pointy.elementType.alignedSize val logElemSize = elementSize match { case 1 => 0 case 2 => 1 @@ -1409,9 +1409,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { ctx.log.error("Writing to a constant array", i.position) } env.evalVariableAndConstantSubParts(i.index) match { - case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset * elementSize).quickSimplify)) + case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + extraOffset + offset * elementSize).quickSimplify)) case (Some(index), offset) => - val constantPart = (baseAddr + offset * elementSize).quickSimplify + val constantPart = (baseAddr + extraOffset + offset * elementSize).quickSimplify if (getExpressionType(ctx, i.index).size == 1 && sizeInBytes.exists(_ < 256) && alignment == WithinPageAlignment) { compileToA(ctx, i.index) ++ List.fill(logElemSize)(ZLine.register(ADD, ZRegister.A)) ++ List( ZLine.imm8(ADD, constantPart.loByte), @@ -1426,7 +1426,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { } case VariablePointy(varAddr, _, _, _) => env.eval(i.index) match { - case Some(NumericConstant(0, _)) => + case Some(NumericConstant(0, _)) if extraOffset == 0 => if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) { List(ZLine.ldAbs16(ZRegister.HL, varAddr)) } else { @@ -1439,12 +1439,12 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { } case _ => if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) { - compileToBC(ctx, i.index) ++ + compileToBC(ctx, i.index #*# elementSize #+# extraOffset) ++ List(ZLine.ldAbs16(ZRegister.HL, varAddr)) ++ List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) } else { // TODO: is this reasonable? - compileToBC(ctx, i.index) ++ + compileToBC(ctx, i.index #*# elementSize #+# extraOffset) ++ List( ZLine.ldAbs8(ZRegister.A, varAddr), ZLine.ld8(ZRegister.L, ZRegister.A), @@ -1455,7 +1455,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { } case _: StackVariablePointy => compileToHL(ctx, VariableExpression(i.name).pos(i.position)) ++ - stashHLIfChanged(ctx, compileToBC(ctx, i.index)) ++ + stashHLIfChanged(ctx, compileToBC(ctx, i.index #*# elementSize #+# extraOffset)) ++ List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) } } diff --git a/src/main/scala/millfork/compiler/z80/Z80Shifting.scala b/src/main/scala/millfork/compiler/z80/Z80Shifting.scala index e807b03f..dbb29478 100644 --- a/src/main/scala/millfork/compiler/z80/Z80Shifting.scala +++ b/src/main/scala/millfork/compiler/z80/Z80Shifting.scala @@ -1,13 +1,14 @@ package millfork.compiler.z80 import millfork.CompilationFlag -import millfork.assembly.z80.{NoRegisters, ZLine, ZOpcode} +import millfork.assembly.z80.{LocalVariableAddressViaHL, LocalVariableAddressViaIX, LocalVariableAddressViaIY, NoRegisters, ZLine, ZOpcode} import millfork.compiler.CompilationContext import millfork.env.NumericConstant import millfork.error.ConsoleLogger import millfork.node._ import scala.collection.GenTraversableOnce +import scala.collection.mutable.ListBuffer /** * @author Karol Stasiak @@ -244,44 +245,117 @@ object Z80Shifting { } def compileLongShiftInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, size: Int, left: Boolean): List[ZLine] = { + import ZOpcode._ + import ZRegister._ val extended = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes) - val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size, includeStep = false) - val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL) - val shiftOne = if (left) { - loadLeft.zip(store).zipWithIndex.flatMap { - case ((ld, st), ix) => - import ZOpcode._ - import ZRegister._ - val shiftByte = - if (ix == 0) List(ZLine.register(ADD, A)) - else List(ZLine.implied(RLA)) - ld ++ shiftByte ++ st + val intel8080 = ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes) + val Some((loadRegisterOperand, loadSequence)) = + Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs, forWriting = true, extraOffset = if (left) 0 else size-1) + val result = ListBuffer[ZLine]() + result ++= loadSequence + def appendShift(preserveHL: Boolean): Unit = { + if (loadRegisterOperand == LocalVariableAddressViaHL && preserveHL) { + result += ZLine.ld8(D, H) + result += ZLine.ld8(E, L) } - } else { - loadLeft.reverse.zip(store.reverse).zipWithIndex.flatMap { - case ((ld, st), ix) => - import ZOpcode._ - import ZRegister._ - val shiftByte = if (ix == 0) { - if (extended) List(ZLine.register(SRL, A)) - else List(ZLine.register(OR, A), ZLine.implied(RRA)) - } else List(ZLine.implied(RRA)) - ld ++ shiftByte ++ st + if (left) { + for (ix <- 0 until size) { + val shiftedOperand = loadRegisterOperand match { + case LocalVariableAddressViaHL => + LocalVariableAddressViaHL + case LocalVariableAddressViaIX(offset) => + LocalVariableAddressViaIX(offset + ix) + case LocalVariableAddressViaIY(offset) => + LocalVariableAddressViaIY(offset + ix) + } + if (ix == 0) { + if (extended) { + result += ZLine.register(SLA, shiftedOperand) + } else { + result += ZLine.ld8(A, shiftedOperand) + result += ZLine.register(ADD, A) + result += ZLine.ld8(shiftedOperand, A) + } + } else { + if (extended) { + result += ZLine.register(RL, shiftedOperand) + } else { + result += ZLine.ld8(A, shiftedOperand) + result += ZLine.implied(RLA) + result += ZLine.ld8(shiftedOperand, A) + } + } + if (loadRegisterOperand == LocalVariableAddressViaHL && ix != size - 1) { + result += ZLine.register(INC_16, HL) + } + } + } else { + for (ix <- (size - 1).to(0, -1)) { + val shiftedOperand = loadRegisterOperand match { + case LocalVariableAddressViaHL => + LocalVariableAddressViaHL + case LocalVariableAddressViaIX(offset) => + LocalVariableAddressViaIX(offset + ix) + case LocalVariableAddressViaIY(offset) => + LocalVariableAddressViaIY(offset + ix) + } + if (ix == size - 1) { + if (extended) { + result += ZLine.register(SRL, shiftedOperand) + } else { + result += ZLine.ld8(A, shiftedOperand) + result += ZLine.register(OR, A) + result += ZLine.implied(RRA) + result += ZLine.ld8(shiftedOperand, A) + } + } else { + if (extended) { + result += ZLine.register(RR, shiftedOperand) + } else { + result += ZLine.ld8(A, shiftedOperand) + result += ZLine.implied(RRA) + result += ZLine.ld8(shiftedOperand, A) + } + } + if (loadRegisterOperand == LocalVariableAddressViaHL && ix != 0) { + result += ZLine.register(DEC_16, HL) + } + } + } + if (loadRegisterOperand == LocalVariableAddressViaHL && preserveHL) { + if (intel8080) { + result += ZLine.implied(EX_DE_HL) + } else { + result += ZLine.ld8(H, D) + result += ZLine.ld8(L, E) + } } } ctx.env.eval(rhs) match { - case Some(NumericConstant(0, _)) => Nil + case Some(NumericConstant(0, _)) => () case Some(NumericConstant(n, _)) if n < 0 => ctx.log.error("Negative shift amount", rhs.position) // TODO - Nil - case Some(NumericConstant(n, _)) => - List.fill(n.toInt)(shiftOne).flatten + () + case Some(NumericConstant(n, _)) if n < 3 => // TODO: more exact math on performance and size + for(i <- 0 until n.toInt) { + appendShift(i != n-1) + } case _ => val calcCount = calculateIterationCountPlus1(ctx, rhs) + if (loadRegisterOperand == LocalVariableAddressViaHL) { + result ++= Z80ExpressionCompiler.stashHLIfChanged(ctx, calcCount) + } else { + result ++= calcCount + } val labelL = ctx.nextLabel("sh") val labelS = ctx.nextLabel("sh") - calcCount ++ List(ZLine.jumpR(ctx, labelS), ZLine.label(labelL)) ++ shiftOne ++ List(ZLine.label(labelS)) ++ ZLine.djnz(ctx, labelL) + result += ZLine.jumpR(ctx, labelS) + result += ZLine.label(labelL) + appendShift(true) + result += ZLine.label(labelS) + result ++= ZLine.djnz(ctx, labelL) } + result.toList } } diff --git a/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala b/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala index 4cfe75e7..5f97647d 100644 --- a/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala +++ b/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala @@ -9,6 +9,7 @@ import millfork.env._ import millfork.error.ConsoleLogger import scala.collection.mutable +import scala.collection.mutable.ListBuffer /** * @author Karol Stasiak @@ -548,8 +549,35 @@ object ZBuiltIns { } } } else { - ctx.log.error("Too complex left-hand-side expression", lhs.position) - return Z80ExpressionCompiler.compile(ctx, lhs, ZExpressionTarget.NOTHING) ++ Z80ExpressionCompiler.compile(ctx, rhs, ZExpressionTarget.NOTHING) + val target = Z80ExpressionCompiler.compileToHL(ctx, inner #+# offset) + val sourceBytes = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size, ZExpressionTarget.BC).map(l => Z80ExpressionCompiler.stashHLIfChanged(ctx, l)) + val result = ListBuffer[ZLine]() + result ++= target + if (opcodeFirst == SUB && decimal) { + ??? + } else if (opcodeFirst == SUB && !decimal) { + for (i <- 0 until size) { + if (i != 0) result += ZLine.register(PUSH, ZRegister.AF) + result ++= sourceBytes(i) + result += ZLine.ld8(ZRegister.E, ZRegister.A) + if (i != 0) result += ZLine.register(POP, ZRegister.AF) + result += ZLine.ld8(ZRegister.A, ZRegister.MEM_HL) + result += ZLine.register(if (i == 0) opcodeFirst else opcodeLater, ZRegister.E) + result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A) + if (i != size - 1) result += ZLine.register(INC_16, ZRegister.HL) + } + } else { + for (i <- 0 until size) { + result ++= sourceBytes(i) + result += ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.MEM_HL) + if (decimal) result += ZLine.implied(DAA) + result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A) + if (i != size -1) result += ZLine.register(INC_16, ZRegister.HL) + } + } + return result.toList +// ctx.log.error("Too complex left-hand-side expression", lhs.position) +// return Z80ExpressionCompiler.compile(ctx, lhs, ZExpressionTarget.NOTHING) ++ Z80ExpressionCompiler.compile(ctx, rhs, ZExpressionTarget.NOTHING) } case _ => } diff --git a/src/test/scala/millfork/test/ArraySuite.scala b/src/test/scala/millfork/test/ArraySuite.scala index 9044e5a4..b5fb7026 100644 --- a/src/test/scala/millfork/test/ArraySuite.scala +++ b/src/test/scala/millfork/test/ArraySuite.scala @@ -465,29 +465,30 @@ class ArraySuite extends FunSuite with Matchers with AppendedClues { | a[0] = tmp | a[0] += 2 | a[0] <<= 2 + | a[0] -= 7 | } | noinline int32 f() = 5 """.stripMargin) { m => - m.readLong(0xc000) should equal(28) + m.readLong(0xc000) should equal(21) } } test("Various large assignments involving arrays") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Motorola6809)( """ | array(int32) a[7] @$c000 | void main () { | a[0] = 2 | } """.stripMargin) { m => } - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Motorola6809)( """ | array(int32) a[7] @$c000 | int32 main () { | return a[4] | } """.stripMargin) { m => } - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Motorola6809)( """ | array(int32) a[7] @$c000 | noinline void f(byte i) {