mirror of
https://github.com/irmen/prog8.git
synced 2024-07-26 05:29:15 +00:00
cleaned up various ast checks/mutations
This commit is contained in:
parent
131fe670a4
commit
f2bb238e9b
@ -4,7 +4,7 @@ import prog8.ast.Module
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.target.AsmVariablePreparer
|
import prog8.compiler.target.AsmVariableAndReturnsPreparer
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
||||||
|
|
||||||
|
|
||||||
@ -13,10 +13,10 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: Err
|
|||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.prepareAsmVariables(errors: ErrorReporter) {
|
internal fun Program.prepareAsmVariablesAndReturns(errors: ErrorReporter) {
|
||||||
val mover = AsmVariablePreparer(this, errors)
|
val fixer = AsmVariableAndReturnsPreparer(this, errors)
|
||||||
mover.visit(this)
|
fixer.visit(this)
|
||||||
mover.applyModifications()
|
fixer.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.reorderStatements() {
|
internal fun Program.reorderStatements() {
|
||||||
|
@ -413,9 +413,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
|
|
||||||
if(targetDt in IterableDatatypes)
|
// target type check is already done at the Assignment:
|
||||||
errors.err("cannot assign to a string or array type", assignTarget.position)
|
// val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
|
||||||
|
// if(targetDt in IterableDatatypes)
|
||||||
|
// errors.err("cannot assign to a string or array type", assignTarget.position)
|
||||||
|
|
||||||
if (assignment is Assignment) {
|
if (assignment is Assignment) {
|
||||||
|
|
||||||
@ -1054,11 +1056,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
for((index, stmt) in statements.withIndex()) {
|
for((index, stmt) in statements.withIndex()) {
|
||||||
if(stmt is FunctionCallStatement
|
if(stmt is FunctionCallStatement
|
||||||
&& stmt.target.nameInSource.last()=="exit"
|
&& stmt.target.nameInSource.last()=="exit"
|
||||||
&& index < statements.lastIndex)
|
&& index < statements.lastIndex) {
|
||||||
errors.warn("unreachable code, exit call above never returns", statements[index+1].position)
|
println("STMT AFTER EXIT ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt
|
||||||
|
errors.warn("unreachable code, exit call above never returns", statements[index + 1].position)
|
||||||
|
}
|
||||||
|
|
||||||
if(stmt is Return && index < statements.lastIndex)
|
if(stmt is Return && index < statements.lastIndex) {
|
||||||
errors.warn("unreachable code, return statement above", statements[index+1].position)
|
println("STMT AFTER RETURN ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt
|
||||||
|
errors.warn("unreachable code, return statement above", statements[index + 1].position)
|
||||||
|
}
|
||||||
|
|
||||||
stmt.accept(this)
|
stmt.accept(this)
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,9 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
|
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||||
|
|
||||||
private val addReturns = mutableListOf<Pair<INameScope, Int>>()
|
|
||||||
private val addVardecls = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
private val addVardecls = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
addReturns.clear()
|
|
||||||
addVardecls.clear()
|
addVardecls.clear()
|
||||||
super.visit(module)
|
super.visit(module)
|
||||||
|
|
||||||
@ -59,13 +57,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
module.statements.removeAll(directives)
|
module.statements.removeAll(directives)
|
||||||
module.statements.addAll(0, directives)
|
module.statements.addAll(0, directives)
|
||||||
|
|
||||||
// add new statements
|
|
||||||
for(pos in addReturns) {
|
|
||||||
val returnStmt = Return(null, pos.first.position)
|
|
||||||
returnStmt.linkParents(pos.first as Node)
|
|
||||||
pos.first.statements.add(pos.second, returnStmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
for((where, decls) in addVardecls) {
|
for((where, decls) in addVardecls) {
|
||||||
where.statements.addAll(0, decls)
|
where.statements.addAll(0, decls)
|
||||||
decls.forEach { it.linkParents(where as Node) }
|
decls.forEach { it.linkParents(where as Node) }
|
||||||
@ -93,23 +84,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure there is a 'return' in front of the first subroutine
|
|
||||||
// (if it isn't the first statement in the block itself, and isn't the program's entrypoint)
|
|
||||||
if(numSubroutinesAtEnd>0 && block.statements.size > (numSubroutinesAtEnd+1)) {
|
|
||||||
val firstSub = block.statements[block.statements.size - numSubroutinesAtEnd] as Subroutine
|
|
||||||
if(firstSub.name != "start" && block.name != "main") {
|
|
||||||
val stmtBeforeFirstSub = block.statements[block.statements.size - numSubroutinesAtEnd - 1]
|
|
||||||
if (stmtBeforeFirstSub !is Return
|
|
||||||
&& stmtBeforeFirstSub !is Jump
|
|
||||||
&& stmtBeforeFirstSub !is Subroutine
|
|
||||||
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
|
|
||||||
val ret = Return(null, stmtBeforeFirstSub.position)
|
|
||||||
ret.linkParents(block)
|
|
||||||
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val varDecls = block.statements.filterIsInstance<VarDecl>()
|
val varDecls = block.statements.filterIsInstance<VarDecl>()
|
||||||
block.statements.removeAll(varDecls)
|
block.statements.removeAll(varDecls)
|
||||||
block.statements.addAll(0, varDecls)
|
block.statements.addAll(0, varDecls)
|
||||||
@ -124,19 +98,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
override fun visit(subroutine: Subroutine): Statement {
|
override fun visit(subroutine: Subroutine): Statement {
|
||||||
super.visit(subroutine)
|
super.visit(subroutine)
|
||||||
|
|
||||||
val scope = subroutine.definingScope()
|
|
||||||
if(scope is Subroutine) {
|
|
||||||
for(stmt in scope.statements.withIndex()) {
|
|
||||||
if(stmt.index>0 && stmt.value===subroutine) {
|
|
||||||
val precedingStmt = scope.statements[stmt.index-1]
|
|
||||||
if(precedingStmt !is Jump && precedingStmt !is Subroutine) {
|
|
||||||
// insert a return statement before a nested subroutine, to avoid falling trough inside the subroutine
|
|
||||||
addReturns.add(Pair(scope, stmt.index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
||||||
subroutine.statements.removeAll(varDecls)
|
subroutine.statements.removeAll(varDecls)
|
||||||
subroutine.statements.addAll(0, varDecls)
|
subroutine.statements.addAll(0, varDecls)
|
||||||
@ -144,18 +105,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
subroutine.statements.removeAll(directives)
|
subroutine.statements.removeAll(directives)
|
||||||
subroutine.statements.addAll(0, directives)
|
subroutine.statements.addAll(0, directives)
|
||||||
|
|
||||||
if(subroutine.returntypes.isEmpty()) {
|
|
||||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
|
||||||
// and if an assembly block doesn't contain a rts/rti
|
|
||||||
if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) {
|
|
||||||
if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) {
|
|
||||||
val returnStmt = Return(null, subroutine.position)
|
|
||||||
returnStmt.linkParents(subroutine)
|
|
||||||
subroutine.statements.add(returnStmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,11 +45,10 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||||
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||||
if (valuetype != targettype) {
|
if (valuetype != targettype) {
|
||||||
if (valuetype isAssignableTo targettype)
|
return listOf(IAstModification.ReplaceNode(
|
||||||
return listOf(IAstModification.ReplaceNode(
|
assignment.value,
|
||||||
assignment.value,
|
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
||||||
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
assignment))
|
||||||
assignment))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
@ -40,7 +40,7 @@ fun compileProgram(filepath: Path,
|
|||||||
if (optimize)
|
if (optimize)
|
||||||
optimizeAst(programAst, errors)
|
optimizeAst(programAst, errors)
|
||||||
postprocessAst(programAst, errors, compilationOptions)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
// printAst(programAst)
|
printAst(programAst) // TODO
|
||||||
if(writeAssembly)
|
if(writeAssembly)
|
||||||
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir:
|
|||||||
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
||||||
// asm generation directly from the Ast,
|
// asm generation directly from the Ast,
|
||||||
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
||||||
programAst.prepareAsmVariables(errors)
|
programAst.prepareAsmVariablesAndReturns(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
val assembly = CompilationTarget.asmGenerator(
|
val assembly = CompilationTarget.asmGenerator(
|
||||||
programAst,
|
programAst,
|
||||||
|
@ -11,7 +11,7 @@ import prog8.ast.processing.IAstModification
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstWalker() {
|
class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorReporter): AstWalker() {
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
if(decl.value==null && decl.type==VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
if(decl.value==null && decl.type==VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
@ -49,4 +49,9 @@ class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstW
|
|||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
TODO("insert Return statements at the required places such as at the end of a subroutine if they're missing")
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
}
|
}
|
@ -199,7 +199,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
">>" -> {
|
">>" -> {
|
||||||
// bit-shifts are always by a constant number (for now)
|
// bit-shifts are always by a constant number (for now)
|
||||||
// TODO for everything except UBYTE, if shifting > 2 bits, use a subroutine
|
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
@ -239,7 +238,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
// bit-shifts are always by a constant number (for now)
|
// bit-shifts are always by a constant number (for now)
|
||||||
// TODO for the word types, if shifting > 3 bits, use a subroutine
|
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
if (leftDt in ByteDatatypes) {
|
if (leftDt in ByteDatatypes) {
|
||||||
|
@ -8,7 +8,6 @@ import prog8.ast.processing.IAstModifyingVisitor
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor {
|
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor {
|
||||||
@ -620,74 +619,4 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: type casts are already done elsewhere, remove all this?:
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
|
||||||
super.visit(assignment)
|
|
||||||
val lv = assignment.value as? NumericLiteralValue
|
|
||||||
if(lv!=null) {
|
|
||||||
// see if we can promote/convert a literal value to the required datatype
|
|
||||||
val idt = assignment.target.inferType(program, assignment)
|
|
||||||
if(!idt.isKnown)
|
|
||||||
return assignment
|
|
||||||
when(idt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UWORD -> {
|
|
||||||
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
|
||||||
if(lv.type== DataType.UBYTE)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.BYTE && lv.number.toInt()>=0)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.WORD && lv.number.toInt()>=0)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d>=0 && d<=65535)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, floor(d).toInt(), lv.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
|
|
||||||
if(lv.type== DataType.UWORD && lv.number.toInt() <= 255)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.BYTE && lv.number.toInt() >=0)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d >=0 && d<=255)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UBYTE, floor(d).toShort(), lv.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
|
|
||||||
if(lv.type== DataType.UWORD && lv.number.toInt() <= 127)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.UBYTE && lv.number.toInt() <= 127)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d>=0 && d<=127)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
|
|
||||||
if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.UWORD && lv.number.toInt() <= 32767)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d>=-32768 && d<=32767)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
assignment.value = NumericLiteralValue(DataType.FLOAT, lv.number.toDouble(), lv.position)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,6 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return NopStatement.insteadOf(subroutine)
|
return NopStatement.insteadOf(subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStatements(subroutine.statements)
|
|
||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,7 +547,6 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(linesToRemove.isNotEmpty()) {
|
if(linesToRemove.isNotEmpty()) {
|
||||||
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
||||||
}
|
}
|
||||||
visitStatements(scope.statements)
|
|
||||||
return super.visit(scope)
|
return super.visit(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,21 +559,6 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
return super.visit(label)
|
return super.visit(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun visitStatements(statements: MutableList<Statement>) {
|
|
||||||
// TODO remove all unreachable code statements after call to exit() or a return
|
|
||||||
// this is not yet correct because we still have nested subroutines
|
|
||||||
// val exitCallIndex = statements.indexOfFirst { it is FunctionCallStatement && it.target.nameInSource.last()=="exit" }
|
|
||||||
// if(exitCallIndex>=0) {
|
|
||||||
// while(exitCallIndex < statements.lastIndex) {
|
|
||||||
// val stmt = statements[exitCallIndex+1]
|
|
||||||
// println("after exit() removing: $stmt")
|
|
||||||
// statements.removeAt(exitCallIndex+1)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,9 +26,6 @@ Memory Block Operations integrated in language?
|
|||||||
|
|
||||||
array/string memory block operations?
|
array/string memory block operations?
|
||||||
|
|
||||||
- vector inc/dec/add/sub/mul/div...? (on array or string):
|
|
||||||
``arrayvar++ / arrayvar-- / arrayvar += 2 / arrayvar -= 2 / arrayvar *= 3 / arrayvar /= 3``
|
|
||||||
|
|
||||||
- array operations
|
- array operations
|
||||||
copy (from another array with the same length), shift-N(left,right), rotate-N(left,right)
|
copy (from another array with the same length), shift-N(left,right), rotate-N(left,right)
|
||||||
clear (set whole array to the given value, default 0)
|
clear (set whole array to the given value, default 0)
|
||||||
|
@ -6,6 +6,20 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
c64scr.print("ubyte shift left\n")
|
byte v1
|
||||||
|
byte v2
|
||||||
|
|
||||||
|
bla()
|
||||||
|
exit(4)
|
||||||
|
v1 = 100
|
||||||
|
v2 = 127
|
||||||
|
A=5
|
||||||
|
return
|
||||||
|
|
||||||
|
sub bla () {
|
||||||
|
A=99
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user