From e77811c67cd68579847445b5d2b9cf47f085fffb Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Sun, 21 Jun 2020 02:25:02 +0200 Subject: [PATCH] 6809: Implement decimal operations --- .../compiler/AbstractExpressionCompiler.scala | 1 + .../compiler/m6809/M6809Buitins.scala | 75 ++++-- .../compiler/m6809/M6809DecimalBuiltins.scala | 240 +++++++++++++++++- .../m6809/M6809ExpressionCompiler.scala | 25 +- .../compiler/m6809/M6809LargeBuiltins.scala | 44 +++- .../compiler/z80/Z80DecimalBuiltIns.scala | 2 +- .../millfork/test/ByteDecimalMathSuite.scala | 35 +-- 7 files changed, 370 insertions(+), 52 deletions(-) diff --git a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala index 8b061e54..0073497a 100644 --- a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala @@ -500,6 +500,7 @@ object AbstractExpressionCompiler { case FunctionCallExpression(">>", List(a1, a2)) => if (getExpressionTypeImpl(env, log, a2, loosely).size > 1) log.error("Shift amount too large", a2.position) getExpressionTypeImpl(env, log, a1, loosely) + case FunctionCallExpression("*'", _) => b case FunctionCallExpression("<<'", _) => b case FunctionCallExpression(">>'", _) => b case FunctionCallExpression(">>>>", _) => b diff --git a/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala b/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala index 7b4f4c73..a50daa69 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala @@ -234,23 +234,36 @@ object M6809Buitins { } def compileWordSum(ctx: CompilationContext, expr: SumExpression, fromScratch: Boolean): List[MLine] = { - if (expr.decimal) ??? val (constant, variable) = split(ctx, expr) val addendReads = variable .map(addend => addend._1 -> M6809ExpressionCompiler.compileToD(ctx, addend._2)) .map(code => (if (code._1) 1 else 0, complexityD(code._2)) -> code).sortBy(_._1).map(_._2) val result = ListBuffer[MLine]() + def decimalInvertD(): Unit = { + // TODO: check if it's ok: + val label = ctx.nextLabel("xx") + result += MLine.immediate(CMPD, 0) + result += MLine.shortBranch(BEQ, label) + result += MLine.immediate(EORA, 0xff) + result += MLine.immediate(EORB, 0xff) + result += MLine.immediate(ADDD, 0x999B) + result += MLine.label(label) + } for ((neg, load) <- addendReads) { if (result.isEmpty && fromScratch) { result ++= load if (neg) { - result += MLine.immediate(EORA, 0xff) - result += MLine.immediate(EORB, 0xff) - result += MLine.immediate(ADDD, 1) + if (expr.decimal) { + decimalInvertD() + } else { + result += MLine.immediate(EORA, 0xff) + result += MLine.immediate(EORB, 0xff) + result += MLine.immediate(ADDD, 1) + } } } else { load match { - case List(l@MLine0(LDD, _, _)) => + case List(l@MLine0(LDD, _, _)) if !expr.decimal=> if (neg) { result += l.copy(opcode = SUBD) } else { @@ -258,17 +271,42 @@ object M6809Buitins { } case _ => if (neg) { - result += MLine.pp(PSHS, M6809Register.D) - result ++= load - // TODO: optimize - result += MLine.immediate(EORA, 0xff) - result += MLine.immediate(EORB, 0xff) - result += MLine.immediate(ADDD, 1) - result += MLine.accessAndPullSTwice(ADDD) + if (expr.decimal) { + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.pp(PSHS, M6809Register.D) + result ++= load + decimalInvertD() + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.accessAndPullS(ADDA) + result += MLine.inherent(DAA) + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.accessAndPullS(ADCA) + result += MLine.inherent(DAA) + } else { + result += MLine.pp(PSHS, M6809Register.D) + result ++= load + // TODO: optimize + result += MLine.immediate(EORA, 0xff) + result += MLine.immediate(EORB, 0xff) + result += MLine.immediate(ADDD, 1) + result += MLine.accessAndPullSTwice(ADDD) + } } else { - result += MLine.pp(PSHS, M6809Register.D) - result ++= load - result += MLine.accessAndPullSTwice(ADDD) + if (expr.decimal) { + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.pp(PSHS, M6809Register.D) + result ++= load + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.accessAndPullS(ADDA) + result += MLine.inherent(DAA) + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.accessAndPullS(ADCA) + result += MLine.inherent(DAA) + } else { + result += MLine.pp(PSHS, M6809Register.D) + result ++= load + result += MLine.accessAndPullSTwice(ADDD) + } } } } @@ -276,6 +314,13 @@ object M6809Buitins { if (!constant.isProvablyZero) { if (result.isEmpty) { result += MLine.immediate(LDD, constant) + } else if (expr.decimal) { + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.immediate(ADDA, constant.loByte) + result += MLine.inherent(DAA) + result += MLine.exg(M6809Register.A, M6809Register.B) + result += MLine.immediate(ADCA, constant.hiByte) + result += MLine.inherent(DAA) } else { result += MLine.immediate(ADDD, constant) } diff --git a/src/main/scala/millfork/compiler/m6809/M6809DecimalBuiltins.scala b/src/main/scala/millfork/compiler/m6809/M6809DecimalBuiltins.scala index e1f06d81..c34a0f75 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809DecimalBuiltins.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809DecimalBuiltins.scala @@ -1,12 +1,14 @@ package millfork.compiler.m6809 -import millfork.assembly.m6809.MLine -import millfork.compiler.CompilationContext -import millfork.node.Expression +import millfork.assembly.m6809.{Immediate, MLine, MLine0} +import millfork.compiler.{AbstractExpressionCompiler, CompilationContext} +import millfork.node.{Expression, LhsExpression} import millfork.assembly.m6809.MOpcode._ -import millfork.env.NumericConstant +import millfork.env.{Constant, NumericConstant} import millfork.node.M6809Register._ +import scala.collection.mutable.ListBuffer + /** * @author Karol Stasiak */ @@ -28,14 +30,14 @@ object M6809DecimalBuiltins { val labelSkip = ctx.nextLabel("ss") val labelRepeat = ctx.nextLabel("sr") M6809ExpressionCompiler.stashAIfNeeded(ctx, M6809ExpressionCompiler.compileToB(ctx, rhs)) ++ List( - MLine.label(labelRepeat), MLine.immediate(CMPB, 0), MLine.shortBranch(BEQ, labelSkip), + MLine.label(labelRepeat), MLine.pp(PSHS, A), MLine.accessAndPullS(ADDA), MLine.inherent(DAA), MLine.inherentB(DEC), - MLine.shortBranch(BRA, labelRepeat), + MLine.shortBranch(BNE, labelRepeat), MLine.label(labelSkip) ) } @@ -45,4 +47,230 @@ object M6809DecimalBuiltins { } load ++ loop ++ store } + + def compileLongDecimalShiftLeft(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression): List[MLine] = { + val result = new ListBuffer[MLine]() + val byteCount = AbstractExpressionCompiler.getExpressionType(ctx, lhs).size + val targetAddr = M6809ExpressionCompiler.compileAddressToX(ctx, lhs) match { + case List(MLine0(LDX, Immediate, addr)) => + Some(addr) + case xs => + result ++= xs + None + } + val once = List.tabulate(byteCount){ i => + val innerResult = new ListBuffer[MLine]() + targetAddr match { + case Some(addr) => + innerResult += MLine.absolute(LDA, addr + (byteCount - 1 - i)) + innerResult += MLine.absolute(if (i == 0) ADDA else ADCA, addr + (byteCount - 1 - i)) + case None => + innerResult += MLine.indexedX(LDA, byteCount - 1 - i) + innerResult += MLine.indexedX(if (i == 0) ADDA else ADCA, byteCount - 1 - i) + } + // TODO: use these if volatile: + // innerResult += MLine.pp(PSHS, A) + // innerResult += MLine.accessAndPullS(if (i == 0) ADDA else ADCA) + innerResult += MLine.inherent(DAA) + targetAddr match { + case Some(addr) => + innerResult += MLine.absolute(STA, addr + (byteCount - 1 - i)) + case None => + innerResult += MLine.indexedX(STA, byteCount - 1 - i) + } + innerResult.toSeq + }.flatten + ctx.env.eval(rhs) match { + case Some(NumericConstant(0, _)) => + // TODO: volatile? + case Some(NumericConstant(1, _)) => + result ++= once + case _ => + val labelSkip = ctx.nextLabel("ss") + val labelRepeat = ctx.nextLabel("sr") + result ++= M6809ExpressionCompiler.compileToB(ctx, rhs) + result += MLine.immediate(CMPB, 0) + result += MLine.shortBranch(BEQ, labelSkip) + result += MLine.label(labelRepeat) + result ++= once + result += MLine.inherentB(DEC) + result += MLine.shortBranch(BNE, labelRepeat) + result += MLine.label(labelSkip) + } + result.toList + } + + def compileInPlaceByteMultiplication(ctx: CompilationContext, target: LhsExpression, multiplicand: Expression): List[MLine] = { + val multiplier = ctx.env.eval(multiplicand) match { + case Some(NumericConstant(v, _)) => + if (v.&(0xf0) > 0x90 || v.&(0xf) > 9) + ctx.log.error("Invalid decimal constant", multiplicand.position) + (v.&(0xf0).>>(4) * 10 + v.&(0xf)).toInt min 99 + case _ => + ctx.log.error("Cannot multiply by a non-constant amount", multiplicand.position) + return Nil + } + val result = new ListBuffer[MLine]() + val targetAddr: Option[Constant] = M6809ExpressionCompiler.compileAddressToX(ctx, target) match { + case List(MLine(LDX, Immediate, addr, _, _)) => + result += MLine.absolute(LDB, addr) + Some(addr) + case xs => + result ++= xs + result += MLine.indexedX(LDB, 0) + None + } + multiplier match { + case 0 => + result += MLine.inherentA(CLR) + case 1 => + result += MLine.tfr(B, A) + case x => + result += MLine.tfr(B, A) + result += MLine.pp(PSHS, D) + val add1 = List() + // TODO: rethink this: + val ways = waysStolenFromTheZ80Implementation + for(way <- ways(x)) way match { + case 1 => + result += MLine.indexedS(ADDA, 1) + result += MLine.inherent(DAA) + result += MLine.indexedS(STA, 0) + case q if q < 10 => + for(i<-0 until (q-1)) { + result += MLine.indexedS(ADDA, 0) + result += MLine.inherent(DAA) + } + result += MLine.indexedS(STA, 0) + case q if q >= 10 => + result += MLine.inherentA(ASL) + result += MLine.inherentA(ASL) + result += MLine.inherentA(ASL) + result += MLine.inherentA(ASL) + for(i<-0 until (q-10)) { + result += MLine.indexedS(ADDA, 0) + result += MLine.inherent(DAA) + } + result += MLine.indexedS(STA, 0) + } + result += MLine.indexedS(LEAS, 2) + } + targetAddr match { + case Some(addr) => + result += MLine.absolute(STA, addr) + case None => + result += MLine.indexedX(STA, 0) + } + result.toList + } + + private lazy val waysStolenFromTheZ80Implementation: Map[Int, List[Int]] = Map( + 2 -> List(2), 3 -> List(3), 4 -> List(2,2), 5 -> List(2,2,1), 6 -> List(3,2), 7 -> List(3,2,1), 8 -> List(2,2,2), 9 -> List(3,3), 10 -> List(10), + 11 -> List(11), 12 -> List(12), 13 -> List(13), 14 -> List(3,2,1,2), 15 -> List(3,5), 16 -> List(2,2,2,2), 17 -> List(2,2,2,2,1), 18 -> List(3,3,2), 19 -> List(3,3,2,1), 20 -> List(2,10), + 21 -> List(2,10,1), 22 -> List(11,2), 23 -> List(11,2,1), 24 -> List(12,2), 25 -> List(12,2,1), 26 -> List(2,13), 27 -> List(3,3,3), 28 -> List(3,2,1,2,2), 29 -> List(3,2,1,2,2,1), 30 -> List(3,10), + 31 -> List(3,10,1), 32 -> List(2,2,2,2,2), 33 -> List(11,3), 34 -> List(11,3,1), 35 -> List(11,3,1,1), 36 -> List(3,12), 37 -> List(3,12,1), 38 -> List(3,3,2,1,2), 39 -> List(3,13), 40 -> List(2,2,10), + 41 -> List(2,2,10,1), 42 -> List(2,10,1,2), 43 -> List(2,10,1,2,1), 44 -> List(11,2,2), 45 -> List(11,2,2,1), 46 -> List(11,2,1,2), 47 -> List(11,2,1,2,1), 48 -> List(12,2,2), 49 -> List(12,2,2,1), 50 -> List(2,2,1,10), + 51 -> List(2,2,1,10,1), 52 -> List(2,2,13), 53 -> List(2,2,13,1), 54 -> List(3,3,3,2), 55 -> List(11,5), 56 -> List(3,2,1,2,2,2), 57 -> List(3,3,2,1,3), 58 -> List(3,2,1,2,2,1,2), 59 -> List(3,2,1,2,2,1,2,1), 60 -> List(3,2,10), + 61 -> List(3,2,10,1), 62 -> List(3,10,1,2), 63 -> List(2,10,1,3), 64 -> List(2,2,2,2,2,2), 65 -> List(2,2,2,2,2,2,1), 66 -> List(11,3,2), 67 -> List(11,3,2,1), 68 -> List(11,3,1,2), 69 -> List(11,2,1,3), 70 -> List(3,2,1,10), + 71 -> List(3,2,1,10,1), 72 -> List(3,12,2), 73 -> List(3,12,2,1), 74 -> List(3,12,1,2), 75 -> List(12,2,1,3), 76 -> List(3,3,2,1,2,2), 77 -> List(3,2,1,11), 78 -> List(3,2,13), 79 -> List(3,2,13,1), 80 -> List(2,2,2,10), + 81 -> List(2,2,2,10,1), 82 -> List(2,2,10,1,2), 83 -> List(2,2,10,1,2,1), 84 -> List(2,10,1,2,2), 85 -> List(2,10,1,2,2,1), 86 -> List(2,10,1,2,1,2), 87 -> List(2,10,1,2,1,2,1), 88 -> List(11,2,2,2), 89 -> List(11,2,2,2,1), 90 -> List(3,3,10), + 91 -> List(3,3,10,1), 92 -> List(11,2,1,2,2), 93 -> List(3,10,1,3), 94 -> List(3,10,1,3,1), 95 -> List(3,3,2,1,5), 96 -> List(12,2,2,2), 97 -> List(12,2,2,2,1), 98 -> List(12,2,2,1,2), 99 -> List(11,3,3), + ) + + + private def shiftOrRotateAccumulatorRight(ctx: CompilationContext, rotate: Boolean, preserveCarry: Boolean): List[MLine] = { + val skipHiDigit = ctx.nextLabel("ds") + val skipLoDigit = ctx.nextLabel("ds") + val result = new ListBuffer[MLine]() + result += (if (rotate) MLine.inherentA(ROR) else MLine.inherentA(LSR)) + if (preserveCarry) result += MLine.pp(PSHS, CC) + result += MLine.shortBranch(BPL, skipHiDigit) + result += MLine.immediate(SUBA, 0x30) + result += MLine.label(skipHiDigit) + result += MLine.immediate(BITA, 8) + result += MLine.shortBranch(BEQ, skipLoDigit) + result += MLine.immediate(SUBA, 0x3) + result += MLine.label(skipLoDigit) + if (preserveCarry) result += MLine.pp(PULS, CC) + result.toList + } + + def compileByteDecimalShiftRight(ctx: CompilationContext, lhs: Option[Expression], rhs: Expression): List[MLine] = { + val result = new ListBuffer[MLine]() + lhs match { + case None => result += MLine.indexedX(LDA, 0) + case Some(l) => result ++= M6809ExpressionCompiler.compileToA(ctx, l) + } + val once = shiftOrRotateAccumulatorRight(ctx, rotate = false, preserveCarry = false) + ctx.env.eval(rhs) match { + case Some(NumericConstant(0, _)) => + case Some(NumericConstant(1, _)) => + result ++= once + case _ => + val labelSkip = ctx.nextLabel("ss") + val labelRepeat = ctx.nextLabel("sr") + result ++= M6809ExpressionCompiler.stashAIfNeeded(ctx, M6809ExpressionCompiler.compileToB(ctx, rhs)) + result += MLine.immediate(CMPB, 0) + result += MLine.shortBranch(BEQ, labelSkip) + result += MLine.label(labelRepeat) + result ++= once + result += MLine.inherentB(DEC) + result += MLine.shortBranch(BNE, labelRepeat) + result += MLine.label(labelSkip) + + } + lhs match { + case None => result += MLine.indexedX(STA, 0) + case _ => + } + result.toList + } + + def compileLongDecimalShiftRight(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression): List[MLine] = { + val result = new ListBuffer[MLine]() + val byteCount = AbstractExpressionCompiler.getExpressionType(ctx, lhs).size + val targetAddr = M6809ExpressionCompiler.compileAddressToX(ctx, lhs) match { + case List(MLine0(LDX, Immediate, addr)) => + Some(addr) + case xs => + result ++= xs + None + } + val once = List.tabulate(byteCount){ i => + val innerResult = new ListBuffer[MLine]() + targetAddr match { + case Some(addr) => + innerResult += MLine.absolute(LDA, addr + i) + case None => + innerResult += MLine.indexedX(LDA, i) + } + innerResult ++= shiftOrRotateAccumulatorRight(ctx, rotate = i != 0, preserveCarry = i != byteCount - 1) + targetAddr match { + case Some(addr) => + innerResult += MLine.absolute(STA, addr + i) + case None => + innerResult += MLine.indexedX(STA, i) + } + innerResult.toSeq + }.flatten + ctx.env.eval(rhs) match { + case Some(NumericConstant(0, _)) => + // TODO: volatile? + case Some(NumericConstant(1, _)) => + result ++= once + case _ => + val labelSkip = ctx.nextLabel("ss") + val labelRepeat = ctx.nextLabel("sr") + result ++= M6809ExpressionCompiler.compileToB(ctx, rhs) + result += MLine.immediate(CMPB, 0) + result += MLine.shortBranch(BEQ, labelSkip) + result += MLine.label(labelRepeat) + result ++= once + result += MLine.inherentB(DEC) + result += MLine.shortBranch(BNE, labelRepeat) + result += MLine.label(labelSkip) + } + result.toList + } + } diff --git a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala index 07753cce..5a607703 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala @@ -222,7 +222,6 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { case 1 => M6809MulDiv.compileByteMultiplication(ctx, params, updateDerefX = false) ++ targetifyB(ctx, target, isSigned = false) case 2 => M6809MulDiv.compileWordMultiplication(ctx, params, updateDerefX = false) ++ targetifyD(ctx, target) } - case "*'" => ctx.log.error("Decimal multiplication not implemented yet", fce.position); Nil case "/" => assertArithmeticBinary(ctx, params) match { case (l, r, 1) => M6809MulDiv.compileByteDivision(ctx, Some(l), r, mod=false) ++ targetifyB(ctx, target, isSigned = false) @@ -353,7 +352,12 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { case (l, r, 2) => ??? case (l, r, _) => ??? } - case ">>'" => ??? + case ">>'" => + assertArithmeticBinary(ctx, params) match { + case (l, r, 1) => M6809DecimalBuiltins.compileByteDecimalShiftRight(ctx, Some(l), r) ++ targetifyA(ctx, target, isSigned = false) + case (l, r, 2) => ??? + case (l, r, _) => ??? + } case "+=" => val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) size match { @@ -399,7 +403,10 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { case 1 => compileAddressToX(ctx, l) ++ M6809MulDiv.compileByteMultiplication(ctx, List(r), updateDerefX = true) case 2 => compileAddressToX(ctx, l) ++ M6809MulDiv.compileWordMultiplication(ctx, List(r), updateDerefX = true) } - case "*'=" => ctx.log.error("Decimal multiplication not implemented yet", fce.position); Nil + case "*'=" => + assertAllArithmeticBytes("Long multiplication not supported", ctx, params) + val (l, r, 1) = assertArithmeticAssignmentLike(ctx, params) + M6809DecimalBuiltins.compileInPlaceByteMultiplication(ctx, l, r) case "/=" => val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) size match { @@ -449,7 +456,7 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { size match { case 1 => compileAddressToX(ctx, l) ++ M6809DecimalBuiltins.compileByteDecimalShiftLeft(ctx, None, r) - case 2 => ??? + case _ => M6809DecimalBuiltins.compileLongDecimalShiftLeft(ctx, l, r) } case ">>=" => val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) @@ -459,7 +466,13 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { case 2 => handleInPlaceModification(ctx, l, 2, M6809Buitins.compileWordShiftForD(ctx, r, left = false)) case _ => M6809LargeBuiltins.compileShiftInPlace(ctx, size, l, r, left = false) } - case ">>'=" => ??? + case ">>'=" => + val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) + size match { + case 1 => + compileAddressToX(ctx, l) ++ M6809DecimalBuiltins.compileByteDecimalShiftRight(ctx, None, r) + case _ => M6809DecimalBuiltins.compileLongDecimalShiftRight(ctx, l, r) + } case ">>>>=" => ??? case _ => env.maybeGet[Type](fce.functionName) match { @@ -532,7 +545,7 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { } val storeResult = f.returnType.size match { case 1 => targetifyB(ctx, target, f.returnType.isSigned) - case 2 => targetifyD(ctx, target) + case 2 => targetifyDWithNarrowing(ctx, target) case _ => if (target == MExpressionTarget.NOTHING) { Nil diff --git a/src/main/scala/millfork/compiler/m6809/M6809LargeBuiltins.scala b/src/main/scala/millfork/compiler/m6809/M6809LargeBuiltins.scala index ae7ca9f9..4ee69260 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809LargeBuiltins.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809LargeBuiltins.scala @@ -63,12 +63,16 @@ object M6809LargeBuiltins { } // TODO: use loop for very large structures // TODO: use D to speed up certain loads - targetInit ++ byteLoads.zipWithIndex.flatMap{ case (loadByte, ix) => - M6809ExpressionCompiler.stashXIfNeeded(ctx, loadByte) :+ ( targetAddr match { - case Some(addr) => MLine.absolute(STB, addr + (byteCount - 1 - ix)) - case None => MLine.indexedX(STB, byteCount - 1 - ix) - } ) - } + targetInit ++ (targetAddr match { + case Some(addr) => + byteLoads.zipWithIndex.flatMap { case (loadByte, ix) => + loadByte :+ MLine.absolute(STB, addr + (byteCount - 1 - ix)) + } + case None => + byteLoads.zipWithIndex.flatMap { case (loadByte, ix) => + M6809ExpressionCompiler.stashXIfNeeded(ctx, loadByte) :+ MLine.indexedX(STB, byteCount - 1 - ix) + } + }) } def convertToLda(ldb: List[MLine]): List[MLine] = { @@ -145,19 +149,41 @@ object M6809LargeBuiltins { case (ADDB | SUBB | ADDA | SUBA, Some(0)) if i == firstNonzeroByte => firstNonzeroByte = i + 1 - case (SUBA, _) => ??? + case (SUBA, _) => + targetAddr match { + case Some(addr) => + if (i == firstNonzeroByte) { + result ++= ldb + } else { + result ++= M6809ExpressionCompiler.stashCarryIfNeeded(ctx, ldb) + } + result += MLine.pp(PSHS, M6809Register.B) + result += MLine.immediate(LDA, if (i == firstNonzeroByte) 0x9a else 0x99) + result += MLine.accessAndPullS(SUBA) + result += MLine.absolute(if (i == firstNonzeroByte) ADDA else ADCA, addr + (sizeInBytes - 1 - i)) + result += MLine.inherent(DAA) + result += MLine.absolute(STA, addr + (sizeInBytes - 1 - i)) + case None => + result ++= M6809ExpressionCompiler.stashXIfNeeded(ctx, ldb) + result += MLine.pp(PSHS, M6809Register.B) + result += MLine.immediate(LDA, if (i == firstNonzeroByte) 0x9a else 0x99) + result += MLine.accessAndPullS(SUBA) + result += MLine.indexedX(if (i == firstNonzeroByte) ADDA else ADCA, sizeInBytes - 1 - i) + result += MLine.inherent(DAA) + result += MLine.indexedX(STA, sizeInBytes - 1 - i) + } case (ADDA, _) if i == firstNonzeroByte => val lda = convertToLda(ldb) targetAddr match { case Some(addr) => result ++= lda - result += MLine.absolute(ADCA, addr + (sizeInBytes - 1 - i)) + result += MLine.absolute(ADDA, addr + (sizeInBytes - 1 - i)) result += MLine.inherent(DAA) result += MLine.absolute(STA, addr + (sizeInBytes - 1 - i)) case None => result ++= M6809ExpressionCompiler.stashXIfNeeded(ctx, lda) - result += MLine.indexedX(ADCA, sizeInBytes - 1 - i) + result += MLine.indexedX(ADDA, sizeInBytes - 1 - i) result += MLine.inherent(DAA) result += MLine.indexedX(STA, sizeInBytes - 1 - i) } diff --git a/src/main/scala/millfork/compiler/z80/Z80DecimalBuiltIns.scala b/src/main/scala/millfork/compiler/z80/Z80DecimalBuiltIns.scala index 97c7c629..6ef6867d 100644 --- a/src/main/scala/millfork/compiler/z80/Z80DecimalBuiltIns.scala +++ b/src/main/scala/millfork/compiler/z80/Z80DecimalBuiltIns.scala @@ -209,7 +209,7 @@ object Z80DecimalBuiltIns { case Some(NumericConstant(v, _)) => if (v.&(0xf0) > 0x90 || v.&(0xf) > 9) ctx.log.error("Invalid decimal constant", r.position) - (v.&(0xf0).>>(4) * 10 + v.&(0xf)).toInt + (v.&(0xf0).>>(4) * 10 + v.&(0xf)).toInt min 99 case _ => ctx.log.error("Cannot multiply by a non-constant amount", r.position) return Nil diff --git a/src/test/scala/millfork/test/ByteDecimalMathSuite.scala b/src/test/scala/millfork/test/ByteDecimalMathSuite.scala index 5bccdd50..d9cf551e 100644 --- a/src/test/scala/millfork/test/ByteDecimalMathSuite.scala +++ b/src/test/scala/millfork/test/ByteDecimalMathSuite.scala @@ -2,12 +2,12 @@ package millfork.test import millfork.Cpu import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotCompile} -import org.scalatest.{FunSuite, Matchers} +import org.scalatest.{AppendedClues, FunSuite, Matchers} /** * @author Karol Stasiak */ -class ByteDecimalMathSuite extends FunSuite with Matchers { +class ByteDecimalMathSuite extends FunSuite with Matchers with AppendedClues { test("Decimal byte addition") { EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( @@ -120,7 +120,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal word addition") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | word output @$c000 | void main () { @@ -136,7 +136,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal word subtraction") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | word output @$c000 | void main () { @@ -152,7 +152,8 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("In-place decimal word subtraction") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + // TODO: enable 6809 after the DAA bug in the emulator is fixed + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086/*, Cpu.Motorola6809*/)( """ | word output @$c000 | word a @@ -166,7 +167,8 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("In-place decimal long subtraction") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + // TODO: enable 6809 after the DAA bug in the emulator is fixed + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086/*, Cpu.Motorola6809*/)( """ | long output @$c000 | word a @@ -243,7 +245,8 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal left shift test 3") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + // TODO: enable 6809 after the DAA bug in the emulator is fixed + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086/*, Cpu.Motorola6809*/)( """ | word output @$c000 | void main () { @@ -257,7 +260,8 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal left shift test 4") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + // TODO: enable 6809 after the DAA bug in the emulator is fixed + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086/*, Cpu.Motorola6809*/)( """ | long output @$c000 | void main () { @@ -271,7 +275,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal right shift test") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000 | void main () { @@ -286,7 +290,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal right shift test 2") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000 | void main () { @@ -300,7 +304,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { } test("Decimal right shift test 3") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | word output @$c000 | void main () { @@ -335,7 +339,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { test("Decimal word right-shift comprehensive suite") { for (i <- List(0, 1, 10, 100, 1000, 2000, 500, 200, 280, 300, 5234, 7723, 7344, 9, 16, 605, 1111, 2222, 3333, 9999, 8888, 8100)) { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | word output @$c000 | void main () { @@ -351,7 +355,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { test("Decimal byte multiplication comprehensive suite") { val numbers = List(0, 1, 2, 3, 6, 8, 10, 11, 12, 14, 15, 16, 20, 40, 73, 81, 82, 98, 99) for (i <- numbers; j <- numbers) { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000 | void main () { @@ -361,7 +365,8 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { | void init() { output = $#i } | void run () { output *'= $#j } """.stripMargin.replace("#i", i.toString).replace("#j", j.toString)) { m => - toDecimal(m.readByte(0xc000)) should equal((i * j) % 100) + val actualHex = m.readByte(0xc000) + toDecimal(actualHex) should equal((i * j) % 100) withClue s"$i×$j = should be ${i*j} was ${actualHex.toHexString}" } } } @@ -392,7 +397,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers { test("Decimal comparison") { // CMP#0 shouldn't be elided after a decimal operation. // Currently no emulator used for testing can catch that. - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000 | void main () {