mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 16:23:42 +00:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
b37231d0f5 | |||
3c55719bf1 | |||
af8279a9b9 | |||
c38508c262 | |||
b0e8738ab8 | |||
cae480768e | |||
a70276c190 | |||
0c461ffe2e | |||
237511f2d6 | |||
cdcb652033 | |||
71e678b382 | |||
3050156325 | |||
4bfdbad2e4 | |||
06137ecdc4 | |||
d89f5b0df8 | |||
b6e2b36692 | |||
a6d789cfbc | |||
c07907e7bd | |||
7d8496c874 | |||
164ac56db1 | |||
fdddb8ca64 | |||
a9d4b8b0fa | |||
ec7b9f54c2 | |||
307558a7e7 | |||
febf423eab | |||
a999c23014 | |||
69f1ade595 | |||
b166576e54 | |||
ee2ba5f398 | |||
cb9825484d |
@ -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 |
@ -781,3 +781,18 @@ set_array_float .proc
|
|||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
swap_floats .proc
|
||||||
|
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||||
|
ldy #4
|
||||||
|
- lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
lda (c64.SCRATCH_ZPWORD2),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
@ -1 +1 @@
|
|||||||
2.2
|
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)
|
assignTarget.memoryAddress?.accept(this)
|
||||||
output(assignTarget.register.toString())
|
assignTarget.identifier?.accept(this)
|
||||||
else {
|
|
||||||
assignTarget.memoryAddress?.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
|
||||||
@ -155,6 +155,7 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||||
|
fun containsNoVars() = statements.all { it !is VarDecl }
|
||||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||||
|
|
||||||
fun remove(stmt: Statement) {
|
fun remove(stmt: Statement) {
|
||||||
@ -174,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 */ }
|
||||||
@ -186,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 {
|
||||||
@ -229,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.indexOf(node)
|
val idx = modules.indexOfFirst { it===node }
|
||||||
modules[idx] = replacement
|
modules[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -256,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.indexOf(node)
|
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 {
|
||||||
|
@ -6,7 +6,6 @@ import prog8.ast.processing.*
|
|||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
import prog8.optimizer.AssignmentTransformer
|
import prog8.optimizer.AssignmentTransformer
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||||
@ -32,6 +31,11 @@ internal fun Program.addTypecasts(errors: ErrorReporter) {
|
|||||||
caster.applyModifications()
|
caster.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.verifyFunctionArgTypes() {
|
||||||
|
val fixer = VerifyFunctionArgTypes(this)
|
||||||
|
fixer.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
||||||
val transform = AssignmentTransformer(this, errors)
|
val transform = AssignmentTransformer(this, errors)
|
||||||
transform.visit(this)
|
transform.visit(this)
|
||||||
@ -71,7 +75,8 @@ internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.removeNopsFlattenAnonScopes() {
|
internal fun Program.variousCleanups() {
|
||||||
val flattener = FlattenAnonymousScopesAndNopRemover()
|
val process = VariousCleanups()
|
||||||
flattener.visit(this)
|
process.visit(this)
|
||||||
|
process.applyModifications()
|
||||||
}
|
}
|
||||||
|
@ -22,28 +22,26 @@ sealed class Expression: Node {
|
|||||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
abstract fun referencesIdentifiers(vararg name: String): Boolean
|
||||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
|
|
||||||
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.indexOf(node)
|
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
|
||||||
|
|
||||||
@ -731,6 +681,9 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
||||||
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 hashCode() = nameInSource.hashCode()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
@ -798,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.indexOf(node)
|
val idx = args.indexOfFirst { it===node }
|
||||||
args[idx] = replacement as Expression
|
args[idx] = replacement as Expression
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
|
@ -110,49 +110,37 @@ 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(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||||
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
|
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||||
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 {
|
} else {
|
||||||
// loop variable
|
when (loopvar.datatype) {
|
||||||
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
|
DataType.UBYTE -> {
|
||||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
||||||
} else {
|
|
||||||
when (loopvar.datatype) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
|
||||||
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
|
||||||
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
|
||||||
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
|
||||||
errors.err("byte loop variable can only loop over bytes", forLoop.position)
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
|
|
||||||
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
|
|
||||||
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
errors.err("for loop only supports integers", forLoop.position)
|
|
||||||
}
|
|
||||||
else -> errors.err("loop variable must be numeric type", forLoop.position)
|
|
||||||
}
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
||||||
|
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
||||||
|
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
||||||
|
errors.err("byte loop variable can only loop over bytes", forLoop.position)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
|
||||||
|
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
|
||||||
|
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
errors.err("for loop only supports integers", forLoop.position)
|
||||||
|
}
|
||||||
|
else -> errors.err("loop variable must be numeric type", forLoop.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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")) // TODO use callgraph?
|
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")) // TODO use callgraph?
|
|
||||||
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
|
||||||
@ -463,7 +446,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO use callgraph for check?
|
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
}
|
}
|
||||||
@ -518,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) {
|
||||||
@ -546,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)
|
||||||
@ -585,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) {
|
||||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
@ -733,7 +722,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
"**" -> {
|
"**" -> {
|
||||||
if(leftDt in IntegerDatatypes)
|
if(leftDt in IntegerDatatypes)
|
||||||
errors.err("power operator requires floating point", expr.position)
|
errors.err("power operator requires floating point operands", expr.position)
|
||||||
}
|
}
|
||||||
"and", "or", "xor" -> {
|
"and", "or", "xor" -> {
|
||||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||||
@ -845,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1293,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)
|
||||||
|
@ -10,6 +10,46 @@ import prog8.ast.statements.*
|
|||||||
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCallStatement.target.nameInSource == listOf("swap")) {
|
||||||
|
// if x and y are both just identifiers, do not rewrite (there should be asm generation for that)
|
||||||
|
// otherwise:
|
||||||
|
// rewrite swap(x,y) as follows:
|
||||||
|
// - declare a temp variable of the same datatype
|
||||||
|
// - temp = x, x = y, y= temp
|
||||||
|
val first = functionCallStatement.args[0]
|
||||||
|
val second = functionCallStatement.args[1]
|
||||||
|
if(first !is IdentifierReference && second !is IdentifierReference) {
|
||||||
|
val dt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val tempname = "prog8_swaptmp_${functionCallStatement.hashCode()}"
|
||||||
|
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 assignTemp = Assignment(
|
||||||
|
AssignTarget(tempvar, null, null, first.position),
|
||||||
|
null,
|
||||||
|
first,
|
||||||
|
first.position
|
||||||
|
)
|
||||||
|
val assignFirst = Assignment(
|
||||||
|
AssignTarget.fromExpr(first),
|
||||||
|
null,
|
||||||
|
second,
|
||||||
|
first.position
|
||||||
|
)
|
||||||
|
val assignSecond = Assignment(
|
||||||
|
AssignTarget.fromExpr(second),
|
||||||
|
null,
|
||||||
|
tempvar,
|
||||||
|
first.position
|
||||||
|
)
|
||||||
|
val scope = AnonymousScope(mutableListOf(tempvardecl, assignTemp, assignFirst, assignSecond), first.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||||
@ -43,13 +83,16 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
if(subroutine.asmAddress==null) {
|
if(subroutine.asmAddress==null) {
|
||||||
if(subroutine.asmParameterRegisters.isEmpty()) {
|
if(subroutine.asmParameterRegisters.isEmpty() && subroutine.parameters.isNotEmpty()) {
|
||||||
return subroutine.parameters
|
val vars = subroutine.statements.filterIsInstance<VarDecl>().map { it.name }.toSet()
|
||||||
.filter { it.name !in namesInSub }
|
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
|
||||||
.map {
|
return subroutine.parameters
|
||||||
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
.filter { it.name !in namesInSub }
|
||||||
IAstModification.InsertFirst(vardecl, subroutine)
|
.map {
|
||||||
}
|
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
||||||
|
IAstModification.InsertFirst(vardecl, subroutine)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,11 +147,11 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
// this array literal is part of an expression, turn it into an identifier reference
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
if(litval2!=null && litval2!=array) {
|
if(litval2!=null && litval2!=array) {
|
||||||
val vardecl = VarDecl.createAuto(litval2)
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(array, identifier, parent),
|
IAstModification.ReplaceNode(array, identifier, parent),
|
||||||
IAstModification.InsertFirst(vardecl, array.definingScope() as Node)
|
IAstModification.InsertFirst(vardecl2, array.definingScope() as Node)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.indexOf(after)+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")
|
||||||
|
@ -87,7 +87,9 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
|
|
||||||
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
||||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||||
return when(val sub = call.target.targetStatement(scope)) {
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
|
when(val sub = call.target.targetStatement(scope)) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
||||||
val argItype = arg.second.value.inferType(program)
|
val argItype = arg.second.value.inferType(program)
|
||||||
@ -96,48 +98,55 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val requiredType = arg.first.type
|
val requiredType = arg.first.type
|
||||||
if (requiredType != argtype) {
|
if (requiredType != argtype) {
|
||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||||
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
}
|
} else if(arg.second.value is NumericLiteralValue) {
|
||||||
}
|
try {
|
||||||
}
|
val castedValue = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||||
}
|
modifications += IAstModification.ReplaceNode(
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
|
||||||
val func = BuiltinFunctions.getValue(sub.name)
|
|
||||||
if(func.pure) {
|
|
||||||
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
|
|
||||||
for (arg in func.parameters.zip(call.args.withIndex())) {
|
|
||||||
val argItype = arg.second.value.inferType(program)
|
|
||||||
if (argItype.isKnown) {
|
|
||||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
|
||||||
if (arg.first.possibleDatatypes.any { argtype == it })
|
|
||||||
continue
|
|
||||||
for (possibleType in arg.first.possibleDatatypes) {
|
|
||||||
if (argtype isAssignableTo possibleType) {
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
castedValue,
|
||||||
call as Node))
|
call as Node)
|
||||||
|
} catch (x: ExpressionError) {
|
||||||
|
// no cast possible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyList()
|
|
||||||
}
|
}
|
||||||
null -> emptyList()
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
|
val func = BuiltinFunctions.getValue(sub.name)
|
||||||
|
for (arg in func.parameters.zip(call.args.withIndex())) {
|
||||||
|
val argItype = arg.second.value.inferType(program)
|
||||||
|
if (argItype.isKnown) {
|
||||||
|
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||||
|
if (arg.first.possibleDatatypes.any { argtype == it })
|
||||||
|
continue
|
||||||
|
for (possibleType in arg.first.possibleDatatypes) {
|
||||||
|
if (argtype isAssignableTo possibleType) {
|
||||||
|
modifications += IAstModification.ReplaceNode(
|
||||||
|
call.args[arg.second.index],
|
||||||
|
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
||||||
|
call as Node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null -> { }
|
||||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return modifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
@ -170,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
|
||||||
|
43
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
43
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.NopStatement
|
||||||
|
|
||||||
|
|
||||||
|
internal class VariousCleanups: AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
return listOf(IAstModification.Remove(nopStatement, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
return if(parent is INameScope)
|
||||||
|
listOf(ScopeFlatten(scope, parent as INameScope))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
val idx = into.statements.indexOf(scope)
|
||||||
|
if(idx>=0) {
|
||||||
|
into.statements.addAll(idx+1, scope.statements)
|
||||||
|
into.statements.remove(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(typecast.expression is NumericLiteralValue) {
|
||||||
|
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||||
|
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
42
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
42
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.expressions.FunctionCall
|
||||||
|
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||||
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.CompilerException
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
|
override fun visit(functionCall: FunctionCall)
|
||||||
|
= checkTypes(functionCall as IFunctionCall, functionCall.definingScope())
|
||||||
|
|
||||||
|
override fun visit(functionCallStatement: FunctionCallStatement)
|
||||||
|
= checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
|
||||||
|
|
||||||
|
private fun checkTypes(call: IFunctionCall, scope: INameScope) {
|
||||||
|
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||||
|
val target = call.target.targetStatement(scope)
|
||||||
|
when(target) {
|
||||||
|
is Subroutine -> {
|
||||||
|
val paramtypes = target.parameters.map { it.type }
|
||||||
|
if(argtypes!=paramtypes)
|
||||||
|
throw CompilerException("parameter type mismatch $call")
|
||||||
|
}
|
||||||
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
|
val func = BuiltinFunctions.getValue(target.name)
|
||||||
|
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||||
|
for(x in argtypes.zip(paramtypes)) {
|
||||||
|
if(x.first !in x.second)
|
||||||
|
throw CompilerException("parameter type mismatch $call")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,8 +29,6 @@ sealed class Statement : Node {
|
|||||||
return scope.joinToString(".")
|
return scope.joinToString(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val expensiveToInline: Boolean
|
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
fun definingBlock(): Block {
|
||||||
if(this is Block)
|
if(this is Block)
|
||||||
return this
|
return this
|
||||||
@ -48,7 +46,6 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
|
|||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
override val expensiveToInline = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
||||||
@ -59,8 +56,6 @@ class Block(override val name: String,
|
|||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -69,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.indexOf(node)
|
val idx = statements.indexOfFirst { it ===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -86,7 +81,6 @@ class Block(override val name: String,
|
|||||||
|
|
||||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -109,7 +103,6 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : Statement() {
|
data class Label(val name: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -126,7 +119,6 @@ data class Label(val name: String, override val position: Position) : Statement(
|
|||||||
|
|
||||||
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -158,7 +150,6 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
|||||||
|
|
||||||
class Continue(override val position: Position) : Statement() {
|
class Continue(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
@ -171,7 +162,6 @@ class Continue(override val position: Position) : Statement() {
|
|||||||
|
|
||||||
class Break(override val position: Position) : Statement() {
|
class Break(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
@ -207,9 +197,6 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
var structHasBeenFlattened = false // set later
|
var structHasBeenFlattened = false // set later
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override val expensiveToInline
|
|
||||||
get() = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
// prefix for literal values that are turned into a variable on the heap
|
// prefix for literal values that are turned into a variable on the heap
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -283,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,
|
||||||
@ -339,8 +326,6 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
|
|
||||||
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -369,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)
|
||||||
@ -389,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 {
|
||||||
@ -418,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)
|
||||||
@ -455,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 &&
|
||||||
@ -469,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) {
|
||||||
@ -489,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) {
|
||||||
@ -509,7 +483,6 @@ data class AssignTarget(val register: Register?,
|
|||||||
|
|
||||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -535,7 +508,6 @@ class Jump(val address: Int?,
|
|||||||
val generatedLabel: String?, // used in code generation scenarios
|
val generatedLabel: String?, // used in code generation scenarios
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -556,8 +528,6 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
val void: Boolean,
|
val void: Boolean,
|
||||||
override val position: Position) : Statement(), IFunctionCall {
|
override val position: Position) : Statement(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = args.any { it !is NumericLiteralValue }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -569,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.indexOf(node)
|
val idx = args.indexOfFirst { it===node }
|
||||||
args[idx] = replacement as Expression
|
args[idx] = replacement as Expression
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -585,7 +555,6 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
|
|
||||||
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -600,8 +569,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
override val position: Position) : INameScope, Statement() {
|
override val position: Position) : INameScope, Statement() {
|
||||||
override val name: String
|
override val name: String
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var sequenceNumber = 1
|
private var sequenceNumber = 1
|
||||||
@ -619,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.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -630,7 +597,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
|
|
||||||
class NopStatement(override val position: Position): Statement() {
|
class NopStatement(override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -639,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,
|
||||||
@ -657,20 +615,14 @@ 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>,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
var keepAlways: Boolean = false
|
var keepAlways: Boolean = false
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
val calledBy = mutableListOf<Node>()
|
|
||||||
val calls = mutableSetOf<Subroutine>()
|
|
||||||
|
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -681,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.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -722,8 +674,6 @@ class IfStatement(var condition: Expression,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -752,8 +702,6 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -775,17 +723,15 @@ 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() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -804,21 +750,16 @@ 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,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -839,18 +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 val expensiveToInline = true
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -858,11 +802,10 @@ 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
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -887,7 +830,6 @@ class WhenStatement(var condition: Expression,
|
|||||||
var choices: MutableList<WhenChoice>,
|
var choices: MutableList<WhenChoice>,
|
||||||
override val position: Position): Statement() {
|
override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -899,7 +841,7 @@ class WhenStatement(var condition: Expression,
|
|||||||
if(node===condition)
|
if(node===condition)
|
||||||
condition = replacement as Expression
|
condition = replacement as Expression
|
||||||
else {
|
else {
|
||||||
val idx = choices.indexOf(node)
|
val idx = choices.withIndex().find { it.value===node }!!.index
|
||||||
choices[idx] = replacement as WhenChoice
|
choices[idx] = replacement as WhenChoice
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -957,7 +899,6 @@ class StructDecl(override val name: String,
|
|||||||
override val position: Position): Statement(), INameScope {
|
override val position: Position): Statement(), INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -966,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.indexOf(node)
|
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)
|
||||||
|
@ -42,7 +42,7 @@ fun compileProgram(filepath: Path,
|
|||||||
optimizeAst(programAst, errors)
|
optimizeAst(programAst, errors)
|
||||||
postprocessAst(programAst, errors, compilationOptions)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst) // TODO
|
// printAst(programAst)
|
||||||
|
|
||||||
if(writeAssembly)
|
if(writeAssembly)
|
||||||
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
||||||
@ -146,10 +146,10 @@ private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptio
|
|||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.constantFold(errors)
|
programAst.constantFold(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
|
||||||
programAst.reorderStatements()
|
programAst.reorderStatements()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors)
|
programAst.checkValid(compilerOptions, errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkIdentifiers(errors)
|
programAst.checkIdentifiers(errors)
|
||||||
@ -179,11 +179,12 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
|||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.verifyFunctionArgTypes()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||||
|
@ -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,26 +673,114 @@ 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)
|
|
||||||
translate(stmt.body)
|
when (stmt.iterations) {
|
||||||
out(" jmp $foreverLabel")
|
null -> {
|
||||||
out(endLabel)
|
// endless loop
|
||||||
|
out(repeatLabel)
|
||||||
|
translate(stmt.body)
|
||||||
|
out(" jmp $repeatLabel")
|
||||||
|
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")
|
||||||
loopEndLabels.push(endLabel)
|
loopEndLabels.push(endLabel)
|
||||||
loopContinueLabels.push(whileLabel)
|
loopContinueLabels.push(whileLabel)
|
||||||
out(whileLabel)
|
out(whileLabel)
|
||||||
// TODO optimize for the simple cases, can we avoid stack use?
|
|
||||||
expressionsAsmGen.translateExpression(stmt.condition)
|
expressionsAsmGen.translateExpression(stmt.condition)
|
||||||
val conditionDt = stmt.condition.inferType(program)
|
val conditionDt = stmt.condition.inferType(program)
|
||||||
if(!conditionDt.isKnown)
|
if(!conditionDt.isKnown)
|
||||||
@ -714,13 +803,12 @@ 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)
|
||||||
loopContinueLabels.push(repeatLabel)
|
loopContinueLabels.push(repeatLabel)
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
// TODO optimize this for the simple cases, can we avoid stack use?
|
|
||||||
translate(stmt.body)
|
translate(stmt.body)
|
||||||
expressionsAsmGen.translateExpression(stmt.untilCondition)
|
expressionsAsmGen.translateExpression(stmt.untilCondition)
|
||||||
val conditionDt = stmt.untilCondition.inferType(program)
|
val conditionDt = stmt.untilCondition.inferType(program)
|
||||||
@ -771,7 +859,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
bne +
|
bne +
|
||||||
cpy #>${value.toHex()}
|
cpy #>${value.toHex()}
|
||||||
beq $choiceLabel
|
beq $choiceLabel
|
||||||
+
|
+
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -838,10 +926,13 @@ 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)
|
||||||
val assign = Assignment(target, null, stmt.value!!, stmt.position)
|
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
|
||||||
assign.linkParents(stmt.parent)
|
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
|
||||||
translate(assign)
|
val assign = Assignment(target, null, stmt.value!!, stmt.position)
|
||||||
|
assign.linkParents(stmt.parent)
|
||||||
|
translate(assign)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -903,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")
|
||||||
@ -925,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")
|
||||||
@ -974,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)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,12 +2,8 @@ package prog8.compiler.target.c64.codegen
|
|||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ByteDatatypes
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.base.WordDatatypes
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
@ -176,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")
|
||||||
@ -235,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")
|
||||||
@ -287,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")
|
||||||
@ -346,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")
|
||||||
@ -384,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) {
|
||||||
@ -468,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) {
|
||||||
@ -581,32 +535,32 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
ldy $firstName
|
ldy $firstName
|
||||||
lda $secondName
|
lda $secondName
|
||||||
sta $firstName
|
sta $firstName
|
||||||
tya
|
sty $secondName
|
||||||
sta $secondName
|
|
||||||
ldy $firstName+1
|
ldy $firstName+1
|
||||||
lda $secondName+1
|
lda $secondName+1
|
||||||
sta $firstName+1
|
sta $firstName+1
|
||||||
tya
|
sty $secondName+1
|
||||||
sta $secondName+1
|
|
||||||
""")
|
""")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(dt.istype(DataType.FLOAT)) {
|
if(dt.istype(DataType.FLOAT)) {
|
||||||
TODO("optimized case for swapping 2 float vars-- asm subroutine")
|
asmgen.out("""
|
||||||
|
lda #<$firstName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
lda #>$firstName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1+1}
|
||||||
|
lda #<$secondName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W2}
|
||||||
|
lda #>$secondName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W2+1}
|
||||||
|
jsr c64flt.swap_floats
|
||||||
|
""")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO more optimized cases? for instance swapping elements of array vars?
|
// other types of swap() calls should have been replaced by a different statement sequence involving a temp variable
|
||||||
|
throw AssemblyError("no asm generation for swap funccall $fcall")
|
||||||
// suboptimal code via the evaluation stack...
|
|
||||||
asmgen.translateExpression(first)
|
|
||||||
asmgen.translateExpression(second)
|
|
||||||
// pop in reverse order
|
|
||||||
val firstTarget = AssignTarget.fromExpr(first)
|
|
||||||
val secondTarget = AssignTarget.fromExpr(second)
|
|
||||||
asmgen.assignFromEvalResult(firstTarget)
|
|
||||||
asmgen.assignFromEvalResult(secondTarget)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
|
@ -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(" txa | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
Register.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
||||||
@ -240,16 +230,26 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(amount<=2)
|
var left = amount
|
||||||
repeat(amount) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
while(left>=7) {
|
||||||
|
asmgen.out(" jsr math.shift_right_uw_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_uw_$amount") // 3-7 (8+ is done via other optimizations)
|
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount<=2)
|
var left = amount
|
||||||
repeat(amount) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
while(left>=7) {
|
||||||
|
asmgen.out(" jsr math.shift_right_w_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_w_$amount") // 3-7 (8+ is done via other optimizations)
|
asmgen.out(" jsr math.shift_right_w_$left")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -269,11 +269,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(amount<=2) {
|
var left=amount
|
||||||
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
while(left>=7) {
|
||||||
} else {
|
asmgen.out(" jsr math.shift_left_w_7")
|
||||||
asmgen.out(" jsr math.shift_left_w_$amount") // 3-7 (8+ is done via other optimizations)
|
left -= 7
|
||||||
}
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_left_w_$left")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
@ -16,7 +15,7 @@ import prog8.compiler.toHex
|
|||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
// todo choose more efficient comparisons to avoid needless lda's
|
// todo choose more efficient comparisons to avoid needless lda's
|
||||||
// todo optimize common case step == 2 / -2
|
// todo optimize common case when step == 2 or -2
|
||||||
|
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -55,31 +54,11 @@ 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 over byte range via loopvar
|
||||||
// loop register over range
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
if(stmt.loopRegister!= Register.A)
|
asmgen.translateExpression(range.to)
|
||||||
throw AssemblyError("can only use A")
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.out("""
|
||||||
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
|
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
|
||||||
asmgen.translateExpression(range.to)
|
|
||||||
asmgen.translateExpression(range.from)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO_HEX},x
|
lda ${ESTACK_LO_HEX},x
|
||||||
sta $varname
|
sta $varname
|
||||||
@ -92,76 +71,41 @@ $continueLabel lda $varname
|
|||||||
$incdec $varname
|
$incdec $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 over byte range via loopvar
|
||||||
// loop register over range
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
if(stmt.loopRegister!= Register.A)
|
asmgen.translateExpression(range.to)
|
||||||
throw AssemblyError("can only use A")
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.out("""
|
||||||
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
|
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
|
||||||
asmgen.translateExpression(range.to)
|
|
||||||
asmgen.translateExpression(range.from)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO_HEX},x
|
lda ${ESTACK_LO_HEX},x
|
||||||
sta $varname
|
sta $varname
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel lda $varname""")
|
$continueLabel lda $varname""")
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc #$stepsize
|
adc #$stepsize
|
||||||
sta $varname
|
sta $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||||
bcc $loopLabel
|
bcc $loopLabel
|
||||||
beq $loopLabel""")
|
beq $loopLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sec
|
sec
|
||||||
sbc #${stepsize.absoluteValue}
|
sbc #${stepsize.absoluteValue}
|
||||||
sta $varname
|
sta $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||||
bcs $loopLabel""")
|
bcs $loopLabel""")
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
$endLabel inx""")
|
|
||||||
}
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
$endLabel inx""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
@ -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
|
||||||
@ -339,10 +280,8 @@ $continueLabel inc $loopLabel+1
|
|||||||
$endLabel""")
|
$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 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
|
||||||
@ -366,14 +304,12 @@ $counterLabel .byte 0
|
|||||||
$endLabel""")
|
$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 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
|
||||||
@ -410,7 +346,7 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter in such cases
|
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter var in such cases
|
||||||
if (range.isEmpty())
|
if (range.isEmpty())
|
||||||
throw AssemblyError("empty range")
|
throw AssemblyError("empty range")
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
@ -421,93 +357,12 @@ $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 over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
// loop register over range
|
when {
|
||||||
|
range.step==1 -> {
|
||||||
if(stmt.loopRegister!= Register.A)
|
// step = 1
|
||||||
throw AssemblyError("can only use A")
|
asmgen.out("""
|
||||||
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
|
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
|
||||||
when {
|
|
||||||
range.step==1 -> {
|
|
||||||
// step = 1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
lda #${range.last-range.first+1 and 255}
|
lda #${range.last-range.first+1 and 255}
|
||||||
@ -521,34 +376,34 @@ $continueLabel dec $counterLabel
|
|||||||
jmp $loopLabel
|
jmp $loopLabel
|
||||||
$counterLabel .byte 0
|
$counterLabel .byte 0
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
range.step==-1 -> {
|
range.step==-1 -> {
|
||||||
// step = -1
|
// step = -1
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
lda #${range.first-range.last+1 and 255}
|
lda #${range.first-range.last+1 and 255}
|
||||||
sta $counterLabel
|
sta $counterLabel
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel dec $counterLabel
|
$continueLabel dec $counterLabel
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
jmp $loopLabel
|
jmp $loopLabel
|
||||||
$counterLabel .byte 0
|
$counterLabel .byte 0
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
range.step >= 2 -> {
|
range.step >= 2 -> {
|
||||||
// step >= 2
|
// step >= 2
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${(range.last-range.first) / range.step + 1}
|
lda #${(range.last-range.first) / range.step + 1}
|
||||||
sta $counterLabel
|
sta $counterLabel
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel dec $counterLabel
|
$continueLabel dec $counterLabel
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
lda $varname
|
lda $varname
|
||||||
@ -558,17 +413,17 @@ $continueLabel dec $counterLabel
|
|||||||
jmp $loopLabel
|
jmp $loopLabel
|
||||||
$counterLabel .byte 0
|
$counterLabel .byte 0
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// step <= -2
|
// step <= -2
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
||||||
sta $counterLabel
|
sta $counterLabel
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel dec $counterLabel
|
$continueLabel dec $counterLabel
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
lda $varname
|
lda $varname
|
||||||
@ -578,13 +433,12 @@ $continueLabel dec $counterLabel
|
|||||||
jmp $loopLabel
|
jmp $loopLabel
|
||||||
$counterLabel .byte 0
|
$counterLabel .byte 0
|
||||||
$endLabel""")
|
$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,14 +19,42 @@ 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...
|
||||||
|
|
||||||
val subName = asmgen.asmIdentifierName(stmt.target)
|
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
translateFuncArguments(arg.first, arg.second, sub)
|
// via variables
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
|
argumentViaVariable(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// via registers
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
// just a single parameter, no risk of clobbering registers
|
||||||
|
argumentViaRegister(sub, sub.parameters.withIndex().single(), stmt.args[0])
|
||||||
|
} else {
|
||||||
|
// multiple register arguments, risk of register clobbering.
|
||||||
|
// evaluate arguments onto the stack, and load the registers from the evaluated values on the stack.
|
||||||
|
when {
|
||||||
|
stmt.args.all {it is AddressOf ||
|
||||||
|
it is NumericLiteralValue ||
|
||||||
|
it is StringLiteralValue ||
|
||||||
|
it is ArrayLiteralValue ||
|
||||||
|
it is IdentifierReference} -> {
|
||||||
|
// no risk of clobbering for these simple argument types. Optimize the register loading.
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
|
argumentViaRegister(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Risk of clobbering due to complex expression args. Work via the stack.
|
||||||
|
argsViaStackEvaluation(stmt, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
@ -35,88 +63,126 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFuncArguments(parameter: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
|
private fun argsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
val sourceIDt = value.inferType(program)
|
for (arg in stmt.args.reversed())
|
||||||
if(!sourceIDt.isKnown)
|
asmgen.translateExpression(arg)
|
||||||
throw AssemblyError("arg type unknown")
|
for (regparam in sub.asmParameterRegisters) {
|
||||||
val sourceDt = sourceIDt.typeOrElse(DataType.STRUCT)
|
when (regparam.registerOrPair) {
|
||||||
if(!argumentTypeCompatible(sourceDt, parameter.value.type))
|
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
throw AssemblyError("argument type incompatible")
|
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
if(sub.asmParameterRegisters.isEmpty()) {
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||||
// pass parameter via a regular variable (not via registers)
|
RegisterOrPair.AX -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
val paramVar = parameter.value
|
RegisterOrPair.AY -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
RegisterOrPair.XY -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
null -> {
|
||||||
target.linkParents(value.parent)
|
|
||||||
when (value) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
// optimize when the argument is a constant literal
|
|
||||||
when(parameter.value.type) {
|
|
||||||
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
|
||||||
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
|
||||||
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as argument via a variable?") // TODO huh
|
|
||||||
else -> throw AssemblyError("weird parameter datatype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
// optimize when the argument is a variable
|
|
||||||
when (parameter.value.type) {
|
|
||||||
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
|
||||||
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
|
||||||
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as argument via a variable?") // TODO huh
|
|
||||||
else -> throw AssemblyError("weird parameter datatype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RegisterExpr -> {
|
|
||||||
asmgen.assignFromRegister(target, value.register)
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
when(value.addressExpression) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
|
||||||
asmgen.assignFromMemoryByte(target, address, null)
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value.addressExpression)
|
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
|
||||||
asmgen.assignFromRegister(target, Register.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value)
|
|
||||||
asmgen.assignFromEvalResult(target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
when (regparam.statusflag) {
|
||||||
// pass parameter via a register parameter
|
Statusflag.Pc -> asmgen.out("""
|
||||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
inx
|
||||||
val statusflag = paramRegister.statusflag
|
pha
|
||||||
val register = paramRegister.registerOrPair
|
lda $ESTACK_LO_HEX,x
|
||||||
val stack = paramRegister.stack
|
beq +
|
||||||
when {
|
sec
|
||||||
stack -> {
|
bcs ++
|
||||||
// push arg onto the stack
|
+ clc
|
||||||
// note: argument order is reversed (first argument will be deepest on the stack)
|
+ pla
|
||||||
asmgen.translateExpression(value)
|
""")
|
||||||
|
null -> {
|
||||||
}
|
}
|
||||||
statusflag!=null -> {
|
else -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
if (statusflag == Statusflag.Pc) {
|
}
|
||||||
// this param needs to be set last, right before the jsr
|
}
|
||||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
}
|
||||||
when(value) {
|
|
||||||
is NumericLiteralValue -> {
|
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
val carrySet = value.number.toInt() != 0
|
// pass parameter via a regular variable (not via registers)
|
||||||
asmgen.out(if(carrySet) " sec" else " clc")
|
val valueIDt = value.inferType(program)
|
||||||
}
|
if(!valueIDt.isKnown)
|
||||||
is IdentifierReference -> {
|
throw AssemblyError("arg type unknown")
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
asmgen.out("""
|
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
|
val paramVar = parameter.value
|
||||||
|
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||||
|
val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
when(parameter.value.type) {
|
||||||
|
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||||
|
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
||||||
|
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as argument via a variable?") // TODO huh
|
||||||
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
// optimize when the argument is a variable
|
||||||
|
when (parameter.value.type) {
|
||||||
|
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
||||||
|
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
||||||
|
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as argument via a variable?") // TODO huh
|
||||||
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
when(value.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
asmgen.assignFromMemoryByte(target, address, null)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
||||||
|
asmgen.assignFromRegister(target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
asmgen.assignFromEvalResult(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
|
// pass argument via a register parameter
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
|
val statusflag = paramRegister.statusflag
|
||||||
|
val register = paramRegister.registerOrPair
|
||||||
|
val stack = paramRegister.stack
|
||||||
|
when {
|
||||||
|
stack -> {
|
||||||
|
// push arg onto the stack
|
||||||
|
// note: argument order is reversed (first argument will be deepest on the stack)
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
}
|
||||||
|
statusflag!=null -> {
|
||||||
|
if (statusflag == Statusflag.Pc) {
|
||||||
|
// this param needs to be set last, right before the jsr
|
||||||
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
|
when(value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val carrySet = value.number.toInt() != 0
|
||||||
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
|
asmgen.out("""
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
@ -124,108 +190,90 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
+ clc
|
+ clc
|
||||||
+
|
+
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
else -> {
|
||||||
when(value.register) {
|
asmgen.translateExpression(value)
|
||||||
Register.A -> asmgen.out(" cmp #0")
|
asmgen.out("""
|
||||||
Register.X -> asmgen.out(" txa")
|
inx
|
||||||
Register.Y -> asmgen.out(" tya")
|
pha
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
beq +
|
|
||||||
sec
|
|
||||||
bcs ++
|
|
||||||
+ clc
|
|
||||||
+
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
lda $ESTACK_LO_HEX,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+
|
+ pla
|
||||||
""")
|
""")
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else throw AssemblyError("can only use Carry as status flag parameter")
|
|
||||||
}
|
|
||||||
register!=null && register.name.length==1 -> {
|
|
||||||
when (value) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
|
||||||
target.linkParents(value.parent)
|
|
||||||
asmgen.assignFromByteConstant(target, value.number.toShort())
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
|
||||||
target.linkParents(value.parent)
|
|
||||||
asmgen.assignFromByteVariable(target, value)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value)
|
|
||||||
when(register) {
|
|
||||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
|
||||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
|
||||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
|
||||||
else -> throw AssemblyError("cannot assign to register pair")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
register!=null && register.name.length==2 -> {
|
else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
// register pair as a 16-bit value (only possible for subroutine parameters)
|
}
|
||||||
when (value) {
|
register!=null && register.name.length==1 -> {
|
||||||
is NumericLiteralValue -> {
|
when (value) {
|
||||||
// optimize when the argument is a constant literal
|
is NumericLiteralValue -> {
|
||||||
val hex = value.number.toHex()
|
asmgen.assignToRegister(CpuRegister.valueOf(register.name), value.number.toShort(), null)
|
||||||
when (register) {
|
}
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex")
|
is IdentifierReference -> {
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex")
|
asmgen.assignToRegister(CpuRegister.valueOf(register.name), null, value)
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex")
|
}
|
||||||
else -> {}
|
else -> {
|
||||||
}
|
asmgen.translateExpression(value)
|
||||||
|
when(register) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
|
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||||
|
else -> throw AssemblyError("cannot assign to register pair")
|
||||||
}
|
}
|
||||||
is AddressOf -> {
|
}
|
||||||
// optimize when the argument is an address of something
|
}
|
||||||
val sourceName = asmgen.asmIdentifierName(value.identifier)
|
}
|
||||||
|
register!=null && register.name.length==2 -> {
|
||||||
|
// register pair as a 16-bit value (only possible for subroutine parameters)
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
val hex = value.number.toHex()
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AddressOf -> {
|
||||||
|
// optimize when the argument is an address of something
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value.identifier)
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
|
if(valueDt in PassByReferenceDatatypes) {
|
||||||
when (register) {
|
when (register) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
is IdentifierReference -> {
|
when (register) {
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
||||||
if(sourceDt in PassByReferenceDatatypes) {
|
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
||||||
when (register) {
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
else -> {}
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (register) {
|
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
}
|
||||||
asmgen.translateExpression(value)
|
else -> {
|
||||||
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
asmgen.translateExpression(value)
|
||||||
throw AssemblyError("can't use X register here - use a variable")
|
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
||||||
else if (register == RegisterOrPair.AY)
|
throw AssemblyError("can't use X register here - use a variable")
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
else if (register == RegisterOrPair.AY)
|
||||||
}
|
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -24,10 +24,10 @@ private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexO
|
|||||||
|
|
||||||
class CallGraph(private val program: Program) : IAstVisitor {
|
class CallGraph(private val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val calls = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
|
|
||||||
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
||||||
val usedSymbols = mutableSetOf<Statement>()
|
val usedSymbols = mutableSetOf<Statement>()
|
||||||
@ -55,17 +55,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
it.importedBy.clear()
|
it.importedBy.clear()
|
||||||
it.imports.clear()
|
it.imports.clear()
|
||||||
|
|
||||||
it.importedBy.addAll(modulesImportedBy.getValue(it))
|
it.importedBy.addAll(importedBy.getValue(it))
|
||||||
it.imports.addAll(modulesImporting.getValue(it))
|
it.imports.addAll(imports.getValue(it))
|
||||||
|
|
||||||
forAllSubroutines(it) { sub ->
|
|
||||||
sub.calledBy.clear()
|
|
||||||
sub.calls.clear()
|
|
||||||
|
|
||||||
sub.calledBy.addAll(subroutinesCalledBy.getValue(sub))
|
|
||||||
sub.calls.addAll(subroutinesCalling.getValue(sub))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootmodule = program.modules.first()
|
val rootmodule = program.modules.first()
|
||||||
@ -85,8 +76,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val thisModule = directive.definingModule()
|
val thisModule = directive.definingModule()
|
||||||
if (directive.directive == "%import") {
|
if (directive.directive == "%import") {
|
||||||
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
||||||
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
|
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
||||||
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
|
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
||||||
} else if (directive.directive == "%asminclude") {
|
} else if (directive.directive == "%asminclude") {
|
||||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
||||||
val scope = directive.definingScope()
|
val scope = directive.definingScope()
|
||||||
@ -141,8 +132,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCall.definingSubroutine()?.let { thisSub ->
|
functionCall.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
@ -152,8 +143,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCallStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
@ -163,8 +154,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
jump.definingSubroutine()?.let { thisSub ->
|
jump.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(jump)
|
super.visit(jump)
|
||||||
@ -190,14 +181,14 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
||||||
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
} else if (jumptarget.contains('.')) {
|
} else if (jumptarget.contains('.')) {
|
||||||
// maybe only the first part already refers to a subroutine
|
// maybe only the first part already refers to a subroutine
|
||||||
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
||||||
if (node2 is Subroutine) {
|
if (node2 is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node2)
|
calls[scope] = calls.getValue(scope).plus(node2)
|
||||||
subroutinesCalledBy[node2] = subroutinesCalledBy.getValue(node2).plus(context)
|
calledBy[node2] = calledBy.getValue(node2).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,8 +200,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (target.contains('.')) {
|
if (target.contains('.')) {
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,40 +342,38 @@ 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 -> {
|
if(rangeFrom.type!= DataType.UBYTE) {
|
||||||
if(rangeFrom.type!= DataType.UBYTE) {
|
// attempt to translate the iterable into ubyte values
|
||||||
// attempt to translate the iterable into ubyte values
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
|
||||||
if(rangeFrom.type!= DataType.BYTE) {
|
|
||||||
// attempt to translate the iterable into byte values
|
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
if(rangeFrom.type!= DataType.UWORD) {
|
|
||||||
// attempt to translate the iterable into uword values
|
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
if(rangeFrom.type!= DataType.WORD) {
|
|
||||||
// attempt to translate the iterable into word values
|
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
|
||||||
}
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(rangeFrom.type!= DataType.BYTE) {
|
||||||
|
// attempt to translate the iterable into byte values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(rangeFrom.type!= DataType.UWORD) {
|
||||||
|
// attempt to translate the iterable into uword values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(rangeFrom.type!= DataType.WORD) {
|
||||||
|
// attempt to translate the iterable into word values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
package prog8.optimizer
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
|
||||||
import prog8.ast.statements.NopStatement
|
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
|
|
||||||
internal class FlattenAnonymousScopesAndNopRemover: IAstVisitor {
|
|
||||||
private var scopesToFlatten = mutableListOf<INameScope>()
|
|
||||||
private val nopStatements = mutableListOf<NopStatement>()
|
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
|
||||||
super.visit(program)
|
|
||||||
for(scope in scopesToFlatten.reversed()) {
|
|
||||||
val namescope = scope.parent as INameScope
|
|
||||||
val idx = namescope.statements.indexOf(scope as Statement)
|
|
||||||
if(idx>=0) {
|
|
||||||
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
|
||||||
nop.parent = namescope as Node
|
|
||||||
namescope.statements[idx] = nop
|
|
||||||
namescope.statements.addAll(idx, scope.statements)
|
|
||||||
scope.statements.forEach { it.parent = namescope }
|
|
||||||
visit(nop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nopStatements.forEach {
|
|
||||||
it.definingScope().remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope) {
|
|
||||||
if(scope.parent is INameScope) {
|
|
||||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
|
||||||
nopStatements.add(nopStatement)
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,6 @@ import kotlin.math.floor
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: remove unreachable code after return and exit()
|
TODO: remove unreachable code after return and exit()
|
||||||
TODO: proper inlining of tiny subroutines (at first, restrict to subs without parameters and variables in them, and build it up from there: correctly renaming/relocating all variables in them and refs to those as well)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -47,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,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))
|
||||||
}
|
}
|
||||||
@ -208,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))
|
||||||
}
|
}
|
||||||
@ -223,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))
|
||||||
}
|
}
|
||||||
@ -236,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))
|
||||||
@ -248,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
|
||||||
@ -270,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
|
||||||
@ -281,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 {
|
||||||
|
@ -17,7 +17,7 @@ internal class UnusedCodeRemover: AstWalker() {
|
|||||||
val entrypoint = program.entrypoint()
|
val entrypoint = program.entrypoint()
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
callgraph.forAllSubroutines(it) { sub ->
|
callgraph.forAllSubroutines(it) { sub ->
|
||||||
if (sub !== entrypoint && !sub.keepAlways && (sub.calledBy.isEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
if (sub !== entrypoint && !sub.keepAlways && (callgraph.calledBy[sub].isNullOrEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
||||||
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ TODO
|
|||||||
- finalize (most) of the still missing "new" assignment asm code generation
|
- finalize (most) of the still missing "new" assignment asm code generation
|
||||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
||||||
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
||||||
- investigate support for 8bitguy's Commander X16 platform https://murray2.com/forums/commander-x16.9/ and https://github.com/commanderx16/x16-docs
|
- investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs
|
||||||
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
||||||
|
|
||||||
|
|
||||||
@ -17,13 +17,13 @@ Add more compiler optimizations to the existing ones.
|
|||||||
- more targeted optimizations for assigment asm code, such as the following:
|
- more targeted optimizations for assigment asm code, such as the following:
|
||||||
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
||||||
- remove unreachable code after an exit(), return or goto
|
- remove unreachable code after an exit(), return or goto
|
||||||
- working subroutine inlining (start with trivial routines, grow to taking care of vars and identifier refs to them)
|
|
||||||
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
||||||
the program will then rely solely on the values as they are in memory at the time of program startup.
|
the program will then rely solely on the values as they are in memory at the time of program startup.
|
||||||
- Also some library routines and code patterns could perhaps be optimized further
|
- Also some library routines and code patterns could perhaps be optimized further
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
- can the parameter passing to subroutines be optimized to avoid copying?
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
|
- note: abandoned subroutine inlining because of problems referencing non-local stuff. Can't move everything around.
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
Eval stack redesign? (lot of work)
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
;%import c64flt
|
|
||||||
;%option enable_floats
|
|
||||||
%zeropage dontuse
|
%zeropage dontuse
|
||||||
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
BIN
examples/compiled/mandelbrot-gfx.prg
Normal file
BIN
examples/compiled/mandelbrot-gfx.prg
Normal file
Binary file not shown.
Binary file not shown.
@ -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()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64utils
|
%import c64utils
|
||||||
|
|
||||||
|
|
||||||
spritedata $2000 {
|
spritedata $2000 {
|
||||||
; this memory block contains the sprite data
|
; this memory block contains the sprite data
|
||||||
; it must start on an address aligned to 64 bytes.
|
; it must start on an address aligned to 64 bytes.
|
||||||
@ -81,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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64graphics
|
%import c64graphics
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimizations
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
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
|
||||||
|
54
examples/mandelbrot-gfx.p8
Normal file
54
examples/mandelbrot-gfx.p8
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
%import c64lib
|
||||||
|
%import c64flt
|
||||||
|
%import c64graphics
|
||||||
|
%zeropage floatsafe
|
||||||
|
|
||||||
|
; Draw a mandelbrot in graphics mode (the image will be 256 x 200 pixels).
|
||||||
|
; NOTE: this will take an eternity to draw on a real c64.
|
||||||
|
; even in Vice in warp mode (700% speed on my machine) it's slow, but you can see progress
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimizations
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
const ubyte width = 255
|
||||||
|
const ubyte height = 200
|
||||||
|
const ubyte max_iter = 16
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
graphics.enable_bitmap_mode()
|
||||||
|
|
||||||
|
ubyte pixelx
|
||||||
|
ubyte pixely
|
||||||
|
|
||||||
|
for pixely in 0 to height-1 {
|
||||||
|
float yy = (pixely as float)/0.4/height - 1.0
|
||||||
|
|
||||||
|
for pixelx in 0 to width-1 {
|
||||||
|
float xx = (pixelx as float)/0.3/width - 2.2
|
||||||
|
|
||||||
|
float xsquared = 0.0
|
||||||
|
float ysquared = 0.0
|
||||||
|
float x = 0.0
|
||||||
|
float y = 0.0
|
||||||
|
ubyte iter = 0
|
||||||
|
|
||||||
|
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||||
|
y = x*y*2.0 + yy
|
||||||
|
x = xsquared - ysquared + xx
|
||||||
|
xsquared = x*x
|
||||||
|
ysquared = y*y
|
||||||
|
iter++
|
||||||
|
}
|
||||||
|
|
||||||
|
if iter & 1 {
|
||||||
|
graphics.plotx = pixelx
|
||||||
|
graphics.plot(pixely)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ main {
|
|||||||
float y = 0.0
|
float y = 0.0
|
||||||
ubyte iter = 0
|
ubyte iter = 0
|
||||||
|
|
||||||
while (iter<max_iter and xsquared+ysquared<4.0) {
|
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||||
y = x*y*2.0 + yy
|
y = x*y*2.0 + yy
|
||||||
x = xsquared - ysquared + xx
|
x = xsquared - ysquared + xx
|
||||||
xsquared = x*x
|
xsquared = x*x
|
||||||
@ -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,6 +1,9 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimization ( /= )
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
@ -11,7 +14,7 @@ main {
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
Color purple = {255, 0, 255}
|
Color purple = [255, 0, 255]
|
||||||
|
|
||||||
Color other
|
Color other
|
||||||
|
|
||||||
@ -27,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,11 +11,9 @@ main {
|
|||||||
float t
|
float t
|
||||||
ubyte color
|
ubyte color
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
float x = sin(t)
|
ubyte xx=(sin(t) * width/2.2) + width/2.0 as ubyte
|
||||||
float y = cos(t*1.1356)
|
ubyte yy=(cos(t*1.1356) * height/2.2) + height/2.0 as ubyte
|
||||||
ubyte xx=(x * width/2.2) + width/2.0 as ubyte
|
|
||||||
ubyte yy=(y * height/2.2) + height/2.0 as ubyte
|
|
||||||
c64scr.setcc(xx, yy, 81, color)
|
c64scr.setcc(xx, yy, 81, color)
|
||||||
t += 0.08
|
t += 0.08
|
||||||
color++
|
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)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
; staged speed increase
|
; staged speed increase
|
||||||
; some simple sound effects
|
; some simple sound effects
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
const ubyte boardOffsetX = 14
|
const ubyte boardOffsetX = 14
|
||||||
@ -38,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
|
||||||
|
|
||||||
@ -107,6 +106,7 @@ waitkey:
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dropypos>ypos {
|
if dropypos>ypos {
|
||||||
ypos = dropypos
|
ypos = dropypos
|
||||||
sound.blockdrop()
|
sound.blockdrop()
|
||||||
@ -389,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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,15 +2,45 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
%option enable_floats
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
A=42
|
|
||||||
|
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,24 @@
|
|||||||
%import c64graphics
|
%import c64graphics
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimizations
|
||||||
|
|
||||||
main {
|
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,26 +41,32 @@ turtle {
|
|||||||
c64.SPENA = 1
|
c64.SPENA = 1
|
||||||
c64.SP0COL = 5
|
c64.SP0COL = 5
|
||||||
|
|
||||||
turtlepos()
|
update_turtle_sprite()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub turtlepos() {
|
sub update_turtle_sprite() {
|
||||||
uword xx = xpos as uword
|
uword xx = xpos as uword
|
||||||
c64.SPXY[0] = lsb(xx) + 12
|
c64.SPXY[0] = lsb(xx) + 12
|
||||||
if msb(xx)
|
c64.MSIGX = msb(xx) > 0
|
||||||
c64.MSIGX = 1
|
|
||||||
else
|
|
||||||
c64.MSIGX = 0
|
|
||||||
c64.SPXY[1] = lsb(ypos) + 40
|
c64.SPXY[1] = lsb(ypos) + 40
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub pos(float x, float y) {
|
||||||
|
if pendown {
|
||||||
|
graphics.line(xpos as uword, ypos as ubyte, x as uword, y as ubyte)
|
||||||
|
}
|
||||||
|
xpos = x
|
||||||
|
ypos = y
|
||||||
|
update_turtle_sprite()
|
||||||
|
}
|
||||||
|
|
||||||
sub fd(uword length) {
|
sub fd(uword length) {
|
||||||
float flen = length as float
|
float flen = length as float
|
||||||
float sx = xpos
|
float sx = xpos
|
||||||
float sy = ypos
|
float sy = ypos
|
||||||
xpos += flen * sin(angle)
|
xpos += flen * sin(angle)
|
||||||
ypos -= flen * cos(angle)
|
ypos -= flen * cos(angle)
|
||||||
turtlepos()
|
update_turtle_sprite()
|
||||||
if pendown {
|
if pendown {
|
||||||
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
||||||
}
|
}
|
||||||
|
@ -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