From 60b2c44a445a8adf8b1fff8be0c56853c0b5884d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 21 Feb 2022 01:32:29 +0100 Subject: [PATCH] fix returntype handling of builtinfunctions, fix errors in pipe expressions --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 23 ++- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 11 +- .../prog8/optimizer/ExpressionSimplifier.kt | 8 + .../src/prog8/optimizer/UnusedCodeRemover.kt | 3 +- compiler/src/prog8/compiler/Compiler.kt | 2 - .../compiler/astprocessing/AstChecker.kt | 42 ++-- compiler/test/TestBuiltinFunctions.kt | 93 +++++++++ compiler/test/TestPipes.kt | 90 ++++++++- .../prog8/ast/expressions/AstExpressions.kt | 35 +++- .../compilerinterface/BuiltinFunctions.kt | 185 +++++++++--------- docs/source/todo.rst | 3 +- examples/test.p8 | 72 +------ 12 files changed, 367 insertions(+), 200 deletions(-) create mode 100644 compiler/test/TestBuiltinFunctions.kt diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 358a3e53d..f5ad27311 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -968,14 +968,27 @@ $repeatLabel lda $counterVar assemblyLines.add(assembly) } - internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair { + internal fun returnRegisterOfFunction(it: IdentifierReference, argumentTypesForBuiltinFunc: List?): RegisterOrPair { return when (val targetRoutine = it.targetStatement(program)!!) { is BuiltinFunctionPlaceholder -> { - when (BuiltinFunctions.getValue(targetRoutine.name).known_returntype) { + val func = BuiltinFunctions.getValue(targetRoutine.name) + when (func.returnType) { in ByteDatatypes -> RegisterOrPair.A in WordDatatypes -> RegisterOrPair.AY DataType.FLOAT -> RegisterOrPair.FAC1 - else -> throw AssemblyError("weird returntype") + else -> { + if(!func.hasReturn) + throw AssemblyError("func has no returntype") + else { + val args = argumentTypesForBuiltinFunc!!.map { defaultZero(it, Position.DUMMY) } + when(builtinFunctionReturnType(func.name, args, program).getOrElse { DataType.UNDEFINED }) { + in ByteDatatypes -> RegisterOrPair.A + in WordDatatypes -> RegisterOrPair.AY + DataType.FLOAT -> RegisterOrPair.FAC1 + else -> throw AssemblyError("weird returntype") + } + } + } } } is Subroutine -> targetRoutine.asmReturnvaluesRegisters.single().registerOrPair!! @@ -2833,7 +2846,7 @@ $repeatLabel lda $counterVar var valueDt = firstTerm.inferType(program).getOrElse { throw FatalAstException("invalid dt") } var valueSource: AsmAssignSource = if(firstTerm is IFunctionCall) { - val resultReg = returnRegisterOfFunction(firstTerm.target) + val resultReg = returnRegisterOfFunction(firstTerm.target, listOf(valueDt)) assignExpressionToRegister(firstTerm, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)) AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg) } else { @@ -2844,7 +2857,7 @@ $repeatLabel lda $counterVar // directly assign their argument from the previous call's returnvalue. expressions.drop(1).dropLast(1).forEach { valueDt = functioncallAsmGen.translateUnaryFunctionCallWithArgSource(it as IdentifierReference, valueSource, false, subroutine) - val resultReg = returnRegisterOfFunction(it) + val resultReg = returnRegisterOfFunction(it, listOf(valueDt)) valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg) } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 57468e5b8..2a5db6dc9 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -11,10 +11,7 @@ import prog8.ast.statements.DirectMemoryWrite import prog8.ast.statements.Subroutine import prog8.ast.toHex import prog8.codegen.cpu6502.assignment.* -import prog8.compilerinterface.AssemblyError -import prog8.compilerinterface.BuiltinFunctions -import prog8.compilerinterface.CpuType -import prog8.compilerinterface.FSignature +import prog8.compilerinterface.* internal class BuiltinFunctionsAsmGen(private val program: Program, @@ -60,7 +57,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY) fcall.linkParents(scope) translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null) - return if(isStatement) DataType.UNDEFINED else func.known_returntype!! + if(isStatement) { + return DataType.UNDEFINED + } else { + return builtinFunctionReturnType(func.name, argExpressions, program).getOrElse { throw AssemblyError("unknown dt") } + } } private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { diff --git a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt index 509283e4d..a355569ae 100644 --- a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt @@ -335,6 +335,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr override fun after(pipeExpr: PipeExpression, parent: Node): Iterable { val expressions = pipeExpr.expressions + if(expressions.size==2 && expressions[0].isSimple) { + // just replace with a normal function call + val funcname = expressions[1] as IdentifierReference + val arg = expressions[0] + val call = FunctionCallExpression(funcname.copy(), mutableListOf(arg), arg.position) + return listOf(IAstModification.ReplaceNode(pipeExpr, call, parent)) + } + require(expressions.size>=2) { "pipe expression should have 2 or more parts" } val firstValue = expressions.first() if(firstValue.isSimple) { val funcname = expressions[1] as IdentifierReference diff --git a/codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt b/codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt index f90f64195..ca724d82c 100644 --- a/codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt @@ -238,7 +238,8 @@ class UnusedCodeRemover(private val program: Program, is PrefixExpression, is BinaryExpression, is TypecastExpression, - is FunctionCallExpression -> { /* don't remove */ } + is PipeExpression, + is IFunctionCall -> { /* don't remove */ } else -> { if(assign1.value !is IFunctionCall) linesToRemove.add(assign1) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index a4a12a7bd..9148f0f70 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -171,8 +171,6 @@ private class BuiltinFunctionsFacade(functions: Map): IBuilt null } } - else if(func.known_returntype==null) - return null // builtin function $name can't be used here because it doesn't return a value } return null } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 9ca10d26d..c8e583b8a 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1245,27 +1245,30 @@ internal class AstChecker(private val program: Program, override fun visit(pipe: PipeExpression) { processPipe(pipe.expressions, pipe) - val last = pipe.expressions.last() as IdentifierReference - val target = last.targetStatement(program)!! - when(target) { - is BuiltinFunctionPlaceholder -> { - if(BuiltinFunctions.getValue(target.name).known_returntype==null) - errors.err("invalid pipe expression; last term doesn't return a value", last.position) + if(errors.noErrors()) { + val last = pipe.expressions.last() as IdentifierReference + when (val target = last.targetStatement(program)!!) { + is BuiltinFunctionPlaceholder -> { + if (!BuiltinFunctions.getValue(target.name).hasReturn) + errors.err("invalid pipe expression; last term doesn't return a value", last.position) + } + is Subroutine -> { + if (target.returntypes.isEmpty()) + errors.err("invalid pipe expression; last term doesn't return a value", last.position) + else if (target.returntypes.size != 1) + errors.err("invalid pipe expression; last term doesn't return a single value", last.position) + } + else -> errors.err("invalid pipe expression; last term doesn't return a value", last.position) } - is Subroutine -> { - if(target.returntypes.isEmpty()) - errors.err("invalid pipe expression; last term doesn't return a value", last.position) - else if(target.returntypes.size!=1) - errors.err("invalid pipe expression; last term doesn't return a single value", last.position) - } - else -> errors.err("invalid pipe expression; last term doesn't return a value", last.position) + super.visit(pipe) } - super.visit(pipe) } override fun visit(pipe: Pipe) { processPipe(pipe.expressions, pipe) - super.visit(pipe) + if(errors.noErrors()) { + super.visit(pipe) + } } private fun processPipe(expressions: List, scope: Node) { @@ -1289,14 +1292,19 @@ internal class AstChecker(private val program: Program, val func = BuiltinFunctions.getValue(function.name) if(func.parameters.size!=1) errors.err("can only use unary function", expr.position) - else if(func.known_returntype==null && expr !== expressions.last()) + else if(!func.hasReturn && expr !== expressions.last()) errors.err("function must return a single value", expr.position) val paramDts = func.parameters.firstOrNull()?.possibleDatatypes if(paramDts!=null && !paramDts.any { valueDt isAssignableTo it }) errors.err("pipe value datatype $valueDt incompatible withfunction argument ${paramDts.toList()}", functionName.position) - valueDt = func.known_returntype!! + if(errors.noErrors()) { + // type can depend on the argument(s) of the function. For now, we only deal with unary functions, + // so we know there must be a single argument. Take its type from the previous expression in the pipe chain. + val zero = defaultZero(valueDt, expr.position) + valueDt = builtinFunctionReturnType(func.name, listOf(zero), program).getOrElse { DataType.UNDEFINED } + } } is Subroutine -> { if(function.parameters.size!=1) diff --git a/compiler/test/TestBuiltinFunctions.kt b/compiler/test/TestBuiltinFunctions.kt new file mode 100644 index 000000000..fdbba1892 --- /dev/null +++ b/compiler/test/TestBuiltinFunctions.kt @@ -0,0 +1,93 @@ +package prog8tests + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import prog8.ast.base.DataType +import prog8.ast.base.NumericDatatypes +import prog8.ast.base.RegisterOrPair +import prog8.compilerinterface.BuiltinFunctions + + +class TestBuiltinFunctions: FunSpec({ + + test("pure func with fixed type") { + val func = BuiltinFunctions.getValue("sin8u") + func.name shouldBe "sin8u" + func.parameters.size shouldBe 1 + func.parameters[0].name shouldBe "angle8" + func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UBYTE) + func.pure shouldBe true + func.hasReturn shouldBe true + func.returnType shouldBe DataType.UBYTE + + val conv = func.callConvention(listOf(DataType.UBYTE)) + conv.params.size shouldBe 1 + conv.params[0].dt shouldBe DataType.UBYTE + conv.params[0].reg shouldBe RegisterOrPair.A + conv.params[0].variable shouldBe false + conv.returns.dt shouldBe DataType.UBYTE + conv.returns.floatFac1 shouldBe false + conv.returns.reg shouldBe RegisterOrPair.A + } + + test("not-pure func with fixed type") { + val func = BuiltinFunctions.getValue("rnd") + func.name shouldBe "rnd" + func.parameters.size shouldBe 0 + func.pure shouldBe false + func.hasReturn shouldBe true + func.returnType shouldBe DataType.UBYTE + + val conv = func.callConvention(emptyList()) + conv.params.size shouldBe 0 + conv.returns.dt shouldBe DataType.UBYTE + conv.returns.floatFac1 shouldBe false + conv.returns.reg shouldBe RegisterOrPair.A + } + + test("func without return type") { + val func = BuiltinFunctions.getValue("poke") + func.name shouldBe "poke" + func.parameters.size shouldBe 2 + func.parameters[0].name shouldBe "address" + func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD) + func.parameters[1].name shouldBe "value" + func.parameters[1].possibleDatatypes shouldBe arrayOf(DataType.UBYTE) + func.pure shouldBe false + func.hasReturn shouldBe false + func.returnType shouldBe null + + val conv = func.callConvention(listOf(DataType.UWORD, DataType.UBYTE)) + conv.params.size shouldBe 2 + conv.params[0].dt shouldBe DataType.UWORD + conv.params[0].reg shouldBe null + conv.params[0].variable shouldBe true + conv.params[1].dt shouldBe DataType.UBYTE + conv.params[1].reg shouldBe null + conv.params[1].variable shouldBe true + conv.returns.dt shouldBe null + conv.returns.floatFac1 shouldBe false + conv.returns.reg shouldBe null + } + + test("func with variable return type") { + val func = BuiltinFunctions.getValue("abs") + func.name shouldBe "abs" + func.parameters.size shouldBe 1 + func.parameters[0].name shouldBe "value" + func.parameters[0].possibleDatatypes.toSet() shouldBe NumericDatatypes.toSet() + func.pure shouldBe true + func.hasReturn shouldBe true + func.returnType shouldBe null + + val conv = func.callConvention(listOf(DataType.UWORD)) + conv.params.size shouldBe 1 + conv.params[0].dt shouldBe DataType.UWORD + conv.params[0].reg shouldBe RegisterOrPair.AY + conv.params[0].variable shouldBe false + conv.returns.dt shouldBe DataType.UWORD + conv.returns.floatFac1 shouldBe false + conv.returns.reg shouldBe RegisterOrPair.AY + } +}) + diff --git a/compiler/test/TestPipes.kt b/compiler/test/TestPipes.kt index aaf5b0357..c8adf80d4 100644 --- a/compiler/test/TestPipes.kt +++ b/compiler/test/TestPipes.kt @@ -4,10 +4,9 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.types.instanceOf -import prog8.ast.expressions.FunctionCallExpression -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.PipeExpression +import prog8.ast.expressions.* import prog8.ast.statements.Assignment +import prog8.ast.statements.FunctionCallStatement import prog8.ast.statements.Pipe import prog8.codegen.target.C64Target import prog8tests.helpers.ErrorReporterForTests @@ -31,7 +30,12 @@ class TestPipes: FunSpec({ 9999 |> addword |> txt.print_uw - + + ; these are optimized into just the function calls: + 9999 |> abs |> txt.print_uw + 9999 |> txt.print_uw + 99 |> abs |> txt.print_ub + 99 |> txt.print_ub } sub addfloat(float fl) -> float { @@ -44,7 +48,7 @@ class TestPipes: FunSpec({ """ val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess() val stmts = result.program.entrypoint.statements - stmts.size shouldBe 3 + stmts.size shouldBe 7 val pipef = stmts[0] as Pipe pipef.expressions.size shouldBe 2 pipef.expressions[0] shouldBe instanceOf() @@ -54,6 +58,15 @@ class TestPipes: FunSpec({ pipew.expressions.size shouldBe 2 pipew.expressions[0] shouldBe instanceOf() pipew.expressions[1] shouldBe instanceOf() + + var stmt = stmts[2] as FunctionCallStatement + stmt.target.nameInSource shouldBe listOf("txt", "print_uw") + stmt = stmts[3] as FunctionCallStatement + stmt.target.nameInSource shouldBe listOf("txt", "print_uw") + stmt = stmts[4] as FunctionCallStatement + stmt.target.nameInSource shouldBe listOf("txt", "print_ub") + stmt = stmts[5] as FunctionCallStatement + stmt.target.nameInSource shouldBe listOf("txt", "print_ub") } test("incorrect type in pipe statement") { @@ -93,7 +106,9 @@ class TestPipes: FunSpec({ uword @shared ww = 9999 |> addword |> addword - + + ubyte @shared cc = 30 |> sin8u |> cos8u ; will be optimized away into a const number + cc = cc |> sin8u |> cos8u } sub addfloat(float fl) -> float { @@ -102,11 +117,14 @@ class TestPipes: FunSpec({ sub addword(uword ww) -> uword { return ww+2222 } - } + sub addbyte(ubyte bb) -> ubyte { + return bb+1 + } + } """ val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess() val stmts = result.program.entrypoint.statements - stmts.size shouldBe 5 + stmts.size shouldBe 8 val assignf = stmts[1] as Assignment val pipef = assignf.value as PipeExpression pipef.expressions.size shouldBe 2 @@ -118,6 +136,16 @@ class TestPipes: FunSpec({ pipew.expressions.size shouldBe 2 pipew.expressions[0] shouldBe instanceOf() pipew.expressions[1] shouldBe instanceOf() + + var assigncc = stmts[5] as Assignment + val value = assigncc.value as NumericLiteral + value.number shouldBe 194.0 + + assigncc = stmts[6] as Assignment + val pipecc = assignw.value as PipeExpression + pipecc.expressions.size shouldBe 2 + pipecc.expressions[0] shouldBe instanceOf() + pipecc.expressions[1] shouldBe instanceOf() } test("incorrect type in pipe expression") { @@ -143,4 +171,50 @@ class TestPipes: FunSpec({ errors.errors.size shouldBe 1 errors.errors[0] shouldContain "incompatible" } + + test("correct pipe statement with builtin expression") { + val text = """ + %import textio + + main { + sub start() { + uword ww = 9999 + ubyte bb = 99 + ww |> abs |> txt.print_uw + bb |> abs |> txt.print_ub + } + } + """ + val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess() + val stmts = result.program.entrypoint.statements + stmts.size shouldBe 7 + val pipef = stmts[4] as Pipe + pipef.expressions.size shouldBe 2 + pipef.expressions[0] shouldBe instanceOf() + pipef.expressions[1] shouldBe instanceOf() + + val pipew = stmts[5] as Pipe + pipew.expressions.size shouldBe 2 + pipew.expressions[0] shouldBe instanceOf() + pipew.expressions[1] shouldBe instanceOf() + } + + test("pipe statement with type errors") { + val text = """ + %import textio + + main { + sub start() { + uword ww = 9999 + 9999 |> abs |> txt.print_ub + ww |> abs |> txt.print_ub + } + } + """ + val errors = ErrorReporterForTests() + compileText(C64Target(), true, text, writeAssembly = true, errors=errors).assertFailure() + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "UWORD incompatible" + errors.errors[1] shouldContain "UWORD incompatible" + } }) diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index c936f0b43..697931b35 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -1098,17 +1098,34 @@ class PipeExpression(val expressions: MutableList, override val posi override fun referencesIdentifier(nameInSource: List) = expressions.any { it.referencesIdentifier(nameInSource) } - override fun inferType(program: Program): InferredTypes.InferredType { - val last = expressions.last() - val type = last.inferType(program) - if(type.isKnown) - return type - val identifier = last as? IdentifierReference + override fun inferType(program: Program): InferredTypes.InferredType = inferType(program, expressions) + + private fun inferType(program: Program, functionNames: List): InferredTypes.InferredType { + val identifier = functionNames.last() as? IdentifierReference if(identifier!=null) { - val call = FunctionCallExpression(identifier, mutableListOf(), identifier.position) - return call.inferType(program) + val target = identifier.targetStatement(program) + when(target) { + is BuiltinFunctionPlaceholder -> { + val typeOfPrev = inferType(program, functionNames.dropLast(1)) + return if(typeOfPrev.isKnown) { + val zero = defaultZero(typeOfPrev.getOr(DataType.UNDEFINED), identifier.position) + val args = mutableListOf(zero) + program.builtinFunctions.returnType(identifier.nameInSource[0], args) + } else { + InferredTypes.InferredType.unknown() + } + } + is Subroutine -> { + val call = FunctionCallExpression(identifier, mutableListOf(), identifier.position) + return call.inferType(program) + } + is VarDecl -> { + return InferredTypes.InferredType.known(target.datatype) + } + else -> return InferredTypes.InferredType.unknown() + } } - return InferredTypes.InferredType.unknown() + return functionNames.last().inferType(program) } override fun replaceChildNode(node: Node, replacement: Node) { diff --git a/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt b/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt index 5ea7f93aa..ddfcf8f18 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt @@ -9,7 +9,7 @@ import kotlin.math.* private typealias ConstExpressionCaller = (args: List, position: Position, program: Program) -> NumericLiteral -class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean) +class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean) class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean) class CallConvention(val params: List, val returns: ReturnConvention) { override fun toString(): String { @@ -35,35 +35,42 @@ class FParam(val name: String, val possibleDatatypes: Array) class FSignature(val name: String, val pure: Boolean, // does it have side effects? val parameters: List, - val known_returntype: DataType?, // specify return type if fixed, otherwise null if it depends on the arguments + val hasReturn: Boolean, // is there a return value at all? + val returnType: DataType?, // specify return type if fixed, otherwise null if it depends on the arguments val constExpressionFunc: ConstExpressionCaller? = null) { + init { + require(hasReturn || returnType==null) { "$name has invalid return spec" } + } + fun callConvention(actualParamTypes: List): CallConvention { - val returns = when(known_returntype) { - DataType.UBYTE, DataType.BYTE -> ReturnConvention(known_returntype, RegisterOrPair.A, false) - DataType.UWORD, DataType.WORD -> ReturnConvention(known_returntype, RegisterOrPair.AY, false) - DataType.FLOAT -> ReturnConvention(known_returntype, null, true) - in PassByReferenceDatatypes -> ReturnConvention(known_returntype!!, RegisterOrPair.AY, false) - else -> { - val paramType = actualParamTypes.first() - if(pure) - // return type depends on arg type - when(paramType) { + val returns: ReturnConvention + if(hasReturn) { + returns = when (returnType) { + DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) + DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) + DataType.FLOAT -> ReturnConvention(returnType, null, true) + in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) + else -> { + // return type depends on arg type + when (val paramType = actualParamTypes.first()) { DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) DataType.FLOAT -> ReturnConvention(paramType, null, true) in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) else -> ReturnConvention(paramType, null, false) } - else { - ReturnConvention(paramType, null, false) } } + } else { + require(returnType==null) + returns = ReturnConvention(null, null, false) } + return when { actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns) actualParamTypes.size==1 -> { - // one parameter? via register/registerpair + // one parameter goes via register/registerpair val paramConv = when(val paramType = actualParamTypes[0]) { DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) @@ -74,7 +81,7 @@ class FSignature(val name: String, CallConvention(listOf(paramConv), returns) } else -> { - // multiple parameters? via variables + // multiple parameters go via variables val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) } CallConvention(paramConvs, returns) } @@ -88,78 +95,78 @@ class FSignature(val name: String, @Suppress("UNUSED_ANONYMOUS_PARAMETER") private val functionSignatures: List = listOf( // this set of function have no return value and operate in-place: - FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), - FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), - FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), - FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), - FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null), - FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null), - FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null), + FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false,null), + FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null), + FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null), + FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null), + FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), false, null), + FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), false, null), + // cmp returns a status in the carry flag, but not a proper return value + FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), false, null), // these few have a return value depending on the argument(s): - FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args - FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args - FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args - FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument - FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length - FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE, ::builtinSizeof), + FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args + FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args + FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args + FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), true, null, ::builtinAbs), // type depends on argument + FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), true, null, ::builtinLen), // type is UBYTE or UWORD depending on actual length + FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), true, DataType.UBYTE, ::builtinSizeof), // normal functions follow: - FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ), - FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, - FSignature("sin8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ), - FSignature("sin8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ), - FSignature("sin16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ), - FSignature("sin16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ), - FSignature("sinr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinSinR8 ), - FSignature("sinr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSinR8u ), - FSignature("sinr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinSinR16 ), - FSignature("sinr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinSinR16u ), - FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) }, - FSignature("cos8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ), - FSignature("cos8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ), - FSignature("cos16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ), - FSignature("cos16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ), - FSignature("cosr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinCosR8 ), - FSignature("cosr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCosR8u ), - FSignature("cosr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinCosR16 ), - FSignature("cosr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinCosR16u ), - FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) }, - FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) }, - FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) }, - FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) }, - FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, - FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) }, - FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) }, - FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) }, - FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, ::round) }, - FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) }, - FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) }, - FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, - FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, - FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, - FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, - FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword), - FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), - FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), - FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), - FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), - FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), - FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), null), - FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), null), - FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), null), - FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), null), - FSignature("rsave" , false, emptyList(), null), - FSignature("rsavex" , false, emptyList(), null), - FSignature("rrestore" , false, emptyList(), null), - FSignature("rrestorex" , false, emptyList(), null), - FSignature("rnd" , false, emptyList(), DataType.UBYTE), - FSignature("rndw" , false, emptyList(), DataType.UWORD), - FSignature("rndf" , false, emptyList(), DataType.FLOAT), - FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), - FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null), - FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), - FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), - - ) + FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), true, DataType.BYTE, ::builtinSgn ), + FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, + FSignature("sin8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinSin8 ), + FSignature("sin8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinSin8u ), + FSignature("sin16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinSin16 ), + FSignature("sin16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinSin16u ), + FSignature("sinr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinSinR8 ), + FSignature("sinr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinSinR8u ), + FSignature("sinr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinSinR16 ), + FSignature("sinr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinSinR16u ), + FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) }, + FSignature("cos8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinCos8 ), + FSignature("cos8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinCos8u ), + FSignature("cos16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinCos16 ), + FSignature("cos16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinCos16u ), + FSignature("cosr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinCosR8 ), + FSignature("cosr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinCosR8u ), + FSignature("cosr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinCosR16 ), + FSignature("cosr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinCosR16u ), + FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) }, + FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) }, + FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) }, + FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) }, + FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, + FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) }, + FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) }, + FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) }, + FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, ::round) }, + FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) }, + FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) }, + FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), true, DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, + FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), true, DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, + FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, + FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, + FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinMkword), + FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), true, DataType.UBYTE), + FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), true, DataType.UWORD), + FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), false,null), + FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), false,null), + FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), false,null), + FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), false, null), + FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), false, null), + FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), false, null), + FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), false, null), + FSignature("rsave" , false, emptyList(), false,null), + FSignature("rsavex" , false, emptyList(), false,null), + FSignature("rrestore" , false, emptyList(), false,null), + FSignature("rrestorex" , false, emptyList(), false,null), + FSignature("rnd" , false, emptyList(), true, DataType.UBYTE), + FSignature("rndw" , false, emptyList(), true, DataType.UWORD), + FSignature("rndf" , false, emptyList(), true, DataType.FLOAT), + FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), true, DataType.UWORD), + FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), false, null), + FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), false, null), + FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), false, null), +) val BuiltinFunctions = functionSignatures.associateBy { it.name } @@ -203,8 +210,10 @@ fun builtinFunctionReturnType(function: String, args: List, program: } val func = BuiltinFunctions.getValue(function) - if(func.known_returntype!=null) - return InferredTypes.knownFor(func.known_returntype) + if(func.returnType!=null) + return InferredTypes.knownFor(func.returnType) + if(!func.hasReturn) + return InferredTypes.InferredType.void() // function has return values, but the return type depends on the arguments return when (function) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index c0a700ffc..b8566d7f1 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -... +- allow the last term in a pipe *statement* to be a variable, rewrite this as var = + Need help with ^^^^^^^^^^^^^^ diff --git a/examples/test.p8 b/examples/test.p8 index 57521f901..0e6824f28 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -2,73 +2,17 @@ main { sub start() { - ubyte xx = 200 - ubyte yy = 100 - uword @shared cc + ubyte xx = 30 + ubyte cc - ubyte[200] array +; cc = 30 |> sin8u |> cos8u +; txt.print_ub(cc) +; txt.nl() + cc = xx |> sin8u |> cos8u + txt.print_ub(cc) + txt.nl() - cc=array[xx+yy+10] - - cc = xx-yy>10 - - uword qq = 100 - cmp(qq,xx) - - simple(xx+yy) - void routine(xx+yy, yy+99, 99, true) - uword @shared zz = mkword(xx+yy,yy+99) - zz = routine(1000+xx+yy, yy+99, 55, true) - - - txt.print("1300 199 55 1 ?:\n") - txt.print_uw(r_arg) - txt.spc() - txt.print_ub(r_arg2) - txt.spc() - txt.print_ub(r_arg3) - txt.spc() - txt.print_ub(r_arg4) - txt.spc() - - memory.mem() repeat { } } - - uword @shared r_arg - ubyte @shared r_arg2 - ubyte @shared r_arg3 - ubyte @shared r_arg4 - - asmsub simple(ubyte arg @A) { - %asm {{ - rts - }} - } - - asmsub routine(uword arg @AY, ubyte arg2 @X, ubyte arg3 @R0, ubyte arg4 @Pc) -> ubyte @A { - %asm {{ - pha - lda #0 - adc #0 - sta r_arg4 - pla - sta r_arg - sty r_arg+1 - stx r_arg2 - lda cx16.r0 - sta r_arg3 - lda #99 - rts - }} - } -} - -memory { - sub mem() { - %asm {{ - nop - }} - } }