From 3e0d1d4978cbea3550623b7c5e850dac7fa8cc38 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 1 Jan 2018 22:13:05 +0100 Subject: [PATCH] References to variables in assembly should always refer to their addresses --- .../scala/millfork/compiler/MfCompiler.scala | 21 +++---- src/main/scala/millfork/env/Environment.scala | 57 ++++++++++++++++++- .../scala/millfork/test/AssemblySuite.scala | 14 +++++ 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/main/scala/millfork/compiler/MfCompiler.scala b/src/main/scala/millfork/compiler/MfCompiler.scala index a4e02c82..ffcc7401 100644 --- a/src/main/scala/millfork/compiler/MfCompiler.scala +++ b/src/main/scala/millfork/compiler/MfCompiler.scala @@ -1090,13 +1090,9 @@ object MlCompiler { case function: InlinedFunction => inlineFunction(function, params, Some(ctx)).map { case AssemblyStatement(opcode, addrMode, expression, elidable) => - val param = env.eval(expression).getOrElse { - expression match { - case VariableExpression(name) => env.get[ThingInMemory](name).toAddress - case _ => - ErrorReporting.error("Inlining failed due to non-constant things", expression.position) - Constant.Zero - } + val param = env.evalForAsm(expression).getOrElse { + ErrorReporting.error("Inlining failed due to non-constant things", expression.position) + Constant.Zero } AssemblyLine(opcode, addrMode, param, elidable) @@ -1427,12 +1423,17 @@ object MlCompiler { if (OpcodeClasses.ShortBranching(o) || o == JMP || o == LABEL) { MemoryAddressConstant(Label(name)) } else{ - env.eval(x).getOrElse(env.get[ThingInMemory](name, x.position).toAddress) + env.evalForAsm(x).getOrElse(env.get[ThingInMemory](name, x.position).toAddress) } case _ => - env.eval(x).getOrElse(Constant.error(s"`$x` is not a constant", x.position)) + env.evalForAsm(x).getOrElse(Constant.error(s"`$x` is not a constant", x.position)) + } + val actualAddrMode = a match { + case Absolute if OpcodeClasses.ShortBranching(o) => Relative + case IndexedX if o == JMP => AbsoluteIndexedX + case Indirect if o != JMP => ZeroPageIndirect + case _ => a } - val actualAddrMode = if (OpcodeClasses.ShortBranching(o) && a == Absolute) Relative else a LinearChunk(List(AssemblyLine(o, actualAddrMode, c, e))) case Assignment(dest, source) => LinearChunk(compileAssignment(ctx, source, dest)) diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 955b2a95..fb05d295 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -285,7 +285,62 @@ class Environment(val parent: Option[Environment], val prefix: String) { } private def constantOperation(op: MathOperator.Value, params: List[Expression]) = { - params.map(eval(_)).reduceLeft[Option[Constant]] { (oc, om) => + params.map(eval).reduceLeft[Option[Constant]] { (oc, om) => + for { + c <- oc + m <- om + } yield CompoundConstant(op, c, m) + } + } + + def evalForAsm(e: Expression): Option[Constant] = { + e match { + case LiteralExpression(value, size) => Some(NumericConstant(value, size)) + case VariableExpression(name) => + maybeGet[ConstantThing](name).map(_.value).orElse(maybeGet[ThingInMemory](name).map(_.toAddress)) + case IndexedExpression(name, index) => (evalForAsm(VariableExpression(name)), evalForAsm(index)) match { + case (Some(a), Some(b)) => Some(CompoundConstant(MathOperator.Plus, a, b).quickSimplify) + } + case HalfWordExpression(param, hi) => evalForAsm(e).map(c => if (hi) c.hiByte else c.loByte) + case SumExpression(params, decimal) => + params.map { + case (minus, param) => (minus, evalForAsm(param)) + }.foldLeft(Some(Constant.Zero).asInstanceOf[Option[Constant]]) { (oc, pair) => + oc.flatMap { c => + pair match { + case (_, None) => None + case (minus, Some(addend)) => + val op = if (decimal) { + if (minus) MathOperator.DecimalMinus else MathOperator.DecimalPlus + } else { + if (minus) MathOperator.Minus else MathOperator.Plus + } + Some(CompoundConstant(op, c, addend)) + } + } + } + case SeparateBytesExpression(h, l) => for { + lc <- evalForAsm(l) + hc <- evalForAsm(h) + } yield hc.asl(8) + lc + case FunctionCallExpression(name, params) => + name match { + case "*" => + constantOperationForAsm(MathOperator.Times, params) + case "&&" | "&" => + constantOperationForAsm(MathOperator.And, params) + case "^" => + constantOperationForAsm(MathOperator.Exor, params) + case "||" | "|" => + constantOperationForAsm(MathOperator.Or, params) + case _ => + None + } + } + } + + private def constantOperationForAsm(op: MathOperator.Value, params: List[Expression]) = { + params.map(evalForAsm).reduceLeft[Option[Constant]] { (oc, om) => for { c <- oc m <- om diff --git a/src/test/scala/millfork/test/AssemblySuite.scala b/src/test/scala/millfork/test/AssemblySuite.scala index 1c7acd5c..4ed1b9d1 100644 --- a/src/test/scala/millfork/test/AssemblySuite.scala +++ b/src/test/scala/millfork/test/AssemblySuite.scala @@ -96,4 +96,18 @@ class AssemblySuite extends FunSuite with Matchers { """.stripMargin)(_.readByte(0xc000) should equal(5)) } + test("Adresses in asm") { + EmuBenchmarkRun( + """ + | word output @$c000 + | void main () { + | output = 0 + | add256(output) + | } + | inline asm void add256(word ref v) { + | inc v+1 + | } + """.stripMargin)(_.readWord(0xc000) should equal(0x100)) + } + }