From 9b5b39181a50892961ecf30f86a397a50327f44e Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Wed, 1 Aug 2018 15:33:07 +0200 Subject: [PATCH] Z80: Decimal subtraction on 8080, fixes for adding longs returned from functions --- .../millfork/compiler/z80/ZBuiltIns.scala | 144 +++++++++++++++--- .../millfork/test/ByteDecimalMathSuite.scala | 21 ++- src/test/scala/millfork/test/LongTest.scala | 34 +++++ .../scala/millfork/test/emu/EmuZ80Run.scala | 11 +- 4 files changed, 182 insertions(+), 28 deletions(-) diff --git a/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala b/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala index ecf8cd46..6bf1d47a 100644 --- a/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala +++ b/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala @@ -129,13 +129,24 @@ object ZBuiltIns { result += ZLine.register(INC, ZRegister.A) } } else { - // TODO: optimize - result += ZLine.ld8(ZRegister.D, ZRegister.A) - result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr)) - result += ZLine.ld8(ZRegister.E, ZRegister.A) - result += ZLine.ld8(ZRegister.A, ZRegister.D) - result += ZLine.register(SUB, ZRegister.E) - if (decimal) { + if (!decimal || ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) { + // TODO: optimize + result += ZLine.ld8(ZRegister.D, ZRegister.A) + result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr)) + result += ZLine.ld8(ZRegister.E, ZRegister.A) + result += ZLine.ld8(ZRegister.A, ZRegister.D) + result += ZLine.register(SUB, ZRegister.E) + if (decimal) { + result += ZLine.implied(DAA) + } + } else { + // -' on Intel 8080 + result += ZLine.ld8(ZRegister.D, ZRegister.A) + result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr)) + result += ZLine.ld8(ZRegister.E, ZRegister.A) + result += ZLine.ldImm8(ZRegister.A, 0x9a) + result += ZLine.register(SUB, ZRegister.E) + result += ZLine.register(ADD, ZRegister.D) result += ZLine.implied(DAA) } } @@ -151,8 +162,17 @@ object ZBuiltIns { if (hasConst) { const match { case CompoundConstant(MathOperator.DecimalMinus | MathOperator.Minus, NumericConstant(0, _), c) => - result += ZLine.imm8(SUB, c.loByte) - if (decimal) { + if (!decimal || ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) { + result += ZLine.imm8(SUB, c.loByte) + if (decimal) { + result += ZLine.implied(DAA) + } + } else { + import ZRegister._ + result += ZLine.ld8(E, A) + result += ZLine.ldImm8(A, 0x9a) + result += ZLine.imm8(SUB, c.loByte) + result += ZLine.register(ADD, E) result += ZLine.implied(DAA) } case NumericConstant(n, _) if n < 0 && !decimal => @@ -183,11 +203,11 @@ object ZBuiltIns { result += ZLine.ld8(ZRegister.D, ZRegister.H) result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr)) result += ZLine.ld8(ZRegister.A, ZRegister.L) - result += ZLine.registers(ADD, ZRegister.A, ZRegister.E) + result += ZLine.register(ADD, ZRegister.E) result += ZLine.implied(DAA) result += ZLine.ld8(ZRegister.L, ZRegister.A) result += ZLine.ld8(ZRegister.A, ZRegister.H) - result += ZLine.registers(ADC, ZRegister.A, ZRegister.D) + result += ZLine.register(ADC, ZRegister.D) result += ZLine.implied(DAA) result += ZLine.ld8(ZRegister.H, ZRegister.A) } @@ -198,7 +218,36 @@ object ZBuiltIns { case (true, expr) => ctx.env.eval(expr) match { case None => - ctx.log.error("Decimal subtraction not supported on Intel 8080-like CPUs.", expr.position) + if (result.isEmpty) { + ??? + } else if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) { + result += ZLine.ld8(ZRegister.E, ZRegister.L) + result += ZLine.ld8(ZRegister.D, ZRegister.H) + result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr)) + result += ZLine.ld8(ZRegister.A, ZRegister.E) + result += ZLine.registers(SUB, ZRegister.A, ZRegister.L) + result += ZLine.implied(DAA) + result += ZLine.ld8(ZRegister.L, ZRegister.A) + result += ZLine.ld8(ZRegister.A, ZRegister.D) + result += ZLine.registers(SBC, ZRegister.A, ZRegister.H) + result += ZLine.implied(DAA) + result += ZLine.ld8(ZRegister.H, ZRegister.A) + } else { + result += ZLine.ld8(ZRegister.E, ZRegister.L) + result += ZLine.ld8(ZRegister.D, ZRegister.H) + result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr)) + result += ZLine.ldImm8(ZRegister.A, 0x9A) + result += ZLine.register(SUB, ZRegister.L) + result += ZLine.register(ADD, ZRegister.E) + result += ZLine.implied(DAA) + result += ZLine.ld8(ZRegister.L, ZRegister.A) + result += ZLine.ldImm8(ZRegister.A, 0x99) + result += ZLine.imm8(ADC, 0) + result += ZLine.register(SUB, ZRegister.H) + result += ZLine.register(ADD, ZRegister.D) + result += ZLine.implied(DAA) + result += ZLine.ld8(ZRegister.H, ZRegister.A) + } case Some(c) => hasConst = true const = CompoundConstant(MathOperator.DecimalMinus, const, c).quickSimplify @@ -309,12 +358,23 @@ object ZBuiltIns { ZLine.ld8(lv, ZRegister.A)) } case SUB if decimal => - setup ++ List( - ZLine.ld8(ZRegister.E, ZRegister.A), - ZLine.ld8(ZRegister.A, lv), - ZLine.register(SUB, ZRegister.E), - ZLine.implied(DAA), - ZLine.ld8(lv, ZRegister.A)) + if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) { + setup ++ List( + ZLine.ld8(ZRegister.E, ZRegister.A), + ZLine.ld8(ZRegister.A, lv), + ZLine.register(SUB, ZRegister.E), + ZLine.implied(DAA), + ZLine.ld8(lv, ZRegister.A)) + } else { + setup ++ List( + ZLine.ld8(ZRegister.E, ZRegister.A), + ZLine.ld8(ZRegister.D, lv), + ZLine.ldImm8(ZRegister.A, 0x9a), + ZLine.register(SUB, ZRegister.E), + ZLine.register(ADD, ZRegister.D), + ZLine.implied(DAA), + ZLine.ld8(lv, ZRegister.A)) + } case SUB if !decimal=> constantRight match { case Some(NumericConstant(1, _)) => @@ -447,14 +507,50 @@ object ZBuiltIns { } } val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size, includeStep = false) - val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL) val loadRight = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size, ZExpressionTarget.BC) - List.tabulate(size) {i => + val rightIsBig = loadRight.exists(_.exists(l => l.changesRegister(ZRegister.HL) || l.changesRegister(ZRegister.DE))) + val (leftStasher, calcStasher, loadLeft) = if (rightIsBig) { + ( + ((e: List[ZLine]) => Z80ExpressionCompiler.stashHLIfChanged(ctx, e)), + ((e: List[ZLine]) => Z80ExpressionCompiler.stashDEIfChanged(ctx, e)), + Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.BC) + ) + } else { + ( + ((e: List[ZLine]) => Z80ExpressionCompiler.stashBCIfChanged(ctx, e)), + ((e: List[ZLine]) => e), + Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL) + ) + } + List.tabulate(size) { i => + import ZRegister._ // TODO: stash things correctly? - val firstPhase = loadRight(i) ++ List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ - (Z80ExpressionCompiler.stashBCIfChanged(ctx, loadLeft(i)) :+ ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.E)) - val secondPhase = if (decimal) firstPhase :+ ZLine.implied(ZOpcode.DAA) else firstPhase - secondPhase ++ store(i) + if (opcodeFirst == ZOpcode.SUB && decimal && !ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) { + // TODO: check if carry is ok + if (i == 0) { + loadRight(i) ++ calcStasher(List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ + (leftStasher(loadLeft(i)) ++ List( + ZLine.ld8(D, A), + ZLine.ldImm8(A, 0x9A), + ZLine.register(SUB, E), + ZLine.register(ADD, D), + ZLine.implied(DAA)) ++ store(i))) + } else { + loadRight(i) ++ calcStasher(List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ + (leftStasher(loadLeft(i)) ++ List( + ZLine.ld8(D, A), + ZLine.ldImm8(A, 0x99), + ZLine.imm8(ADC, 0), + ZLine.register(SUB, E), + ZLine.register(ADD, D), + ZLine.implied(DAA)) ++ store(i))) + } + } else { + val firstPhase = loadRight(i) ++ calcStasher(List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ + (leftStasher(loadLeft(i)) :+ ZLine.register(if (i == 0) opcodeFirst else opcodeLater, ZRegister.E))) + val secondPhase = if (decimal) firstPhase :+ ZLine.implied(ZOpcode.DAA) else firstPhase + secondPhase ++ store(i) + } }.flatten } } diff --git a/src/test/scala/millfork/test/ByteDecimalMathSuite.scala b/src/test/scala/millfork/test/ByteDecimalMathSuite.scala index 563c2cc3..6952d70c 100644 --- a/src/test/scala/millfork/test/ByteDecimalMathSuite.scala +++ b/src/test/scala/millfork/test/ByteDecimalMathSuite.scala @@ -34,7 +34,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal byte subtraction") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Sharp)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( """ | byte output @$c000 | byte a @@ -80,7 +80,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("In-place decimal byte subtraction") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Sharp)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( """ | byte output @$c000 | byte a @@ -92,7 +92,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("In-place decimal word subtraction") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Sharp)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( """ | word output @$c000 | word a @@ -105,6 +105,21 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { """.stripMargin)(_.readWord(0xc000) should equal(0x781)) } + test("In-place decimal long subtraction") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( + """ + | long output @$c000 + | word a + | void main () { + | output = $22334455 + | output -'= f() + | } + | noinline long f() { + | return $20304050 + | } + """.stripMargin)(_.readLong(0xc000) should equal(0x2030405)) + } + test("Flag switching test") { EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( """ diff --git a/src/test/scala/millfork/test/LongTest.scala b/src/test/scala/millfork/test/LongTest.scala index 0dabc632..77ab64a2 100644 --- a/src/test/scala/millfork/test/LongTest.scala +++ b/src/test/scala/millfork/test/LongTest.scala @@ -85,6 +85,40 @@ class LongTest extends FunSuite with Matchers { m.readLong(0xc000) should equal(0x58008) } } + test("Long addition 3") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( + """ + | long output @$c000 + | void main () { + | output = 0 + | output += f() + | output += $8000 + | output += $8 + | } + | long f() { + | return $50000 + | } + """.stripMargin) { m => + m.readLong(0xc000) should equal(0x58008) + } + } + test("Extralong addition") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( + """ + | int128 output @$c000 + | void main () { + | output = 0 + | output += f() + | output += $8000 + | output += $8 + | } + | int128 f() { + | return $50000 + | } + """.stripMargin) { m => + m.readLong(0xc000) should equal(0x58008) + } + } test("Long subtraction") { EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)( """ diff --git a/src/test/scala/millfork/test/emu/EmuZ80Run.scala b/src/test/scala/millfork/test/emu/EmuZ80Run.scala index 40d14af7..ae6a1206 100644 --- a/src/test/scala/millfork/test/emu/EmuZ80Run.scala +++ b/src/test/scala/millfork/test/emu/EmuZ80Run.scala @@ -113,7 +113,13 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio memoryBank.output(0x1f3) = 0x76.toByte (0x200 until 0x2000).takeWhile(memoryBank.occupied(_)).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(log.debug(_)) - + val resetN = source.contains("-'") && !options.flag(CompilationFlag.EmitExtended80Opcodes) + val resetNMethod = { + val clazz = classOf[Z80Core] + val method = clazz.getDeclaredMethod("resetN") + method.setAccessible(true) + method + } val timings = platform.cpu match { case millfork.Cpu.Z80 | millfork.Cpu.Intel8080 => val cpu = new Z80Core(Z80Memory(memoryBank), DummyIO) @@ -122,6 +128,9 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio cpu.resetTStates() while (!cpu.getHalt) { cpu.executeOneInstruction() + if (resetN) { + resetNMethod.invoke(cpu) + } // dump(cpu) cpu.getTStates should be < TooManyCycles }