mirror of
https://github.com/KarolS/millfork.git
synced 2024-11-01 05:05:32 +00:00
6809: Implement decimal operations
This commit is contained in:
parent
b387ece71d
commit
e77811c67c
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 () {
|
||||
|
Loading…
Reference in New Issue
Block a user