Compare commits

..

8 Commits
v2.4 ... v3.0

Author SHA1 Message Date
b37231d0f5 version 3.0 2020-07-26 01:33:02 +02:00
3c55719bf1 finalize repeat asmgen 2020-07-26 01:32:27 +02:00
af8279a9b9 empty for loops are removed 2020-07-25 22:54:50 +02:00
c38508c262 introduced repeat loop. repeat-until changed to do-util.
forever loop is gone (use repeat without iteration count).
struct literal is now same as array literal [...] to avoid parsing ambiguity with scope blocks.
2020-07-25 16:56:34 +02:00
b0e8738ab8 remove unused c64 resources 2020-07-25 14:47:31 +02:00
cae480768e version is work in progress 2020-07-25 14:45:06 +02:00
a70276c190 use indexOfFirst. Also avoid initializing a for loop variable twice in a row. 2020-07-25 14:44:24 +02:00
0c461ffe2e removed Register expression (directly accessing cpu register) 2020-07-25 14:14:24 +02:00
78 changed files with 622 additions and 1853 deletions

View File

@ -18,7 +18,7 @@ which aims to provide many conveniences over raw assembly code (even when using
- modularity, symbol scoping, subroutines - modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- automatic variable allocations, automatic string and array variables and string sharing - automatic variable allocations, automatic string and array variables and string sharing
- subroutines with a input- and output parameter signature - subroutines with an input- and output parameter signature
- constant folding in expressions - constant folding in expressions
- conditional branches - conditional branches
- 'when' statement to provide a concise jump table alternative to if/elseif chains - 'when' statement to provide a concise jump table alternative to if/elseif chains

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -1 +1 @@
2.4 3.0

View File

@ -310,10 +310,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
override fun visit(forLoop: ForLoop) { override fun visit(forLoop: ForLoop) {
output("for ") output("for ")
if(forLoop.loopRegister!=null) forLoop.loopVar.accept(this)
output(forLoop.loopRegister.toString())
else
forLoop.loopVar!!.accept(this)
output(" in ") output(" in ")
forLoop.iterable.accept(this) forLoop.iterable.accept(this)
output(" ") output(" ")
@ -327,16 +324,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
whileLoop.body.accept(this) whileLoop.body.accept(this)
} }
override fun visit(foreverLoop: ForeverLoop) {
output("forever ")
foreverLoop.body.accept(this)
}
override fun visit(repeatLoop: RepeatLoop) { override fun visit(repeatLoop: RepeatLoop) {
output("repeat ") output("repeat ")
repeatLoop.iterations?.accept(this)
output(" ")
repeatLoop.body.accept(this) repeatLoop.body.accept(this)
}
override fun visit(untilLoop: UntilLoop) {
output("do ")
untilLoop.body.accept(this)
output(" until ") output(" until ")
repeatLoop.untilCondition.accept(this) untilLoop.untilCondition.accept(this)
} }
override fun visit(returnStmt: Return) { override fun visit(returnStmt: Return) {
@ -352,12 +351,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
} }
override fun visit(assignTarget: AssignTarget) { override fun visit(assignTarget: AssignTarget) {
if(assignTarget.register!=null)
output(assignTarget.register.toString())
else {
assignTarget.memoryAddress?.accept(this) assignTarget.memoryAddress?.accept(this)
assignTarget.identifier?.accept(this) assignTarget.identifier?.accept(this)
}
assignTarget.arrayindexed?.accept(this) assignTarget.arrayindexed?.accept(this)
} }
@ -398,10 +393,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputlni("}}") outputlni("}}")
} }
override fun visit(registerExpr: RegisterExpr) {
output(registerExpr.register.toString())
}
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) { override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
output(builtinFunctionStatementPlaceholder.name) output(builtinFunctionStatementPlaceholder.name)
} }
@ -436,10 +427,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputln("") outputln("")
} }
override fun visit(structLv: StructLiteralValue) {
outputListMembers(structLv.values.asSequence(), '{', '}')
}
override fun visit(nopStatement: NopStatement) { override fun visit(nopStatement: NopStatement) {
output("; NOP @ ${nopStatement.position} $nopStatement") output("; NOP @ ${nopStatement.position} $nopStatement")
} }

View File

