mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
b37231d0f5 | |||
3c55719bf1 | |||
af8279a9b9 | |||
c38508c262 | |||
b0e8738ab8 | |||
cae480768e | |||
a70276c190 | |||
0c461ffe2e |
@ -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 |
@ -1 +1 @@
|
|||||||
2.4
|
3.0
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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)) {
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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++
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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')
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ main {
|
|||||||
graphics.enable_bitmap_mode()
|
graphics.enable_bitmap_mode()
|
||||||
draw_lines()
|
draw_lines()
|
||||||
draw_circles()
|
draw_circles()
|
||||||
forever {
|
repeat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -48,7 +48,7 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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 :)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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? ;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user