From 5a8fe547bf99bbabf790d7236807142fe71bcb86 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 16 Jul 2018 23:02:34 +0200 Subject: [PATCH] Fix macros and few other things when compiling for Z80 --- .../compiler/z80/Z80ExpressionCompiler.scala | 2 + .../compiler/z80/Z80MacroExpander.scala | 58 +++++++++++++++++-- .../compiler/z80/Z80StatementCompiler.scala | 22 ++++--- 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala index 26805202..5d10d629 100644 --- a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala @@ -517,6 +517,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.HL, typ2), AssemblyParameterPassingBehaviour.Copy))) if typ1.size == 2 && typ2.size == 2 => compileToHL(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress) + case AssemblyParamSignature(Nil) => + List(ZLine(CALL, NoRegisters, function.toAddress)) case AssemblyParamSignature(paramConvs) => // TODO: stop being lazy and implement this ??? diff --git a/src/main/scala/millfork/compiler/z80/Z80MacroExpander.scala b/src/main/scala/millfork/compiler/z80/Z80MacroExpander.scala index be531199..272b2bfa 100644 --- a/src/main/scala/millfork/compiler/z80/Z80MacroExpander.scala +++ b/src/main/scala/millfork/compiler/z80/Z80MacroExpander.scala @@ -1,9 +1,12 @@ package millfork.compiler.z80 +import java.util.Locale + import millfork.assembly.z80.ZLine import millfork.compiler.{CompilationContext, MacroExpander} -import millfork.env.AssemblyParam -import millfork.node.{ExecutableStatement, Expression} +import millfork.env._ +import millfork.error.ErrorReporting +import millfork.node._ /** * @author Karol Stasiak @@ -11,5 +14,52 @@ import millfork.node.{ExecutableStatement, Expression} object Z80MacroExpander extends MacroExpander[ZLine] { override def nextLabel(prefix: String): String = Z80Compiler.nextLabel(prefix) - override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[ZLine], List[ExecutableStatement]) = ??? -} + override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[ZLine], List[ExecutableStatement]) = { + var paramPreparation = List[ZLine]() + var actualCode = code + var hadRegisterParam = false + assParams.zip(params).foreach { + case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) => + actualParam match { + case VariableExpression(vname) => + ctx.env.get[ThingInMemory](vname) + case l: LhsExpression => + // TODO: ?? + Z80ExpressionCompiler.compileToA(ctx, l) + case _ => + ErrorReporting.error("A non-assignable expression was passed to an inlineable function as a `ref` parameter", actualParam.position) + } + actualCode = actualCode.map { + case a@MosAssemblyStatement(_, _, expr, _) => + a.copy(expression = expr.replaceVariable(ph, actualParam)) + case x => x + } + case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) => + ctx.env.eval(actualParam).getOrElse(Constant.error("Non-constant expression was passed to an inlineable function as a `const` parameter", actualParam.position)) + actualCode = actualCode.map { + case a@Z80AssemblyStatement(_, _, _, expr, _) => + a.copy(expression = expr.replaceVariable(ph, actualParam)) + case x => x + } + case (AssemblyParam(typ, v@ZRegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) => + if (hadRegisterParam) { + ErrorReporting.error("Only one macro assembly function parameter can be passed via a register", actualParam.position) + } + hadRegisterParam = true + paramPreparation = (register, typ.size) match { + case (ZRegister.A, 1) => Z80ExpressionCompiler.compileToA(ctx, actualParam) + case (r@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E | ZRegister.H | ZRegister.L), 1) => Z80ExpressionCompiler.compileToA(ctx, actualParam) :+ ZLine.ld8(r, ZRegister.A) + case (ZRegister.HL, 2) => Z80ExpressionCompiler.compileToHL(ctx, actualParam) + case (ZRegister.BC, 2) => Z80ExpressionCompiler.compileToBC(ctx, actualParam) + case (ZRegister.DE, 2) => Z80ExpressionCompiler.compileToDE(ctx, actualParam) + case _ => + ErrorReporting.error(s"Invalid parameter for macro: ${typ.name} ${register.toString.toLowerCase(Locale.ROOT)}", actualParam.position) + Nil + } + case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) => + ??? + case (_, actualParam) => + } + paramPreparation -> actualCode + } + } diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala index abcad5ad..00b9c418 100644 --- a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala @@ -19,6 +19,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { def compile(ctx: CompilationContext, statement: ExecutableStatement): List[ZLine] = { val options = ctx.options + val env = ctx.env statement match { case ReturnStatement(None) => fixStackOnReturn(ctx) ++ (ctx.function.returnType match { @@ -132,7 +133,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { case s: ContinueStatement => compileContinueStatement(ctx, s) case ExpressionStatement(e@FunctionCallExpression(name, params)) => - ctx.env.lookupFunction(name, params.map(p => Z80ExpressionCompiler.getExpressionType(ctx, p) -> p)) match { + env.lookupFunction(name, params.map(p => Z80ExpressionCompiler.getExpressionType(ctx, p) -> p)) match { case Some(i: MacroFunction) => val (paramPreparation, inlinedStatements) = Z80MacroExpander.inlineFunction(ctx, i, params, e.position) paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, Z80Compiler.nextLabel("en")), inlinedStatements) @@ -142,14 +143,19 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { case ExpressionStatement(e) => Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING) case Z80AssemblyStatement(op, reg, offset, expression, elidable) => - val param = ctx.env.evalForAsm(expression) match { - case Some(v) => v - case None => - ErrorReporting.error("Inlining failed due to non-constant things", expression.position) - Constant.Zero + val param: Constant = expression match { + // TODO: hmmm + case VariableExpression(name) => + if (Seq(JP, JR, DJNZ, LABEL).contains(op)) { + MemoryAddressConstant(Label(name)) + } else { + env.evalForAsm(expression).getOrElse(env.get[ThingInMemory](name, expression.position).toAddress) + } + case _ => + env.evalForAsm(expression).getOrElse(Constant.error(s"`$expression` is not a constant", expression.position)) } val registers = (reg, offset) match { - case (OneRegister(r), Some(o)) => ctx.env.evalForAsm(expression) match { + case (OneRegister(r), Some(o)) => env.evalForAsm(expression) match { case Some(NumericConstant(v, _)) => OneRegisterOffset(r, v.toInt) case Some(_) => ErrorReporting.error("Non-numeric constant", o.position) @@ -158,7 +164,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { ErrorReporting.error("Inlining failed due to non-constant things", o.position) reg } - case (TwoRegisters(t, s), Some(o)) => ctx.env.evalForAsm(expression) match { + case (TwoRegisters(t, s), Some(o)) => env.evalForAsm(expression) match { case Some(NumericConstant(v, _)) => TwoRegistersOffset(t, s, v.toInt) case Some(_) => ErrorReporting.error("Non-numeric constant", o.position)