@ -57,7 +57,7 @@ interface INameScope {
when(stmt) { when(stmt) {
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here! // NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
is ForLoop -> if(stmt.body.name==name) return stmt.body is ForLoop -> if(stmt.body.name==name) return stmt.body
is RepeatLoop -> if(stmt.body.name==name) return stmt.body is UntilLoop -> if(stmt.body.name==name) return stmt.body
is WhileLoop -> if(stmt.body.name==name) return stmt.body is WhileLoop -> if(stmt.body.name==name) return stmt.body
is BranchStatement -> { is BranchStatement -> {
if(stmt.truepart.name==name) return stmt.truepart if(stmt.truepart.name==name) return stmt.truepart
@ -175,8 +175,8 @@ interface INameScope {
find(it.truepart) find(it.truepart)
find(it.elsepart) find(it.elsepart)
} }
is UntilLoop -> find(it.body)
is RepeatLoop -> find(it.body) is RepeatLoop -> find(it.body)
is ForeverLoop -> find(it.body)
is WhileLoop -> find(it.body) is WhileLoop -> find(it.body)
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) } is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
else -> { /* do nothing */ } else -> { /* do nothing */ }
@ -187,6 +187,14 @@ interface INameScope {
find(this) find(this)
return result return result
} }
fun nextSibling(stmt: Statement): Statement? {
val nextIdx = statements.indexOfFirst { it===stmt } + 1
return if(nextIdx < statements.size)
statements[nextIdx]
else
null
}
} }
interface IAssignable { interface IAssignable {
@ -230,7 +238,7 @@ class Program(val name: String, val modules: MutableList<Module>): Node {
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(node is Module && replacement is Module) require(node is Module && replacement is Module)
val idx = modules.withIndex().find { it.value===node }!!.index val idx = modules.indexOfFirst { it===node }
modules[idx] = replacement modules[idx] = replacement
replacement.parent = this replacement.parent = this
} }
@ -257,7 +265,7 @@ class Module(override val name: String,
override fun definingScope(): INameScope = program.namespace override fun definingScope(): INameScope = program.namespace
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(node is Statement && replacement is Statement) require(node is Statement && replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement statements[idx] = replacement
replacement.parent = this replacement.parent = this
} }

View File

@ -205,14 +205,14 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
val forloop = forloop()?.toAst() val forloop = forloop()?.toAst()
if(forloop!=null) return forloop if(forloop!=null) return forloop
val repeatloop = repeatloop()?.toAst() val untilloop = untilloop()?.toAst()
if(repeatloop!=null) return repeatloop if(untilloop!=null) return untilloop
val whileloop = whileloop()?.toAst() val whileloop = whileloop()?.toAst()
if(whileloop!=null) return whileloop if(whileloop!=null) return whileloop
val foreverloop = foreverloop()?.toAst() val repeatloop = repeatloop()?.toAst()
if(foreverloop!=null) return foreverloop if(repeatloop!=null) return repeatloop
val breakstmt = breakstmt()?.toAst() val breakstmt = breakstmt()?.toAst()
if(breakstmt!=null) return breakstmt if(breakstmt!=null) return breakstmt
@ -247,7 +247,7 @@ private class AsmsubDecl(val name: String,
val returntypes: List<DataType>, val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>, val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>, val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<Register>) val asmClobbers: Set<CpuRegister>)
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl { private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
val name = identifier().text val name = identifier().text
@ -274,24 +274,43 @@ private class AsmSubroutineReturn(val type: DataType,
val stack: Boolean, val stack: Boolean,
val position: Position) val position: Position)
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
= this.register().asSequence().map { it.toAst() }.toSet()
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn> private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
= asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) } = asmsub_return().map {
val register = it.identifier()?.toAst()
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (val name = register.nameInSource.single()) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
else -> throw FatalAstException("invalid register or status flag in $it")
}
}
AsmSubroutineReturn(
it.datatype().toAst(),
registerorpair,
statusregister,
!it.stack?.text.isNullOrEmpty(), toPosition())
}
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter> private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
= asmsub_param().map { = asmsub_param().map {
val vardecl = it.vardecl() val vardecl = it.vardecl()
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
AsmSubroutineParameter(vardecl.varname.text, datatype, val register = it.identifier()?.toAst()
it.registerorpair()?.toAst(), var registerorpair: RegisterOrPair? = null
it.statusregister()?.toAst(), var statusregister: Statusflag? = null
if(register!=null) {
when (val name = register.nameInSource.single()) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
else -> throw FatalAstException("invalid register or status flag in $it")
}
}
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister,
!it.stack?.text.isNullOrEmpty(), toPosition()) !it.stack?.text.isNullOrEmpty(), toPosition())
} }
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement { private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
val void = this.VOID() != null val void = this.VOID() != null
val location = scoped_identifier().toAst() val location = scoped_identifier().toAst()
@ -350,23 +369,22 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
} }
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget { private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
val register = register()?.toAst()
val identifier = scoped_identifier() val identifier = scoped_identifier()
return when { return when {
register!=null -> AssignTarget(register, null, null, null, toPosition()) identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition()) arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition())
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition()) directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition()) else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition())
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
} }
} }
private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase()) private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
val names = this.identifier().map { it.toAst().nameInSource.single() }
return names.map { CpuRegister.valueOf(it) }.toSet()
}
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase()) private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex = private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
ArrayIndex(expression().toAst(), toPosition()) ArrayIndex(expression().toAst(), toPosition())
@ -469,18 +487,11 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
// the ConstantFold takes care of that and converts the type if needed. // the ConstantFold takes care of that and converts the type if needed.
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition()) ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
} }
litval.structliteral()!=null -> {
val values = litval.structliteral().expression().map { it.toAst() }
StructLiteralValue(values, litval.toPosition())
}
else -> throw FatalAstException("invalid parsed literal") else -> throw FatalAstException("invalid parsed literal")
} }
} }
} }
if(register()!=null)
return RegisterExpr(register().toAst(), register().toPosition())
if(scoped_identifier()!=null) if(scoped_identifier()!=null)
return scoped_identifier().toAst() return scoped_identifier().toAst()
@ -572,15 +583,14 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase()) private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
private fun prog8Parser.ForloopContext.toAst(): ForLoop { private fun prog8Parser.ForloopContext.toAst(): ForLoop {
val loopregister = register()?.toAst() val loopvar = identifier().toAst()
val loopvar = identifier()?.toAst()
val iterable = expression()!!.toAst() val iterable = expression()!!.toAst()
val scope = val scope =
if(statement()!=null) if(statement()!=null)
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition()) AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
else else
AnonymousScope(statement_block().toAst(), statement_block().toPosition()) AnonymousScope(statement_block().toAst(), statement_block().toPosition())
return ForLoop(loopregister, loopvar, iterable, scope, toPosition()) return ForLoop(loopvar, iterable, scope, toPosition())
} }
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition()) private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
@ -595,19 +605,20 @@ private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
return WhileLoop(condition, scope, toPosition()) return WhileLoop(condition, scope, toPosition())
} }
private fun prog8Parser.ForeverloopContext.toAst(): ForeverLoop { private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
val iterations = expression()?.toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition() val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
return ForeverLoop(scope, toPosition()) return RepeatLoop(iterations, scope, toPosition())
} }
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop { private fun prog8Parser.UntilloopContext.toAst(): UntilLoop {
val untilCondition = expression().toAst() val untilCondition = expression().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition() val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
return RepeatLoop(scope, untilCondition, toPosition()) return UntilLoop(scope, untilCondition, toPosition())
} }
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement { private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {

View File

@ -64,7 +64,7 @@ enum class DataType {
} }
} }
enum class Register { enum class CpuRegister {
A, A,
X, X,
Y Y
@ -76,14 +76,23 @@ enum class RegisterOrPair {
Y, Y,
AX, AX,
AY, AY,
XY XY;
companion object {
val names by lazy { values().map { it.toString()} }
}
} // only used in parameter and return value specs in asm subroutines } // only used in parameter and return value specs in asm subroutines
enum class Statusflag { enum class Statusflag {
Pc, Pc,
Pz, Pz,
Pv, Pv,
Pn Pn;
companion object {
val names by lazy { values().map { it.toString()} }
}
} }
enum class BranchCondition { enum class BranchCondition {

View File

@ -28,22 +28,20 @@ sealed class Expression: Node {
infix fun isSameAs(other: Expression): Boolean { infix fun isSameAs(other: Expression): Boolean {
if(this===other) if(this===other)
return true return true
when(this) { return when(this) {
is RegisterExpr ->
return (other is RegisterExpr && other.register==register)
is IdentifierReference -> is IdentifierReference ->
return (other is IdentifierReference && other.nameInSource==nameInSource) (other is IdentifierReference && other.nameInSource==nameInSource)
is PrefixExpression -> is PrefixExpression ->
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression) (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
is BinaryExpression -> is BinaryExpression ->
return (other is BinaryExpression && other.operator==operator (other is BinaryExpression && other.operator==operator
&& other.left isSameAs left && other.left isSameAs left
&& other.right isSameAs right) && other.right isSameAs right)
is ArrayIndexedExpression -> { is ArrayIndexedExpression -> {
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
&& other.arrayspec.index isSameAs arrayspec.index) && other.arrayspec.index isSameAs arrayspec.index)
} }
else -> return other==this else -> other==this
} }
} }
} }
@ -457,31 +455,6 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
} }
} }
class StructLiteralValue(var values: List<Expression>,
override val position: Position): Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent=parent
values.forEach { it.linkParents(this) }
}
override fun replaceChildNode(node: Node, replacement: Node) {
throw FatalAstException("can't replace here")
}
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
override fun toString(): String {
return "struct{ ${values.joinToString(", ")} }"
}
}
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap" private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
class StringLiteralValue(val value: String, class StringLiteralValue(val value: String,
@ -529,7 +502,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Expression) require(replacement is Expression)
val idx = value.withIndex().find { it.value===node }!!.index val idx = value.indexOfFirst { it===node }
value[idx] = replacement value[idx] = replacement
replacement.parent = this replacement.parent = this
} }
@ -696,29 +669,6 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
} }
} }
class RegisterExpr(val register: Register, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
}
override fun replaceChildNode(node: Node, replacement: Node) {
throw FatalAstException("can't replace here")
}
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
override fun toString(): String {
return "RegisterExpr(register=$register, pos=$position)"
}
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
}
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable { data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node override lateinit var parent: Node
@ -732,6 +682,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource
override fun hashCode() = nameInSource.hashCode()
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
@ -800,7 +751,7 @@ class FunctionCall(override var target: IdentifierReference,
if(node===target) if(node===target)
target=replacement as IdentifierReference target=replacement as IdentifierReference
else { else {
val idx = args.withIndex().find { it.value===node }!!.index val idx = args.indexOfFirst { it===node }
args[idx] = replacement as Expression args[idx] = replacement as Expression
} }
replacement.parent = this replacement.parent = this

View File

@ -110,22 +110,11 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(forLoop: ForLoop) { override fun visit(forLoop: ForLoop) {
if(forLoop.body.containsNoCodeNorVars())
errors.warn("for loop body is empty", forLoop.position)
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE) val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) { if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
errors.err("can only loop over an iterable type", forLoop.position) errors.err("can only loop over an iterable type", forLoop.position)
} else { } else {
if (forLoop.loopRegister != null) { val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)
// loop register
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
errors.err("register can only loop over bytes", forLoop.position)
if(forLoop.loopRegister!=Register.A)
errors.err("it's only possible to use A as a loop register", forLoop.position)
} else {
// loop variable
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
if(loopvar==null || loopvar.type== VarDeclType.CONST) { if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position) errors.err("for loop requires a variable to loop with", forLoop.position)
} else { } else {
@ -155,7 +144,6 @@ internal class AstChecker(private val program: Program,
} }
} }
} }
}
super.visit(forLoop) super.visit(forLoop)
} }
@ -260,27 +248,27 @@ internal class AstChecker(private val program: Program,
} }
} }
val regCounts = mutableMapOf<Register, Int>().withDefault { 0 } val regCounts = mutableMapOf<CpuRegister, Int>().withDefault { 0 }
val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 } val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 }
fun countRegisters(from: Iterable<RegisterOrStatusflag>) { fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
regCounts.clear() regCounts.clear()
statusflagCounts.clear() statusflagCounts.clear()
for(p in from) { for(p in from) {
when(p.registerOrPair) { when(p.registerOrPair) {
RegisterOrPair.A -> regCounts[Register.A]=regCounts.getValue(Register.A)+1 RegisterOrPair.A -> regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
RegisterOrPair.X -> regCounts[Register.X]=regCounts.getValue(Register.X)+1 RegisterOrPair.X -> regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
RegisterOrPair.Y -> regCounts[Register.Y]=regCounts.getValue(Register.Y)+1 RegisterOrPair.Y -> regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
RegisterOrPair.AX -> { RegisterOrPair.AX -> {
regCounts[Register.A]=regCounts.getValue(Register.A)+1 regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
regCounts[Register.X]=regCounts.getValue(Register.X)+1 regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
} }
RegisterOrPair.AY -> { RegisterOrPair.AY -> {
regCounts[Register.A]=regCounts.getValue(Register.A)+1 regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1 regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
} }
RegisterOrPair.XY -> { RegisterOrPair.XY -> {
regCounts[Register.X]=regCounts.getValue(Register.X)+1 regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1 regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
} }
null -> null ->
if(p.statusflag!=null) if(p.statusflag!=null)
@ -325,17 +313,13 @@ internal class AstChecker(private val program: Program,
visitStatements(subroutine.statements) visitStatements(subroutine.statements)
} }
override fun visit(repeatLoop: RepeatLoop) { override fun visit(untilLoop: UntilLoop) {
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y")) if(untilLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.warn("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position) errors.err("condition value should be an integer type", untilLoop.untilCondition.position)
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes) super.visit(untilLoop)
errors.err("condition value should be an integer type", repeatLoop.untilCondition.position)
super.visit(repeatLoop)
} }
override fun visit(whileLoop: WhileLoop) { override fun visit(whileLoop: WhileLoop) {
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
errors.warn("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes) if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", whileLoop.condition.position) errors.err("condition value should be an integer type", whileLoop.condition.position)
super.visit(whileLoop) super.visit(whileLoop)
@ -361,9 +345,9 @@ internal class AstChecker(private val program: Program,
if(targetIdent!=null) { if(targetIdent!=null) {
val targetVar = targetIdent.targetVarDecl(program.namespace) val targetVar = targetIdent.targetVarDecl(program.namespace)
if(targetVar?.struct != null) { if(targetVar?.struct != null) {
val sourceStructLv = assignment.value as? StructLiteralValue val sourceStructLv = assignment.value as? ArrayLiteralValue
if (sourceStructLv != null) { if (sourceStructLv != null) {
if (sourceStructLv.values.size != targetVar.struct?.numberOfElements) if (sourceStructLv.value.size != targetVar.struct?.numberOfElements)
errors.err("number of elements doesn't match struct definition", sourceStructLv.position) errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
} else { } else {
val sourceIdent = assignment.value as? IdentifierReference val sourceIdent = assignment.value as? IdentifierReference
@ -397,8 +381,7 @@ internal class AstChecker(private val program: Program,
val targetIdentifier = assignTarget.identifier val targetIdentifier = assignTarget.identifier
if (targetIdentifier != null) { if (targetIdentifier != null) {
val targetName = targetIdentifier.nameInSource val targetName = targetIdentifier.nameInSource
val targetSymbol = program.namespace.lookup(targetName, assignment) when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
when (targetSymbol) {
null -> { null -> {
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position) errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
return return
@ -517,21 +500,14 @@ internal class AstChecker(private val program: Program,
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue) checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
} }
is ArrayLiteralValue -> { is ArrayLiteralValue -> {
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
}
is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
}
is StructLiteralValue -> {
if(decl.datatype==DataType.STRUCT) { if(decl.datatype==DataType.STRUCT) {
val struct = decl.struct!! val struct = decl.struct!!
val structLv = decl.value as StructLiteralValue val structLv = decl.value as ArrayLiteralValue
if(struct.numberOfElements != structLv.values.size) { if(struct.numberOfElements != structLv.value.size) {
errors.err("struct value has incorrect number of elements", structLv.position) errors.err("struct value has incorrect number of elements", structLv.position)
return return
} }
for(value in structLv.values.zip(struct.statements)) { for(value in structLv.value.zip(struct.statements)) {
val memberdecl = value.second as VarDecl val memberdecl = value.second as VarDecl
val constValue = value.first.constValue(program) val constValue = value.first.constValue(program)
if(constValue==null) { if(constValue==null) {
@ -545,9 +521,13 @@ internal class AstChecker(private val program: Program,
} }
} }
} else { } else {
errors.err("struct literal is wrong type to initialize this variable", decl.value!!.position) val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
} }
} }
is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
}
else -> { else -> {
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}") err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
super.visit(decl) super.visit(decl)
@ -584,8 +564,18 @@ internal class AstChecker(private val program: Program,
} }
val declValue = decl.value val declValue = decl.value
if(declValue!=null && decl.type==VarDeclType.VAR && !declValue.inferType(program).istype(decl.datatype)) if(declValue!=null && decl.type==VarDeclType.VAR) {
if(decl.datatype==DataType.STRUCT) {
val valueIdt = declValue.inferType(program)
if(valueIdt.isUnknown)
throw AstException("invalid value type")
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
if(valueDt !in ArrayDatatypes)
err("initialisation of struct should be with array value", declValue.position)
} else if (!declValue.inferType(program).istype(decl.datatype)) {
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position) err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
}
}
super.visit(decl) super.visit(decl)
} }
@ -844,7 +834,7 @@ internal class AstChecker(private val program: Program,
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) { if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
// in-place modification, can't be done on literals // in-place modification, can't be done on literals
if(functionCallStatement.args.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) { if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position) errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
} }
} }
@ -1292,8 +1282,8 @@ internal class AstChecker(private val program: Program,
DataType.STR -> sourceDatatype== DataType.STR DataType.STR -> sourceDatatype== DataType.STR
DataType.STRUCT -> { DataType.STRUCT -> {
if(sourceDatatype==DataType.STRUCT) { if(sourceDatatype==DataType.STRUCT) {
val structLv = sourceValue as StructLiteralValue val structLv = sourceValue as ArrayLiteralValue
val numValues = structLv.values.size val numValues = structLv.value.size
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!! val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
return targetstruct.numberOfElements == numValues return targetstruct.numberOfElements == numValues
} }

View File

@ -57,8 +57,8 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
return super.visit(decl) return super.visit(decl)
} }
if (decl.value != null && decl.value !is StructLiteralValue) { if (decl.value != null && decl.value !is ArrayLiteralValue) {
errors.err("initializing requires struct literal value", decl.value?.position ?: decl.position) errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
return super.visit(decl) return super.visit(decl)
} }
} }
@ -137,21 +137,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
super.visit(label) super.visit(label)
} }
override fun visit(forLoop: ForLoop) {
if (forLoop.loopRegister != null) {
if (forLoop.loopRegister == Register.X)
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
}
super.visit(forLoop)
}
override fun visit(assignTarget: AssignTarget) {
if(assignTarget.register== Register.X)
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
super.visit(assignTarget)
}
override fun visit(string: StringLiteralValue) { override fun visit(string: StringLiteralValue) {
if (string.value.length !in 1..255) if (string.value.length !in 1..255)
errors.err("string literal length must be between 1 and 255", string.position) errors.err("string literal length must be between 1 and 255", string.position)

View File

@ -25,7 +25,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
val tempvardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, tempname, null, null, isArray = false, autogeneratedDontRemove = true, position = first.position) val tempvardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, tempname, null, null, isArray = false, autogeneratedDontRemove = true, position = first.position)
val tempvar = IdentifierReference(listOf(tempname), first.position) val tempvar = IdentifierReference(listOf(tempname), first.position)
val assignTemp = Assignment( val assignTemp = Assignment(
AssignTarget(null, tempvar, null, null, first.position), AssignTarget(tempvar, null, null, first.position),
null, null,
first, first,
first.position first.position

View File

@ -52,7 +52,7 @@ interface IAstModification {
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification { class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
override fun perform() { override fun perform() {
if(parent is INameScope) { if(parent is INameScope) {
val idx = parent.statements.withIndex().find { it.value===after }!!.index + 1 val idx = parent.statements.indexOfFirst { it===after } + 1
parent.statements.add(idx, stmt) parent.statements.add(idx, stmt)
stmt.linkParents(parent) stmt.linkParents(parent)
} else { } else {
@ -94,7 +94,7 @@ abstract class AstWalker {
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList() open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList() open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
@ -110,13 +110,11 @@ abstract class AstWalker {
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList() open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList() open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList() open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList() open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList() open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList() open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList() open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList() open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList() open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
@ -138,7 +136,7 @@ abstract class AstWalker {
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList() open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList() open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
@ -154,13 +152,11 @@ abstract class AstWalker {
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList() open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList() open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList() open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList() open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList() open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList() open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList() open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList() open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList() open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
@ -325,7 +321,7 @@ abstract class AstWalker {
fun visit(forLoop: ForLoop, parent: Node) { fun visit(forLoop: ForLoop, parent: Node) {
track(before(forLoop, parent), forLoop, parent) track(before(forLoop, parent), forLoop, parent)
forLoop.loopVar?.accept(this, forLoop) forLoop.loopVar.accept(this, forLoop)
forLoop.iterable.accept(this, forLoop) forLoop.iterable.accept(this, forLoop)
forLoop.body.accept(this, forLoop) forLoop.body.accept(this, forLoop)
track(after(forLoop, parent), forLoop, parent) track(after(forLoop, parent), forLoop, parent)
@ -338,19 +334,20 @@ abstract class AstWalker {
track(after(whileLoop, parent), whileLoop, parent) track(after(whileLoop, parent), whileLoop, parent)
} }
fun visit(foreverLoop: ForeverLoop, parent: Node) {
track(before(foreverLoop, parent), foreverLoop, parent)
foreverLoop.body.accept(this, foreverLoop)
track(after(foreverLoop, parent), foreverLoop, parent)
}
fun visit(repeatLoop: RepeatLoop, parent: Node) { fun visit(repeatLoop: RepeatLoop, parent: Node) {
track(before(repeatLoop, parent), repeatLoop, parent) track(before(repeatLoop, parent), repeatLoop, parent)
repeatLoop.untilCondition.accept(this, repeatLoop) repeatLoop.iterations?.accept(this, repeatLoop)
repeatLoop.body.accept(this, repeatLoop) repeatLoop.body.accept(this, repeatLoop)
track(after(repeatLoop, parent), repeatLoop, parent) track(after(repeatLoop, parent), repeatLoop, parent)
} }
fun visit(untilLoop: UntilLoop, parent: Node) {
track(before(untilLoop, parent), untilLoop, parent)
untilLoop.untilCondition.accept(this, untilLoop)
untilLoop.body.accept(this, untilLoop)
track(after(untilLoop, parent), untilLoop, parent)
}
fun visit(returnStmt: Return, parent: Node) { fun visit(returnStmt: Return, parent: Node) {
track(before(returnStmt, parent), returnStmt, parent) track(before(returnStmt, parent), returnStmt, parent)
returnStmt.value?.accept(this, returnStmt) returnStmt.value?.accept(this, returnStmt)
@ -407,11 +404,6 @@ abstract class AstWalker {
track(after(inlineAssembly, parent), inlineAssembly, parent) track(after(inlineAssembly, parent), inlineAssembly, parent)
} }
fun visit(registerExpr: RegisterExpr, parent: Node) {
track(before(registerExpr, parent), registerExpr, parent)
track(after(registerExpr, parent), registerExpr, parent)
}
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) { fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent) track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent) track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
@ -441,11 +433,5 @@ abstract class AstWalker {
structDecl.statements.forEach { it.accept(this, structDecl) } structDecl.statements.forEach { it.accept(this, structDecl) }
track(after(structDecl, parent), structDecl, parent) track(after(structDecl, parent), structDecl, parent)
} }
fun visit(structLv: StructLiteralValue, parent: Node) {
track(before(structLv, parent), structLv, parent)
structLv.values.forEach { it.accept(this, structLv) }
track(after(structLv, parent), structLv, parent)
}
} }

View File

@ -102,7 +102,7 @@ interface IAstVisitor {
} }
fun visit(forLoop: ForLoop) { fun visit(forLoop: ForLoop) {
forLoop.loopVar?.accept(this) forLoop.loopVar.accept(this)
forLoop.iterable.accept(this) forLoop.iterable.accept(this)
forLoop.body.accept(this) forLoop.body.accept(this)
} }
@ -112,13 +112,14 @@ interface IAstVisitor {
whileLoop.body.accept(this) whileLoop.body.accept(this)
} }
fun visit(foreverLoop: ForeverLoop) { fun visit(repeatLoop: RepeatLoop) {
foreverLoop.body.accept(this) repeatLoop.iterations?.accept(this)
repeatLoop.body.accept(this)
} }
fun visit(repeatLoop: RepeatLoop) { fun visit(untilLoop: UntilLoop) {
repeatLoop.untilCondition.accept(this) untilLoop.untilCondition.accept(this)
repeatLoop.body.accept(this) untilLoop.body.accept(this)
} }
fun visit(returnStmt: Return) { fun visit(returnStmt: Return) {
@ -159,9 +160,6 @@ interface IAstVisitor {
fun visit(inlineAssembly: InlineAssembly) { fun visit(inlineAssembly: InlineAssembly) {
} }
fun visit(registerExpr: RegisterExpr) {
}
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) { fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
} }
@ -181,8 +179,4 @@ interface IAstVisitor {
fun visit(structDecl: StructDecl) { fun visit(structDecl: StructDecl) {
structDecl.statements.forEach { it.accept(this) } structDecl.statements.forEach { it.accept(this) }
} }
fun visit(structLv: StructLiteralValue) {
structLv.values.forEach { it.accept(this) }
}
} }

View File

@ -78,7 +78,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
if(declConstValue==null) { if(declConstValue==null) {
// move the vardecl (without value) to the scope and replace this with a regular assignment // move the vardecl (without value) to the scope and replace this with a regular assignment
decl.value = null decl.value = null
val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val assign = Assignment(target, null, declValue, decl.position) val assign = Assignment(target, null, declValue, decl.position)
return listOf( return listOf(
IAstModification.ReplaceNode(decl, assign, parent), IAstModification.ReplaceNode(decl, assign, parent),
@ -106,8 +106,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
val valueType = assignment.value.inferType(program) val valueType = assignment.value.inferType(program)
val targetType = assignment.target.inferType(program, assignment) val targetType = assignment.target.inferType(program, assignment)
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) { if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
val assignments = if (assignment.value is StructLiteralValue) { val assignments = if (assignment.value is ArrayLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = { ..... } ' flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] '
} else { } else {
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2' flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
} }
@ -128,15 +128,15 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
val targetVar = identifier.targetVarDecl(program.namespace)!! val targetVar = identifier.targetVarDecl(program.namespace)!!
val struct = targetVar.struct!! val struct = targetVar.struct!!
val slv = structAssignment.value as? StructLiteralValue val slv = structAssignment.value as? ArrayLiteralValue
if(slv==null || slv.values.size != struct.numberOfElements) if(slv==null || slv.value.size != struct.numberOfElements)
throw FatalAstException("element count mismatch") throw FatalAstException("element count mismatch")
return struct.statements.zip(slv.values).map { (targetDecl, sourceValue) -> return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
targetDecl as VarDecl targetDecl as VarDecl
val mangled = mangledStructMemberName(identifierName, targetDecl.name) val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position) val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
null, sourceValue, sourceValue.position) null, sourceValue, sourceValue.position)
assign.linkParents(structAssignment) assign.linkParents(structAssignment)
assign assign
@ -168,13 +168,13 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
val idref = IdentifierReference(listOf(mangled), structAssignment.position) val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name) val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position) val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
null, sourceIdref, member.second.position) null, sourceIdref, member.second.position)
assign.linkParents(structAssignment) assign.linkParents(structAssignment)
assign assign
} }
} }
is StructLiteralValue -> { is ArrayLiteralValue -> {
throw IllegalArgumentException("not going to flatten a structLv assignment here") throw IllegalArgumentException("not going to flatten a structLv assignment here")
} }
else -> throw FatalAstException("strange struct value") else -> throw FatalAstException("strange struct value")

View File

@ -179,51 +179,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
return noModifications return noModifications
} }
override fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> {
// assignment of a struct literal value, some member values may need proper typecast
fun addTypecastsIfNeeded(struct: StructDecl): Iterable<IAstModification> {
val newValues = struct.statements.zip(structLv.values).map { (structMemberDecl, memberValue) ->
val memberDt = (structMemberDecl as VarDecl).datatype
val valueDt = memberValue.inferType(program)
if (valueDt.typeOrElse(memberDt) != memberDt)
TypecastExpression(memberValue, memberDt, true, memberValue.position)
else
memberValue
}
class StructLvValueReplacer(val targetStructLv: StructLiteralValue, val typecastValues: List<Expression>) : IAstModification {
override fun perform() {
targetStructLv.values = typecastValues
typecastValues.forEach { it.linkParents(targetStructLv) }
}
}
return if(structLv.values.zip(newValues).any { (v1, v2) -> v1 !== v2})
listOf(StructLvValueReplacer(structLv, newValues))
else
emptyList()
}
val decl = structLv.parent as? VarDecl
if(decl != null) {
val struct = decl.struct
if(struct != null)
return addTypecastsIfNeeded(struct)
} else {
val assign = structLv.parent as? Assignment
if (assign != null) {
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
if(decl2 != null) {
val struct = decl2.struct
if(struct != null)
return addTypecastsIfNeeded(struct)
}
}
}
return noModifications
}
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> { override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
// add a typecast to the return type if it doesn't match the subroutine's signature // add a typecast to the return type if it doesn't match the subroutine's signature
val returnValue = returnStmt.value val returnValue = returnStmt.value

View File

@ -64,7 +64,7 @@ class Block(override val name: String,
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement) require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index val idx = statements.indexOfFirst { it ===node }
statements[idx] = replacement statements[idx] = replacement
replacement.parent = this replacement.parent = this
} }
@ -270,7 +270,7 @@ open class VarDecl(val type: VarDeclType,
fun flattenStructMembers(): MutableList<Statement> { fun flattenStructMembers(): MutableList<Statement> {
val result = struct!!.statements.withIndex().map { val result = struct!!.statements.withIndex().map {
val member = it.value as VarDecl val member = it.value as VarDecl
val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null val initvalue = if(value!=null) (value as ArrayLiteralValue).value[it.index] else null
VarDecl( VarDecl(
VarDeclType.VAR, VarDeclType.VAR,
member.datatype, member.datatype,
@ -354,7 +354,6 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
val leftOperand: Expression = val leftOperand: Expression =
when { when {
target.register != null -> RegisterExpr(target.register!!, target.position)
target.identifier != null -> target.identifier!! target.identifier != null -> target.identifier!!
target.arrayindexed != null -> target.arrayindexed!! target.arrayindexed != null -> target.arrayindexed!!
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position) target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
@ -374,8 +373,7 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
} }
} }
data class AssignTarget(val register: Register?, data class AssignTarget(var identifier: IdentifierReference?,
var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?, var arrayindexed: ArrayIndexedExpression?,
val memoryAddress: DirectMemoryWrite?, val memoryAddress: DirectMemoryWrite?,
override val position: Position) : Node { override val position: Position) : Node {
@ -403,19 +401,15 @@ data class AssignTarget(val register: Register?,
companion object { companion object {
fun fromExpr(expr: Expression): AssignTarget { fun fromExpr(expr: Expression): AssignTarget {
return when (expr) { return when (expr) {
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position) is IdentifierReference -> AssignTarget(expr, null, null, expr.position)
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position) is ArrayIndexedExpression -> AssignTarget(null, expr, null, expr.position)
is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position) is DirectMemoryRead -> AssignTarget(null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
else -> throw FatalAstException("invalid expression object $expr") else -> throw FatalAstException("invalid expression object $expr")
} }
} }
} }
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
if(register!=null)
return InferredTypes.knownFor(DataType.UBYTE)
if(identifier!=null) { if(identifier!=null) {
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown() val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype) if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
@ -440,7 +434,6 @@ data class AssignTarget(val register: Register?,
else else
false false
} }
this.register!=null -> value is RegisterExpr && value.register==register
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
this.arrayindexed!=null -> value is ArrayIndexedExpression && this.arrayindexed!=null -> value is ArrayIndexedExpression &&
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource && value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
@ -454,8 +447,6 @@ data class AssignTarget(val register: Register?,
fun isSameAs(other: AssignTarget, program: Program): Boolean { fun isSameAs(other: AssignTarget, program: Program): Boolean {
if(this===other) if(this===other)
return true return true
if(this.register!=null && other.register!=null)
return this.register==other.register
if(this.identifier!=null && other.identifier!=null) if(this.identifier!=null && other.identifier!=null)
return this.identifier!!.nameInSource==other.identifier!!.nameInSource return this.identifier!!.nameInSource==other.identifier!!.nameInSource
if(this.memoryAddress!=null && other.memoryAddress!=null) { if(this.memoryAddress!=null && other.memoryAddress!=null) {
@ -474,8 +465,6 @@ data class AssignTarget(val register: Register?,
} }
fun isNotMemory(namespace: INameScope): Boolean { fun isNotMemory(namespace: INameScope): Boolean {
if(this.register!=null)
return true
if(this.memoryAddress!=null) if(this.memoryAddress!=null)
return false return false
if(this.arrayindexed!=null) { if(this.arrayindexed!=null) {
@ -550,7 +539,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
if(node===target) if(node===target)
target = replacement as IdentifierReference target = replacement as IdentifierReference
else { else {
val idx = args.withIndex().find { it.value===node }!!.index val idx = args.indexOfFirst { it===node }
args[idx] = replacement as Expression args[idx] = replacement as Expression
} }
replacement.parent = this replacement.parent = this
@ -597,7 +586,7 @@ class AnonymousScope(override var statements: MutableList<Statement>,
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement) require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement statements[idx] = replacement
replacement.parent = this replacement.parent = this
} }
@ -616,14 +605,6 @@ class NopStatement(override val position: Position): Statement() {
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
companion object {
fun insteadOf(stmt: Statement): NopStatement {
val nop = NopStatement(stmt.position)
nop.parent = stmt.parent
return nop
}
}
} }
// the subroutine class covers both the normal user-defined subroutines, // the subroutine class covers both the normal user-defined subroutines,
@ -634,7 +615,7 @@ class Subroutine(override val name: String,
val returntypes: List<DataType>, val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>, val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>, val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<Register>, val asmClobbers: Set<CpuRegister>,
val asmAddress: Int?, val asmAddress: Int?,
val isAsmSubroutine: Boolean, val isAsmSubroutine: Boolean,
override var statements: MutableList<Statement>, override var statements: MutableList<Statement>,
@ -652,7 +633,7 @@ class Subroutine(override val name: String,
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement) require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement statements[idx] = replacement
replacement.parent = this replacement.parent = this
} }
@ -671,32 +652,6 @@ class Subroutine(override val name: String,
.filter { it is InlineAssembly } .filter { it is InlineAssembly }
.map { (it as InlineAssembly).assembly } .map { (it as InlineAssembly).assembly }
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it } .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
fun countStatements(): Int {
class StatementCounter: IAstVisitor {
var count = 0
override fun visit(block: Block) {
count += block.statements.size
super.visit(block)
}
override fun visit(subroutine: Subroutine) {
count += subroutine.statements.size
super.visit(subroutine)
}
override fun visit(scope: AnonymousScope) {
count += scope.statements.size
super.visit(scope)
}
}
// the (recursive) number of statements
val counter = StatementCounter()
counter.visit(this)
return counter.count
}
} }
@ -768,8 +723,7 @@ class BranchStatement(var condition: BranchCondition,
} }
class ForLoop(val loopRegister: Register?, class ForLoop(var loopVar: IdentifierReference,
var loopVar: IdentifierReference?,
var iterable: Expression, var iterable: Expression,
var body: AnonymousScope, var body: AnonymousScope,
override val position: Position) : Statement() { override val position: Position) : Statement() {
@ -777,7 +731,7 @@ class ForLoop(val loopRegister: Register?,
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent=parent this.parent=parent
loopVar?.linkParents(this) loopVar.linkParents(this)
iterable.linkParents(this) iterable.linkParents(this)
body.linkParents(this) body.linkParents(this)
} }
@ -796,14 +750,10 @@ class ForLoop(val loopRegister: Register?,
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun toString(): String { override fun toString(): String {
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)" return "ForLoop(loopVar: $loopVar, iterable: $iterable, pos=$position)"
} }
fun loopVarDt(program: Program): InferredTypes.InferredType { fun loopVarDt(program: Program) = loopVar.inferType(program)
val lv = loopVar
return if(loopRegister!=null) InferredTypes.InferredType.known(DataType.UBYTE)
else lv?.inferType(program) ?: InferredTypes.InferredType.unknown()
}
} }
class WhileLoop(var condition: Expression, class WhileLoop(var condition: Expression,
@ -830,17 +780,21 @@ class WhileLoop(var condition: Expression,
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
} }
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() { class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
iterations?.linkParents(this)
body.linkParents(this) body.linkParents(this)
} }
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is AnonymousScope && node===body) when {
body = replacement node===iterations -> iterations = replacement as Expression
node===body -> body = replacement as AnonymousScope
else -> throw FatalAstException("invalid replace")
}
replacement.parent = this replacement.parent = this
} }
@ -848,7 +802,7 @@ class ForeverLoop(var body: AnonymousScope, override val position: Position) : S
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
} }
class RepeatLoop(var body: AnonymousScope, class UntilLoop(var body: AnonymousScope,
var untilCondition: Expression, var untilCondition: Expression,
override val position: Position) : Statement() { override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
@ -953,7 +907,7 @@ class StructDecl(override val name: String,
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement) require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement statements[idx] = replacement
replacement.parent = this replacement.parent = this
} }

View File

@ -39,7 +39,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
return numericVarsWithValue.map { return numericVarsWithValue.map {
val initValue = it.value!! // assume here that value has always been set by now val initValue = it.value!! // assume here that value has always been set by now
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous) it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position) val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
val assign = Assignment(target, null, initValue, it.position) val assign = Assignment(target, null, initValue, it.position)
initValue.parent = assign initValue.parent = assign
IAstModification.InsertFirst(assign, scope) IAstModification.InsertFirst(assign, scope)

View File

@ -5,9 +5,6 @@ import prog8.compiler.CompilerException
import prog8.compiler.Zeropage import prog8.compiler.Zeropage
import prog8.compiler.ZeropageType import prog8.compiler.ZeropageType
import prog8.compiler.target.IMachineDefinition import prog8.compiler.target.IMachineDefinition
import java.awt.Color
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.pow import kotlin.math.pow
@ -177,90 +174,4 @@ object C64MachineDefinition: IMachineDefinition {
return if (sign) -result else result return if (sign) -result else result
} }
} }
object Charset {
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
transparent.createGraphics().drawImage(img, 0, 0, null)
val black = Color(0, 0, 0).rgb
val nopixel = Color(0, 0, 0, 0).rgb
for (y in 0 until transparent.height) {
for (x in 0 until transparent.width) {
val col = transparent.getRGB(x, y)
if (col == black)
transparent.setRGB(x, y, nopixel)
}
}
val numColumns = transparent.width / 8
val charImages = (0..255).map {
val charX = it % numColumns
val charY = it / numColumns
transparent.getSubimage(charX * 8, charY * 8, 8, 8)
}
return charImages.toTypedArray()
}
val normalChars = scanChars(normalImg)
val shiftedChars = scanChars(shiftedImg)
private val coloredNormalChars = mutableMapOf<Short, Array<BufferedImage>>()
fun getColoredChar(screenCode: Short, color: Short): BufferedImage {
val colorIdx = (color % colorPalette.size).toShort()
val chars = coloredNormalChars[colorIdx]
if (chars != null)
return chars[screenCode.toInt()]
val coloredChars = mutableListOf<BufferedImage>()
val transparent = Color(0, 0, 0, 0).rgb
val rgb = colorPalette[colorIdx.toInt()].rgb
for (c in normalChars) {
val colored = c.copy()
for (y in 0 until colored.height)
for (x in 0 until colored.width) {
if (colored.getRGB(x, y) != transparent) {
colored.setRGB(x, y, rgb)
}
}
coloredChars.add(colored)
}
coloredNormalChars[colorIdx] = coloredChars.toTypedArray()
return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()]
}
}
private fun BufferedImage.copy(): BufferedImage {
val bcopy = BufferedImage(this.width, this.height, this.type)
val g = bcopy.graphics
g.drawImage(this, 0, 0, null)
g.dispose()
return bcopy
}
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
Color(0x000000), // 0 = black
Color(0xFFFFFF), // 1 = white
Color(0x813338), // 2 = red
Color(0x75cec8), // 3 = cyan
Color(0x8e3c97), // 4 = purple
Color(0x56ac4d), // 5 = green
Color(0x2e2c9b), // 6 = blue
Color(0xedf171), // 7 = yellow
Color(0x8e5029), // 8 = orange
Color(0x553800), // 9 = brown
Color(0xc46c71), // 10 = light red
Color(0x4a4a4a), // 11 = dark grey
Color(0x7b7b7b), // 12 = medium grey
Color(0xa9ff9f), // 13 = light green
Color(0x706deb), // 14 = light blue
Color(0xb2b2b2) // 15 = light grey
)
} }

