mirror of
https://github.com/irmen/prog8.git
synced 2024-11-18 19:12:44 +00:00
replace subroutine calls (statement) by GoSub
This commit is contained in:
parent
17d403d812
commit
110e047681
@ -545,6 +545,7 @@ class AsmGen(private val program: Program,
|
||||
fun asmVariableName(identifier: IdentifierReference) =
|
||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||
|
||||
// TODO use INamedStatement.scopedName
|
||||
private fun getScopedSymbolNameForTarget(actualName: String, target: Statement): MutableList<String> {
|
||||
val scopedName = mutableListOf(actualName)
|
||||
var node: Node = target
|
||||
@ -857,8 +858,8 @@ class AsmGen(private val program: Program,
|
||||
internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack, resultRegister)
|
||||
|
||||
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCall)
|
||||
internal fun translateFunctionCall(functionCall: FunctionCall, isExpression: Boolean) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCall, isExpression)
|
||||
|
||||
internal fun saveXbeforeCall(functionCall: IFunctionCall) =
|
||||
functioncallAsmGen.saveXbeforeCall(functionCall)
|
||||
|
@ -53,7 +53,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
} else {
|
||||
sub as Subroutine
|
||||
asmgen.saveXbeforeCall(call)
|
||||
asmgen.translateFunctionCall(call)
|
||||
asmgen.translateFunctionCall(call, true)
|
||||
if(sub.regXasResult()) {
|
||||
// store the return value in X somewhere that we can acces again below
|
||||
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
||||
|
@ -5,10 +5,7 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.InlineAssembly
|
||||
import prog8.ast.statements.RegisterOrStatusflag
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.SubroutineParameter
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.AssemblyError
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
||||
@ -21,7 +18,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
|
||||
saveXbeforeCall(stmt)
|
||||
translateFunctionCall(stmt)
|
||||
translateFunctionCall(stmt, false)
|
||||
restoreXafterCall(stmt)
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
@ -48,26 +45,30 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
||||
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
|
||||
// Output only the code to set up the parameters and perform the actual call
|
||||
// NOTE: does NOT output the code to deal with the result values!
|
||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||
|
||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
val subName = asmgen.asmSymbolName(stmt.target)
|
||||
if(stmt.args.isNotEmpty()) {
|
||||
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
|
||||
|
||||
if(!isExpression && !sub.isAsmSubroutine) {
|
||||
throw AssemblyError("functioncall statments to non-asmsub should have been replaced by GoSub $call")
|
||||
}
|
||||
|
||||
if(call.args.isNotEmpty()) {
|
||||
|
||||
if(sub.asmParameterRegisters.isEmpty()) {
|
||||
// via variables
|
||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||
for(arg in sub.parameters.withIndex().zip(call.args)) {
|
||||
argumentViaVariable(sub, arg.first, arg.second)
|
||||
}
|
||||
} else {
|
||||
require(sub.isAsmSubroutine)
|
||||
if(sub.parameters.size==1) {
|
||||
// just a single parameter, no risk of clobbering registers
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0])
|
||||
} else {
|
||||
|
||||
fun isNoClobberRisk(expr: Expression): Boolean {
|
||||
@ -89,10 +90,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
}
|
||||
|
||||
when {
|
||||
stmt.args.all {isNoClobberRisk(it)} -> {
|
||||
call.args.all {isNoClobberRisk(it)} -> {
|
||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
||||
// register assignment order: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag.
|
||||
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||
val argsInfo = sub.parameters.withIndex().zip(call.args).zip(sub.asmParameterRegisters)
|
||||
val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||
val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
|
||||
for(arg in cx16virtualRegs)
|
||||
@ -104,13 +105,14 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
}
|
||||
else -> {
|
||||
// Risk of clobbering due to complex expression args. Evaluate first, then assign registers.
|
||||
registerArgsViaStackEvaluation(stmt, sub)
|
||||
registerArgsViaStackEvaluation(call, sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val subName = asmgen.asmSymbolName(call.target)
|
||||
if(!sub.inline || !asmgen.options.optimize) {
|
||||
asmgen.out(" jsr $subName")
|
||||
} else {
|
||||
@ -267,7 +269,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name)
|
||||
val varName = asmgen.asmVariableName(sub.scopedName + parameter.value.name)
|
||||
asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub)
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
when (val sub = value.target.targetStatement(program)) {
|
||||
is Subroutine -> {
|
||||
asmgen.saveXbeforeCall(value)
|
||||
asmgen.translateFunctionCall(value)
|
||||
asmgen.translateFunctionCall(value, true)
|
||||
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
|
||||
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
|
||||
when (returnValue.first) {
|
||||
|
@ -57,6 +57,7 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
// TODO use functionCallStatement.target.targetStatement() is BuiltinFunctionStatementPlaceholder ?
|
||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in functions.names) {
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in functions.purefunctionNames) {
|
||||
|
@ -69,35 +69,10 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
||||
}
|
||||
|
||||
private val subroutineVariables = mutableListOf<Pair<String, VarDecl>>()
|
||||
private val addedIfConditionVars = mutableSetOf<Pair<Subroutine, String>>()
|
||||
|
||||
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
subroutineVariables.clear()
|
||||
addedIfConditionVars.clear()
|
||||
|
||||
if(!subroutine.isAsmSubroutine) {
|
||||
// change 'str' parameters into 'uword' (just treat it as an address)
|
||||
val stringParams = subroutine.parameters.filter { it.type==DataType.STR }
|
||||
val parameterChanges = stringParams.map {
|
||||
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
||||
IAstModification.ReplaceNode(it, uwordParam, subroutine)
|
||||
}
|
||||
|
||||
val stringParamsByNames = stringParams.associateBy { it.name }
|
||||
val varsChanges =
|
||||
if(stringParamsByNames.isNotEmpty()) {
|
||||
subroutine.statements
|
||||
.filterIsInstance<VarDecl>()
|
||||
.filter { it.subroutineParameter!=null && it.name in stringParamsByNames }
|
||||
.map {
|
||||
val newvar = VarDecl(it.type, DataType.UWORD, it.zeropage, null, it.name, null, false, true, it.sharedWithAsm, stringParamsByNames.getValue(it.name), it.position)
|
||||
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||
}
|
||||
}
|
||||
else emptyList()
|
||||
|
||||
return parameterChanges + varsChanges
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -418,11 +418,10 @@ internal class AstChecker(private val program: Program,
|
||||
val stmt = (assignment.value as FunctionCall).target.targetStatement(program)
|
||||
if (stmt is Subroutine) {
|
||||
val idt = assignment.target.inferType(program)
|
||||
if(!idt.isKnown) {
|
||||
errors.err("return type mismatch", assignment.value.position)
|
||||
}
|
||||
if(!idt.isKnown)
|
||||
throw FatalAstException("assignment target invalid dt")
|
||||
if(stmt.returntypes.isEmpty() || (stmt.returntypes.size == 1 && stmt.returntypes.single() isNotAssignableTo idt.getOr(DataType.BYTE))) {
|
||||
errors.err("return type mismatch", assignment.value.position)
|
||||
errors.err("return type mismatch: ${stmt.returntypes.single()} expected $idt", assignment.value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -977,21 +976,9 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||
if(targetStatement!=null)
|
||||
if(targetStatement!=null) {
|
||||
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
||||
if (!functionCallStatement.void) {
|
||||
// check for unused return values
|
||||
if (targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
|
||||
if(targetStatement.returntypes.size==1)
|
||||
errors.warn("result value of subroutine call is discarded (use void?)", functionCallStatement.position)
|
||||
else
|
||||
errors.warn("result values of subroutine call are discarded (use void?)", functionCallStatement.position)
|
||||
}
|
||||
else if(targetStatement is BuiltinFunctionStatementPlaceholder) {
|
||||
val rt = builtinFunctionReturnType(targetStatement.name, functionCallStatement.args, program)
|
||||
if(rt.isKnown)
|
||||
errors.warn("result value of a function call is discarded (use void?)", functionCallStatement.position)
|
||||
}
|
||||
checkUnusedReturnValues(functionCallStatement, targetStatement, program, errors)
|
||||
}
|
||||
|
||||
if(functionCallStatement.target.nameInSource.last() == "sort") {
|
||||
@ -1402,3 +1389,19 @@ internal class AstChecker(private val program: Program,
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, program: Program, errors: IErrorReporter) {
|
||||
if (!call.void) {
|
||||
// check for unused return values
|
||||
if (target is Subroutine && target.returntypes.isNotEmpty()) {
|
||||
if (target.returntypes.size == 1)
|
||||
errors.warn("result value of subroutine call is discarded (use void?)", call.position)
|
||||
else
|
||||
errors.warn("result values of subroutine call are discarded (use void?)", call.position)
|
||||
} else if (target is BuiltinFunctionStatementPlaceholder) {
|
||||
val rt = builtinFunctionReturnType(target.name, call.args, program)
|
||||
if (rt.isKnown)
|
||||
errors.warn("result value of a function call is discarded (use void?)", call.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
||||
// - sorts the choices in when statement.
|
||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc.).
|
||||
// - replace subroutine calls (statement) by just assigning the arguments to the parameters and then a GoSub to the routine.
|
||||
|
||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||
|
||||
@ -122,6 +123,30 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
subs.map { IAstModification.InsertLast(it, subroutine) }
|
||||
}
|
||||
|
||||
if(!subroutine.isAsmSubroutine) {
|
||||
// change 'str' parameters into 'uword' (just treat it as an address)
|
||||
val stringParams = subroutine.parameters.filter { it.type==DataType.STR }
|
||||
val parameterChanges = stringParams.map {
|
||||
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
||||
IAstModification.ReplaceNode(it, uwordParam, subroutine)
|
||||
}
|
||||
|
||||
val stringParamsByNames = stringParams.associateBy { it.name }
|
||||
val varsChanges =
|
||||
if(stringParamsByNames.isNotEmpty()) {
|
||||
subroutine.statements
|
||||
.filterIsInstance<VarDecl>()
|
||||
.filter { it.subroutineParameter!=null && it.name in stringParamsByNames }
|
||||
.map {
|
||||
val newvar = VarDecl(it.type, DataType.UWORD, it.zeropage, null, it.name, null, false, true, it.sharedWithAsm, stringParamsByNames.getValue(it.name), it.position)
|
||||
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||
}
|
||||
}
|
||||
else emptyList()
|
||||
|
||||
return parameterChanges + varsChanges
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@ -333,4 +358,34 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent))
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
val function = functionCallStatement.target.targetStatement(program)!!
|
||||
checkUnusedReturnValues(functionCallStatement, function, program, errors)
|
||||
|
||||
if(function is Subroutine) {
|
||||
if(function.isAsmSubroutine)
|
||||
return noModifications // TODO new logic for passing arguments to asmsub
|
||||
|
||||
// regular subroutine call: replace the call with assigning the params directly + actual call with a GoSub
|
||||
require(function.asmParameterRegisters.isEmpty())
|
||||
val assignParams =
|
||||
function.parameters.zip(functionCallStatement.args).map {
|
||||
var argumentValue = it.second
|
||||
val paramIdentifier = IdentifierReference(function.scopedName + it.first.name, argumentValue.position)
|
||||
val argDt = argumentValue.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
||||
if(argDt in ArrayDatatypes) {
|
||||
// pass the address of the array instead
|
||||
argumentValue = AddressOf(argumentValue as IdentifierReference, argumentValue.position)
|
||||
}
|
||||
Assignment(AssignTarget(paramIdentifier, null, null, argumentValue.position), argumentValue, argumentValue.position)
|
||||
}
|
||||
return assignParams.map { IAstModification.InsertBefore(functionCallStatement, it, parent as IStatementContainer) } +
|
||||
IAstModification.ReplaceNode(
|
||||
functionCallStatement as Node,
|
||||
GoSub(null, functionCallStatement.target, null, (functionCallStatement as Node).position),
|
||||
parent)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
@ -50,17 +50,18 @@ class TestSubroutines: FunSpec({
|
||||
asmfunc.parameters.single().type shouldBe DataType.STR
|
||||
asmfunc.statements.isEmpty() shouldBe true
|
||||
func.isAsmSubroutine shouldBe false
|
||||
func.parameters.single().type shouldBe DataType.STR
|
||||
withClue("str param for normal subroutine should be changed into UWORD") {
|
||||
func.parameters.single().type shouldBe DataType.UWORD
|
||||
func.statements.size shouldBe 4
|
||||
val paramvar = func.statements[0] as VarDecl
|
||||
paramvar.name shouldBe "thing"
|
||||
paramvar.datatype shouldBe DataType.STR
|
||||
paramvar.datatype shouldBe DataType.UWORD
|
||||
}
|
||||
val assign = func.statements[2] as Assignment
|
||||
assign.target.identifier!!.nameInSource shouldBe listOf("t2")
|
||||
withClue("str param in function body should not be transformed by normal compiler steps") {
|
||||
assign.value shouldBe instanceOf<TypecastExpression>()
|
||||
withClue("str param in function body should have been transformed into just uword assignment") {
|
||||
assign.value shouldBe instanceOf<IdentifierReference>()
|
||||
}
|
||||
(assign.value as TypecastExpression).type shouldBe DataType.UWORD
|
||||
val call = func.statements[3] as FunctionCallStatement
|
||||
call.target.nameInSource.single() shouldBe "asmfunc"
|
||||
withClue("str param in function body should not be transformed by normal compiler steps") {
|
||||
|
@ -29,7 +29,7 @@ enum class DataType {
|
||||
UWORD -> targetType.oneOf(UWORD, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, FLOAT)
|
||||
FLOAT -> targetType == FLOAT
|
||||
STR -> targetType == STR
|
||||
STR -> targetType.oneOf(STR, UWORD)
|
||||
in ArrayDatatypes -> targetType == this
|
||||
else -> false
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
||||
val target = arrayvar.targetStatement(program)
|
||||
if (target is VarDecl) {
|
||||
return when (target.datatype) {
|
||||
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||
DataType.STR, DataType.UWORD -> InferredTypes.knownFor(DataType.UBYTE)
|
||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayToElementTypes.getValue(target.datatype))
|
||||
else -> InferredTypes.unknown()
|
||||
}
|
||||
|
@ -9,6 +9,19 @@ import prog8.ast.walk.IAstVisitor
|
||||
|
||||
interface INamedStatement {
|
||||
val name: String
|
||||
|
||||
val scopedName: List<String>
|
||||
get() {
|
||||
val scopedName = mutableListOf(name)
|
||||
var node: Node = this as Node
|
||||
while (node !is Block) {
|
||||
node = node.parent
|
||||
if(node is INameScope) {
|
||||
scopedName.add(0, node.name)
|
||||
}
|
||||
}
|
||||
return scopedName
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Statement : Node {
|
||||
@ -16,6 +29,7 @@ sealed class Statement : Node {
|
||||
abstract fun accept(visitor: IAstVisitor)
|
||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||
|
||||
@Deprecated("get rid of this in favor of INamedStatement.scopedName") // TODO
|
||||
fun makeScopedName(name: String): String {
|
||||
// easy way out is to always return the full scoped name.
|
||||
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||
@ -34,6 +48,7 @@ sealed class Statement : Node {
|
||||
return scope.joinToString(".")
|
||||
}
|
||||
|
||||
|
||||
fun nextSibling(): Statement? {
|
||||
val statements = (parent as? IStatementContainer)?.statements ?: return null
|
||||
val nextIdx = statements.indexOfFirst { it===this } + 1
|
||||
@ -664,7 +679,6 @@ class Subroutine(override val name: String,
|
||||
|
||||
override lateinit var parent: Node
|
||||
val asmGenInfo = AsmGenInfo()
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine")
|
||||
|
||||
@ -751,6 +765,7 @@ open class SubroutineParameter(val name: String,
|
||||
}
|
||||
|
||||
override fun copy() = SubroutineParameter(name, type, position)
|
||||
override fun toString() = "Param($type:$name)"
|
||||
}
|
||||
|
||||
class IfStatement(var condition: Expression,
|
||||
|
@ -3,12 +3,16 @@ TODO
|
||||
|
||||
For next compiler release (7.4)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Use GoSub to call subroutines (not in expressions?):
|
||||
Use GoSub to call subroutines (statements):
|
||||
- [DONE] allow separate assigns to subroutine's parameter variables / registers
|
||||
- for asmsubs: implement asmgen for assigning to asm parameter (register)
|
||||
- for asmsubs: implement asmgen for reading asm parameter (register)
|
||||
- replace subroutine param load code with just the right order of those assigns
|
||||
- finally replace the actual call with a GoSub
|
||||
- turn a regular subroutine call into assignments to the parameters + GoSub (take code from gosub branch)
|
||||
|
||||
Function call expression:
|
||||
- move args to assignments to params
|
||||
- add tempvar immediately in front of expression with the fuction call
|
||||
- replace the function call in the expression with the tempvar
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
@ -1,26 +1,46 @@
|
||||
%import textio
|
||||
%import conv
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
ubyte @shared xx
|
||||
main.routine.r1arg = 20
|
||||
; main.routine2.r2arg = 20 ; TODO asmgen
|
||||
|
||||
xx = main.routine.r1arg
|
||||
xx++
|
||||
;xx = main.routine2.r2arg ; TODO asmgen
|
||||
uword xx
|
||||
xx = random_name()
|
||||
; concat_string(random_name())
|
||||
|
||||
; ubyte xx=20
|
||||
; ubyte yy=10
|
||||
;
|
||||
; routine(33)
|
||||
; txt.setcc(xx+1, yy+3, 81, 7)
|
||||
; txt.setcc(xx+2, yy+2, 81, 7)
|
||||
; txt.setcc(xx+3, yy+1, 81, 7)
|
||||
;
|
||||
; ; TODO test new param load with subroutine call in expression:
|
||||
; ; yy=routine(33)
|
||||
;
|
||||
; main.routine.r1arg = 20
|
||||
; ; main.routine2.r2arg = 20 ; TODO asmgen
|
||||
;
|
||||
; xx = main.routine.r1arg
|
||||
; xx++
|
||||
; ;xx = main.routine2.r2arg ; TODO asmgen
|
||||
; ;xx++
|
||||
|
||||
printstuff("hello")
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
||||
sub printstuff(str addr) {
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
return name
|
||||
}
|
||||
sub routine(ubyte r1arg) {
|
||||
|
||||
sub routine(ubyte r1arg) -> ubyte {
|
||||
r1arg++
|
||||
return r1arg
|
||||
}
|
||||
|
||||
asmsub routine2(ubyte r2arg @ A) {
|
||||
|
Loading…
Reference in New Issue
Block a user