mirror of
https://github.com/KarolS/millfork.git
synced 2026-04-19 10:42:10 +00:00
Changes to macros and parameter list syntax:
* non-asm macros can now take `const` and `call` parameters * register parameters to asm functions and macros can be given names if annotated explicitly
This commit is contained in:
@@ -135,7 +135,7 @@ object ReverseFlowAnalyzer {
|
||||
// this case has to be handled first, because the generic JSR importance handler is too conservative
|
||||
var result = importanceBeforeJsr
|
||||
fun.params match {
|
||||
case AssemblyParamSignature(params) =>
|
||||
case AssemblyOrMacroParamSignature(params) =>
|
||||
params.foreach(_.variable match {
|
||||
case M6809RegisterVariable(M6809Register.A, _) =>
|
||||
result = result.copy(a = Important)
|
||||
|
||||
@@ -192,7 +192,7 @@ object ReverseFlowAnalyzer {
|
||||
// this case has to be handled first, because the generic JSR importance handler is too conservative
|
||||
var result = importanceBeforeJsr
|
||||
fun.params match {
|
||||
case AssemblyParamSignature(params) =>
|
||||
case AssemblyOrMacroParamSignature(params) =>
|
||||
params.foreach(_.variable match {
|
||||
case RegisterVariable(MosRegister.A, _) =>
|
||||
result = result.copy(a = Important)
|
||||
|
||||
@@ -2,7 +2,7 @@ package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
|
||||
import millfork.assembly.z80._
|
||||
import millfork.env.{AssemblyParam, AssemblyParamSignature, FunctionInMemory, MemoryAddressConstant, NormalFunction, NormalParamSignature, NumericConstant, ParamSignature, ZRegisterVariable}
|
||||
import millfork.env.{AssemblyOrMacroParam, AssemblyOrMacroParamSignature, FunctionInMemory, MemoryAddressConstant, NormalFunction, NormalParamSignature, NumericConstant, ParamSignature, ZRegisterVariable}
|
||||
import millfork.error.Logger
|
||||
import millfork.node.ZRegister
|
||||
|
||||
@@ -45,7 +45,7 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
||||
override def name = "Changing registers pairs"
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
|
||||
if (f.params.isInstanceOf[AssemblyParamSignature]) return code
|
||||
if (f.params.isInstanceOf[AssemblyOrMacroParamSignature]) return code
|
||||
val usesBC = code.exists(l => (l.readsRegister(BC) || l.changesRegister(BC)) && l.opcode != CALL)
|
||||
val usesDE = code.exists(l => (l.readsRegister(DE) || l.changesRegister(DE)) && l.opcode != CALL)
|
||||
val canDE2BC = f.returnType.size < 3 && canOptimize(code, DE2BC, Loaded()) && canOptimize2(code, DE2BC) && (f.params match {
|
||||
@@ -102,8 +102,8 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
||||
private def readsBCorDE(params: ParamSignature): Boolean = {
|
||||
import ZRegister._
|
||||
params match {
|
||||
case AssemblyParamSignature(ps) => ps.exists {
|
||||
case AssemblyParam(_, ZRegisterVariable(B | C | D | E | BC | DE, _), _) => true
|
||||
case AssemblyOrMacroParamSignature(ps) => ps.exists {
|
||||
case AssemblyOrMacroParam(_, ZRegisterVariable(B | C | D | E | BC | DE, _), _) => true
|
||||
case _ => false
|
||||
}
|
||||
case NormalParamSignature(List(p)) => p.typ.size == 3 || p.typ.size == 4
|
||||
|
||||
@@ -435,7 +435,7 @@ object ReverseFlowAnalyzer {
|
||||
sf = Unimportant,
|
||||
hf = Unimportant
|
||||
)
|
||||
case NormalParamSignature(_) | AssemblyParamSignature(Nil) =>
|
||||
case NormalParamSignature(_) | AssemblyOrMacroParamSignature(Nil) =>
|
||||
currentImportance = currentImportance.copy(
|
||||
a = if (readsA(n)) Important else Unimportant,
|
||||
b = if (readsB(n)) Important else Unimportant,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.assembly.AbstractCode
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.z80.ZOpcode
|
||||
import millfork.env._
|
||||
@@ -11,8 +12,13 @@ import millfork.node._
|
||||
*/
|
||||
abstract class MacroExpander[T <: AbstractCode] {
|
||||
|
||||
def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[T], List[ExecutableStatement])
|
||||
def stmtPreprocess(ctx: CompilationContext, stmts: List[ExecutableStatement]): List[ExecutableStatement]
|
||||
|
||||
def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyOrMacroParam], params: List[Expression], code: List[ExecutableStatement]): (List[T], List[ExecutableStatement])
|
||||
|
||||
def replaceVariableX(stmt: Statement, paramName: String, target: Expression): ExecutableStatement = {
|
||||
replaceVariable(stmt, paramName, target).asInstanceOf[ExecutableStatement]
|
||||
}
|
||||
def replaceVariable(stmt: Statement, paramName: String, target: Expression): Statement = {
|
||||
val paramNamePeriod = paramName + "."
|
||||
def f[S <: Expression](e: S) = e.replaceVariable(paramName, target)
|
||||
@@ -52,6 +58,10 @@ abstract class MacroExpander[T <: AbstractCode] {
|
||||
}).pos(stmt.position)
|
||||
}
|
||||
|
||||
def renameVariableX(stmt: Statement, paramName: String, target: String): ExecutableStatement = {
|
||||
renameVariable(stmt, paramName, target).asInstanceOf[ExecutableStatement]
|
||||
}
|
||||
|
||||
def renameVariable(stmt: Statement, paramName: String, target: String): Statement = {
|
||||
val paramNamePeriod = paramName + "."
|
||||
def f[S <: Expression](e: S) = e.renameVariable(paramName, target)
|
||||
@@ -91,24 +101,41 @@ abstract class MacroExpander[T <: AbstractCode] {
|
||||
}).pos(stmt.position)
|
||||
}
|
||||
|
||||
def inlineFunction(ctx: CompilationContext, i: MacroFunction, params: List[Expression], position: Option[Position]): (List[T], List[ExecutableStatement]) = {
|
||||
def inlineFunction(ctx: CompilationContext, i: MacroFunction, actualParams: List[Expression], position: Option[Position]): (List[T], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[T]()
|
||||
var actualCode = i.code
|
||||
i.params match {
|
||||
case AssemblyParamSignature(assParams) =>
|
||||
val pair = prepareAssemblyParams(ctx, assParams, params, i.code)
|
||||
paramPreparation = pair._1
|
||||
actualCode = pair._2
|
||||
case NormalParamSignature(normalParams) =>
|
||||
if (params.length != normalParams.length) {
|
||||
case AssemblyOrMacroParamSignature(params) =>
|
||||
params.foreach{ param =>
|
||||
val vName = param.variable.name
|
||||
i.environment.removeVariable(vName)
|
||||
ctx.env.maybeGet[Thing](vName) match {
|
||||
case Some(thing) => i.environment.things += vName -> thing
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
if (actualParams.length != params.length) {
|
||||
ctx.log.error(s"Invalid number of params for macro function ${i.name}", position)
|
||||
} else if (i.isInAssembly) {
|
||||
val pair = prepareAssemblyParams(ctx, params, actualParams, i.code)
|
||||
paramPreparation = pair._1
|
||||
actualCode = pair._2
|
||||
} else {
|
||||
normalParams.foreach(param => i.environment.removeVariable(param.name))
|
||||
params.zip(normalParams).foreach {
|
||||
case (v@VariableExpression(_), MemoryVariable(paramName, paramType, _)) =>
|
||||
actualCode = actualCode.map(stmt => renameVariable(stmt, paramName.stripPrefix(i.environment.prefix), v.name).asInstanceOf[ExecutableStatement])
|
||||
case (v@IndexedExpression(_, _), MemoryVariable(paramName, paramType, _)) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariable(stmt, paramName.stripPrefix(i.environment.prefix), v).asInstanceOf[ExecutableStatement])
|
||||
actualParams.zip(params).foreach {
|
||||
case (v@VariableExpression(_), AssemblyOrMacroParam(_, paramVariable, AssemblyParameterPassingBehaviour.ByReference)) =>
|
||||
actualCode = actualCode.map(stmt => renameVariableX(stmt, paramVariable.name.stripPrefix(i.environment.prefix), v.name))
|
||||
case (v@IndexedExpression(_, _), AssemblyOrMacroParam(_, paramVariable, AssemblyParameterPassingBehaviour.ByReference)) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariableX(stmt, paramVariable.name.stripPrefix(i.environment.prefix), v))
|
||||
case (expr, AssemblyOrMacroParam(_, _, AssemblyParameterPassingBehaviour.ByReference)) =>
|
||||
ctx.log.error("By-reference parameters to macro functions have to be variables", expr.position)
|
||||
case (expr, AssemblyOrMacroParam(_, paramVariable, AssemblyParameterPassingBehaviour.ByConstant)) =>
|
||||
if (ctx.env.eval(expr).isEmpty) {
|
||||
ctx.log.error("Const parameters to macro functions have to be constants", expr.position)
|
||||
}
|
||||
actualCode = actualCode.map(stmt => replaceVariableX(stmt, paramVariable.name.stripPrefix(i.environment.prefix), expr))
|
||||
case (expr, AssemblyOrMacroParam(paramType, paramVariable, AssemblyParameterPassingBehaviour.Eval)) =>
|
||||
val castParam = FunctionCallExpression(paramType.name, List(expr))
|
||||
actualCode = actualCode.map(stmt => replaceVariableX(stmt, paramVariable.name.stripPrefix(i.environment.prefix), castParam))
|
||||
case _ =>
|
||||
ctx.log.error(s"Parameters to macro functions have to be variables", position)
|
||||
}
|
||||
@@ -120,14 +147,18 @@ abstract class MacroExpander[T <: AbstractCode] {
|
||||
val localLabels = actualCode.flatMap {
|
||||
case MosAssemblyStatement(Opcode.LABEL, _, VariableExpression(l), _) => Some(l)
|
||||
case Z80AssemblyStatement(ZOpcode.LABEL, _, _, VariableExpression(l), _) => Some(l)
|
||||
case M6809AssemblyStatement(MOpcode.LABEL, _, VariableExpression(l), _) => Some(l)
|
||||
case _ => None
|
||||
}.toSet
|
||||
val labelPrefix = ctx.nextLabel("il")
|
||||
actualCode = stmtPreprocess(ctx, actualCode)
|
||||
paramPreparation -> actualCode.map {
|
||||
case s@MosAssemblyStatement(_, _, VariableExpression(v), _) if localLabels(v) =>
|
||||
s.copy(expression = VariableExpression(labelPrefix + v))
|
||||
case s@Z80AssemblyStatement(_, _, _, VariableExpression(v), _) if localLabels(v) =>
|
||||
s.copy(expression = VariableExpression(labelPrefix + v))
|
||||
case s@M6809AssemblyStatement(_, _, VariableExpression(v), _) if localLabels(v) =>
|
||||
s.copy(expression = VariableExpression(labelPrefix + v))
|
||||
case s => s
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import millfork.assembly.m6809.{DAccumulatorIndexed, Immediate, Indexed, Inheren
|
||||
import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching}
|
||||
import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SumExpression, VariableExpression}
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.env.{AssemblyParamSignature, BuiltInBooleanType, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FlagBooleanType, FunctionInMemory, FunctionPointerType, Label, M6809RegisterVariable, MacroFunction, MathOperator, MemoryAddressConstant, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, StackVariablePointy, ThingInMemory, Type, Variable, VariableInMemory, VariablePointy}
|
||||
import millfork.env.{AssemblyOrMacroParamSignature, BuiltInBooleanType, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FlagBooleanType, FunctionInMemory, FunctionPointerType, Label, M6809RegisterVariable, MacroFunction, MathOperator, MemoryAddressConstant, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, StackVariablePointy, ThingInMemory, Type, Variable, VariableInMemory, VariablePointy}
|
||||
|
||||
import scala.collection.GenTraversableOnce
|
||||
|
||||
@@ -428,7 +428,7 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
||||
???
|
||||
}
|
||||
}
|
||||
case AssemblyParamSignature(signature) =>
|
||||
case AssemblyOrMacroParamSignature(signature) =>
|
||||
params.zip(signature).flatMap { case (e, a) =>
|
||||
val compiled = a.variable match {
|
||||
case M6809RegisterVariable(M6809Register.A, _) => compileToA(ctx, e)
|
||||
|
||||
@@ -13,12 +13,14 @@ import millfork.node._
|
||||
*/
|
||||
object M6809MacroExpander extends MacroExpander[MLine] {
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[MLine], List[ExecutableStatement]) = {
|
||||
override def stmtPreprocess(ctx: CompilationContext, stmts: List[ExecutableStatement]): List[ExecutableStatement] = new M6809StatementPreprocessor(ctx, stmts)()
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyOrMacroParam], params: List[Expression], code: List[ExecutableStatement]): (List[MLine], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[MLine]()
|
||||
var actualCode = code
|
||||
var hadRegisterParam = false
|
||||
assParams.zip(params).foreach {
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
actualParam match {
|
||||
case VariableExpression(vname) =>
|
||||
ctx.env.get[ThingInMemory](vname)
|
||||
@@ -33,14 +35,14 @@ object M6809MacroExpander extends MacroExpander[MLine] {
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
ctx.env.eval(actualParam).getOrElse(ctx.env.errorConstant("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@M6809RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, v@M6809RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
if (hadRegisterParam) {
|
||||
ctx.log.error("Only one macro assembly function parameter can be passed via a register", actualParam.position)
|
||||
}
|
||||
@@ -56,10 +58,10 @@ object M6809MacroExpander extends MacroExpander[MLine] {
|
||||
ctx.log.error(s"Invalid parameter for macro: ${typ.name} ${register.toString.toLowerCase(Locale.ROOT)}", actualParam.position)
|
||||
Nil
|
||||
}
|
||||
case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
case (AssemblyOrMacroParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
???
|
||||
case (_, actualParam) =>
|
||||
}
|
||||
paramPreparation -> actualCode
|
||||
paramPreparation -> stmtPreprocess(ctx, actualCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1745,15 +1745,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case _ => ()
|
||||
}
|
||||
val result = function.params match {
|
||||
case AssemblyParamSignature(paramConvs) =>
|
||||
case AssemblyOrMacroParamSignature(paramConvs) =>
|
||||
val pairs = params.zip(paramConvs)
|
||||
val secondViaMemory = pairs.flatMap {
|
||||
case (paramExpr, AssemblyParam(typ, paramVar: VariableInMemory, AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
case (paramExpr, AssemblyOrMacroParam(typ, paramVar: VariableInMemory, AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
compile(ctx, paramExpr, Some(typ -> paramVar), NoBranching)
|
||||
case _ => Nil
|
||||
}
|
||||
val thirdViaRegisters = pairs.flatMap {
|
||||
case (paramExpr, AssemblyParam(typ, paramVar@RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
case (paramExpr, AssemblyOrMacroParam(typ, paramVar@RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
Some(register -> compile(ctx, paramExpr, Some(typ -> paramVar), NoBranching))
|
||||
case _ => Nil
|
||||
} match {
|
||||
|
||||
@@ -11,12 +11,14 @@ import millfork.node._
|
||||
*/
|
||||
object MosMacroExpander extends MacroExpander[AssemblyLine] {
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[AssemblyLine], List[ExecutableStatement]) = {
|
||||
override def stmtPreprocess(ctx: CompilationContext, stmts: List[ExecutableStatement]): List[ExecutableStatement] = new MosStatementPreprocessor(ctx, stmts)()
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyOrMacroParam], params: List[Expression], code: List[ExecutableStatement]): (List[AssemblyLine], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[AssemblyLine]()
|
||||
var actualCode = code
|
||||
var hadRegisterParam = false
|
||||
assParams.zip(params).foreach {
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
actualParam match {
|
||||
case VariableExpression(vname) =>
|
||||
ctx.env.get[ThingInMemory](vname)
|
||||
@@ -31,23 +33,23 @@ object MosMacroExpander extends MacroExpander[AssemblyLine] {
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
ctx.env.eval(actualParam).getOrElse(ctx.env.errorConstant("Non-constant expression was passed to an inlineable function as a `const` parameter", actualParam.position))
|
||||
actualCode = actualCode.map {
|
||||
case a@MosAssemblyStatement(_, _, expr, _) =>
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, v@RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, v@RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
if (hadRegisterParam) {
|
||||
ctx.log.error("Only one macro assembly function parameter can be passed via a register", actualParam.position)
|
||||
}
|
||||
hadRegisterParam = true
|
||||
paramPreparation = MosExpressionCompiler.compile(ctx, actualParam, Some(typ, v), BranchSpec.None)
|
||||
case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
case (AssemblyOrMacroParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
???
|
||||
case (_, actualParam) =>
|
||||
}
|
||||
paramPreparation -> actualCode
|
||||
paramPreparation -> stmtPreprocess(ctx, actualCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,34 +1236,34 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case _ => ()
|
||||
}
|
||||
val result = function.params match {
|
||||
case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.A, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
case AssemblyOrMacroParamSignature(List(AssemblyOrMacroParam(typ1, ZRegisterVariable(ZRegister.A, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
if typ1.size == 1 && typ2.size == 1 =>
|
||||
compileToA(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(
|
||||
case AssemblyOrMacroParamSignature(List(AssemblyOrMacroParam(typ1, ZRegisterVariable(
|
||||
register@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E | ZRegister.H | ZRegister.L),
|
||||
typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
if typ1.size == 1 && typ2.size == 1 =>
|
||||
compile8BitTo(ctx, params.head, register) :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.HL, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
case AssemblyOrMacroParamSignature(List(AssemblyOrMacroParam(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(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.DE, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
case AssemblyOrMacroParamSignature(List(AssemblyOrMacroParam(typ1, ZRegisterVariable(ZRegister.DE, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
if typ1.size == 2 && typ2.size == 2 =>
|
||||
compileToDE(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.BC, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
case AssemblyOrMacroParamSignature(List(AssemblyOrMacroParam(typ1, ZRegisterVariable(ZRegister.BC, typ2), AssemblyParameterPassingBehaviour.Copy)))
|
||||
if typ1.size == 2 && typ2.size == 2 =>
|
||||
compileToBC(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case AssemblyParamSignature(Nil) =>
|
||||
case AssemblyOrMacroParamSignature(Nil) =>
|
||||
List(ZLine(CALL, NoRegisters, function.toAddress))
|
||||
case AssemblyParamSignature(paramConvs) =>val pairs = params.zip(paramConvs)
|
||||
case AssemblyOrMacroParamSignature(paramConvs) =>val pairs = params.zip(paramConvs)
|
||||
val viaMemory = pairs.flatMap {
|
||||
case (paramExpr, AssemblyParam(typ, paramVar: VariableInMemory, AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
case (paramExpr, AssemblyOrMacroParam(typ, paramVar: VariableInMemory, AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
ctx.log.error("Variable parameters to assembly functions are not supported", expression.position)
|
||||
Nil
|
||||
case _ => Nil
|
||||
}
|
||||
val viaRegisters = pairs.flatMap {
|
||||
case (paramExpr, AssemblyParam(typ, paramVar@ZRegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
case (paramExpr, AssemblyOrMacroParam(typ, paramVar@ZRegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
if (typ.size != ZRegister.registerSize(register)) {
|
||||
ctx.log.error(s"Type ${typ.name} and register $register are of different sizes", expression.position)
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@ import millfork.node._
|
||||
*/
|
||||
object Z80MacroExpander extends MacroExpander[ZLine] {
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[ZLine], List[ExecutableStatement]) = {
|
||||
override def stmtPreprocess(ctx: CompilationContext, stmts: List[ExecutableStatement]): List[ExecutableStatement] = new Z80StatementPreprocessor(ctx, stmts)()
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyOrMacroParam], 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) =>
|
||||
case (AssemblyOrMacroParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
actualParam match {
|
||||
case VariableExpression(vname) =>
|
||||
ctx.env.get[ThingInMemory](vname)
|
||||
@@ -33,14 +35,14 @@ object Z80MacroExpander extends MacroExpander[ZLine] {
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
case (AssemblyOrMacroParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
ctx.env.eval(actualParam).getOrElse(ctx.env.errorConstant("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) =>
|
||||
case (AssemblyOrMacroParam(typ, v@ZRegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
if (hadRegisterParam) {
|
||||
ctx.log.error("Only one macro assembly function parameter can be passed via a register", actualParam.position)
|
||||
}
|
||||
@@ -56,10 +58,10 @@ object Z80MacroExpander extends MacroExpander[ZLine] {
|
||||
ctx.log.error(s"Invalid parameter for macro: ${typ.name} ${register.toString.toLowerCase(Locale.ROOT)}", actualParam.position)
|
||||
Nil
|
||||
}
|
||||
case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
case (AssemblyOrMacroParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
???
|
||||
case (_, actualParam) =>
|
||||
}
|
||||
paramPreparation -> actualCode
|
||||
paramPreparation -> stmtPreprocess(ctx, actualCode)
|
||||
}
|
||||
}
|
||||
|
||||
+42
-24
@@ -1175,23 +1175,25 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
new Environment(Some(this), name + "$", cpuFamily, options)
|
||||
}
|
||||
stmt.params.foreach(p => env.registerParameter(p, options, pointies))
|
||||
def params: ParamSignature = if (stmt.assembly) {
|
||||
AssemblyParamSignature(stmt.params.map {
|
||||
def params: ParamSignature = if (stmt.assembly || stmt.isMacro) {
|
||||
AssemblyOrMacroParamSignature(stmt.params.map {
|
||||
pd =>
|
||||
val typ = env.get[Type](pd.typ)
|
||||
pd.assemblyParamPassingConvention match {
|
||||
case ByVariable(vn) =>
|
||||
AssemblyParam(typ, env.get[MemoryVariable](vn), AssemblyParameterPassingBehaviour.Copy)
|
||||
AssemblyOrMacroParam(typ, env.get[MemoryVariable](vn), AssemblyParameterPassingBehaviour.Copy)
|
||||
case ByMosRegister(reg) =>
|
||||
AssemblyParam(typ, RegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
AssemblyOrMacroParam(typ, RegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
case ByZRegister(reg) =>
|
||||
AssemblyParam(typ, ZRegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
AssemblyOrMacroParam(typ, ZRegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
case ByM6809Register(reg) =>
|
||||
AssemblyParam(typ, M6809RegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
AssemblyOrMacroParam(typ, M6809RegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
case ByConstant(vn) =>
|
||||
AssemblyParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.ByConstant)
|
||||
AssemblyOrMacroParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.ByConstant)
|
||||
case ByReference(vn) =>
|
||||
AssemblyParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.ByReference)
|
||||
AssemblyOrMacroParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.ByReference)
|
||||
case ByLazilyEvaluableExpressionVariable(vn) =>
|
||||
AssemblyOrMacroParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.Eval)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -1318,7 +1320,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
val mangled = MacroFunction(
|
||||
name,
|
||||
resultType,
|
||||
params,
|
||||
params.asInstanceOf[AssemblyOrMacroParamSignature],
|
||||
stmt.assembly,
|
||||
env,
|
||||
executableStatements
|
||||
)
|
||||
@@ -1488,6 +1491,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
addThing(subv, stmt.position)
|
||||
registerAddressConstant(subv, stmt.position, options, Some(t))
|
||||
}
|
||||
case ByLazilyEvaluableExpressionVariable(_) => ()
|
||||
case ByMosRegister(_) => ()
|
||||
case ByZRegister(_) => ()
|
||||
case ByM6809Register(_) => ()
|
||||
@@ -2097,20 +2101,34 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
function.params match {
|
||||
case NormalParamSignature(params) =>
|
||||
function.params.types.zip(actualParams).zip(params).foreach { case ((required, (actual, expr)), m) =>
|
||||
if (function.isInstanceOf[MacroFunction]) {
|
||||
if (required != VoidType && actual != required) {
|
||||
log.error(s"Invalid argument type for parameter `${m.name}` of macro function `$name`: required: ${required.name}, actual: ${actual.name}", expr.position)
|
||||
}
|
||||
} else {
|
||||
if (!actual.isAssignableTo(required)) {
|
||||
log.error(s"Invalid value for parameter `${m.name}` of function `$name`", expr.position)
|
||||
}
|
||||
if (!actual.isAssignableTo(required)) {
|
||||
log.error(s"Invalid value for parameter `${m.name}` of function `$name`", expr.position)
|
||||
}
|
||||
}
|
||||
case AssemblyParamSignature(params) =>
|
||||
function.params.types.zip(actualParams).zipWithIndex.foreach { case ((required, (actual, expr)), ix) =>
|
||||
if (!actual.isAssignableTo(required)) {
|
||||
log.error(s"Invalid value for parameter ${ix + 1} of function `$name`", expr.position)
|
||||
case AssemblyOrMacroParamSignature(params) =>
|
||||
params.zip(actualParams).zipWithIndex.foreach { case ((AssemblyOrMacroParam(requiredType, variable, behaviour), (actual, expr)), ix) =>
|
||||
function match {
|
||||
case m: MacroFunction =>
|
||||
behaviour match {
|
||||
case AssemblyParameterPassingBehaviour.ByReference =>
|
||||
if (!m.isInAssembly) {
|
||||
if (requiredType != VoidType && actual != requiredType) {
|
||||
log.error(s"Invalid argument type for parameter `${variable.name}` of macro function `$name`: required: ${requiredType.name}, actual: ${actual.name}", expr.position)
|
||||
}
|
||||
}
|
||||
case AssemblyParameterPassingBehaviour.Copy if m.isInAssembly =>
|
||||
if (!actual.isAssignableTo(requiredType)) {
|
||||
log.error(s"Invalid value for parameter #${ix + 1} of macro function `$name`", expr.position)
|
||||
}
|
||||
case _ =>
|
||||
if (!actual.isAssignableTo(requiredType)) {
|
||||
log.error(s"Invalid value for parameter #${ix + 1} `${variable.name}` of macro function `$name`", expr.position)
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
if (!actual.isAssignableTo(requiredType)) {
|
||||
log.error(s"Invalid value for parameter #${ix + 1} `${variable.name}` of function `$name`", expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2249,7 +2267,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
|
||||
if (!things.contains("memory_barrier")) {
|
||||
things("memory_barrier") = MacroFunction("memory_barrier", v, NormalParamSignature(Nil), this, CpuFamily.forType(options.platform.cpu) match {
|
||||
things("memory_barrier") = MacroFunction("memory_barrier", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, CpuFamily.forType(options.platform.cpu) match {
|
||||
case CpuFamily.M6502 => List(MosAssemblyStatement(Opcode.CHANGED_MEM, AddrMode.DoesNotExist, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.I80 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.I86 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
@@ -2261,7 +2279,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (!things.contains("breakpoint")) {
|
||||
val p = get[VariableType]("pointer")
|
||||
if (options.flag(CompilationFlag.EnableBreakpoints)) {
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, NormalParamSignature(Nil), this, CpuFamily.forType(options.platform.cpu) match {
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, CpuFamily.forType(options.platform.cpu) match {
|
||||
case CpuFamily.M6502 => List(MosAssemblyStatement(Opcode.CHANGED_MEM, AddrMode.DoesNotExist, VariableExpression("..brk"), Elidability.Fixed))
|
||||
case CpuFamily.I80 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, VariableExpression("..brk"), Elidability.Fixed))
|
||||
case CpuFamily.I86 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, VariableExpression("..brk"), Elidability.Fixed))
|
||||
@@ -2269,7 +2287,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case _ => ???
|
||||
})
|
||||
} else {
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, NormalParamSignature(Nil), this, Nil)
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -396,7 +396,8 @@ case class EmptyFunction(name: String,
|
||||
|
||||
case class MacroFunction(name: String,
|
||||
returnType: Type,
|
||||
params: ParamSignature,
|
||||
params: AssemblyOrMacroParamSignature,
|
||||
isInAssembly: Boolean,
|
||||
environment: Environment,
|
||||
code: List[ExecutableStatement]) extends MangledFunction {
|
||||
override def interrupt = false
|
||||
@@ -525,6 +526,12 @@ case class ByVariable(name: String) extends ParamPassingConvention {
|
||||
override def inNonInlinedOnly = true
|
||||
}
|
||||
|
||||
case class ByLazilyEvaluableExpressionVariable(name: String) extends ParamPassingConvention {
|
||||
override def inInlinedOnly = true
|
||||
|
||||
override def inNonInlinedOnly = false
|
||||
}
|
||||
|
||||
case class ByConstant(name: String) extends ParamPassingConvention {
|
||||
override def inInlinedOnly = true
|
||||
|
||||
@@ -538,10 +545,10 @@ case class ByReference(name: String) extends ParamPassingConvention {
|
||||
}
|
||||
|
||||
object AssemblyParameterPassingBehaviour extends Enumeration {
|
||||
val Copy, ByReference, ByConstant = Value
|
||||
val Copy, Eval, ByReference, ByConstant = Value
|
||||
}
|
||||
|
||||
case class AssemblyParam(typ: Type, variable: TypedThing, behaviour: AssemblyParameterPassingBehaviour.Value) {
|
||||
case class AssemblyOrMacroParam(typ: Type, variable: TypedThing, behaviour: AssemblyParameterPassingBehaviour.Value) {
|
||||
def canBePointedTo: Boolean = behaviour == AssemblyParameterPassingBehaviour.Copy && (variable match {
|
||||
case RegisterVariable(MosRegister.A | MosRegister.AX, _) => true
|
||||
case ZRegisterVariable(ZRegister.A | ZRegister.HL, _) => true
|
||||
@@ -550,7 +557,7 @@ case class AssemblyParam(typ: Type, variable: TypedThing, behaviour: AssemblyPar
|
||||
}
|
||||
|
||||
|
||||
case class AssemblyParamSignature(params: List[AssemblyParam]) extends ParamSignature {
|
||||
case class AssemblyOrMacroParamSignature(params: List[AssemblyOrMacroParam]) extends ParamSignature {
|
||||
override def length: Int = params.length
|
||||
|
||||
override def types: List[Type] = params.map(_.typ)
|
||||
|
||||
@@ -135,9 +135,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
case _: StackOverflowError =>
|
||||
log.fatal("Stack overflow " + c)
|
||||
}
|
||||
case UnexpandedConstant(name, _) =>
|
||||
case badThing@UnexpandedConstant(name, _) =>
|
||||
if (labelMap.contains(name)) labelMap(name)._2
|
||||
else ???
|
||||
else {
|
||||
println(badThing)
|
||||
???
|
||||
}
|
||||
case SubbyteConstant(cc, i) => deepConstResolve(cc).>>>(i * 8).&(0xff)
|
||||
case s: StructureConstant =>
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,7 @@ case class M6809Parser(filename: String,
|
||||
|
||||
override def allowIntelHexAtomsInAssembly: Boolean = false
|
||||
|
||||
val appcSimple: P[ParamPassingConvention] = P(("a" | "b" | "d" | "x" | "y" | "u" | "s" | "dp") ~ !letterOrDigit).!.map {
|
||||
override val appcRegister: P[ParamPassingConvention] = P(("a" | "b" | "d" | "x" | "y" | "u" | "s" | "dp") ~ !letterOrDigit).!.map {
|
||||
case "a" => ByM6809Register(M6809Register.A)
|
||||
case "b" => ByM6809Register(M6809Register.B)
|
||||
case "d" => ByM6809Register(M6809Register.D)
|
||||
@@ -35,7 +35,7 @@ case class M6809Parser(filename: String,
|
||||
override val asmParamDefinition: P[ParameterDeclaration] = for {
|
||||
p <- position()
|
||||
typ <- identifier ~ SWS
|
||||
appc <- appcSimple | appcComplex
|
||||
appc <- appcRegister | appcComplex
|
||||
} yield ParameterDeclaration(typ, appc).pos(p)
|
||||
|
||||
def fastAlignmentForArrays: MemoryAlignment = WithinPageAlignment
|
||||
|
||||
@@ -186,6 +186,9 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
typ <- identifier ~/ SWS ~/ Pass
|
||||
name <- identifier ~/ Pass
|
||||
} yield {
|
||||
if (name == "register" || name == "const" || name == "ref" || name == "call") {
|
||||
log.error(s"Invalid parameter name: `$name`. Did you mean writing a macro?", Some(p))
|
||||
}
|
||||
ParameterDeclaration(typ, ByVariable(name)).pos(p)
|
||||
}
|
||||
|
||||
@@ -206,14 +209,53 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
asmExpression.map(_ -> false)
|
||||
)).map { case (p, e) => e._1.pos(p) -> e._2 }
|
||||
|
||||
val appcComplex: P[ParamPassingConvention] = P((("const" | "ref").! ~/ AWS).? ~ AWS ~ identifier) map {
|
||||
case (None, name) => ByVariable(name)
|
||||
case (Some("const"), name) => ByConstant(name)
|
||||
case (Some("ref"), name) => ByReference(name)
|
||||
case x => log.fatal(s"Unknown assembly parameter passing convention: `$x`")
|
||||
}
|
||||
def appcRegister: P[ParamPassingConvention]
|
||||
|
||||
def asmParamDefinition: P[ParameterDeclaration]
|
||||
val appcComplex: P[ParamPassingConvention] =
|
||||
for {
|
||||
pos <- position("passing method")
|
||||
keyword <- (("const" | "ref" | "register" | "call").! ~ !letterOrDigit ~/ Pass).? ~/ Pass
|
||||
_ <- if (keyword.contains("call")) {log.error(s"Invalid assembly macro parameter passing convention: `call`", Some(pos)) ; Pass } else Pass
|
||||
if !keyword.contains("call")
|
||||
_ <- position("register name")
|
||||
register <- keyword match {
|
||||
case Some("register") => (AWS ~ "(" ~/ AWS ~ appcRegister ~/ AWS ~ ")" ~/ AWS).map(Some(_))
|
||||
case Some(_) => SWS.map(_ => None)
|
||||
case None => Pass.map(_ => None)
|
||||
}
|
||||
_ <- position("parameter name")
|
||||
ident <- identifier
|
||||
} yield ((keyword, register, ident) match {
|
||||
case (None, _, name) => ByVariable(name)
|
||||
case (Some("const"), _, name) => ByConstant(name)
|
||||
case (Some("ref"), _, name) => ByReference(name)
|
||||
case (Some("register"), Some(reg), _) => reg
|
||||
case x => log.fatal(s"Unknown assembly parameter passing convention: `$x`")
|
||||
})
|
||||
|
||||
val asmParamDefinition: P[ParameterDeclaration] = for {
|
||||
p <- position()
|
||||
typ <- identifier ~ SWS ~/ Pass
|
||||
appc <- appcRegister | appcComplex
|
||||
} yield ParameterDeclaration(typ, appc).pos(p)
|
||||
|
||||
val macroParamDefinition: P[ParameterDeclaration] = for {
|
||||
p <- position()
|
||||
typ <- identifier ~ SWS ~/ Pass
|
||||
pos <- position("passing method")
|
||||
keyword <- ( ("const" | "ref" | "call" | "register").! ~ !letterOrDigit ~/ Pass).?
|
||||
_ <- if (keyword.contains("register")) {log.error(s"Invalid non-assembly macro parameter passing convention: `register`. Did you forget `asm`?", Some(pos)) ; Pass } else Pass
|
||||
if !keyword.contains("register")
|
||||
_ <- if (keyword.isDefined) SWS else Pass
|
||||
_ <- position("parameter name")
|
||||
name <- identifier
|
||||
} yield ParameterDeclaration(typ, (keyword match {
|
||||
case None => ByReference(name)
|
||||
case Some("ref") => ByReference(name)
|
||||
case Some("const") => ByConstant(name)
|
||||
case Some("call") => ByLazilyEvaluableExpressionVariable(name)
|
||||
case Some(x) => log.fatal(s"Invalid non-assembly macro parameter passing convention: `$x`")
|
||||
})).pos(p)
|
||||
|
||||
def arrayListElement: P[ArrayContents] = arrayStringContents | arrayProcessedContents | arrayLoopContents | arrayFileContents | mfExpression(nonStatementLevel, false).map(e => LiteralContents(List(e)))
|
||||
|
||||
@@ -578,7 +620,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
returnType <- identifier ~ SWS
|
||||
if !Environment.neverValidTypeIdentifiers(returnType)
|
||||
name <- identifier ~ HWS
|
||||
params <- "(" ~/ AWS ~/ (if (flags("asm")) asmParamDefinition else paramDefinition).rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ ")" ~/ AWS
|
||||
params <- "(" ~/ AWS ~/ (if (flags("asm")) asmParamDefinition else if (flags("macro")) macroParamDefinition else paramDefinition).rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ ")" ~/ AWS
|
||||
alignment <- alignmentDeclaration(fastAlignmentForFunctions).? ~/ AWS
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("<address>") ~/ AWS
|
||||
statements <- (externFunctionBody | (if (flags("asm")) asmStatements else mfFunctionBody).map(l => Some(l))) ~/ Pass
|
||||
|
||||
@@ -117,7 +117,7 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
|
||||
val asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros
|
||||
|
||||
|
||||
val appcSimple: P[ParamPassingConvention] = P(("xy" | "yx" | "ax" | "ay" | "xa" | "ya" | "stack" | "a" | "x" | "y") ~ !letterOrDigit).!.map {
|
||||
override val appcRegister: P[ParamPassingConvention] = P(("xy" | "yx" | "ax" | "ay" | "xa" | "ya" | "a" | "x" | "y") ~ !letterOrDigit).!.map {
|
||||
case "xy" => ByMosRegister(MosRegister.XY)
|
||||
case "yx" => ByMosRegister(MosRegister.YX)
|
||||
case "ax" => ByMosRegister(MosRegister.AX)
|
||||
@@ -130,12 +130,6 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
|
||||
case x => log.fatal(s"Unknown assembly parameter passing convention: `$x`")
|
||||
}
|
||||
|
||||
override val asmParamDefinition: P[ParameterDeclaration] = for {
|
||||
p <- position()
|
||||
typ <- identifier ~ SWS
|
||||
appc <- appcSimple | appcComplex
|
||||
} yield ParameterDeclaration(typ, appc).pos(p)
|
||||
|
||||
def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = {
|
||||
if (!options.flag(CompilationFlag.BuggyCodeWarning)) return
|
||||
statements match {
|
||||
|
||||
@@ -31,7 +31,7 @@ case class Z80Parser(filename: String,
|
||||
|
||||
private val zero = LiteralExpression(0, 1)
|
||||
|
||||
val appcSimple: P[ParamPassingConvention] = (P("hl" | "bc" | "de" | "a" | "b" | "c" | "d" | "e" | "h" | "l").! ~ !letterOrDigit).map {
|
||||
override val appcRegister: P[ParamPassingConvention] = (P("hl" | "bc" | "de" | "a" | "b" | "c" | "d" | "e" | "h" | "l").! ~ !letterOrDigit).map {
|
||||
case "a" => ByZRegister(ZRegister.A)
|
||||
case "b" => ByZRegister(ZRegister.B)
|
||||
case "c" => ByZRegister(ZRegister.C)
|
||||
@@ -48,7 +48,7 @@ case class Z80Parser(filename: String,
|
||||
override val asmParamDefinition: P[ParameterDeclaration] = for {
|
||||
p <- position()
|
||||
typ <- identifier ~ SWS
|
||||
appc <- appcSimple | appcComplex
|
||||
appc <- appcRegister | appcComplex
|
||||
} yield ParameterDeclaration(typ, appc).pos(p)
|
||||
|
||||
// TODO: label and instruction in one line
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.assembly.mos.opt.DangerousOptimizations
|
||||
import millfork.test.emu.EmuBenchmarkRun
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun, ShouldNotParse}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class AssemblyMacroSuite extends FunSuite with Matchers {
|
||||
class AssemblyMacroSuite extends FunSuite with Matchers with AppendedClues {
|
||||
|
||||
test("Poke test 1") {
|
||||
EmuBenchmarkRun(
|
||||
@@ -99,4 +97,42 @@ class AssemblyMacroSuite extends FunSuite with Matchers {
|
||||
m.readByte(0xc000) should equal(3)
|
||||
}
|
||||
}
|
||||
|
||||
test("All assembly macro param types") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
"""
|
||||
| array output [3] @$c000
|
||||
|
|
||||
| macro asm void test0(byte register(a) value) {
|
||||
| sta $c000
|
||||
| }
|
||||
| macro asm void test1(byte ref variable) {
|
||||
| lda variable
|
||||
| sta $c001
|
||||
| }
|
||||
| macro asm void test2(byte const constant) {
|
||||
| lda #constant
|
||||
| sta $c002
|
||||
| }
|
||||
|
|
||||
| void main() {
|
||||
| byte variable
|
||||
| variable = 42
|
||||
| test0(42)
|
||||
| test1(variable)
|
||||
| test2(42)
|
||||
| }
|
||||
|""".stripMargin)
|
||||
m.readByte(0xc000) should equal(42) withClue "$c000"
|
||||
m.readByte(0xc001) should equal(42) withClue "$c001"
|
||||
m.readByte(0xc002) should equal(42) withClue "$c002"
|
||||
}
|
||||
|
||||
test("Invalid assembly macro param types") {
|
||||
ShouldNotParse(
|
||||
"""
|
||||
| macro asm void test0(byte call value) {
|
||||
| }
|
||||
|""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package millfork.test
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedCmosRun, EmuOptimizedHudsonRun, EmuOptimizedRun, EmuUndocumentedRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedHudsonRun, ShouldNotCompile}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedCmosRun, EmuOptimizedHudsonRun, EmuOptimizedRun, EmuUndocumentedRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedHudsonRun, EmuUnoptimizedRun, ShouldNotCompile}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@@ -362,4 +362,21 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
|
||||
|""".stripMargin, Set(Cpu.Mos))
|
||||
}
|
||||
|
||||
test("Compile params properly") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
"""
|
||||
|noinline asm void f(byte register(a) v0, byte register(x) v1) {
|
||||
| sta $c000
|
||||
| stx $c001
|
||||
| rts
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| f(9,3)
|
||||
|}
|
||||
|""".stripMargin)
|
||||
m.readByte(0xc000) should equal(9)
|
||||
m.readByte(0xc001) should equal(3)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.output.MemoryBank
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedAccordingToLevelRun, EmuOptimizedRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedAccordingToLevelRun, EmuOptimizedRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotCompile, ShouldNotParse}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class MacroSuite extends FunSuite with Matchers {
|
||||
class MacroSuite extends FunSuite with Matchers with AppendedClues {
|
||||
|
||||
test("Most basic test") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)(
|
||||
@@ -215,4 +215,83 @@ class MacroSuite extends FunSuite with Matchers {
|
||||
assertAtLeastOneTrueInZeroPage(EmuOptimizedAccordingToLevelRun(2)(src))
|
||||
assertAtLeastOneTrueInZeroPage(EmuOptimizedAccordingToLevelRun(3)(src))
|
||||
}
|
||||
|
||||
test("All non-assembly macro param types") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
|
||||
"""
|
||||
| array(word) output [3] @$c000
|
||||
|
|
||||
| macro void test0(byte const constant) {
|
||||
| output[0] = constant
|
||||
| }
|
||||
| macro void test1(byte ref variable) {
|
||||
| variable += 1
|
||||
| output[1] = variable
|
||||
| }
|
||||
| macro void test2(byte call tooMuch) {
|
||||
| output[2] = tooMuch
|
||||
| output[2].lo &= $7f
|
||||
| }
|
||||
|
|
||||
| macro void twice(void call expr) {
|
||||
| expr
|
||||
| expr
|
||||
| }
|
||||
|
|
||||
| inline void inc0() {
|
||||
| output[0] += 1
|
||||
| }
|
||||
| void main() {
|
||||
| byte variable
|
||||
| variable = 41
|
||||
| output[0]=0
|
||||
| output[1]=0
|
||||
| output[2]=0
|
||||
| test0(40)
|
||||
| twice(inc0())
|
||||
| test1(variable)
|
||||
| test2(sbyte($80 | 42))
|
||||
| }
|
||||
|""".stripMargin) { m =>
|
||||
m.readWord(0xc000) should equal(42) withClue "$c000"
|
||||
m.readWord(0xc002) should equal(42) withClue "$c002"
|
||||
m.readWord(0xc004) should equal(42) withClue "$c004"
|
||||
}
|
||||
}
|
||||
|
||||
test("Macros as params to void call macros") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| macro void inc_x() {
|
||||
| x += 1
|
||||
| }
|
||||
| macro void inc(void ref x) {
|
||||
| inc_x()
|
||||
| }
|
||||
| macro void perform_twice(void call f) {
|
||||
| f
|
||||
| f
|
||||
| }
|
||||
| byte add_two_3(byte x) {
|
||||
| perform_twice(inc(x))
|
||||
| return x
|
||||
| }
|
||||
|
|
||||
| void main() {
|
||||
| output = add_two_3(40)
|
||||
| }
|
||||
|
|
||||
|""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(42)
|
||||
}
|
||||
}
|
||||
|
||||
test("Invalid non-assembly macro param types") {
|
||||
ShouldNotParse(
|
||||
"""
|
||||
| macro void test0(byte register(a) value) {
|
||||
| }
|
||||
|""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user