View File

@ -1,5 +1,6 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.c64.codegen
import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.antlr.escape import prog8.ast.antlr.escape
@ -183,7 +184,7 @@ internal class AsmGen(private val program: Program,
blockLevelVarInits.getValue(block).forEach { decl -> blockLevelVarInits.getValue(block).forEach { decl ->
val scopedFullName = decl.makeScopedName(decl.name).split('.') val scopedFullName = decl.makeScopedName(decl.name).split('.')
require(scopedFullName.first()==block.name) require(scopedFullName.first()==block.name)
val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position) val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
val assign = Assignment(target, null, decl.value!!, decl.position) val assign = Assignment(target, null, decl.value!!, decl.position)
assign.linkParents(decl.parent) assign.linkParents(decl.parent)
assignmentAsmGen.translate(assign) assignmentAsmGen.translate(assign)
@ -561,19 +562,19 @@ internal class AsmGen(private val program: Program,
} }
} }
internal fun saveRegister(register: Register) { internal fun saveRegister(register: CpuRegister) {
when(register) { when(register) {
Register.A -> out(" pha") CpuRegister.A -> out(" pha")
Register.X -> out(" txa | pha") CpuRegister.X -> out(" txa | pha")
Register.Y -> out(" tya | pha") CpuRegister.Y -> out(" tya | pha")
} }
} }
internal fun restoreRegister(register: Register) { internal fun restoreRegister(register: CpuRegister) {
when(register) { when(register) {
Register.A -> out(" pla") CpuRegister.A -> out(" pla")
Register.X -> out(" pla | tax") CpuRegister.X -> out(" pla | tax")
Register.Y -> out(" pla | tay") CpuRegister.Y -> out(" pla | tay")
} }
} }
@ -640,8 +641,8 @@ internal class AsmGen(private val program: Program,
is Continue -> out(" jmp ${loopContinueLabels.peek()}") is Continue -> out(" jmp ${loopContinueLabels.peek()}")
is Break -> out(" jmp ${loopEndLabels.peek()}") is Break -> out(" jmp ${loopEndLabels.peek()}")
is WhileLoop -> translate(stmt) is WhileLoop -> translate(stmt)
is ForeverLoop -> translate(stmt)
is RepeatLoop -> translate(stmt) is RepeatLoop -> translate(stmt)
is UntilLoop -> translate(stmt)
is WhenStatement -> translate(stmt) is WhenStatement -> translate(stmt)
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?") is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
is AnonymousScope -> translate(stmt) is AnonymousScope -> translate(stmt)
@ -672,19 +673,108 @@ internal class AsmGen(private val program: Program,
} }
} }
private fun translate(stmt: ForeverLoop) { private fun translate(stmt: RepeatLoop) {
val foreverLabel = makeLabel("forever") val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("foreverend") val endLabel = makeLabel("repeatend")
val counterLabel = makeLabel("repeatcounter")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
loopContinueLabels.push(foreverLabel) loopContinueLabels.push(repeatLabel)
out(foreverLabel)
when (stmt.iterations) {
null -> {
// endless loop
out(repeatLabel)
translate(stmt.body) translate(stmt.body)
out(" jmp $foreverLabel") out(" jmp $repeatLabel")
out(endLabel) out(endLabel)
}
is NumericLiteralValue -> {
val iterations = (stmt.iterations as NumericLiteralValue).number.toInt()
if(iterations<0 || iterations > 65536)
throw AssemblyError("invalid number of iterations")
when {
iterations == 0 -> {}
iterations <= 255 -> {
out(" lda #${iterations}")
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
}
else -> {
out(" lda #<${iterations} | ldy #>${iterations}")
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
}
}
}
is IdentifierReference -> {
val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program.namespace) as VarDecl
val name = asmIdentifierName(stmt.iterations as IdentifierReference)
when(vardecl.datatype) {
DataType.UBYTE, DataType.BYTE -> {
out(" lda $name")
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
}
DataType.UWORD, DataType.WORD -> {
out(" lda $name | ldy $name+1")
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
}
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
}
}
else -> {
translateExpression(stmt.iterations!!)
val dt = stmt.iterations!!.inferType(program).typeOrElse(DataType.STRUCT)
when (dt) {
in ByteDatatypes -> {
out(" inx | lda ${ESTACK_LO_HEX},x")
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
}
in WordDatatypes -> {
out(" inx | lda ${ESTACK_LO_HEX},x | ldy ${ESTACK_HI_HEX},x")
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
}
else -> throw AssemblyError("invalid loop expression datatype $dt")
}
}
}
loopEndLabels.pop() loopEndLabels.pop()
loopContinueLabels.pop() loopContinueLabels.pop()
} }
private fun repeatWordCountInAY(counterLabel: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A/Y must have been loaded with the number of iterations already!
out("""
sta $counterLabel
sty $counterLabel+1
$repeatLabel lda $counterLabel
bne +
lda $counterLabel+1
beq $endLabel
+ lda $counterLabel
bne +
dec $counterLabel+1
+ dec $counterLabel
""")
translate(body)
out("""
jmp $repeatLabel
$counterLabel .word 0
$endLabel""")
}
private fun repeatByteCountInA(counterLabel: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A must have been loaded with the number of iterations already!
out("""
sta $counterLabel
$repeatLabel lda $counterLabel
beq $endLabel
dec $counterLabel""")
translate(body)
out("""
jmp $repeatLabel
$counterLabel .byte 0
$endLabel""")
}
private fun translate(stmt: WhileLoop) { private fun translate(stmt: WhileLoop) {
val whileLabel = makeLabel("while") val whileLabel = makeLabel("while")
val endLabel = makeLabel("whileend") val endLabel = makeLabel("whileend")
@ -713,7 +803,7 @@ internal class AsmGen(private val program: Program,
loopContinueLabels.pop() loopContinueLabels.pop()
} }
private fun translate(stmt: RepeatLoop) { private fun translate(stmt: UntilLoop) {
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend") val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
@ -836,13 +926,16 @@ internal class AsmGen(private val program: Program,
} }
inits.add(stmt) inits.add(stmt)
} else { } else {
val target = AssignTarget(null, IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position) val next = (stmt.parent as INameScope).nextSibling(stmt)
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
val assign = Assignment(target, null, stmt.value!!, stmt.position) val assign = Assignment(target, null, stmt.value!!, stmt.position)
assign.linkParents(stmt.parent) assign.linkParents(stmt.parent)
translate(assign) translate(assign)
} }
} }
} }
}
private fun translate(stmt: Directive) { private fun translate(stmt: Directive) {
when(stmt.directive) { when(stmt.directive) {
@ -901,13 +994,6 @@ internal class AsmGen(private val program: Program,
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) { internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) { when (val index = expr.arrayspec.index) {
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly") is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
is RegisterExpr -> {
when (index.register) {
Register.A -> {}
Register.X -> out(" txa")
Register.Y -> out(" tya")
}
}
is IdentifierReference -> { is IdentifierReference -> {
val indexName = asmIdentifierName(index) val indexName = asmIdentifierName(index)
out(" lda $indexName") out(" lda $indexName")
@ -923,13 +1009,6 @@ internal class AsmGen(private val program: Program,
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) { internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) { when (val index = expr.arrayspec.index) {
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly") is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
is RegisterExpr -> {
when (index.register) {
Register.A -> out(" tay")
Register.X -> out(" txa | tay")
Register.Y -> {}
}
}
is IdentifierReference -> { is IdentifierReference -> {
val indexName = asmIdentifierName(index) val indexName = asmIdentifierName(index)
out(" ldy $indexName") out(" ldy $indexName")
@ -972,9 +1051,12 @@ internal class AsmGen(private val program: Program,
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) = fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromFloatVariable(target, variable) assignmentAsmGen.assignFromFloatVariable(target, variable)
fun assignFromRegister(target: AssignTarget, register: Register) = fun assignFromRegister(target: AssignTarget, register: CpuRegister) =
assignmentAsmGen.assignFromRegister(target, register) assignmentAsmGen.assignFromRegister(target, register)
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) = fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
assignmentAsmGen.assignFromMemoryByte(target, address, identifier) assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
assignmentAsmGen.assignToRegister(reg, value, identifier)
} }

View File

@ -28,10 +28,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
require(assign.aug_op != null) require(assign.aug_op != null)
when { when {
assign.target.register != null -> {
if (inplaceAssignToRegister(assign))
return
}
assign.target.identifier != null -> { assign.target.identifier != null -> {
if (inplaceAssignToIdentifier(assign)) if (inplaceAssignToIdentifier(assign))
return return
@ -99,37 +95,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
// non-const value. // non-const value.
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) { when (assign.value) {
is RegisterExpr -> {
when(assign.aug_op) {
"setvalue" -> {
val reg = (assign.value as RegisterExpr).register
if(reg!=Register.Y) {
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(targetArray)
}
when (reg) {
Register.A -> asmgen.out(" sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" stx (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> {
if (arrayIndex is NumericLiteralValue)
asmgen.out(" lda #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoA(targetArray)
asmgen.out("""
sta ${C64Zeropage.SCRATCH_REG}
tya
ldy ${C64Zeropage.SCRATCH_REG}
sta (${C64Zeropage.SCRATCH_W1}),y
""")
}
}
}
else -> TODO("$assign")
}
return true
}
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when(arrayDt) { when(arrayDt) {
@ -171,11 +136,8 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
return false // we don't put effort into optimizing anything beside simple assignment return false // we don't put effort into optimizing anything beside simple assignment
val valueArrayExpr = assign.value as ArrayIndexedExpression val valueArrayExpr = assign.value as ArrayIndexedExpression
val valueArrayIndex = valueArrayExpr.arrayspec.index val valueArrayIndex = valueArrayExpr.arrayspec.index
if(valueArrayIndex is RegisterExpr || arrayIndex is RegisterExpr) {
throw AssemblyError("cannot generate code for array operations with registers as index")
}
val valueVariablename = asmgen.asmIdentifierName(valueArrayExpr.identifier) val valueVariablename = asmgen.asmIdentifierName(valueArrayExpr.identifier)
val valueDt = valueArrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT) // val valueDt = valueArrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
when(arrayDt) { when(arrayDt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> {
if (valueArrayIndex is NumericLiteralValue) if (valueArrayIndex is NumericLiteralValue)
@ -309,59 +271,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
// non-const value. // non-const value.
when (assign.value) { when (assign.value) {
is RegisterExpr -> {
when (assign.aug_op) {
"setvalue" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" sta $hexAddr")
Register.X -> asmgen.out(" stx $hexAddr")
Register.Y -> asmgen.out(" sty $hexAddr")
}
}
"+=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" clc | adc $hexAddr | sta $hexAddr")
Register.X -> asmgen.out(" txa | clc | adc $hexAddr | sta $hexAddr")
Register.Y -> asmgen.out(" tya | clc | adc $hexAddr | sta $hexAddr")
}
}
"-=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1} | lda $hexAddr | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta $hexAddr")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | lda $hexAddr | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta $hexAddr")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | lda $hexAddr | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta $hexAddr")
}
}
"/=" -> TODO("membyte /= register")
"*=" -> TODO("membyte *= register")
"&=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" and $hexAddr | sta $hexAddr")
Register.X -> asmgen.out(" txa | and $hexAddr | sta $hexAddr")
Register.Y -> asmgen.out(" tya | and $hexAddr | sta $hexAddr")
}
}
"|=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ora $hexAddr | sta $hexAddr")
Register.X -> asmgen.out(" txa | ora $hexAddr | sta $hexAddr")
Register.Y -> asmgen.out(" tya | ora $hexAddr | sta $hexAddr")
}
}
"^=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" eor $hexAddr | sta $hexAddr")
Register.X -> asmgen.out(" txa | eor $hexAddr | sta $hexAddr")
Register.Y -> asmgen.out(" tya | eor $hexAddr | sta $hexAddr")
}
}
"%=" -> TODO("membyte %= register")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when(assign.aug_op) { when(assign.aug_op) {
@ -426,59 +335,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
// non-const value. // non-const value.
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) { when (assign.value) {
is RegisterExpr -> {
when (assign.aug_op) {
"setvalue" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ldy #0 | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" ldy #0 | stx (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> asmgen.out(" tya | ldy #0 | sta (${C64Zeropage.SCRATCH_W1}),y")
}
}
"+=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ldy #0 | clc | adc (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" ldy #0 | txa | clc | adc (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> asmgen.out(" tya | ldy #0 | clc | adc (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
}
}
"-=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ldy #0 | sta ${C64Zeropage.SCRATCH_B1} | lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" ldy #0 | stx ${C64Zeropage.SCRATCH_B1} | lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> asmgen.out(" tya | ldy #0 | sta ${C64Zeropage.SCRATCH_B1} | lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta (${C64Zeropage.SCRATCH_W1}),y")
}
}
"/=" -> TODO("membyte /= register")
"*=" -> TODO("membyte *= register")
"&=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ldy #0 | and (${C64Zeropage.SCRATCH_W1}),y| sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" ldy #0 | txa | and (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> asmgen.out(" tya | ldy #0 | and (${C64Zeropage.SCRATCH_W1}),y| sta (${C64Zeropage.SCRATCH_W1}),y")
}
}
"|=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ldy #0 | ora (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" ldy #0 | txa | ora (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> asmgen.out(" tya | ldy #0 | ora (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
}
}
"^=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" ldy #0 | eor (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.X -> asmgen.out(" ldy #0 | txa | eor (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
Register.Y -> asmgen.out(" tya | ldy #0 | eor (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y")
}
}
"%=" -> TODO("membyte %= register")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
TODO("membyte = variable $assign") TODO("membyte = variable $assign")
@ -551,19 +407,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
// non-const (u)byte value // non-const (u)byte value
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) { when (assign.value) {
is RegisterExpr -> {
when(assign.aug_op) {
"setvalue" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> asmgen.out(" sta $targetName")
Register.X -> asmgen.out(" stx $targetName")
Register.Y -> asmgen.out(" sty $targetName")
}
}
else -> TODO("aug.assign variable = register $assign")
}
return true
}
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when (assign.aug_op) { when (assign.aug_op) {
@ -654,7 +497,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
// non-const value // non-const value
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) { when (assign.value) {
is RegisterExpr -> throw AssemblyError("expected a typecast for assigning register to word")
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when (assign.aug_op) { when (assign.aug_op) {
@ -867,324 +709,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
return false return false
} }
private fun inplaceAssignToRegister(assign: Assignment): Boolean {
val constValue = assign.value.constValue(program)
if (constValue != null) {
val hexValue = constValue.number.toHex()
when (assign.target.register) {
Register.A -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda #$hexValue")
"+=" -> asmgen.out(" clc | adc #$hexValue")
"-=" -> asmgen.out(" sec | sbc #$hexValue")
"/=" -> TODO("A /= const $hexValue")
"*=" -> TODO("A *= const $hexValue")
"&=" -> asmgen.out(" and #$hexValue")
"|=" -> asmgen.out(" ora #$hexValue")
"^=" -> asmgen.out(" eor #$hexValue")
"%=" -> TODO("A %= const $hexValue")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
Register.X -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" ldx #$hexValue")
"+=" -> asmgen.out(" txa | clc | adc #$hexValue | tax")
"-=" -> asmgen.out(" txa | sec | sbc #$hexValue | tax")
"/=" -> TODO("X /= const $hexValue")
"*=" -> TODO("X *= const $hexValue")
"&=" -> asmgen.out(" txa | and #$hexValue | tax")
"|=" -> asmgen.out(" txa | ora #$hexValue | tax")
"^=" -> asmgen.out(" txa | eor #$hexValue | tax")
"%=" -> TODO("X %= const $hexValue")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
Register.Y -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" ldy #$hexValue")
"+=" -> asmgen.out(" tya | clc | adc #$hexValue | tay")
"-=" -> asmgen.out(" tya | sec | sbc #$hexValue | tay")
"/=" -> TODO("Y /= const $hexValue")
"*=" -> TODO("Y *= const $hexValue")
"&=" -> asmgen.out(" tya | and #$hexValue | tay")
"|=" -> asmgen.out(" tya | ora #$hexValue | tay")
"^=" -> asmgen.out(" tya | eor #$hexValue | tay")
"%=" -> TODO("Y %= const $hexValue")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
}
return true
}
// non-const value.
when (assign.value) {
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when (assign.target.register) {
Register.A -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda $sourceName")
"+=" -> asmgen.out(" clc | adc $sourceName")
"-=" -> asmgen.out(" sec | sbc $sourceName")
"/=" -> TODO("A /= variable")
"*=" -> TODO("A *= variable")
"&=" -> asmgen.out(" and $sourceName")
"|=" -> asmgen.out(" ora $sourceName")
"^=" -> asmgen.out(" eor $sourceName")
"%=" -> TODO("A %= variable")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
Register.X -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" ldx $sourceName")
"+=" -> asmgen.out(" txa | clc | adc $sourceName | tax")
"-=" -> asmgen.out(" txa | sec | sbc $sourceName | tax")
"/=" -> TODO("X /= variable")
"*=" -> TODO("X *= variable")
"&=" -> asmgen.out(" txa | and $sourceName | tax")
"|=" -> asmgen.out(" txa | ora $sourceName | tax")
"^=" -> asmgen.out(" txa | eor $sourceName | tax")
"%=" -> TODO("X %= variable")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
Register.Y -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" ldy $sourceName")
"+=" -> asmgen.out(" tya | clc | adc $sourceName | tay")
"-=" -> asmgen.out(" tya | sec | sbc $sourceName | tay")
"/=" -> TODO("Y /= variable")
"*=" -> TODO("Y *= variable")
"&=" -> asmgen.out(" tya | and $sourceName | tay")
"|=" -> asmgen.out(" tya | ora $sourceName | tay")
"^=" -> asmgen.out(" tya | eor $sourceName | tay")
"%=" -> TODO("Y %= variable")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
}
return true
}
is RegisterExpr -> {
when (assign.aug_op) {
"setvalue" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> {
when (assign.target.register!!) {
Register.A -> {}
Register.X -> asmgen.out(" tax")
Register.Y -> asmgen.out(" tay")
}
}
Register.X -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" txa")
Register.X -> {}
Register.Y -> asmgen.out(" txa | tay")
}
}
Register.Y -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" tya")
Register.X -> asmgen.out(" tya | tax")
Register.Y -> {}
}
}
}
}
"+=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> {
when (assign.target.register!!) {
Register.A -> throw AssemblyError("should have been optimized away")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1} | tax")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1} | tay")
}
}
Register.X -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1}")
Register.X -> throw AssemblyError("should have been optimized away")
Register.Y -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | clc | adc ${C64Zeropage.SCRATCH_B1} | tay")
}
}
Register.Y -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | txa | clc | adc ${C64Zeropage.SCRATCH_B1} | tax")
Register.Y -> throw AssemblyError("should have been optimized away")
}
}
}
}
"-=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> {
when (assign.target.register!!) {
Register.A -> throw AssemblyError("should have been optimized away")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1} | tax")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1} | tay")
}
}
Register.X -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1}")
Register.X -> throw AssemblyError("should have been optimized away")
Register.Y -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | sec | sbc ${C64Zeropage.SCRATCH_B1} | tay")
}
}
Register.Y -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | txa | sec | sbc ${C64Zeropage.SCRATCH_B1} | tax")
Register.Y -> throw AssemblyError("should have been optimized away")
}
}
}
}
"/=" -> TODO("register /= register")
"*=" -> TODO("register *= register")
"&=" -> {
when ((assign.value as RegisterExpr).register) {
Register.A -> {
when (assign.target.register!!) {
Register.A -> throw AssemblyError("should have been optimized away")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1} | tax")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1} | tay")
}
}
Register.X -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1}")
Register.X -> throw AssemblyError("should have been optimized away")
Register.Y -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | and ${C64Zeropage.SCRATCH_B1} | tay")
}
}
Register.Y -> {
when (assign.target.register!!) {
Register.A -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | and ${C64Zeropage.SCRATCH_B1} | tax")
Register.Y -> throw AssemblyError("should have been optimized away")
}
}
}
}
"|=" -> TODO("register |= register")
"^=" -> TODO("register ^= register")
"%=" -> TODO("register %= register")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
is DirectMemoryRead -> {
val address = (assign.value as DirectMemoryRead).addressExpression.constValue(program)?.number
if (address != null) {
val hexAddr = address.toHex()
when (assign.target.register) {
Register.A -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda $hexAddr")
"+=" -> asmgen.out(" clc | adc $hexAddr")
"-=" -> asmgen.out(" sec | sbc $hexAddr")
"/=" -> TODO("A /= memory $hexAddr")
"*=" -> TODO("A *= memory $hexAddr")
"&=" -> asmgen.out(" and $hexAddr")
"|=" -> asmgen.out(" ora $hexAddr")
"^=" -> asmgen.out(" eor $hexAddr")
"%=" -> TODO("A %= memory $hexAddr")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
Register.X -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" ldx $hexAddr")
"+=" -> asmgen.out(" txa | clc | adc $hexAddr | tax")
"-=" -> asmgen.out(" txa | sec | sbc $hexAddr | tax")
"/=" -> TODO("X /= memory $hexAddr")
"*=" -> TODO("X *= memory $hexAddr")
"&=" -> asmgen.out(" txa | and $hexAddr | tax")
"|=" -> asmgen.out(" txa | ora $hexAddr | tax")
"^=" -> asmgen.out(" txa | eor $hexAddr | tax")
"%=" -> TODO("X %= memory $hexAddr")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
Register.Y -> {
when (assign.aug_op) {
"setvalue" -> asmgen.out(" ldy $hexAddr")
"+=" -> asmgen.out(" tya | clc | adc $hexAddr | tay")
"-=" -> asmgen.out(" tya | sec | sbc $hexAddr | tay")
"/=" -> TODO("Y /= memory $hexAddr")
"*=" -> TODO("Y *= memory $hexAddr")
"&=" -> asmgen.out(" tya | and $hexAddr | tay")
"|=" -> asmgen.out(" tya | ora $hexAddr | tay")
"^=" -> asmgen.out(" tya | eor $hexAddr | tay")
"%=" -> TODO("Y %= memory $hexAddr")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
}
}
return true
}
}
is ArrayIndexedExpression -> {
if (assign.aug_op == "setvalue") {
val arrayExpr = assign.value as ArrayIndexedExpression
val arrayIndex = arrayExpr.arrayspec.index
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
if (arrayDt != DataType.ARRAY_B && arrayDt != DataType.ARRAY_UB && arrayDt != DataType.STR)
throw AssemblyError("assign to register: expected byte array or string source")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(arrayExpr)
when(assign.target.register!!) {
Register.A -> asmgen.out(" lda $variablename,y")
Register.X -> asmgen.out(" ldx $variablename,y")
Register.Y -> asmgen.out(" lda $variablename,y | tay")
}
} else {
// TODO optimize more augmented assignment cases
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
}
return true
}
is AddressOf -> throw AssemblyError("can't load a memory address into a register")
else -> {
fallbackAssignment(assign)
return true
}
}
return false
}
private fun fallbackAssignment(assign: Assignment) { private fun fallbackAssignment(assign: Assignment) {
if (assign.aug_op != "setvalue") { if (assign.aug_op != "setvalue") {
/* stack-based evaluation of the expression is required */ /* stack-based evaluation of the expression is required */
@ -1226,12 +750,8 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
else -> throw AssemblyError("weird numval type") else -> throw AssemblyError("weird numval type")
} }
} }
is RegisterExpr -> {
assignFromRegister(assign.target, (assign.value as RegisterExpr).register)
}
is IdentifierReference -> { is IdentifierReference -> {
val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT) when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) {
when (type) {
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference) DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference)
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference) DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference)
DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference) DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
@ -1312,7 +832,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
assignFromEvalResult(assign.target) assignFromEvalResult(assign.target)
} }
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign") is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign")
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened ${assign.value.position}")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}") is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}")
} }
} }
@ -1320,15 +839,9 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
internal fun assignFromEvalResult(target: AssignTarget) { internal fun assignFromEvalResult(target: AssignTarget) {
val targetIdent = target.identifier val targetIdent = target.identifier
when { when {
target.register != null -> {
if (target.register == Register.X)
throw AssemblyError("can't pop into X register - use variable instead")
asmgen.out(" inx | ld${target.register.name.toLowerCase()} $ESTACK_LO_HEX,x ")
}
targetIdent != null -> { targetIdent != null -> {
val targetName = asmgen.asmIdentifierName(targetIdent) val targetName = asmgen.asmIdentifierName(targetIdent)
val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT) when (val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)) {
when (targetDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $targetName") asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $targetName")
} }
@ -1353,7 +866,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} }
target.memoryAddress != null -> { target.memoryAddress != null -> {
asmgen.out(" inx | ldy $ESTACK_LO_HEX,x") asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
} }
target.arrayindexed != null -> { target.arrayindexed != null -> {
val arrayDt = target.arrayindexed!!.identifier.inferType(program).typeOrElse(DataType.STRUCT) val arrayDt = target.arrayindexed!!.identifier.inferType(program).typeOrElse(DataType.STRUCT)
@ -1470,9 +983,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
val targetIdent = target.identifier val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed val targetArrayIdx = target.arrayindexed
when { when {
target.register != null -> {
asmgen.out(" ld${target.register.name.toLowerCase()} $sourceName")
}
targetIdent != null -> { targetIdent != null -> {
val targetName = asmgen.asmIdentifierName(targetIdent) val targetName = asmgen.asmIdentifierName(targetIdent)
asmgen.out(""" asmgen.out("""
@ -1516,7 +1026,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} }
} }
internal fun assignFromRegister(target: AssignTarget, register: Register) { internal fun assignFromRegister(target: AssignTarget, register: CpuRegister) {
val targetIdent = target.identifier val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed val targetArrayIdx = target.arrayindexed
when { when {
@ -1524,28 +1034,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
val targetName = asmgen.asmIdentifierName(targetIdent) val targetName = asmgen.asmIdentifierName(targetIdent)
asmgen.out(" st${register.name.toLowerCase()} $targetName") asmgen.out(" st${register.name.toLowerCase()} $targetName")
} }
target.register != null -> {
when (register) {
Register.A -> when (target.register) {
Register.A -> {
}
Register.X -> asmgen.out(" tax")
Register.Y -> asmgen.out(" tay")
}
Register.X -> when (target.register) {
Register.A -> asmgen.out(" txa")
Register.X -> {
}
Register.Y -> asmgen.out(" txy")
}
Register.Y -> when (target.register) {
Register.A -> asmgen.out(" tya")
Register.X -> asmgen.out(" tyx")
Register.Y -> {
}
}
}
}
target.memoryAddress != null -> { target.memoryAddress != null -> {
storeRegisterInMemoryAddress(register, target.memoryAddress) storeRegisterInMemoryAddress(register, target.memoryAddress)
} }
@ -1556,34 +1044,16 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
is NumericLiteralValue -> { is NumericLiteralValue -> {
val memindex = index.number.toInt() val memindex = index.number.toInt()
when (register) { when (register) {
Register.A -> asmgen.out(" sta $targetName+$memindex") CpuRegister.A -> asmgen.out(" sta $targetName+$memindex")
Register.X -> asmgen.out(" stx $targetName+$memindex") CpuRegister.X -> asmgen.out(" stx $targetName+$memindex")
Register.Y -> asmgen.out(" sty $targetName+$memindex") CpuRegister.Y -> asmgen.out(" sty $targetName+$memindex")
} }
} }
is RegisterExpr -> {
when (register) {
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
}
when (index.register) {
Register.A -> {
}
Register.X -> asmgen.out(" txa")
Register.Y -> asmgen.out(" tya")
}
asmgen.out("""
tay
lda ${C64Zeropage.SCRATCH_B1}
sta $targetName,y
""")
}
is IdentifierReference -> { is IdentifierReference -> {
when (register) { when (register) {
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") CpuRegister.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") CpuRegister.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") CpuRegister.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
} }
asmgen.out(""" asmgen.out("""
lda ${asmgen.asmIdentifierName(index)} lda ${asmgen.asmIdentifierName(index)}
@ -1597,9 +1067,9 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
asmgen.translateExpression(index) asmgen.translateExpression(index)
asmgen.restoreRegister(register) asmgen.restoreRegister(register)
when (register) { when (register) {
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") CpuRegister.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") CpuRegister.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") CpuRegister.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
} }
asmgen.out(""" asmgen.out("""
inx inx
@ -1615,7 +1085,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} }
} }
private fun storeRegisterInMemoryAddress(register: Register, memoryAddress: DirectMemoryWrite) { private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
val addressExpr = memoryAddress.addressExpression val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue val addressLv = addressExpr as? NumericLiteralValue
val registerName = register.name.toLowerCase() val registerName = register.name.toLowerCase()
@ -1624,19 +1094,19 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
addressExpr is IdentifierReference -> { addressExpr is IdentifierReference -> {
val targetName = asmgen.asmIdentifierName(addressExpr) val targetName = asmgen.asmIdentifierName(addressExpr)
when (register) { when (register) {
Register.A -> asmgen.out(""" CpuRegister.A -> asmgen.out("""
ldy $targetName ldy $targetName
sty (+) +1 sty (+) +1
ldy $targetName+1 ldy $targetName+1
sty (+) +2 sty (+) +2
+ sta ${'$'}ffff ; modified""") + sta ${'$'}ffff ; modified""")
Register.X -> asmgen.out(""" CpuRegister.X -> asmgen.out("""
ldy $targetName ldy $targetName
sty (+) +1 sty (+) +1
ldy $targetName+1 ldy $targetName+1
sty (+) +2 sty (+) +2
+ stx ${'$'}ffff ; modified""") + stx ${'$'}ffff ; modified""")
Register.Y -> asmgen.out(""" CpuRegister.Y -> asmgen.out("""
lda $targetName lda $targetName
sta (+) +1 sta (+) +1
lda $targetName+1 lda $targetName+1
@ -1649,10 +1119,9 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
asmgen.translateExpression(addressExpr) asmgen.translateExpression(addressExpr)
asmgen.restoreRegister(register) asmgen.restoreRegister(register)
when (register) { when (register) {
Register.A -> asmgen.out(" tay") CpuRegister.A -> asmgen.out(" tay")
Register.X -> throw AssemblyError("can't use X register here") CpuRegister.X -> throw AssemblyError("can't use X register here")
Register.Y -> { CpuRegister.Y -> {}
}
} }
asmgen.out(""" asmgen.out("""
inx inx
@ -1715,16 +1184,13 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
val targetIdent = target.identifier val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed val targetArrayIdx = target.arrayindexed
when { when {
target.register != null -> {
asmgen.out(" ld${target.register.name.toLowerCase()} #${byte.toHex()}")
}
targetIdent != null -> { targetIdent != null -> {
val targetName = asmgen.asmIdentifierName(targetIdent) val targetName = asmgen.asmIdentifierName(targetIdent)
asmgen.out(" lda #${byte.toHex()} | sta $targetName ") asmgen.out(" lda #${byte.toHex()} | sta $targetName ")
} }
target.memoryAddress != null -> { target.memoryAddress != null -> {
asmgen.out(" ldy #${byte.toHex()}") asmgen.out(" ldy #${byte.toHex()}")
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
} }
targetArrayIdx != null -> { targetArrayIdx != null -> {
val index = targetArrayIdx.arrayspec.index val index = targetArrayIdx.arrayspec.index
@ -1847,9 +1313,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
val targetArrayIdx = target.arrayindexed val targetArrayIdx = target.arrayindexed
if (address != null) { if (address != null) {
when { when {
target.register != null -> {
asmgen.out(" ld${target.register.name.toLowerCase()} ${address.toHex()}")
}
targetIdent != null -> { targetIdent != null -> {
val targetName = asmgen.asmIdentifierName(targetIdent) val targetName = asmgen.asmIdentifierName(targetIdent)
asmgen.out(""" asmgen.out("""
@ -1859,7 +1322,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} }
target.memoryAddress != null -> { target.memoryAddress != null -> {
asmgen.out(" ldy ${address.toHex()}") asmgen.out(" ldy ${address.toHex()}")
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
} }
targetArrayIdx != null -> { targetArrayIdx != null -> {
val index = targetArrayIdx.arrayspec.index val index = targetArrayIdx.arrayspec.index
@ -1871,18 +1334,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} else if (identifier != null) { } else if (identifier != null) {
val sourceName = asmgen.asmIdentifierName(identifier) val sourceName = asmgen.asmIdentifierName(identifier)
when { when {
target.register != null -> {
asmgen.out("""
lda $sourceName
sta (+) + 1
lda $sourceName+1
sta (+) + 2""")
when (target.register) {
Register.A -> asmgen.out("+ lda ${'$'}ffff\t; modified")
Register.X -> asmgen.out("+ ldx ${'$'}ffff\t; modified")
Register.Y -> asmgen.out("+ ldy ${'$'}ffff\t; modified")
}
}
targetIdent != null -> { targetIdent != null -> {
val targetName = asmgen.asmIdentifierName(targetIdent) val targetName = asmgen.asmIdentifierName(targetIdent)
asmgen.out(""" asmgen.out("""
@ -1895,7 +1346,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} }
target.memoryAddress != null -> { target.memoryAddress != null -> {
asmgen.out(" ldy $sourceName") asmgen.out(" ldy $sourceName")
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
} }
targetArrayIdx != null -> { targetArrayIdx != null -> {
val index = targetArrayIdx.arrayspec.index val index = targetArrayIdx.arrayspec.index
@ -1926,4 +1377,13 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
throw AssemblyError("weird array type") throw AssemblyError("weird array type")
} }
} }
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) {
if(value!=null) {
asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}")
} else if(identifier!=null) {
val name = asmgen.asmIdentifierName(identifier)
asmgen.out(" ld${reg.toString().toLowerCase()} $name")
}
}
} }

View File

@ -172,13 +172,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" jsr prog8_lib.ror2_mem_ub") asmgen.out(" jsr prog8_lib.ror2_mem_ub")
} }
} }
is RegisterExpr -> {
when (what.register) {
Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ")
Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ")
Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ")
}
}
is IdentifierReference -> { is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what) val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable") asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
@ -231,13 +224,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
""") """)
} }
} }
is RegisterExpr -> {
when (what.register) {
Register.A -> asmgen.out(" ror a")
Register.X -> asmgen.out(" txa | ror a | tax")
Register.Y -> asmgen.out(" tya | ror a | tay")
}
}
is IdentifierReference -> { is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what) val variable = asmgen.asmIdentifierName(what)
asmgen.out(" ror $variable") asmgen.out(" ror $variable")
@ -283,13 +269,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" jsr prog8_lib.rol2_mem_ub") asmgen.out(" jsr prog8_lib.rol2_mem_ub")
} }
} }
is RegisterExpr -> {
when (what.register) {
Register.A -> asmgen.out(" cmp #\$80 | rol a ")
Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax")
Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay")
}
}
is IdentifierReference -> { is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what) val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable") asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
@ -342,13 +321,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
""") """)
} }
} }
is RegisterExpr -> {
when (what.register) {
Register.A -> asmgen.out(" rol a")
Register.X -> asmgen.out(" txa | rol a | tax")
Register.Y -> asmgen.out(" tya | rol a | tay")
}
}
is IdentifierReference -> { is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what) val variable = asmgen.asmIdentifierName(what)
asmgen.out(" rol $variable") asmgen.out(" rol $variable")
@ -380,13 +352,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (dt.typeOrElse(DataType.STRUCT)) { when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> { DataType.UBYTE -> {
when (what) { when (what) {
is RegisterExpr -> {
when (what.register) {
Register.A -> asmgen.out(" lsr a")
Register.X -> asmgen.out(" txa | lsr a | tax")
Register.Y -> asmgen.out(" tya | lsr a | tay")
}
}
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}") is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
is DirectMemoryRead -> { is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) { if (what.addressExpression is NumericLiteralValue) {
@ -464,13 +429,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (dt.typeOrElse(DataType.STRUCT)) { when (dt.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> { in ByteDatatypes -> {
when (what) { when (what) {
is RegisterExpr -> {
when (what.register) {
Register.A -> asmgen.out(" asl a")
Register.X -> asmgen.out(" txa | asl a | tax")
Register.Y -> asmgen.out(" tya | asl a | tay")
}
}
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}") is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
is DirectMemoryRead -> { is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) { if (what.addressExpression is NumericLiteralValue) {

View File

@ -25,11 +25,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
is AddressOf -> translateExpression(expression) is AddressOf -> translateExpression(expression)
is DirectMemoryRead -> translateExpression(expression) is DirectMemoryRead -> translateExpression(expression)
is NumericLiteralValue -> translateExpression(expression) is NumericLiteralValue -> translateExpression(expression)
is RegisterExpr -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression) is IdentifierReference -> translateExpression(expression)
is FunctionCall -> translateExpression(expression) is FunctionCall -> translateExpression(expression)
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable") is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values") is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
} }
} }
@ -175,14 +173,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
} }
} }
private fun translateExpression(expr: RegisterExpr) {
when(expr.register) {
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
Register.X -> asmgen.out(" pha | txa | sta $ESTACK_LO_HEX,x | dex | pla")
Register.Y -> asmgen.out(" pha | tya | sta $ESTACK_LO_HEX,x | dex | pla")
}
}
private fun translateExpression(expr: IdentifierReference) { private fun translateExpression(expr: IdentifierReference) {
val varname = asmgen.asmIdentifierName(expr) val varname = asmgen.asmIdentifierName(expr)
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) { when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {

View File

@ -2,7 +2,6 @@ package prog8.compiler.target.c64.codegen
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.Register
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.RangeExpr import prog8.ast.expressions.RangeExpr
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
@ -55,28 +54,8 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
// bytes, step 1 or -1 // bytes, step 1 or -1
val incdec = if(stepsize==1) "inc" else "dec" val incdec = if(stepsize==1) "inc" else "dec"
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
sta $loopLabel+1
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
beq $endLabel
$incdec $loopLabel+1
jmp $loopLabel
$endLabel inx""")
} else {
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.translateExpression(range.to) asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from) asmgen.translateExpression(range.from)
asmgen.out(""" asmgen.out("""
@ -93,46 +72,12 @@ $continueLabel lda $varname
jmp $loopLabel jmp $loopLabel
$endLabel inx""") $endLabel inx""")
} }
}
else { else {
// bytes, step >= 2 or <= -2 // bytes, step >= 2 or <= -2
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
sta $loopLabel+1
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $loopLabel+1""")
if(stepsize>0) {
asmgen.out("""
clc
adc #$stepsize
sta $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
bcc $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
sec
sbc #${stepsize.absoluteValue}
sta $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
bcs $loopLabel""")
}
asmgen.out("""
$endLabel inx""")
} else {
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.translateExpression(range.to) asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from) asmgen.translateExpression(range.from)
asmgen.out(""" asmgen.out("""
@ -163,7 +108,6 @@ $continueLabel lda $varname""")
$endLabel inx""") $endLabel inx""")
} }
} }
}
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
when { when {
@ -171,8 +115,8 @@ $endLabel inx""")
stepsize == 1 || stepsize == -1 -> { stepsize == 1 || stepsize == -1 -> {
asmgen.translateExpression(range.to) asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position), val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
null, range.from, range.position) null, range.from, range.position)
assignLoopvar.linkParents(stmt) assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar) asmgen.translate(assignLoopvar)
@ -207,8 +151,8 @@ $endLabel inx""")
// (u)words, step >= 2 // (u)words, step >= 2
asmgen.translateExpression(range.to) asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position), val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
null, range.from, range.position) null, range.from, range.position)
assignLoopvar.linkParents(stmt) assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar) asmgen.translate(assignLoopvar)
@ -256,8 +200,8 @@ $endLabel inx""")
// (u)words, step <= -2 // (u)words, step <= -2
asmgen.translateExpression(range.to) asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position), val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
null, range.from, range.position) null, range.from, range.position)
assignLoopvar.linkParents(stmt) assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar) asmgen.translate(assignLoopvar)
@ -319,8 +263,6 @@ $endLabel inx""")
val decl = ident.targetVarDecl(program.namespace)!! val decl = ident.targetVarDecl(program.namespace)!!
when(iterableDt) { when(iterableDt) {
DataType.STR -> { DataType.STR -> {
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.out(""" asmgen.out("""
lda #<$iterableName lda #<$iterableName
ldy #>$iterableName ldy #>$iterableName
@ -328,8 +270,7 @@ $endLabel inx""")
sty $loopLabel+2 sty $loopLabel+2
$loopLabel lda ${65535.toHex()} ; modified $loopLabel lda ${65535.toHex()} ; modified
beq $endLabel""") beq $endLabel""")
if(stmt.loopVar!=null) asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel inc $loopLabel+1 $continueLabel inc $loopLabel+1
@ -341,8 +282,6 @@ $endLabel""")
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases // TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
val length = decl.arraysize!!.size()!! val length = decl.arraysize!!.size()!!
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
val counterLabel = asmgen.makeLabel("for_counter") val counterLabel = asmgen.makeLabel("for_counter")
val modifiedLabel = asmgen.makeLabel("for_modified") val modifiedLabel = asmgen.makeLabel("for_modified")
asmgen.out(""" asmgen.out("""
@ -353,8 +292,7 @@ $endLabel""")
ldy #0 ldy #0
$loopLabel sty $counterLabel $loopLabel sty $counterLabel
$modifiedLabel lda ${65535.toHex()},y ; modified""") $modifiedLabel lda ${65535.toHex()},y ; modified""")
if(stmt.loopVar!=null) asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel ldy $counterLabel $continueLabel ldy $counterLabel
@ -368,12 +306,10 @@ $endLabel""")
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases // TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
val length = decl.arraysize!!.size()!! * 2 val length = decl.arraysize!!.size()!! * 2
if(stmt.loopRegister!=null)
throw AssemblyError("can't use register to loop over words")
val counterLabel = asmgen.makeLabel("for_counter") val counterLabel = asmgen.makeLabel("for_counter")
val modifiedLabel = asmgen.makeLabel("for_modified") val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modified2") val modifiedLabel2 = asmgen.makeLabel("for_modified2")
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!) val loopvarName = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out(""" asmgen.out("""
lda #<$iterableName lda #<$iterableName
ldy #>$iterableName ldy #>$iterableName
@ -421,89 +357,8 @@ $endLabel""")
when(iterableDt) { when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
val counterLabel = asmgen.makeLabel("for_counter") val counterLabel = asmgen.makeLabel("for_counter")
if(stmt.loopRegister!=null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
when {
range.step==1 -> {
// step = 1
asmgen.out("""
lda #${range.first}
sta $loopLabel+1
lda #${range.last-range.first+1 and 255}
sta $counterLabel
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
inc $loopLabel+1
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
range.step==-1 -> {
// step = -1
asmgen.out("""
lda #${range.first}
sta $loopLabel+1
lda #${range.first-range.last+1 and 255}
sta $counterLabel
$loopLabel lda #0 ; modified """)
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
dec $loopLabel+1
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
range.step >= 2 -> {
// step >= 2
asmgen.out("""
lda #${(range.last-range.first) / range.step + 1}
sta $counterLabel
lda #${range.first}
$loopLabel pha""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel pla
dec $counterLabel
beq $endLabel
clc
adc #${range.step}
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
else -> {
// step <= -2
asmgen.out("""
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
sta $counterLabel
lda #${range.first}
$loopLabel pha""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel pla
dec $counterLabel
beq $endLabel
sec
sbc #${range.step.absoluteValue}
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
}
} else {
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
when { when {
range.step==1 -> { range.step==1 -> {
// step = 1 // step = 1
@ -581,10 +436,9 @@ $endLabel""")
} }
} }
} }
}
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
// loop over word range via loopvar // loop over word range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!) val varname = asmgen.asmIdentifierName(stmt.loopVar)
when { when {
range.step == 1 -> { range.step == 1 -> {
// word, step = 1 // word, step = 1

View File

@ -19,7 +19,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// output the code to setup the parameters and perform the actual call // output the code to setup the parameters and perform the actual call
// does NOT output the code to deal with the result values! // does NOT output the code to deal with the result values!
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = Register.X in sub.asmClobbers || sub.regXasResult() val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult()
if(saveX) if(saveX)
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y... asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
@ -41,7 +41,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
when { when {
stmt.args.all {it is AddressOf || stmt.args.all {it is AddressOf ||
it is NumericLiteralValue || it is NumericLiteralValue ||
it is StructLiteralValue ||
it is StringLiteralValue || it is StringLiteralValue ||
it is ArrayLiteralValue || it is ArrayLiteralValue ||
it is IdentifierReference} -> { it is IdentifierReference} -> {
@ -50,14 +49,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
argumentViaRegister(sub, arg.first, arg.second) argumentViaRegister(sub, arg.first, arg.second)
} }
} }
stmt.args.all {it is RegisterExpr} -> {
val argRegisters = stmt.args.map {(it as RegisterExpr).register.toString()}
val paramRegisters = sub.asmParameterRegisters.map { it.registerOrPair?.toString() }
if(argRegisters != paramRegisters) {
// all args are registers but differ from the function params. Can't pass directly, work via stack.
argsViaStackEvaluation(stmt, sub)
}
}
else -> { else -> {
// Risk of clobbering due to complex expression args. Work via the stack. // Risk of clobbering due to complex expression args. Work via the stack.
argsViaStackEvaluation(stmt, sub) argsViaStackEvaluation(stmt, sub)
@ -115,7 +106,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val paramVar = parameter.value val paramVar = parameter.value
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".") val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
target.linkParents(value.parent) target.linkParents(value.parent)
when (value) { when (value) {
is NumericLiteralValue -> { is NumericLiteralValue -> {
@ -138,9 +129,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
else -> throw AssemblyError("weird parameter datatype") else -> throw AssemblyError("weird parameter datatype")
} }
} }
is RegisterExpr -> {
asmgen.assignFromRegister(target, value.register)
}
is DirectMemoryRead -> { is DirectMemoryRead -> {
when(value.addressExpression) { when(value.addressExpression) {
is NumericLiteralValue -> { is NumericLiteralValue -> {
@ -153,7 +141,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
else -> { else -> {
asmgen.translateExpression(value.addressExpression) asmgen.translateExpression(value.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx") asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
asmgen.assignFromRegister(target, Register.A) asmgen.assignFromRegister(target, CpuRegister.A)
} }
} }
} }
@ -201,20 +189,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
bcs ++ bcs ++
+ clc + clc
+ +
""")
}
is RegisterExpr -> {
when(value.register) {
Register.A -> asmgen.out(" cmp #0")
Register.X -> asmgen.out(" txa")
Register.Y -> asmgen.out(" tya")
}
asmgen.out("""
beq +
sec
bcs ++
+ clc
+
""") """)
} }
else -> { else -> {
@ -237,14 +211,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
register!=null && register.name.length==1 -> { register!=null && register.name.length==1 -> {
when (value) { when (value) {
is NumericLiteralValue -> { is NumericLiteralValue -> {
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position) asmgen.assignToRegister(CpuRegister.valueOf(register.name), value.number.toShort(), null)
target.linkParents(value.parent)
asmgen.assignFromByteConstant(target, value.number.toShort())
} }
is IdentifierReference -> { is IdentifierReference -> {
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position) asmgen.assignToRegister(CpuRegister.valueOf(register.name), null, value)
target.linkParents(value.parent)
asmgen.assignFromByteVariable(target, value)
} }
else -> { else -> {
asmgen.translateExpression(value) asmgen.translateExpression(value)

View File

@ -4,7 +4,6 @@ import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.RegisterExpr
import prog8.ast.statements.PostIncrDecr import prog8.ast.statements.PostIncrDecr
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
@ -17,28 +16,10 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
val targetIdent = stmt.target.identifier val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memoryAddress val targetMemory = stmt.target.memoryAddress
val targetArrayIdx = stmt.target.arrayindexed val targetArrayIdx = stmt.target.arrayindexed
val targetRegister = stmt.target.register
when { when {
targetRegister!=null -> {
when(targetRegister) {
Register.A -> {
if(incr)
asmgen.out(" clc | adc #1 ")
else
asmgen.out(" sec | sbc #1 ")
}
Register.X -> {
if(incr) asmgen.out(" inx") else asmgen.out(" dex")
}
Register.Y -> {
if(incr) asmgen.out(" iny") else asmgen.out(" dey")
}
}
}
targetIdent!=null -> { targetIdent!=null -> {
val what = asmgen.asmIdentifierName(targetIdent) val what = asmgen.asmIdentifierName(targetIdent)
val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT) when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
when (dt) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what") in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
in WordDatatypes -> { in WordDatatypes -> {
if(incr) if(incr)
@ -103,10 +84,6 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
else -> throw AssemblyError("need numeric type") else -> throw AssemblyError("need numeric type")
} }
} }
is RegisterExpr -> {
asmgen.translateArrayIndexIntoA(targetArrayIdx)
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
}
is IdentifierReference -> { is IdentifierReference -> {
asmgen.translateArrayIndexIntoA(targetArrayIdx) asmgen.translateArrayIndexIntoA(targetArrayIdx)
incrDecrArrayvalueWithIndexA(incr, arrayDt, what) incrDecrArrayvalueWithIndexA(incr, arrayDt, what)

View File

@ -342,8 +342,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
val rangeTo = iterableRange.to as? NumericLiteralValue val rangeTo = iterableRange.to as? NumericLiteralValue
if(rangeFrom==null || rangeTo==null) return noModifications if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar?.targetVarDecl(program.namespace) val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)!!
if(loopvar!=null) {
val stepLiteral = iterableRange.step as? NumericLiteralValue val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) { when(loopvar.datatype) {
DataType.UBYTE -> { DataType.UBYTE -> {
@ -376,7 +375,6 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
} }
else -> throw FatalAstException("invalid loopvar datatype $loopvar") else -> throw FatalAstException("invalid loopvar datatype $loopvar")
} }
}
return noModifications return noModifications
} }

View File

@ -610,8 +610,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (amount == 0) { if (amount == 0) {
return expr.left return expr.left
} }
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT) when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) {
when (targetDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
if (amount >= 8) { if (amount >= 8) {
return NumericLiteralValue.optimalInteger(0, expr.position) return NumericLiteralValue.optimalInteger(0, expr.position)

View File

@ -46,7 +46,11 @@ internal class StatementOptimizer(private val program: Program,
if(subroutine.asmAddress==null && !forceOutput) { if(subroutine.asmAddress==null && !forceOutput) {
if(subroutine.containsNoCodeNorVars()) { if(subroutine.containsNoCodeNorVars()) {
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position) errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, parent)) val removals = callgraph.calledBy.getValue(subroutine).map {
IAstModification.Remove(it, it.parent)
}.toMutableList()
removals += IAstModification.Remove(subroutine, parent)
return removals
} }
} }
@ -191,11 +195,11 @@ internal class StatementOptimizer(private val program: Program,
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> { override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
if(forLoop.body.containsNoCodeNorVars()) { if(forLoop.body.containsNoCodeNorVars()) {
// remove empty for loop errors.warn("removing empty for loop", forLoop.position)
return listOf(IAstModification.Remove(forLoop, parent)) return listOf(IAstModification.Remove(forLoop, parent))
} else if(forLoop.body.statements.size==1) { } else if(forLoop.body.statements.size==1) {
val loopvar = forLoop.body.statements[0] as? VarDecl val loopvar = forLoop.body.statements[0] as? VarDecl
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) { if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
// remove empty for loop (only loopvar decl in it) // remove empty for loop (only loopvar decl in it)
return listOf(IAstModification.Remove(forLoop, parent)) return listOf(IAstModification.Remove(forLoop, parent))
} }
@ -207,7 +211,7 @@ internal class StatementOptimizer(private val program: Program,
// for loop over a (constant) range of just a single value-- optimize the loop away // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@ -222,7 +226,7 @@ internal class StatementOptimizer(private val program: Program,
val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0] val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0]
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position) val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@ -235,7 +239,7 @@ internal class StatementOptimizer(private val program: Program,
if(av!=null) { if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment( scope.statements.add(Assignment(
AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position), AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
forLoop.position)) forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@ -247,18 +251,18 @@ internal class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> { override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val constvalue = repeatLoop.untilCondition.constValue(program) val constvalue = untilLoop.untilCondition.constValue(program)
if(constvalue!=null) { if(constvalue!=null) {
if(constvalue.asBooleanValue) { if(constvalue.asBooleanValue) {
// always true -> keep only the statement block (if there are no continue and break statements) // always true -> keep only the statement block (if there are no continue and break statements)
errors.warn("condition is always true", repeatLoop.untilCondition.position) errors.warn("condition is always true", untilLoop.untilCondition.position)
if(!hasContinueOrBreak(repeatLoop.body)) if(!hasContinueOrBreak(untilLoop.body))
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent)) return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
} else { } else {
// always false // always false
val forever = ForeverLoop(repeatLoop.body, repeatLoop.position) val forever = RepeatLoop(null, untilLoop.body, untilLoop.position)
return listOf(IAstModification.ReplaceNode(repeatLoop, forever, parent)) return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent))
} }
} }
return noModifications return noModifications
@ -269,7 +273,7 @@ internal class StatementOptimizer(private val program: Program,
if(constvalue!=null) { if(constvalue!=null) {
return if(constvalue.asBooleanValue) { return if(constvalue.asBooleanValue) {
// always true // always true
val forever = ForeverLoop(whileLoop.body, whileLoop.position) val forever = RepeatLoop(null, whileLoop.body, whileLoop.position)
listOf(IAstModification.ReplaceNode(whileLoop, forever, parent)) listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
} else { } else {
// always false -> remove the while statement altogether // always false -> remove the while statement altogether
@ -280,6 +284,24 @@ internal class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
val iter = repeatLoop.iterations
if(iter!=null) {
if(repeatLoop.body.containsNoCodeNorVars()) {
errors.warn("empty loop removed", repeatLoop.position)
return listOf(IAstModification.Remove(repeatLoop, parent))
}
val iterations = iter.constValue(program)?.number?.toInt()
if (iterations == 0) {
errors.warn("iterations is always 0, removed loop", iter.position)
return listOf(IAstModification.Remove(repeatLoop, parent))
}
if (iterations == 1)
errors.warn("iterations is always 1", iter.position)
}
return noModifications
}
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> { override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
// remove empty choices // remove empty choices
class ChoiceRemover(val choice: WhenChoice) : IAstModification { class ChoiceRemover(val choice: WhenChoice) : IAstModification {

View File

@ -139,8 +139,8 @@ Design principles and features
- 'One statement per line' code, resulting in clear readable programs. - 'One statement per line' code, resulting in clear readable programs.
- Modular programming and scoping via modules, code blocks, and subroutines. - Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal; - Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses, CPU registers and ROM subroutines, still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every cycle or byte matters and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters - Arbitrary number of subroutine parameters
- Complex nested expressions are possible - Complex nested expressions are possible
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters - Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters

View File

@ -50,7 +50,7 @@ Code
There are different kinds of instructions ('statements' is a better name) such as: There are different kinds of instructions ('statements' is a better name) such as:
- value assignment - value assignment
- looping (for, while, repeat, unconditional jumps) - looping (for, while, do-until, repeat, unconditional jumps)
- conditional execution (if - then - else, when, and conditional jumps) - conditional execution (if - then - else, when, and conditional jumps)
- subroutine calls - subroutine calls
- label definition - label definition
@ -137,7 +137,7 @@ Scopes are created using either of these two statements:
.. important:: .. important::
Unlike most other programming languages, a new scope is *not* created inside Unlike most other programming languages, a new scope is *not* created inside
for, while and repeat statements, the if statement, and the branching conditionals. for, while, repeat, and do-until statements, the if statement, and the branching conditionals.
These all share the same scope from the subroutine they're defined in. These all share the same scope from the subroutine they're defined in.
You can define variables in these blocks, but these will be treated as if they You can define variables in these blocks, but these will be treated as if they
were defined in the subroutine instead. were defined in the subroutine instead.
@ -204,13 +204,6 @@ Example::
byte @zp zeropageCounter = 42 byte @zp zeropageCounter = 42
Variables that represent CPU hardware registers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following variables are reserved
and map directly (read/write) to a CPU hardware register: ``A``, ``X``, ``Y``.
Integers Integers
^^^^^^^^ ^^^^^^^^
@ -393,21 +386,19 @@ expected when the program is restarted.
Loops Loops
----- -----
The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this. The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions. The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead. Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
The *while*-loop is used to repeat a piece of code while a certain condition is still true. The *while*-loop is used to repeat a piece of code while a certain condition is still true.
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true. The *do--until* loop is used to repeat a piece of code until a certain condition is true.
The *repeat* loop is used as a short notation of a for loop where the loop variable doesn't matter and you're only interested in the number of iterations.
The *forever*-loop is used to simply run a piece of code in a loop, forever. You can still (without iteration count specified it simply loops forever).
break out of this loop if desired. A "while true" or "until false" loop is equivalent to
a forever-loop.
You can also create loops by using the ``goto`` statement, but this should usually be avoided. You can also create loops by using the ``goto`` statement, but this should usually be avoided.
.. attention:: .. attention::
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately The value of the loop variable after executing the loop *is undefined*. Don't use it immediately
after the loop without first assigning a new value to it! after the loop without first assigning a new value to it!
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value) (this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
@ -421,15 +412,15 @@ if statements
Conditional execution means that the flow of execution changes based on certiain conditions, Conditional execution means that the flow of execution changes based on certiain conditions,
rather than having fixed gotos or subroutine calls:: rather than having fixed gotos or subroutine calls::
if A>4 goto overflow if aa>4 goto overflow
if X==3 Y = 4 if xx==3 yy = 4
if X==3 Y = 4 else A = 2 if xx==3 yy = 4 else aa = 2
if X==5 { if xx==5 {
Y = 99 yy = 99
} else { } else {
A = 3 aa = 3
} }
@ -493,16 +484,16 @@ Assignments
----------- -----------
Assignment statements assign a single value to a target variable or memory location. Assignment statements assign a single value to a target variable or memory location.
Augmented assignments (such as ``A += X``) are also available, but these are just shorthands Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands
for normal assignments (``A = A + X``). for normal assignments (``aa = aa + xx``).
Only register variables and variables of type byte, word and float can be assigned a new value. Only variables of type byte, word and float can be assigned a new value.
It's not possible to set a new value to string or array variables etc, because they get allocated It's not possible to set a new value to string or array variables etc, because they get allocated
a fixed amount of memory which will not change. a fixed amount of memory which will not change. (You *can* change the value of elements in a string or array though).
.. attention:: .. attention::
**Data type conversion (in assignments):** **Data type conversion (in assignments):**
When assigning a value with a 'smaller' datatype to a register or variable with a 'larger' datatype, When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype,
the value will be automatically converted to the target datatype: byte --> word --> float. the value will be automatically converted to the target datatype: byte --> word --> float.
So assigning a byte to a word variable, or a word to a floating point variable, is fine. So assigning a byte to a word variable, or a word to a floating point variable, is fine.
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
@ -518,7 +509,7 @@ as the memory mapped address $d021.
If you want to access a memory location directly (by using the address itself), without defining If you want to access a memory location directly (by using the address itself), without defining
a memory mapped location, you can do so by enclosing the address in ``@(...)``:: a memory mapped location, you can do so by enclosing the address in ``@(...)``::
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)") color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0") @($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address @(vic+$20) = 6 ; you can also use expressions to 'calculate' the address

View File

@ -24,7 +24,7 @@ Everything after a semicolon ``;`` is a comment and is ignored.
If the whole line is just a comment, it will be copied into the resulting assembly source code. If the whole line is just a comment, it will be copied into the resulting assembly source code.
This makes it easier to understand and relate the generated code. Examples:: This makes it easier to understand and relate the generated code. Examples::
A = 42 ; set the initial value to 42 counter = 42 ; set the initial value to 42
; next is the code that... ; next is the code that...
@ -313,7 +313,7 @@ Direct access to memory locations
Instead of defining a memory mapped name for a specific memory location, you can also Instead of defining a memory mapped name for a specific memory location, you can also
directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that:: directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that::
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)") color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0") @($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address @(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
@ -333,8 +333,6 @@ Reserved names
The following names are reserved, they have a special meaning:: The following names are reserved, they have a special meaning::
A X Y ; 6502 hardware registers
Pc Pz Pn Pv ; 6502 status register flags
true false ; boolean values 1 and 0 true false ; boolean values 1 and 0
@ -406,10 +404,10 @@ assignment: ``=``
Note that an assignment sometimes is not possible or supported. Note that an assignment sometimes is not possible or supported.
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=`` augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
Syntactic sugar; ``A += X`` is equivalent to ``A = A + X`` Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
postfix increment and decrement: ``++`` ``--`` postfix increment and decrement: ``++`` ``--``
Syntactic sugar; ``A++`` is equivalent to ``A = A + 1``, and ``A--`` is equivalent to ``A = A - 1``. Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``.
Because these operations are so common, we have these short forms. Because these operations are so common, we have these short forms.
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=`` comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
@ -427,9 +425,9 @@ range creation: ``to``
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant) 0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
A = 5 aa = 5
X = 10 aa = 10
A to X ; range of 5, 6, 7, 8, 9, 10 aa to xx ; range of 5, 6, 7, 8, 9, 10
byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4] byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4]
@ -551,7 +549,7 @@ Loops
for loop for loop
^^^^^^^^ ^^^^^^^^
The loop variable must be a register or a byte/word variable, The loop variable must be a byte or word variable,
and must be defined first in the local scope of the for loop. and must be defined first in the local scope of the for loop.
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``, The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported). array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
@ -598,31 +596,33 @@ You can use a single statement, or a statement block like in the example below::
} }
repeat-until loop do-until loop
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
Until the given condition is true (1), repeat the given statement(s). Until the given condition is true (1), repeat the given statement(s).
You can use a single statement, or a statement block like in the example below:: You can use a single statement, or a statement block like in the example below::
repeat { do {
; do something... ; do something...
break ; break out of the loop break ; break out of the loop
continue ; immediately enter next iteration continue ; immediately enter next iteration
} until <condition> } until <condition>
forever loop repeat loop
^^^^^^^^^^^^ ^^^^^^^^^^^
Simply run the code in a loop, forever. It's the same as a while true or until false loop, When you're only interested in repeating something a given number of times.
or just a jump back to a previous label. You can still break out of this loop as well, if you want:: It's a short hand for a for loop without an explicit loop variable::
forever { repeat 15 {
; .. do stuff ; do something...
if something break ; you can break out of the loop
break ; you can exit the loop if you want
} }
If you omit the iteration count, it simply loops forever.
You can still ``break`` out of such a loop if you want though.
Conditional Execution and Jumps Conditional Execution and Jumps
------------------------------- -------------------------------
@ -702,3 +702,4 @@ case you have to use { } to enclose them::
} }
else -> c64scr.print("don't know") else -> c64scr.print("don't know")
} }

View File

@ -113,22 +113,14 @@ CPU
Directly Usable Registers Directly Usable Registers
------------------------- -------------------------
The following 6502 CPU hardware registers are directly usable in program code (and are reserved symbols): The hardware CPU registers are not directly accessible from regular Prog8 code.
If you need to mess with them, you'll have to use inline assembly.
Be extra wary of the ``X`` register because it is used as an evaluation stack pointer and
changing its value you will destroy the evaluation stack and likely crash the program.
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits) The status register (P) carry flag and interrupt disable flag can be written via a couple of special
- the status register (P) carry flag and interrupt disable flag can be written via a couple of special builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``), and read via the ``read_flags()`` function.
and read via the ``read_flags()`` function.
However, you must assume that the 3 hardware registers ``A``, ``X`` and ``Y``
are volatile. Their values cannot be depended upon, the compiler will use them as required.
Even simple assignments may require modification of one or more of the registers (for instance, when using arrays).
Even more important, the ``X`` register is used as an evaluation stack pointer.
If you mess with it, you will destroy the evaluation stack and likely crash your program.
In some cases the compiler will warn you about this, but you should really avoid to use
this register. It's possible to store/restore the register's value (using special built in functions)
for the cases you really really need to use it directly.
Subroutine Calling Conventions Subroutine Calling Conventions
@ -173,3 +165,4 @@ as a subroutine ``irq`` in the module ``irq`` so like this::
; ... irq handling here ... ; ... irq handling here ...
} }
} }

View File

@ -104,17 +104,6 @@ main {
ub = all(farr) ub = all(farr)
if ub==0 c64scr.print("error all10\n") if ub==0 c64scr.print("error all10\n")
check_eval_stack()
c64scr.print("\nyou should see no errors printed above (only at first run).") c64scr.print("\nyou should see no errors printed above (only at first run).")
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -5,6 +5,8 @@
main { main {
sub start() { sub start() {
ubyte A
c64scr.print("ubyte shift left\n") c64scr.print("ubyte shift left\n")
A = shiftlb0() A = shiftlb0()
c64scr.print_ubbin(A, true) c64scr.print_ubbin(A, true)

View File

@ -24,8 +24,6 @@ main {
div_float(0,1,0) div_float(0,1,0)
div_float(999.9,111.0,9.008108108108107) div_float(999.9,111.0,9.008108108108107)
check_eval_stack()
} }
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) { sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
@ -103,12 +101,4 @@ main {
c64flt.print_f(r) c64flt.print_f(r)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -32,8 +32,6 @@ main {
minus_float(0,0,0) minus_float(0,0,0)
minus_float(2.5,1.5,1.0) minus_float(2.5,1.5,1.0)
minus_float(-1.5,3.5,-5.0) minus_float(-1.5,3.5,-5.0)
check_eval_stack()
} }
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) { sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
@ -111,13 +109,4 @@ main {
c64flt.print_f(r) c64flt.print_f(r)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -26,8 +26,6 @@ main {
mul_float(0,0,0) mul_float(0,0,0)
mul_float(2.5,10,25) mul_float(2.5,10,25)
mul_float(-1.5,10,-15) mul_float(-1.5,10,-15)
check_eval_stack()
} }
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) { sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
@ -105,12 +103,4 @@ main {
c64flt.print_f(r) c64flt.print_f(r)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -30,8 +30,6 @@ main {
plus_float(1.5,2.5,4.0) plus_float(1.5,2.5,4.0)
plus_float(-1.5,3.5,2.0) plus_float(-1.5,3.5,2.0)
plus_float(-1.1,3.3,2.2) plus_float(-1.1,3.3,2.2)
check_eval_stack()
} }
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) { sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
@ -109,13 +107,4 @@ main {
c64flt.print_f(r) c64flt.print_f(r)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -9,6 +9,7 @@ main {
c64scr.plot(0,24) c64scr.plot(0,24)
ubyte Y
ubyte ub=200 ubyte ub=200
byte bb=-100 byte bb=-100
uword uw = 2000 uword uw = 2000
@ -76,8 +77,6 @@ main {
check_b(barr[1], -100) check_b(barr[1], -100)
check_uw(uwarr[1], 2000) check_uw(uwarr[1], 2000)
check_w(warr[1], -1000) check_w(warr[1], -1000)
check_eval_stack()
} }
sub check_ub(ubyte value, ubyte expected) { sub check_ub(ubyte value, ubyte expected) {
@ -139,13 +138,4 @@ main {
c64flt.print_f(expected) c64flt.print_f(expected)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -15,8 +15,6 @@ main {
remainder_uword(40000,511,142) remainder_uword(40000,511,142)
remainder_uword(40000,500,0) remainder_uword(40000,500,0)
remainder_uword(43211,12,11) remainder_uword(43211,12,11)
check_eval_stack()
} }
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) { sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
@ -48,12 +46,4 @@ main {
c64scr.print_uw(r) c64scr.print_uw(r)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -21,7 +21,7 @@ main {
ubyte active_height = 24 ubyte active_height = 24
ubyte upwards = true ubyte upwards = true
forever { repeat {
ubyte mountain = 223 ; slope upwards ubyte mountain = 223 ; slope upwards
if active_height < target_height { if active_height < target_height {
active_height++ active_height++

View File

@ -16,7 +16,7 @@ sub start() {
void c64.CHRIN() void c64.CHRIN()
c64.CLEARSCR() c64.CLEARSCR()
forever { repeat {
uword note uword note
for note in notes { for note in notes {
ubyte note1 = lsb(note) ubyte note1 = lsb(note)
@ -37,10 +37,8 @@ sub start() {
} }
sub delay() { sub delay() {
ubyte d repeat 32 {
for d in 0 to 12 { while c64.RASTER {
while c64.RASTER!=0 {
; tempo delay synced to screen refresh
} }
} }
} }

View File

@ -39,7 +39,7 @@ graphics {
if dx >= dy { if dx >= dy {
if positive_ix { if positive_ix {
forever { repeat {
plot(y1) plot(y1)
if plotx==x2 if plotx==x2
return return
@ -51,7 +51,7 @@ graphics {
} }
} }
} else { } else {
forever { repeat {
plot(y1) plot(y1)
if plotx==x2 if plotx==x2
return return
@ -66,7 +66,7 @@ graphics {
} }
else { else {
if positive_ix { if positive_ix {
forever { repeat {
plot(y1) plot(y1)
if y1 == y2 if y1 == y2
return return
@ -78,7 +78,7 @@ graphics {
} }
} }
} else { } else {
forever { repeat {
plot(y1) plot(y1)
if y1 == y2 if y1 == y2
return return

View File

@ -105,16 +105,5 @@ main {
c64scr.print("ok: 22 >= 22\n") c64scr.print("ok: 22 >= 22\n")
else else
c64scr.print("error in 22>=22!\n") c64scr.print("error in 22>=22!\n")
check_eval_stack()
}
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
} }
} }

View File

@ -105,16 +105,5 @@ main {
c64scr.print("ok: -22.2 >= -22.2\n") c64scr.print("ok: -22.2 >= -22.2\n")
else else
c64scr.print("error in -22.2>=-22.2!\n") c64scr.print("error in -22.2>=-22.2!\n")
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -105,16 +105,5 @@ main {
c64scr.print("ok: 22 >= 22\n") c64scr.print("ok: 22 >= 22\n")
else else
c64scr.print("error in 22>=22!\n") c64scr.print("error in 22>=22!\n")
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -105,16 +105,5 @@ main {
c64scr.print("ok: 322 >= 322\n") c64scr.print("ok: 322 >= 322\n")
else else
c64scr.print("error in 322>=322!\n") c64scr.print("error in 322>=322!\n")
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -137,16 +137,5 @@ main {
c64scr.print("ok: 1000 >= 1000\n") c64scr.print("ok: 1000 >= 1000\n")
else else
c64scr.print("error in 1000>=1000!\n") c64scr.print("error in 1000>=1000!\n")
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -52,7 +52,6 @@ main {
c64scr.print("v1=20, v2=-111\n") c64scr.print("v1=20, v2=-111\n")
compare() compare()
check_eval_stack()
return return
sub compare() { sub compare() {
@ -91,13 +90,4 @@ main {
} }
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -68,7 +68,6 @@ main {
c64scr.print("v1 = v2 = 0\n") c64scr.print("v1 = v2 = 0\n")
compare() compare()
check_eval_stack()
return return
sub compare() { sub compare() {
@ -108,11 +107,4 @@ main {
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -52,7 +52,6 @@ main {
c64scr.print("v1=220, v2=10\n") c64scr.print("v1=220, v2=10\n")
compare() compare()
check_eval_stack()
return return
sub compare() { sub compare() {
@ -92,12 +91,4 @@ main {
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -82,7 +82,6 @@ main {
c64scr.print("v1 = v2 = aa\n") c64scr.print("v1 = v2 = aa\n")
compare() compare()
check_eval_stack()
return return
sub compare() { sub compare() {
@ -121,13 +120,4 @@ main {
} }
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -118,7 +118,6 @@ main {
c64scr.print("v1 = v2 = aa\n") c64scr.print("v1 = v2 = aa\n")
compare() compare()
check_eval_stack()
return return
sub compare() { sub compare() {
@ -157,13 +156,4 @@ main {
} }
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -19,7 +19,7 @@ main {
sub start() { sub start() {
float time=0.0 float time=0.0
forever { repeat {
rotate_vertices(time) rotate_vertices(time)
c64scr.clear_screenchars(32) c64scr.clear_screenchars(32)
draw_edges() draw_edges()

View File

@ -82,7 +82,7 @@ main {
uword anglex uword anglex
uword angley uword angley
uword anglez uword anglez
forever { repeat {
c64.TIME_LO=0 c64.TIME_LO=0
rotate_vertices(msb(anglex), msb(angley), msb(anglez)) rotate_vertices(msb(anglex), msb(angley), msb(anglez))
position_sprites() position_sprites()

View File

@ -21,7 +21,7 @@ main {
uword anglex uword anglex
uword angley uword angley
uword anglez uword anglez
forever { repeat {
rotate_vertices(msb(anglex), msb(angley), msb(anglez)) rotate_vertices(msb(anglex), msb(angley), msb(anglez))
c64scr.clear_screenchars(32) c64scr.clear_screenchars(32)
draw_edges() draw_edges()

View File

@ -6,7 +6,8 @@
main { main {
sub start() { sub start() {
c64scr.print("fibonacci sequence\n") c64scr.print("fibonacci sequence\n")
for A in 0 to 20 {
repeat 21 {
c64scr.print_uw(fib_next()) c64scr.print_uw(fib_next())
c64.CHROUT('\n') c64.CHROUT('\n')
} }

View File

@ -42,17 +42,5 @@ main {
c64.CHROUT('\n') c64.CHROUT('\n')
c64scr.print("bye!\n") c64scr.print("bye!\n")
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -10,7 +10,7 @@ main {
graphics.enable_bitmap_mode() graphics.enable_bitmap_mode()
draw_lines() draw_lines()
draw_circles() draw_circles()
forever { repeat {
} }
} }

View File

@ -77,7 +77,7 @@ main {
ubyte y = y1 ubyte y = y1
if dx >= dy { if dx >= dy {
forever { repeat {
c64scr.setcc(x, y, 42, 5) c64scr.setcc(x, y, 42, 5)
if x==x2 if x==x2
return return
@ -89,7 +89,7 @@ main {
} }
} }
} else { } else {
forever { repeat {
c64scr.setcc(x, y, 42, 5) c64scr.setcc(x, y, 42, 5)
if y == y2 if y == y2
return return

View File

@ -48,7 +48,7 @@ main {
} }
} }
forever { repeat {
} }
} }
} }

View File

@ -48,15 +48,5 @@ main {
c64scr.print("finished in ") c64scr.print("finished in ")
c64flt.print_f(duration) c64flt.print_f(duration)
c64scr.print(" seconds!\n") c64scr.print(" seconds!\n")
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -57,18 +57,6 @@ main {
c64scr.print("Thanks for playing, ") c64scr.print("Thanks for playing, ")
c64scr.print(name) c64scr.print(name)
c64scr.print(".\n") c64scr.print(".\n")
check_eval_stack()
} }
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -12,7 +12,7 @@ main {
; calculate primes ; calculate primes
c64scr.print("prime numbers up to 255:\n\n") c64scr.print("prime numbers up to 255:\n\n")
ubyte amount=0 ubyte amount=0
forever { repeat {
ubyte prime = find_next_prime() ubyte prime = find_next_prime()
if prime==0 if prime==0
break break
@ -24,8 +24,6 @@ main {
c64scr.print("number of primes (expected 54): ") c64scr.print("number of primes (expected 54): ")
c64scr.print_ub(amount) c64scr.print_ub(amount)
c64.CHROUT('\n') c64.CHROUT('\n')
check_eval_stack()
} }
@ -48,14 +46,4 @@ main {
} }
return candidate_prime return candidate_prime
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -7,7 +7,7 @@ main {
c64.SCROLY &= %11101111 ; blank the screen c64.SCROLY &= %11101111 ; blank the screen
c64utils.set_rasterirq_excl(40) ; register exclusive raster irq handler c64utils.set_rasterirq_excl(40) ; register exclusive raster irq handler
forever { repeat {
; enjoy the moving bars :) ; enjoy the moving bars :)
} }

View File

@ -52,17 +52,5 @@ main {
c64flt.print_f(0.0) c64flt.print_f(0.0)
c64.CHROUT('\n') c64.CHROUT('\n')
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -34,17 +34,5 @@ main {
c64scr.print("\nscreencode z=") c64scr.print("\nscreencode z=")
c64scr.print_ub(c2) c64scr.print_ub(c2)
c64scr.print("\n") c64scr.print("\n")
check_eval_stack()
}
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
} }
} }

View File

@ -30,7 +30,6 @@ main {
c64scr.print("reversed\n") c64scr.print("reversed\n")
print_arrays() print_arrays()
check_eval_stack()
return return
@ -65,14 +64,4 @@ main {
c64.CHROUT('\n') c64.CHROUT('\n')
} }
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -1,7 +1,7 @@
%import c64utils %import c64utils
%zeropage basicsafe %zeropage basicsafe
; TODO fix compiler errors when compiling ( /= ) ; TODO fix compiler errors when compiling without optimization ( /= )
main { main {
@ -14,7 +14,7 @@ main {
sub start() { sub start() {
Color purple = {255, 0, 255} Color purple = [255, 0, 255]
Color other Color other
@ -30,17 +30,5 @@ main {
c64.CHROUT(',') c64.CHROUT(',')
c64scr.print_ub(other.blue) c64scr.print_ub(other.blue)
c64.CHROUT('\n') c64.CHROUT('\n')
check_eval_stack()
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -11,7 +11,7 @@ main {
float t float t
ubyte color ubyte color
forever { repeat {
ubyte xx=(sin(t) * width/2.2) + width/2.0 as ubyte ubyte xx=(sin(t) * width/2.2) + width/2.0 as ubyte
ubyte yy=(cos(t*1.1356) * height/2.2) + height/2.0 as ubyte ubyte yy=(cos(t*1.1356) * height/2.2) + height/2.0 as ubyte
c64scr.setcc(xx, yy, 81, color) c64scr.setcc(xx, yy, 81, color)

View File

@ -15,7 +15,7 @@ main {
Ball ball Ball ball
forever { repeat {
ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width) ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width)
ubyte y = msb(cos8u(msb(ball.angley)) as uword * height) ubyte y = msb(cos8u(msb(ball.angley)) as uword * height)
c64scr.setcc(x, y, 81, ball.color) c64scr.setcc(x, y, 81, ball.color)

View File

@ -39,8 +39,6 @@ newgame:
spawnNextBlock() spawnNextBlock()
waitkey: waitkey:
check_eval_stack()
if c64.TIME_LO>=(60-4*speedlevel) { if c64.TIME_LO>=(60-4*speedlevel) {
c64.TIME_LO = 0 c64.TIME_LO = 0
@ -391,16 +389,6 @@ waitkey:
c64scr.setcc((i&3)+x, (i/4)+y, character, c) c64scr.setcc((i&3)+x, (i/4)+y, character, c)
} }
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -8,5 +8,39 @@
main { main {
sub start() { sub start() {
repeat 10 {
c64.CHROUT('*')
}
c64.CHROUT('\n')
ubyte ub = 9
repeat ub {
c64.CHROUT('*')
}
c64.CHROUT('\n')
repeat 320 {
c64.CHROUT('+')
}
c64.CHROUT('\n')
uword uw = 320
repeat uw {
c64.CHROUT('-')
}
c64.CHROUT('\n')
ub = 7
repeat ub+2 {
c64.CHROUT('*')
}
c64.CHROUT('\n')
uw = 318
repeat uw+2 {
c64.CHROUT('*')
}
c64.CHROUT('\n')
} }
} }

View File

@ -29,6 +29,7 @@ main {
&uword[4] muwarray = $c000 &uword[4] muwarray = $c000
&float[4] mflarray = $c000 &float[4] mflarray = $c000
ubyte A
byte bb byte bb
ubyte ub ubyte ub
word ww word ww

View File

@ -12,6 +12,7 @@ main {
ubyte ub ubyte ub
byte bb byte bb
word total word total
ubyte A
c64scr.plot(0,24) c64scr.plot(0,24)
@ -959,8 +960,6 @@ main {
c64scr.print("ok\n") c64scr.print("ok\n")
else else
c64scr.print("fail!!!\n") c64scr.print("fail!!!\n")
check_eval_stack()
} }
sub wait_input() { sub wait_input() {
@ -969,13 +968,4 @@ main {
void c64scr.input_chars(input) void c64scr.input_chars(input)
c64scr.print("\n\n") c64scr.print("\n\n")
} }
sub check_eval_stack() {
if X!=255 {
c64scr.print("stack x=")
c64scr.print_ub(X)
c64scr.print(" error!\n")
}
}
} }

View File

@ -10,19 +10,17 @@ main {
sub start() { sub start() {
graphics.enable_bitmap_mode() graphics.enable_bitmap_mode()
turtle.init() turtle.init()
; turtle.pu()
; turtle.pos(150, 110)
; turtle.pd()
ubyte i ubyte i
for i in 0 to 255 {
while c64.RASTER {
}
}
for i in 0 to 100 { for i in 0 to 100 {
turtle.fd(i+20) turtle.fd(i+20)
turtle.rt(94) turtle.rt(94)
} }
forever { repeat {
} }
} }
} }

View File

@ -94,8 +94,8 @@ statement :
| returnstmt | returnstmt
| forloop | forloop
| whileloop | whileloop
| untilloop
| repeatloop | repeatloop
| foreverloop
| whenstmt | whenstmt
| breakstmt | breakstmt
| continuestmt | continuestmt
@ -158,8 +158,7 @@ augassignment :
; ;
assign_target: assign_target:
register scoped_identifier
| scoped_identifier
| arrayindexed | arrayindexed
| directmemory | directmemory
; ;
@ -184,7 +183,6 @@ expression :
| left = expression EOL? bop = 'xor' EOL? right = expression | left = expression EOL? bop = 'xor' EOL? right = expression
| prefix = 'not' expression | prefix = 'not' expression
| literalvalue | literalvalue
| register
| scoped_identifier | scoped_identifier
| arrayindexed | arrayindexed
| directmemory | directmemory
@ -222,12 +220,6 @@ identifier : NAME ;
scoped_identifier : NAME ('.' NAME)* ; scoped_identifier : NAME ('.' NAME)* ;
register : 'A' | 'X' | 'Y' ;
registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // pairs can only be used in subroutine params and returnvalues
statusregister : 'Pc' | 'Pz' | 'Pn' | 'Pv' ;
integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) wordsuffix? ; integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) wordsuffix? ;
wordsuffix : '.w' ; wordsuffix : '.w' ;
@ -236,8 +228,6 @@ booleanliteral : 'true' | 'false' ;
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines
structliteral : '{' EOL? expression (',' EOL? expression)* EOL? '}' ; // you can split the values over several lines
stringliteral : ALT_STRING_ENCODING? STRING ; stringliteral : ALT_STRING_ENCODING? STRING ;
charliteral : ALT_STRING_ENCODING? SINGLECHAR ; charliteral : ALT_STRING_ENCODING? SINGLECHAR ;
@ -252,7 +242,6 @@ literalvalue :
| stringliteral | stringliteral
| charliteral | charliteral
| floatliteral | floatliteral
| structliteral
; ;
inlineasm : '%asm' INLINEASMBLOCK; inlineasm : '%asm' INLINEASMBLOCK;
@ -287,15 +276,15 @@ asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns?
asmsub_params : asmsub_param (',' EOL? asmsub_param)* ; asmsub_params : asmsub_param (',' EOL? asmsub_param)* ;
asmsub_param : vardecl '@' (registerorpair | statusregister | stack='stack') ; asmsub_param : vardecl '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed
asmsub_clobbers : 'clobbers' '(' clobber? ')' ; asmsub_clobbers : 'clobbers' '(' clobber? ')' ;
clobber : register (',' register)* ; clobber : identifier (',' identifier)* ; // A,X,Y allowed
asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ; asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ;
asmsub_return : datatype '@' (registerorpair | statusregister | stack='stack') ; asmsub_return : datatype '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed
if_stmt : 'if' expression EOL? (statement | statement_block) EOL? else_part? ; // statement is constrained later if_stmt : 'if' expression EOL? (statement | statement_block) EOL? else_part? ; // statement is constrained later
@ -308,13 +297,13 @@ branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part?
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ; branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;
forloop : 'for' (register | identifier) 'in' expression EOL? (statement | statement_block) ; forloop : 'for' identifier 'in' expression EOL? (statement | statement_block) ;
whileloop: 'while' expression EOL? (statement | statement_block) ; whileloop: 'while' expression EOL? (statement | statement_block) ;
repeatloop: 'repeat' (statement | statement_block) EOL? 'until' expression ; untilloop: 'do' (statement | statement_block) EOL? 'until' expression ;
foreverloop: 'forever' EOL? (statement | statement_block) ; repeatloop: 'repeat' expression? EOL? (statement | statement_block) ;
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ; whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;