Compare commits

...

32 Commits
v2.4 ... v3.1

Author SHA1 Message Date
9cbb8e1a64 version 3.1 2020-08-18 16:26:23 +02:00
53e9ad5088 better asm code for repeat loops 2020-08-18 16:02:40 +02:00
cf6ea63fa6 forloop asm done 2020-08-18 15:29:39 +02:00
1de0ebb7bc more forloop asm 2020-08-18 15:16:56 +02:00
77c1376d6d proper error message for arrays that are declared too big 2020-08-18 14:47:52 +02:00
353f1954a5 for loop codegen 2020-08-18 14:03:31 +02:00
8bf3406cf8 gradle version 2020-08-18 00:53:14 +02:00
936bf9a05c gradle version 2020-08-18 00:47:23 +02:00
4487499663 more forloop codegen 2020-08-17 23:42:43 +02:00
3976cc26a2 more forloop codegen 2020-08-17 23:19:23 +02:00
e6ff87ecd0 upgraded to Kotlin 1.4, fixed several compilation warnings 2020-08-17 19:36:07 +02:00
c0887b5f08 removed 'continue' statement to be able to generate more optimized loop assembly code. started with for loop optimizations 2020-08-17 19:22:29 +02:00
f14dda4eca fix certain corruption of A register argument on asm sub call 2020-08-16 19:15:44 +02:00
bd7f75c130 loop todos 2020-07-30 02:54:37 +02:00
fbe3ce008b slight expression rewrite in case of certain in-place assignments, to try to get the in-place variable operand to the leftmost position 2020-07-30 01:30:21 +02:00
7ac6c8f2d1 todo related to in-place assignment 2020-07-27 00:32:59 +02:00
fdfbb7bdf0 improved call arguments type check 2020-07-27 00:28:48 +02:00
1c16bbb742 tweaks for string handling as arguments 2020-07-27 00:12:27 +02:00
9735527062 cleanup double code 2020-07-26 23:46:06 +02:00
402827497e fix float array assignment 2020-07-26 23:32:20 +02:00
f81aa0d867 Merge branch 'remove_aug_assign' 2020-07-26 19:23:34 +02:00
d32a970101 partly optimize assignments so that simple increments and decrements can be done via separate statements (postincrdecr) 2020-07-26 19:22:12 +02:00
cd651aa416 use repeat 2020-07-26 13:50:14 +02:00
8a3189123a to reduce complexity, augmented assignment has been removed again from internal Ast and codegen for now. 2020-07-26 13:48:31 +02:00
b37231d0f5 version 3.0 2020-07-26 01:33:02 +02:00
3c55719bf1 finalize repeat asmgen 2020-07-26 01:32:27 +02:00
af8279a9b9 empty for loops are removed 2020-07-25 22:54:50 +02:00
c38508c262 introduced repeat loop. repeat-until changed to do-util.
forever loop is gone (use repeat without iteration count).
struct literal is now same as array literal [...] to avoid parsing ambiguity with scope blocks.
2020-07-25 16:56:34 +02:00
b0e8738ab8 remove unused c64 resources 2020-07-25 14:47:31 +02:00
cae480768e version is work in progress 2020-07-25 14:45:06 +02:00
a70276c190 use indexOfFirst. Also avoid initializing a for loop variable twice in a row. 2020-07-25 14:44:24 +02:00
0c461ffe2e removed Register expression (directly accessing cpu register) 2020-07-25 14:14:24 +02:00
97 changed files with 2019 additions and 3230 deletions

View File

@ -4,8 +4,8 @@ sudo: false
# dist: xenial
before_install:
- chmod +x gradlew
- chmod +x ./gradlew
script:
- gradle test
- ./gradlew test

View File

@ -18,7 +18,7 @@ which aims to provide many conveniences over raw assembly code (even when using
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- 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
- conditional branches
- 'when' statement to provide a concise jump table alternative to if/elseif chains
@ -77,7 +77,7 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
c64scr.print("prime numbers up to 255:\n\n")
ubyte amount=0
while true {
repeat {
ubyte prime = find_next_prime()
if prime==0
break

View File

@ -1,11 +1,11 @@
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0"
}
}
plugins {
// id "org.jetbrains.kotlin.jvm" version "1.3.72"
// id "org.jetbrains.kotlin.jvm" version "1.4.0"
id 'application'
id 'org.jetbrains.dokka' version "0.9.18"
id 'com.github.johnrengelman.shadow' version '5.2.0'
@ -110,3 +110,7 @@ dokka {
outputFormat = 'html'
outputDirectory = "$buildDir/kdoc"
}
task wrapper(type: Wrapper) {
gradleVersion = '6.1.1'
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -215,23 +215,13 @@ pop_float_to_indexed_var .proc
copy_float .proc
; -- 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.
sta c64.SCRATCH_ZPWORD2
sty c64.SCRATCH_ZPWORD2+1
ldy #0
lda (c64.SCRATCH_ZPWORD1),y
sta (c64.SCRATCH_ZPWORD2),y
iny
lda (c64.SCRATCH_ZPWORD1),y
sta (c64.SCRATCH_ZPWORD2),y
iny
lda (c64.SCRATCH_ZPWORD1),y
sta (c64.SCRATCH_ZPWORD2),y
iny
lda (c64.SCRATCH_ZPWORD1),y
sta (c64.SCRATCH_ZPWORD2),y
iny
lda (c64.SCRATCH_ZPWORD1),y
sta (c64.SCRATCH_ZPWORD2),y
sta _target+1
sty _target+2
ldy #4
_loop lda (c64.SCRATCH_ZPWORD1),y
_target sta $ffff,y ; modified
dey
bpl _loop
rts
.pend
@ -772,7 +762,6 @@ set_array_float .proc
asl a
clc
adc c64.ESTACK_LO,x
clc
adc c64.SCRATCH_ZPWORD2
ldy c64.SCRATCH_ZPWORD2+1
bcc +

View File

@ -1 +1 @@
2.4
3.1

View File

@ -287,12 +287,17 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
}
override fun visit(assignment: Assignment) {
assignment.target.accept(this)
if (assignment.aug_op != null && assignment.aug_op != "setvalue")
output(" ${assignment.aug_op} ")
else
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null && binExpr.left isSameAs assignment.target) {
// we only support the inplace assignments of the form A = A <operator> <value>
assignment.target.accept(this)
output(" ${binExpr.operator}= ")
binExpr.right.accept(this)
} else {
assignment.target.accept(this)
output(" = ")
assignment.value.accept(this)
assignment.value.accept(this)
}
}
override fun visit(postIncrDecr: PostIncrDecr) {
@ -300,20 +305,13 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
output(postIncrDecr.operator)
}
override fun visit(contStmt: Continue) {
output("continue")
}
override fun visit(breakStmt: Break) {
output("break")
}
override fun visit(forLoop: ForLoop) {
output("for ")
if(forLoop.loopRegister!=null)
output(forLoop.loopRegister.toString())
else
forLoop.loopVar!!.accept(this)
forLoop.loopVar.accept(this)
output(" in ")
forLoop.iterable.accept(this)
output(" ")
@ -327,16 +325,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
whileLoop.body.accept(this)
}
override fun visit(foreverLoop: ForeverLoop) {
output("forever ")
foreverLoop.body.accept(this)
}
override fun visit(repeatLoop: RepeatLoop) {
output("repeat ")
repeatLoop.iterations?.accept(this)
output(" ")
repeatLoop.body.accept(this)
}
override fun visit(untilLoop: UntilLoop) {
output("do ")
untilLoop.body.accept(this)
output(" until ")
repeatLoop.untilCondition.accept(this)
untilLoop.untilCondition.accept(this)
}
override fun visit(returnStmt: Return) {
@ -352,12 +352,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
}
override fun visit(assignTarget: AssignTarget) {
if(assignTarget.register!=null)
output(assignTarget.register.toString())
else {
assignTarget.memoryAddress?.accept(this)
assignTarget.identifier?.accept(this)
}
assignTarget.memoryAddress?.accept(this)
assignTarget.identifier?.accept(this)
assignTarget.arrayindexed?.accept(this)
}
@ -398,10 +394,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputlni("}}")
}
override fun visit(registerExpr: RegisterExpr) {
output(registerExpr.register.toString())
}
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
output(builtinFunctionStatementPlaceholder.name)
}
@ -436,10 +428,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputln("")
}
override fun visit(structLv: StructLiteralValue) {
outputListMembers(structLv.values.asSequence(), '{', '}')
}
override fun visit(nopStatement: NopStatement) {
output("; NOP @ ${nopStatement.position} $nopStatement")
}

View File

@ -57,7 +57,7 @@ interface INameScope {
when(stmt) {
// 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 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 BranchStatement -> {
if(stmt.truepart.name==name) return stmt.truepart
@ -175,8 +175,8 @@ interface INameScope {
find(it.truepart)
find(it.elsepart)
}
is UntilLoop -> find(it.body)
is RepeatLoop -> find(it.body)
is ForeverLoop -> find(it.body)
is WhileLoop -> find(it.body)
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
else -> { /* do nothing */ }
@ -187,6 +187,14 @@ interface INameScope {
find(this)
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 {
@ -230,7 +238,7 @@ class Program(val name: String, val modules: MutableList<Module>): Node {
override fun replaceChildNode(node: Node, replacement: Node) {
require(node is Module && replacement is Module)
val idx = modules.withIndex().find { it.value===node }!!.index
val idx = modules.indexOfFirst { it===node }
modules[idx] = replacement
replacement.parent = this
}
@ -257,7 +265,7 @@ class Module(override val name: String,
override fun definingScope(): INameScope = program.namespace
override fun replaceChildNode(node: Node, replacement: Node) {
require(node is Statement && replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index
val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement
replacement.parent = this
}

View File

@ -161,14 +161,14 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
if(vardecl!=null) return vardecl
assignment()?.let {
return Assignment(it.assign_target().toAst(), null, it.expression().toAst(), it.toPosition())
return Assignment(it.assign_target().toAst(), it.expression().toAst(), it.toPosition())
}
augassignment()?.let {
return Assignment(it.assign_target().toAst(),
it.operator.text,
it.expression().toAst(),
it.toPosition())
// replace A += X with A = A + X
val target = it.assign_target().toAst()
val expression = BinaryExpression(target.toExpression(), it.operator.text.substring(0, 1), it.expression().toAst(), it.expression().toPosition())
return Assignment(it.assign_target().toAst(), expression, it.toPosition())
}
postincrdecr()?.let {
@ -205,21 +205,18 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
val forloop = forloop()?.toAst()
if(forloop!=null) return forloop
val repeatloop = repeatloop()?.toAst()
if(repeatloop!=null) return repeatloop
val untilloop = untilloop()?.toAst()
if(untilloop!=null) return untilloop
val whileloop = whileloop()?.toAst()
if(whileloop!=null) return whileloop
val foreverloop = foreverloop()?.toAst()
if(foreverloop!=null) return foreverloop
val repeatloop = repeatloop()?.toAst()
if(repeatloop!=null) return repeatloop
val breakstmt = breakstmt()?.toAst()
if(breakstmt!=null) return breakstmt
val continuestmt = continuestmt()?.toAst()
if(continuestmt!=null) return continuestmt
val whenstmt = whenstmt()?.toAst()
if(whenstmt!=null) return whenstmt
@ -247,7 +244,7 @@ private class AsmsubDecl(val name: String,
val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<Register>)
val asmClobbers: Set<CpuRegister>)
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
val name = identifier().text
@ -274,24 +271,43 @@ private class AsmSubroutineReturn(val type: DataType,
val stack: Boolean,
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>
= 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>
= asmsub_param().map {
val vardecl = it.vardecl()
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
AsmSubroutineParameter(vardecl.varname.text, datatype,
it.registerorpair()?.toAst(),
it.statusregister()?.toAst(),
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")
}
}
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister,
!it.stack?.text.isNullOrEmpty(), toPosition())
}
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
val void = this.VOID() != null
val location = scoped_identifier().toAst()
@ -350,23 +366,22 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
}
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
val register = register()?.toAst()
val identifier = scoped_identifier()
return when {
register!=null -> AssignTarget(register, null, null, null, toPosition())
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition())
directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
else -> AssignTarget(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.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
ArrayIndex(expression().toAst(), toPosition())
@ -469,18 +484,11 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
// the ConstantFold takes care of that and converts the type if needed.
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")
}
}
}
if(register()!=null)
return RegisterExpr(register().toAst(), register().toPosition())
if(scoped_identifier()!=null)
return scoped_identifier().toAst()
@ -572,19 +580,16 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
val loopregister = register()?.toAst()
val loopvar = identifier()?.toAst()
val loopvar = identifier().toAst()
val iterable = expression()!!.toAst()
val scope =
if(statement()!=null)
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
else
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.BreakstmtContext.toAst() = Break(toPosition())
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
@ -595,19 +600,20 @@ private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
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 scope = AnonymousScope(statements, statement_block()?.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 statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return RepeatLoop(scope, untilCondition, toPosition())
return UntilLoop(scope, untilCondition, toPosition())
}
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {

View File

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

View File

@ -5,7 +5,6 @@ import prog8.ast.Program
import prog8.ast.processing.*
import prog8.compiler.CompilationOptions
import prog8.compiler.BeforeAsmGenerationAstChanger
import prog8.optimizer.AssignmentTransformer
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
@ -36,17 +35,6 @@ internal fun Program.verifyFunctionArgTypes() {
fixer.visit(this)
}
internal fun Program.transformAssignments(errors: ErrorReporter) {
val transform = AssignmentTransformer(this, errors)
transform.visit(this)
while(transform.optimizationsDone>0 && errors.isEmpty()) {
transform.applyModifications()
transform.optimizationsDone = 0
transform.visit(this)
}
transform.applyModifications()
}
internal fun Module.checkImportedValid() {
val imr = ImportedModuleDirectiveRemover()
imr.visit(this, this.parent)

View File

@ -25,25 +25,25 @@ sealed class Expression: Node {
abstract fun referencesIdentifiers(vararg name: String): Boolean
abstract fun inferType(program: Program): InferredTypes.InferredType
infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this)
infix fun isSameAs(other: Expression): Boolean {
if(this===other)
return true
when(this) {
is RegisterExpr ->
return (other is RegisterExpr && other.register==register)
return when(this) {
is IdentifierReference ->
return (other is IdentifierReference && other.nameInSource==nameInSource)
(other is IdentifierReference && other.nameInSource==nameInSource)
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 ->
return (other is BinaryExpression && other.operator==operator
(other is BinaryExpression && other.operator==operator
&& other.left isSameAs left
&& other.right isSameAs right)
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)
}
else -> return other==this
else -> other==this
}
}
}
@ -457,31 +457,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"
class StringLiteralValue(val value: String,
@ -529,7 +504,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Expression)
val idx = value.withIndex().find { it.value===node }!!.index
val idx = value.indexOfFirst { it===node }
value[idx] = replacement
replacement.parent = this
}
@ -696,29 +671,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 {
override lateinit var parent: Node
@ -732,6 +684,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
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) {
this.parent = parent
@ -800,7 +753,7 @@ class FunctionCall(override var target: IdentifierReference,
if(node===target)
target=replacement as IdentifierReference
else {
val idx = args.withIndex().find { it.value===node }!!.index
val idx = args.indexOfFirst { it===node }
args[idx] = replacement as Expression
}
replacement.parent = this

View File

@ -110,49 +110,37 @@ internal class AstChecker(private val program: Program,
}
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)
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
errors.err("can only loop over an iterable type", forLoop.position)
} else {
if (forLoop.loopRegister != null) {
// loop register
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
errors.err("register can only loop over bytes", forLoop.position)
if(forLoop.loopRegister!=Register.A)
errors.err("it's only possible to use A as a loop register", forLoop.position)
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position)
} else {
// loop variable
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", 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)
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)
}
}
}
@ -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 }
fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
regCounts.clear()
statusflagCounts.clear()
for(p in from) {
when(p.registerOrPair) {
RegisterOrPair.A -> regCounts[Register.A]=regCounts.getValue(Register.A)+1
RegisterOrPair.X -> regCounts[Register.X]=regCounts.getValue(Register.X)+1
RegisterOrPair.Y -> regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
RegisterOrPair.A -> regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
RegisterOrPair.X -> regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
RegisterOrPair.Y -> regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
RegisterOrPair.AX -> {
regCounts[Register.A]=regCounts.getValue(Register.A)+1
regCounts[Register.X]=regCounts.getValue(Register.X)+1
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
}
RegisterOrPair.AY -> {
regCounts[Register.A]=regCounts.getValue(Register.A)+1
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
}
RegisterOrPair.XY -> {
regCounts[Register.X]=regCounts.getValue(Register.X)+1
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
}
null ->
if(p.statusflag!=null)
@ -316,26 +304,22 @@ internal class AstChecker(private val program: Program,
} else {
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
// Instead, their reference (address) should be passed (as an UWORD).
// The language has no typed pointers at this time.
// TODO The language has no typed pointers at this time! This should be handled better.
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword for their address, or access the variable from the outer scope directly.")
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
}
}
visitStatements(subroutine.statements)
}
override fun visit(repeatLoop: RepeatLoop) {
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y"))
errors.warn("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", repeatLoop.untilCondition.position)
super.visit(repeatLoop)
override fun visit(untilLoop: UntilLoop) {
if(untilLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", untilLoop.untilCondition.position)
super.visit(untilLoop)
}
override fun visit(whileLoop: WhileLoop) {
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
errors.warn("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", whileLoop.condition.position)
super.visit(whileLoop)
@ -361,9 +345,9 @@ internal class AstChecker(private val program: Program,
if(targetIdent!=null) {
val targetVar = targetIdent.targetVarDecl(program.namespace)
if(targetVar?.struct != null) {
val sourceStructLv = assignment.value as? StructLiteralValue
val sourceStructLv = assignment.value as? ArrayLiteralValue
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)
} else {
val sourceIdent = assignment.value as? IdentifierReference
@ -397,8 +381,7 @@ internal class AstChecker(private val program: Program,
val targetIdentifier = assignTarget.identifier
if (targetIdentifier != null) {
val targetName = targetIdentifier.nameInSource
val targetSymbol = program.namespace.lookup(targetName, assignment)
when (targetSymbol) {
when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
null -> {
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
return
@ -509,29 +492,21 @@ internal class AstChecker(private val program: Program,
when(decl.value) {
null -> {
// a vardecl without an initial value, don't bother with the rest
return super.visit(decl)
// a vardecl without an initial value, don't bother with it
}
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
is StringLiteralValue -> {
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
}
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) {
val struct = decl.struct!!
val structLv = decl.value as StructLiteralValue
if(struct.numberOfElements != structLv.values.size) {
val structLv = decl.value as ArrayLiteralValue
if(struct.numberOfElements != structLv.value.size) {
errors.err("struct value has incorrect number of elements", structLv.position)
return
}
for(value in structLv.values.zip(struct.statements)) {
for(value in structLv.value.zip(struct.statements)) {
val memberdecl = value.second as VarDecl
val constValue = value.first.constValue(program)
if(constValue==null) {
@ -545,9 +520,13 @@ internal class AstChecker(private val program: Program,
}
}
} 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 -> {
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
super.visit(decl)
@ -584,8 +563,38 @@ internal class AstChecker(private val program: Program,
}
val declValue = decl.value
if(declValue!=null && decl.type==VarDeclType.VAR && !declValue.inferType(program).istype(decl.datatype))
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
if(declValue!=null && decl.type==VarDeclType.VAR) {
if(decl.datatype==DataType.STRUCT) {
val valueIdt = declValue.inferType(program)
if(valueIdt.isUnknown)
throw AstException("invalid value type")
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
if(valueDt !in ArrayDatatypes)
err("initialisation of struct should be with array value", declValue.position)
} else if (!declValue.inferType(program).istype(decl.datatype)) {
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
}
}
// array length limits
if(decl.isArray) {
val length = decl.arraysize!!.size() ?: 1
when (decl.datatype) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
if(length==0 || length>256)
err("string and byte array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(length==0 || length>128)
err("word array length must be 1-128")
}
DataType.ARRAY_F -> {
if(length==0 || length>51)
err("float array length must be 1-51")
}
else -> {}
}
}
super.visit(decl)
}
@ -820,6 +829,10 @@ internal class AstChecker(private val program: Program,
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position)
}
val error = VerifyFunctionArgTypes.checkTypes(functionCall, functionCall.definingScope(), program)
if(error!=null)
errors.err(error, functionCall.args.first().position)
super.visit(functionCall)
}
@ -844,10 +857,15 @@ internal class AstChecker(private val program: Program,
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
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)
}
}
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, functionCallStatement.definingScope(), program)
if(error!=null)
errors.err(error, functionCallStatement.args.first().position)
super.visit(functionCallStatement)
}
@ -1292,8 +1310,8 @@ internal class AstChecker(private val program: Program,
DataType.STR -> sourceDatatype== DataType.STR
DataType.STRUCT -> {
if(sourceDatatype==DataType.STRUCT) {
val structLv = sourceValue as StructLiteralValue
val numValues = structLv.values.size
val structLv = sourceValue as ArrayLiteralValue
val numValues = structLv.value.size
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
return targetstruct.numberOfElements == numValues
}

View File

@ -57,8 +57,8 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
return super.visit(decl)
}
if (decl.value != null && decl.value !is StructLiteralValue) {
errors.err("initializing requires struct literal value", decl.value?.position ?: decl.position)
if (decl.value != null && decl.value !is ArrayLiteralValue) {
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
return super.visit(decl)
}
}
@ -137,21 +137,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
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) {
if (string.value.length !in 1..255)
errors.err("string literal length must be between 1 and 255", string.position)

View File

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

View File

@ -52,7 +52,7 @@ interface IAstModification {
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
override fun perform() {
if(parent is INameScope) {
val idx = parent.statements.withIndex().find { it.value===after }!!.index + 1
val idx = parent.statements.indexOfFirst { it===after } + 1
parent.statements.add(idx, stmt)
stmt.linkParents(parent)
} else {
@ -88,13 +88,12 @@ abstract class AstWalker {
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(directive: Directive, 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(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(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
@ -110,13 +109,11 @@ abstract class AstWalker {
open fun before(postIncrDecr: PostIncrDecr, 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(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(untilLoop: UntilLoop, 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(string: StringLiteralValue, 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(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
@ -132,13 +129,12 @@ abstract class AstWalker {
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(directive: Directive, 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(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(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
@ -154,13 +150,11 @@ abstract class AstWalker {
open fun after(postIncrDecr: PostIncrDecr, 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(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(untilLoop: UntilLoop, 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(string: StringLiteralValue, 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(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
@ -313,11 +307,6 @@ abstract class AstWalker {
track(after(postIncrDecr, parent), postIncrDecr, parent)
}
fun visit(contStmt: Continue, parent: Node) {
track(before(contStmt, parent), contStmt, parent)
track(after(contStmt, parent), contStmt, parent)
}
fun visit(breakStmt: Break, parent: Node) {
track(before(breakStmt, parent), breakStmt, parent)
track(after(breakStmt, parent), breakStmt, parent)
@ -325,7 +314,7 @@ abstract class AstWalker {
fun visit(forLoop: ForLoop, parent: Node) {
track(before(forLoop, parent), forLoop, parent)
forLoop.loopVar?.accept(this, forLoop)
forLoop.loopVar.accept(this, forLoop)
forLoop.iterable.accept(this, forLoop)
forLoop.body.accept(this, forLoop)
track(after(forLoop, parent), forLoop, parent)
@ -338,19 +327,20 @@ abstract class AstWalker {
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) {
track(before(repeatLoop, parent), repeatLoop, parent)
repeatLoop.untilCondition.accept(this, repeatLoop)
repeatLoop.iterations?.accept(this, repeatLoop)
repeatLoop.body.accept(this, repeatLoop)
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) {
track(before(returnStmt, parent), returnStmt, parent)
returnStmt.value?.accept(this, returnStmt)
@ -407,11 +397,6 @@ abstract class AstWalker {
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) {
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
@ -441,11 +426,5 @@ abstract class AstWalker {
structDecl.statements.forEach { it.accept(this, structDecl) }
track(after(structDecl, parent), structDecl, parent)
}
fun visit(structLv: StructLiteralValue, parent: Node) {
track(before(structLv, parent), structLv, parent)
structLv.values.forEach { it.accept(this, structLv) }
track(after(structLv, parent), structLv, parent)
}
}

View File

@ -95,14 +95,11 @@ interface IAstVisitor {
postIncrDecr.target.accept(this)
}
fun visit(contStmt: Continue) {
}
fun visit(breakStmt: Break) {
}
fun visit(forLoop: ForLoop) {
forLoop.loopVar?.accept(this)
forLoop.loopVar.accept(this)
forLoop.iterable.accept(this)
forLoop.body.accept(this)
}
@ -112,13 +109,14 @@ interface IAstVisitor {
whileLoop.body.accept(this)
}
fun visit(foreverLoop: ForeverLoop) {
foreverLoop.body.accept(this)
fun visit(repeatLoop: RepeatLoop) {
repeatLoop.iterations?.accept(this)
repeatLoop.body.accept(this)
}
fun visit(repeatLoop: RepeatLoop) {
repeatLoop.untilCondition.accept(this)
repeatLoop.body.accept(this)
fun visit(untilLoop: UntilLoop) {
untilLoop.untilCondition.accept(this)
untilLoop.body.accept(this)
}
fun visit(returnStmt: Return) {
@ -159,9 +157,6 @@ interface IAstVisitor {
fun visit(inlineAssembly: InlineAssembly) {
}
fun visit(registerExpr: RegisterExpr) {
}
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
}
@ -181,8 +176,4 @@ interface IAstVisitor {
fun visit(structDecl: StructDecl) {
structDecl.statements.forEach { it.accept(this) }
}
fun visit(structLv: StructLiteralValue) {
structLv.values.forEach { it.accept(this) }
}
}

View File

@ -14,8 +14,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
// - in every scope, most directives and vardecls are moved to the top.
// - the 'start' subroutine is moved to the top.
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
// - (syntax desugaring) augmented assignment is turned into regular assignment.
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
// - sorts the choices in when statement.
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
@ -78,8 +78,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
if(declConstValue==null) {
// move the vardecl (without value) to the scope and replace this with a regular assignment
decl.value = null
val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val assign = Assignment(target, null, declValue, decl.position)
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val assign = Assignment(target, declValue, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, assign, parent),
IAstModification.InsertFirst(decl, decl.definingScope() as Node)
@ -99,15 +99,11 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
}
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(assignment.aug_op!=null) {
return listOf(IAstModification.ReplaceNode(assignment, assignment.asDesugaredNonaugmented(), parent))
}
val valueType = assignment.value.inferType(program)
val targetType = assignment.target.inferType(program, assignment)
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
val assignments = if (assignment.value is StructLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = { ..... } '
val assignments = if (assignment.value is ArrayLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] '
} else {
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
}
@ -122,22 +118,71 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
return noModifications
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null) {
if(binExpr.left isSameAs assignment.target) {
// A = A <operator> 5, unchanged
return noModifications
}
if(binExpr.operator in associativeOperators) {
if (binExpr.right isSameAs assignment.target) {
// A = v <associative-operator> A ==> A = A <associative-operator> v
return listOf(IAstModification.SwapOperands(binExpr))
}
val leftBinExpr = binExpr.left as? BinaryExpression
if(leftBinExpr?.operator == binExpr.operator) {
return if(leftBinExpr.left isSameAs assignment.target) {
// A = (A <associative-operator> x) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position)
val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position)
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
} else {
// A = (x <associative-operator> A) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position)
val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position)
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
}
}
val rightBinExpr = binExpr.right as? BinaryExpression
if(rightBinExpr?.operator == binExpr.operator) {
return if(rightBinExpr.left isSameAs assignment.target) {
// A = x <associative-operator> (A <same-operator> y) ==> A = A <associative-operator> (x <same-operator> y)
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.right, binExpr.position)
val newValue = BinaryExpression(rightBinExpr.left, binExpr.operator, newRight, binExpr.position)
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
} else {
// A = x <associative-operator> (y <same-operator> A) ==> A = A <associative-operator> (x <same-operator> y)
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.left, binExpr.position)
val newValue = BinaryExpression(rightBinExpr.right, binExpr.operator, newRight, binExpr.position)
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
}
}
}
}
return noModifications
}
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
val identifier = structAssignment.target.identifier!!
val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program.namespace)!!
val struct = targetVar.struct!!
val slv = structAssignment.value as? StructLiteralValue
if(slv==null || slv.values.size != struct.numberOfElements)
val slv = structAssignment.value as? ArrayLiteralValue
if(slv==null || slv.value.size != struct.numberOfElements)
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
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
null, sourceValue, sourceValue.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
sourceValue, sourceValue.position)
assign.linkParents(structAssignment)
assign
}
@ -168,13 +213,12 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
null, sourceIdref, member.second.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
assign.linkParents(structAssignment)
assign
}
}
is StructLiteralValue -> {
is ArrayLiteralValue -> {
throw IllegalArgumentException("not going to flatten a structLv assignment here")
}
else -> throw FatalAstException("strange struct value")

View File

@ -179,51 +179,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
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> {
// add a typecast to the return type if it doesn't match the subroutine's signature
val returnValue = returnStmt.value

View File

@ -13,30 +13,41 @@ import prog8.functions.BuiltinFunctions
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
override fun visit(functionCall: FunctionCall)
= checkTypes(functionCall as IFunctionCall, functionCall.definingScope())
override fun visit(functionCall: FunctionCall) {
val error = checkTypes(functionCall as IFunctionCall, functionCall.definingScope(), program)
if(error!=null)
throw CompilerException(error)
}
override fun visit(functionCallStatement: FunctionCallStatement)
= checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
override fun visit(functionCallStatement: FunctionCallStatement) {
val error = checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope(), program)
if (error!=null)
throw CompilerException(error)
}
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")
companion object {
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
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 }
val mismatch = argtypes.zip(paramtypes).indexOfFirst { it.first != it.second}
if(mismatch>=0)
return "argument ${mismatch+1} type mismatch"
}
is BuiltinFunctionStatementPlaceholder -> {
val func = BuiltinFunctions.getValue(target.name)
val paramtypes = func.parameters.map { it.possibleDatatypes }
for (x in argtypes.zip(paramtypes).withIndex()) {
if (x.value.first !in x.value.second)
return "argument ${x.index+1} type mismatch"
}
}
else -> {
}
}
else -> {}
return null
}
}
}

View File

@ -64,7 +64,7 @@ class Block(override val name: String,
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index
val idx = statements.indexOfFirst { it ===node }
statements[idx] = replacement
replacement.parent = this
}
@ -148,18 +148,6 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
}
class Continue(override val position: Position) : Statement() {
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 accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class Break(override val position: Position) : Statement() {
override lateinit var parent: Node
@ -270,7 +258,7 @@ open class VarDecl(val type: VarDeclType,
fun flattenStructMembers(): MutableList<Statement> {
val result = struct!!.statements.withIndex().map {
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(
VarDeclType.VAR,
member.datatype,
@ -324,7 +312,7 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
fun size() = (index as? NumericLiteralValue)?.number?.toInt()
}
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 value: Expression, override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@ -346,36 +334,40 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun toString(): String {
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)")
return("Assignment(target: $target, value: $value, pos=$position)")
}
fun asDesugaredNonaugmented(): Assignment {
val augmented = aug_op ?: return this
val isInplace: Boolean
get() {
val binExpr = value as? BinaryExpression
if(binExpr!=null) {
if(binExpr.left isSameAs target)
return true // A = A <operator> 5
val leftOperand: Expression =
when {
target.register != null -> RegisterExpr(target.register!!, target.position)
target.identifier != null -> target.identifier!!
target.arrayindexed != null -> target.arrayindexed!!
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
else -> throw FatalAstException("strange this")
if(binExpr.operator in associativeOperators) {
if (binExpr.right isSameAs target)
return true // A = v <associative-operator> A
val leftBinExpr = binExpr.left as? BinaryExpression
if(leftBinExpr?.operator == binExpr.operator) {
// A = (A <associative-operator> x) <same-operator> y
// A = (x <associative-operator> A) <same-operator> y
return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target
}
val rightBinExpr = binExpr.right as? BinaryExpression
if(rightBinExpr?.operator == binExpr.operator) {
// A = y <associative-operator> (A <same-operator> x)
// A = y <associative-operator> (x <same-operator> y)
return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target
}
}
val assignment =
if(augmented=="setvalue") {
Assignment(target, null, value, position)
} else {
val expression = BinaryExpression(leftOperand, augmented.substringBeforeLast('='), value, position)
Assignment(target, null, expression, position)
}
assignment.linkParents(parent)
return assignment
}
return false
}
}
data class AssignTarget(val register: Register?,
var identifier: IdentifierReference?,
data class AssignTarget(var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?,
val memoryAddress: DirectMemoryWrite?,
override val position: Position) : Node {
@ -403,19 +395,15 @@ data class AssignTarget(val register: Register?,
companion object {
fun fromExpr(expr: Expression): AssignTarget {
return when (expr) {
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
is IdentifierReference -> AssignTarget(expr, null, null, expr.position)
is ArrayIndexedExpression -> AssignTarget(null, expr, null, expr.position)
is DirectMemoryRead -> AssignTarget(null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
else -> throw FatalAstException("invalid expression object $expr")
}
}
}
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
if(register!=null)
return InferredTypes.knownFor(DataType.UBYTE)
if(identifier!=null) {
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
@ -431,6 +419,15 @@ data class AssignTarget(val register: Register?,
return InferredTypes.unknown()
}
fun toExpression(): Expression {
return when {
identifier!=null -> identifier!!
arrayindexed!=null -> arrayindexed!!
memoryAddress!=null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position)
else -> throw FatalAstException("invalid assignmenttarget $this")
}
}
infix fun isSameAs(value: Expression): Boolean {
return when {
this.memoryAddress!=null -> {
@ -440,7 +437,6 @@ data class AssignTarget(val register: Register?,
else
false
}
this.register!=null -> value is RegisterExpr && value.register==register
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
@ -454,8 +450,6 @@ data class AssignTarget(val register: Register?,
fun isSameAs(other: AssignTarget, program: Program): Boolean {
if(this===other)
return true
if(this.register!=null && other.register!=null)
return this.register==other.register
if(this.identifier!=null && other.identifier!=null)
return this.identifier!!.nameInSource==other.identifier!!.nameInSource
if(this.memoryAddress!=null && other.memoryAddress!=null) {
@ -474,8 +468,6 @@ data class AssignTarget(val register: Register?,
}
fun isNotMemory(namespace: INameScope): Boolean {
if(this.register!=null)
return true
if(this.memoryAddress!=null)
return false
if(this.arrayindexed!=null) {
@ -550,7 +542,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
if(node===target)
target = replacement as IdentifierReference
else {
val idx = args.withIndex().find { it.value===node }!!.index
val idx = args.indexOfFirst { it===node }
args[idx] = replacement as Expression
}
replacement.parent = this
@ -597,7 +589,7 @@ class AnonymousScope(override var statements: MutableList<Statement>,
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index
val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement
replacement.parent = this
}
@ -616,14 +608,6 @@ class NopStatement(override val position: Position): Statement() {
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: 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,
@ -634,7 +618,7 @@ class Subroutine(override val name: String,
val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<Register>,
val asmClobbers: Set<CpuRegister>,
val asmAddress: Int?,
val isAsmSubroutine: Boolean,
override var statements: MutableList<Statement>,
@ -652,7 +636,7 @@ class Subroutine(override val name: String,
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index
val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement
replacement.parent = this
}
@ -671,32 +655,6 @@ class Subroutine(override val name: String,
.filter { it is InlineAssembly }
.map { (it as InlineAssembly).assembly }
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
fun countStatements(): Int {
class StatementCounter: IAstVisitor {
var count = 0
override fun visit(block: Block) {
count += block.statements.size
super.visit(block)
}
override fun visit(subroutine: Subroutine) {
count += subroutine.statements.size
super.visit(subroutine)
}
override fun visit(scope: AnonymousScope) {
count += scope.statements.size
super.visit(scope)
}
}
// the (recursive) number of statements
val counter = StatementCounter()
counter.visit(this)
return counter.count
}
}
@ -768,8 +726,7 @@ class BranchStatement(var condition: BranchCondition,
}
class ForLoop(val loopRegister: Register?,
var loopVar: IdentifierReference?,
class ForLoop(var loopVar: IdentifierReference,
var iterable: Expression,
var body: AnonymousScope,
override val position: Position) : Statement() {
@ -777,7 +734,7 @@ class ForLoop(val loopRegister: Register?,
override fun linkParents(parent: Node) {
this.parent=parent
loopVar?.linkParents(this)
loopVar.linkParents(this)
iterable.linkParents(this)
body.linkParents(this)
}
@ -796,14 +753,10 @@ class ForLoop(val loopRegister: Register?,
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
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 {
val lv = loopVar
return if(loopRegister!=null) InferredTypes.InferredType.known(DataType.UBYTE)
else lv?.inferType(program) ?: InferredTypes.InferredType.unknown()
}
fun loopVarDt(program: Program) = loopVar.inferType(program)
}
class WhileLoop(var condition: Expression,
@ -830,17 +783,21 @@ class WhileLoop(var condition: Expression,
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 fun linkParents(parent: Node) {
this.parent = parent
iterations?.linkParents(this)
body.linkParents(this)
}
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is AnonymousScope && node===body)
body = replacement
when {
node===iterations -> iterations = replacement as Expression
node===body -> body = replacement as AnonymousScope
else -> throw FatalAstException("invalid replace")
}
replacement.parent = this
}
@ -848,9 +805,9 @@ class ForeverLoop(var body: AnonymousScope, override val position: Position) : S
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class RepeatLoop(var body: AnonymousScope,
var untilCondition: Expression,
override val position: Position) : Statement() {
class UntilLoop(var body: AnonymousScope,
var untilCondition: Expression,
override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@ -953,7 +910,7 @@ class StructDecl(override val name: String,
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement)
val idx = statements.withIndex().find { it.value===node }!!.index
val idx = statements.indexOfFirst { it===node }
statements[idx] = replacement
replacement.parent = this
}

View File

@ -39,8 +39,8 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
return numericVarsWithValue.map {
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)
val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position)
val assign = Assignment(target, null, initValue, it.position)
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
val assign = Assignment(target, initValue, it.position)
initValue.parent = assign
IAstModification.InsertFirst(assign, scope)
} + decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } +

View File

@ -79,7 +79,7 @@ fun compileProgram(filepath: Path,
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
println("Parsing...")
val importer = ModuleImporter(errors)
val importer = ModuleImporter()
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
importer.importModule(programAst, filepath)
errors.handle()
@ -175,8 +175,6 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
}
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
programAst.transformAssignments(errors)
errors.handle()
programAst.addTypecasts(errors)
errors.handle()
programAst.variousCleanups()

View File

@ -16,7 +16,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
if(options.zeropage==ZeropageType.DONTUSE)
throw CompilerException("zero page usage has been disabled")
@ -39,13 +39,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
if(free.size > 0) {
if(size==1) {
for(candidate in free.min()!! .. free.max()!!+1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1) {
if(loneByte(candidate))
return makeAllocation(candidate, 1, datatype, scopedname)
}
return makeAllocation(free[0], 1, datatype, scopedname)
}
for(candidate in free.min()!! .. free.max()!!+1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1) {
if (sequentialFree(candidate, size))
return makeAllocation(candidate, size, datatype, scopedname)
}

View File

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

View File

@ -1,5 +1,6 @@
package prog8.compiler.target.c64.codegen
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.antlr.escape
@ -26,9 +27,9 @@ import kotlin.math.absoluteValue
internal class AsmGen(private val program: Program,
private val errors: ErrorReporter,
private val zeropage: Zeropage,
private val options: CompilationOptions,
val errors: ErrorReporter,
val zeropage: Zeropage,
val options: CompilationOptions,
private val outputDir: Path): IAssemblyGenerator {
private val assemblyLines = mutableListOf<String>()
@ -39,16 +40,14 @@ internal class AsmGen(private val program: Program,
private val forloopsAsmGen = ForLoopsAsmGen(program, this)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, errors, this)
private val assignmentAsmGen = AssignmentAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this)
internal val loopEndLabels = ArrayDeque<String>()
internal val loopContinueLabels = ArrayDeque<String>()
internal val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
assemblyLines.clear()
loopEndLabels.clear()
loopContinueLabels.clear()
println("Generating assembly code... ")
@ -183,8 +182,8 @@ internal class AsmGen(private val program: Program,
blockLevelVarInits.getValue(block).forEach { decl ->
val scopedFullName = decl.makeScopedName(decl.name).split('.')
require(scopedFullName.first()==block.name)
val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
val assign = Assignment(target, null, decl.value!!, decl.position)
val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
val assign = Assignment(target, decl.value!!, decl.position)
assign.linkParents(decl.parent)
assignmentAsmGen.translate(assign)
}
@ -561,19 +560,19 @@ internal class AsmGen(private val program: Program,
}
}
internal fun saveRegister(register: Register) {
internal fun saveRegister(register: CpuRegister) {
when(register) {
Register.A -> out(" pha")
Register.X -> out(" txa | pha")
Register.Y -> out(" tya | pha")
CpuRegister.A -> out(" pha")
CpuRegister.X -> out(" txa | pha")
CpuRegister.Y -> out(" tya | pha")
}
}
internal fun restoreRegister(register: Register) {
internal fun restoreRegister(register: CpuRegister) {
when(register) {
Register.A -> out(" pla")
Register.X -> out(" pla | tax")
Register.Y -> out(" pla | tay")
CpuRegister.A -> out(" pla")
CpuRegister.X -> out(" pla | tax")
CpuRegister.Y -> out(" pla | tay")
}
}
@ -637,11 +636,10 @@ internal class AsmGen(private val program: Program,
is BranchStatement -> translate(stmt)
is IfStatement -> translate(stmt)
is ForLoop -> forloopsAsmGen.translate(stmt)
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
is Break -> out(" jmp ${loopEndLabels.peek()}")
is WhileLoop -> translate(stmt)
is ForeverLoop -> translate(stmt)
is RepeatLoop -> translate(stmt)
is UntilLoop -> translate(stmt)
is WhenStatement -> translate(stmt)
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
is AnonymousScope -> translate(stmt)
@ -672,24 +670,125 @@ internal class AsmGen(private val program: Program,
}
}
private fun translate(stmt: ForeverLoop) {
val foreverLabel = makeLabel("forever")
val endLabel = makeLabel("foreverend")
private fun translate(stmt: RepeatLoop) {
val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel)
loopContinueLabels.push(foreverLabel)
out(foreverLabel)
translate(stmt.body)
out(" jmp $foreverLabel")
out(endLabel)
when (stmt.iterations) {
null -> {
// 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 <= 256 -> {
out(" lda #${iterations and 255}")
repeatByteCountInA(iterations, repeatLabel, endLabel, stmt.body)
}
else -> {
out(" lda #<${iterations} | ldy #>${iterations}")
repeatWordCountInAY(iterations, 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(null, repeatLabel, endLabel, stmt.body)
}
DataType.UWORD, DataType.WORD -> {
out(" lda $name | ldy $name+1")
repeatWordCountInAY(null, 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(null, repeatLabel, endLabel, stmt.body)
}
in WordDatatypes -> {
out(" inx | lda ${ESTACK_LO_HEX},x | ldy ${ESTACK_HI_HEX},x")
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
}
else -> throw AssemblyError("invalid loop expression datatype $dt")
}
}
}
loopEndLabels.pop()
loopContinueLabels.pop()
}
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A/Y must have been loaded with the number of iterations already!
val counterVar = makeLabel("repeatcounter")
out("""
sta $counterVar
sty $counterVar+1
$repeatLabel lda $counterVar
bne +
lda $counterVar+1
beq $endLabel
+ lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
""")
translate(body)
out(" jmp $repeatLabel")
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
out("""$counterVar = $zpAddr ; auto zp UWORD""")
} else {
out("""
$counterVar .word 0""")
}
out(endLabel)
}
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A must have been loaded with the number of iterations already!
val counterVar = makeLabel("repeatcounter")
out("""
sta $counterVar
$repeatLabel""")
translate(body)
out("""
dec $counterVar
bne $repeatLabel
beq $endLabel""")
if(constIterations!=null && constIterations>=16 && zeropage.available() > 0) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, body.position, errors)
out("""$counterVar = $zpAddr ; auto zp UBYTE""")
} else {
out("""
$counterVar .byte 0""")
}
out(endLabel)
}
private fun translate(stmt: WhileLoop) {
val whileLabel = makeLabel("while")
val endLabel = makeLabel("whileend")
loopEndLabels.push(endLabel)
loopContinueLabels.push(whileLabel)
out(whileLabel)
expressionsAsmGen.translateExpression(stmt.condition)
val conditionDt = stmt.condition.inferType(program)
@ -710,14 +809,12 @@ internal class AsmGen(private val program: Program,
out(" jmp $whileLabel")
out(endLabel)
loopEndLabels.pop()
loopContinueLabels.pop()
}
private fun translate(stmt: RepeatLoop) {
private fun translate(stmt: UntilLoop) {
val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel)
loopContinueLabels.push(repeatLabel)
out(repeatLabel)
translate(stmt.body)
expressionsAsmGen.translateExpression(stmt.untilCondition)
@ -737,7 +834,6 @@ internal class AsmGen(private val program: Program,
}
out(endLabel)
loopEndLabels.pop()
loopContinueLabels.pop()
}
private fun translate(stmt: WhenStatement) {
@ -769,7 +865,7 @@ internal class AsmGen(private val program: Program,
bne +
cpy #>${value.toHex()}
beq $choiceLabel
+
+
""")
}
}
@ -836,10 +932,13 @@ internal class AsmGen(private val program: Program,
}
inits.add(stmt)
} else {
val target = AssignTarget(null, IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
val assign = Assignment(target, null, stmt.value!!, stmt.position)
assign.linkParents(stmt.parent)
translate(assign)
val next = (stmt.parent as INameScope).nextSibling(stmt)
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
val assign = Assignment(target, stmt.value!!, stmt.position)
assign.linkParents(stmt.parent)
translate(assign)
}
}
}
}
@ -901,13 +1000,6 @@ internal class AsmGen(private val program: Program,
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) {
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 -> {
val indexName = asmIdentifierName(index)
out(" lda $indexName")
@ -920,28 +1012,6 @@ internal class AsmGen(private val program: Program,
}
}
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) {
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 -> {
val indexName = asmIdentifierName(index)
out(" ldy $indexName")
}
// TODO optimize more cases, see translateArrayIndexIntoA
else -> {
expressionsAsmGen.translateExpression(index)
out(" inx | ldy $ESTACK_LO_HEX,x")
}
}
}
internal fun translateExpression(expression: Expression) =
expressionsAsmGen.translateExpression(expression)
@ -969,12 +1039,18 @@ internal class AsmGen(private val program: Program,
fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromWordVariable(target, variable)
fun assignFromAddressOf(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromAddressOf(target, variable)
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromFloatVariable(target, variable)
fun assignFromRegister(target: AssignTarget, register: Register) =
fun assignFromRegister(target: AssignTarget, register: CpuRegister) =
assignmentAsmGen.assignFromRegister(target, register)
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
assignmentAsmGen.assignToRegister(reg, value, identifier)
}

View File

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

View File

@ -25,11 +25,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
is AddressOf -> translateExpression(expression)
is DirectMemoryRead -> translateExpression(expression)
is NumericLiteralValue -> translateExpression(expression)
is RegisterExpr -> translateExpression(expression)
is IdentifierReference -> 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 StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
}
}
@ -175,14 +173,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
private fun translateExpression(expr: RegisterExpr) {
when(expr.register) {
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
Register.X -> asmgen.out(" pha | txa | sta $ESTACK_LO_HEX,x | dex | pla")
Register.Y -> asmgen.out(" pha | tya | sta $ESTACK_LO_HEX,x | dex | pla")
}
}
private fun translateExpression(expr: IdentifierReference) {
val varname = asmgen.asmIdentifierName(expr)
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
@ -204,7 +194,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
private val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40)
private val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40)
private val powersOfTwo = setOf(0,1,2,4,8,16,32,64,128,256)
private fun translateExpression(expr: BinaryExpression) {
val leftIDt = expr.left.inferType(program)

View File

@ -2,7 +2,6 @@ package prog8.compiler.target.c64.codegen
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.Register
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.RangeExpr
import prog8.ast.statements.AssignTarget
@ -15,10 +14,6 @@ import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.toHex
import kotlin.math.absoluteValue
// todo choose more efficient comparisons to avoid needless lda's
// todo optimize common case when step == 2 or -2
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translate(stmt: ForLoop) {
@ -44,9 +39,9 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel)
val stepsize=range.step.constValue(program)!!.number.toInt()
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
@ -55,113 +50,62 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
// bytes, step 1 or -1
val incdec = if(stepsize==1) "inc" else "dec"
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
sta $loopLabel+1
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
beq $endLabel
$incdec $loopLabel+1
jmp $loopLabel
$endLabel inx""")
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
sta $varname
lda $ESTACK_LO_PLUS1_HEX,x
sta $modifiedLabel+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
lda $varname
$modifiedLabel cmp #0 ; modified
beq $endLabel
$incdec $varname
jmp $loopLabel
$endLabel inx""")
}
}
else {
} else {
// bytes, step >= 2 or <= -2
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
sta $loopLabel+1
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $loopLabel+1""")
if(stepsize>0) {
asmgen.out("""
clc
adc #$stepsize
sta $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
bcc $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
sec
sbc #${stepsize.absoluteValue}
sta $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
bcs $loopLabel""")
}
asmgen.out("""
$endLabel inx""")
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
sta $varname
lda $ESTACK_LO_PLUS1_HEX,x
sta $modifiedLabel+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname""")
if(stepsize>0) {
asmgen.out("""
$continueLabel lda $varname""")
if(stepsize>0) {
asmgen.out("""
clc
adc #$stepsize
sta $varname
cmp $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel cmp #0 ; modified
bcc $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
} else {
asmgen.out("""
sec
sbc #${stepsize.absoluteValue}
sta $varname
cmp $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel cmp #0 ; modified
bcs $loopLabel""")
}
asmgen.out("""
$endLabel inx""")
}
asmgen.out("""
$endLabel inx""")
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
@ -171,45 +115,54 @@ $endLabel inx""")
stepsize == 1 || stepsize == -1 -> {
asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
null, range.from, range.position)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar)
asmgen.out(loopLabel)
asmgen.out("""
lda $ESTACK_HI_PLUS1_HEX,x
sta $modifiedLabel+1
lda $ESTACK_LO_PLUS1_HEX,x
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname+1
cmp $ESTACK_HI_PLUS1_HEX,x
$modifiedLabel cmp #0 ; modified
bne +
lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel2 cmp #0 ; modified
beq $endLabel""")
if(stepsize==1) {
asmgen.out("""
+ inc $varname
bne +
inc $varname+1
bne $loopLabel
inc $varname+1
jmp $loopLabel
""")
} else {
asmgen.out("""
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
+ dec $varname
jmp $loopLabel""")
}
asmgen.out("""
+ jmp $loopLabel
$endLabel inx""")
asmgen.out(endLabel)
asmgen.out(" inx")
}
stepsize > 0 -> {
// (u)words, step >= 2
asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
null, range.from, range.position)
asmgen.out("""
lda $ESTACK_HI_PLUS1_HEX,x
sta $modifiedLabel+1
lda $ESTACK_LO_PLUS1_HEX,x
sta $modifiedLabel2+1
""")
val varname = asmgen.asmIdentifierName(stmt.loopVar)
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar)
asmgen.out(loopLabel)
@ -224,12 +177,11 @@ $endLabel inx""")
lda $varname+1
adc #>$stepsize
sta $varname+1
lda $ESTACK_HI_PLUS1_HEX,x
cmp $varname+1
bcc $endLabel
bne $loopLabel
lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel cmp #0 ; modified
bcc $loopLabel
bne $endLabel
$modifiedLabel2 lda #0 ; modified
cmp $varname
bcc $endLabel
bcs $loopLabel
$endLabel inx""")
@ -242,9 +194,9 @@ $endLabel inx""")
lda $varname+1
adc #>$stepsize
sta $varname+1
lda $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel2 lda #0 ; modified
cmp $varname
lda $ESTACK_HI_PLUS1_HEX,x
$modifiedLabel lda #0 ; modified
sbc $varname+1
bvc +
eor #$80
@ -256,9 +208,14 @@ $endLabel inx""")
// (u)words, step <= -2
asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
null, range.from, range.position)
asmgen.out("""
lda $ESTACK_HI_PLUS1_HEX,x
sta $modifiedLabel+1
lda $ESTACK_LO_PLUS1_HEX,x
sta $modifiedLabel2+1
""")
val varname = asmgen.asmIdentifierName(stmt.loopVar)
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar)
asmgen.out(loopLabel)
@ -273,11 +230,11 @@ $endLabel inx""")
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
cmp $ESTACK_HI_PLUS1_HEX,x
$modifiedLabel cmp #0 ; modified
bcc $endLabel
bne $loopLabel
lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel inx""")
} else {
@ -291,9 +248,9 @@ $endLabel inx""")
sbc #>${stepsize.absoluteValue}
sta $varname+1
pla
cmp $ESTACK_LO_PLUS1_HEX,x
$modifiedLabel2 cmp #0 ; modified
lda $varname+1
sbc $ESTACK_HI_PLUS1_HEX,x
$modifiedLabel sbc #0 ; modified
bvc +
eor #$80
+ bpl $loopLabel
@ -306,99 +263,104 @@ $endLabel inx""")
}
asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop()
}
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel)
val iterableName = asmgen.asmIdentifierName(ident)
val decl = ident.targetVarDecl(program.namespace)!!
when(iterableDt) {
DataType.STR -> {
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.out("""
lda #<$iterableName
ldy #>$iterableName
sta $loopLabel+1
sty $loopLabel+2
$loopLabel lda ${65535.toHex()} ; modified
beq $endLabel""")
if(stmt.loopVar!=null)
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
beq $endLabel
sta ${asmgen.asmIdentifierName(stmt.loopVar)}""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel inc $loopLabel+1
inc $loopLabel+1
bne $loopLabel
inc $loopLabel+2
bne $loopLabel
$endLabel""")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
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 modifiedLabel = asmgen.makeLabel("for_modified")
val indexVar = asmgen.makeLabel("for_index")
asmgen.out("""
lda #<$iterableName
ldy #>$iterableName
sta $modifiedLabel+1
sty $modifiedLabel+2
ldy #0
$loopLabel sty $counterLabel
$modifiedLabel lda ${65535.toHex()},y ; modified""")
if(stmt.loopVar!=null)
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
$loopLabel sty $indexVar
lda $iterableName,y
sta ${asmgen.asmIdentifierName(stmt.loopVar)}""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel ldy $counterLabel
iny
cpy #${length and 255}
beq $endLabel
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
if(length<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$length
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(length>=16 && asmgen.zeropage.available() > 0) {
// allocate index var on ZP
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
} else {
asmgen.out("""
$indexVar .byte 0""")
}
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
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 modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!)
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out("""
lda #<$iterableName
ldy #>$iterableName
sta $modifiedLabel+1
sty $modifiedLabel+2
lda #<$iterableName+1
ldy #>$iterableName+1
sta $modifiedLabel2+1
sty $modifiedLabel2+2
ldy #0
$loopLabel sty $counterLabel
$modifiedLabel lda ${65535.toHex()},y ; modified
$loopLabel sty $indexVar
lda $iterableName,y
sta $loopvarName
$modifiedLabel2 lda ${65535.toHex()},y ; modified
lda $iterableName+1,y
sta $loopvarName+1""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel ldy $counterLabel
iny
iny
cpy #${length and 255}
beq $endLabel
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
if(length<=127) {
asmgen.out("""
ldy $indexVar
iny
iny
cpy #$length
beq $endLabel
bne $loopLabel""")
} else {
// length is 128 words, 256 bytes
asmgen.out("""
ldy $indexVar
iny
iny
bne $loopLabel
beq $endLabel""")
}
if(length>=16 && asmgen.zeropage.available() > 0) {
// allocate index var on ZP
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
} else {
asmgen.out("""
$indexVar .byte 0""")
}
asmgen.out(endLabel)
}
DataType.ARRAY_F -> {
throw AssemblyError("for loop with floating point variables is not supported")
@ -406,287 +368,123 @@ $endLabel""")
else -> throw AssemblyError("can't iterate over $iterableDt")
}
asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop()
}
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 var in such cases
if (range.isEmpty())
throw AssemblyError("empty range")
if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0")
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
}
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
}
// not one of the easy cases, generate more complex code...
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel)
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
val counterLabel = asmgen.makeLabel("for_counter")
if(stmt.loopRegister!=null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
when {
range.step==1 -> {
// step = 1
// loop over byte range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
when (range.step) {
0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
}
2 -> {
if(range.last==255) {
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)
inc $varname
beq $endLabel
inc $varname
bne $loopLabel""")
} else {
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""")
lda $varname
cmp #${range.last}
beq $endLabel
inc $varname
inc $varname
jmp $loopLabel""")
}
}
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
when {
range.step==1 -> {
// step = 1
asmgen.out("""
lda #${range.first}
sta $varname
lda #${range.last-range.first+1 and 255}
sta $counterLabel
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
inc $varname
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
-2 -> {
when (range.last) {
0 -> asmgen.out("""
lda $varname
beq $endLabel
dec $varname
dec $varname
jmp $loopLabel""")
1 -> asmgen.out("""
dec $varname
beq $endLabel
dec $varname
bne $loopLabel""")
else -> asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
dec $varname
dec $varname
jmp $loopLabel""")
}
range.step==-1 -> {
// step = -1
asmgen.out("""
lda #${range.first}
sta $varname
lda #${range.first-range.last+1 and 255}
sta $counterLabel
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
dec $varname
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}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
lda $varname
clc
adc #${range.step}
sta $varname
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}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
lda $varname
sec
sbc #${range.step.absoluteValue}
sta $varname
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
}
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
// loop over word range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
when {
range.step == 1 -> {
// word, step = 1
val lastValue = range.last+1
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel inc $varname
bne +
inc $varname+1
+ lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
}
range.step == -1 -> {
// word, step = 1
val lastValue = range.last-1
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $varname
bne +
dec $varname+1
+ dec $varname
lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
}
range.step >= 2 -> {
// word, step >= 2
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
val lastValue = range.last+range.step
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel clc
lda $varname
adc #<${range.step}
sta $varname
lda $varname+1
adc #>${range.step}
sta $varname+1
lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
}
else -> {
// step <= -2
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
val lastValue = range.last+range.step
// step <= -3 or >= 3
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
lda $varname
cmp #${range.last}
beq $endLabel
clc
adc #${range.step}
sta $varname
jmp $loopLabel""")
}
}
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
// loop over word range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmIdentifierName(stmt.loopVar)
when (range.step) {
0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
}
else -> {
// word, step >= 2 or <= -2
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel sec
lda $varname
sbc #<${range.step.absoluteValue}
sta $varname
lda $varname+1
sbc #>${range.step.absoluteValue}
sta $varname+1
lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
lda $varname
cmp #<${range.last}
bne +
lda $varname+1
cmp #>${range.last}
bne +
beq $endLabel
+ lda $varname
clc
adc #<${range.step}
sta $varname
lda $varname+1
adc #>${range.step}
sta $varname+1
jmp $loopLabel
$endLabel""")
}
}
@ -694,7 +492,127 @@ $endLabel""")
else -> throw AssemblyError("range expression can only be byte or word")
}
asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop()
}
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
if (range.last == 255) {
asmgen.out("""
inc $varname
bne $loopLabel
$endLabel""")
} else {
asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
inc $varname
jmp $loopLabel
$endLabel""")
}
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
when (range.last) {
0 -> {
asmgen.out("""
lda $varname
beq $endLabel
dec $varname
jmp $loopLabel
$endLabel""")
}
1 -> {
asmgen.out("""
dec $varname
jmp $loopLabel
$endLabel""")
}
else -> {
asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
dec $varname
jmp $loopLabel
$endLabel""")
}
}
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname
cmp #<${range.last}
bne +
lda $varname+1
cmp #>${range.last}
bne +
beq $endLabel
+ inc $varname
bne $loopLabel
inc $varname+1
jmp $loopLabel
$endLabel""")
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname
cmp #<${range.last}
bne +
lda $varname+1
cmp #>${range.last}
bne +
beq $endLabel
+ lda $varname
bne +
dec $varname+1
+ dec $varname
jmp $loopLabel
$endLabel""")
asmgen.loopEndLabels.pop()
}
}

View File

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

View File

@ -0,0 +1,744 @@
package prog8.compiler.target.c64.codegen
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.VarDecl
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.toHex
// OLD inplace-assignment code.
// should really come up with a more compact way to generate this kind of code...
/***
internal class InplaceAssignmentAsmGen(private val program: Program, private val errors: ErrorReporter, private val asmgen: AsmGen) {
internal fun translate(assign: Assignment) {
if (assign.aug_op == null)
translateNormalAssignment(assign)
else
translateInplaceAssignment(assign)
}
private fun translateInplaceAssignment(assign: Assignment) {
require(assign.aug_op != null)
when {
assign.target.identifier != null -> {
if (inplaceAssignToIdentifier(assign))
return
}
assign.target.memoryAddress != null -> {
if (inplaceAssignToMemoryByte(assign))
return
}
assign.target.arrayindexed != null -> {
if (inplaceAssignToArrayOrString(assign))
return
}
}
// TODO this is the slow FALLBACK, eventually we don't want to have to use it anymore:
errors.warn("using suboptimal in-place assignment code (this should still be optimized)", assign.position)
val normalAssignment = assign.asDesugaredNonaugmented()
return translateNormalAssignment(normalAssignment)
}
private fun inplaceAssignToArrayOrString(assign: Assignment): Boolean {
val targetArray = assign.target.arrayindexed!!
val arrayName = targetArray.identifier
val arrayIndex = targetArray.arrayspec.index
val targetName = asmgen.asmIdentifierName(arrayName)
val arrayDt = arrayName.targetVarDecl(program.namespace)!!.datatype
val constValue = assign.value.constValue(program)?.number
if (constValue != null) {
// constant value to set in array
val hexValue = constValue.toHex()
if (assign.aug_op == "setvalue") {
when (arrayDt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(targetArray)
asmgen.out(" lda #$hexValue | sta $targetName,y")
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
if (arrayIndex is NumericLiteralValue)
asmgen.out(" lda #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoA(targetArray)
asmgen.out("""
asl a
tay
lda #<$hexValue
sta $targetName,y
lda #>$hexValue
sta $targetName+1,y
""")
}
DataType.ARRAY_F -> {
assignFromFloatConstant(assign.target, constValue.toDouble())
}
else -> throw AssemblyError("assignment to array: invalid array dt $arrayDt")
}
} else {
TODO("aug assignment to element in array/string")
}
return true
}
// non-const value.
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) {
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when(arrayDt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
asmgen.out(" lda $sourceName")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(targetArray)
asmgen.out(" sta $targetName,y")
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
if (arrayIndex is NumericLiteralValue)
asmgen.out(" lda #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoA(targetArray)
asmgen.out("""
asl a
tay
lda $sourceName
sta $targetName,y
lda $sourceName+1
sta $targetName+1,y
""")
}
DataType.ARRAY_F -> return false // TODO optimize instead of fallback?
else -> throw AssemblyError("assignment to array: invalid array dt $arrayDt")
}
return true
}
is AddressOf -> {
TODO("assign address into array $assign")
}
is DirectMemoryRead -> {
TODO("assign memory read into array $assign")
}
is ArrayIndexedExpression -> {
if(assign.aug_op != "setvalue")
return false // we don't put effort into optimizing anything beside simple assignment
val valueArrayExpr = assign.value as ArrayIndexedExpression
val valueArrayIndex = valueArrayExpr.arrayspec.index
val valueVariablename = asmgen.asmIdentifierName(valueArrayExpr.identifier)
// val valueDt = valueArrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
when(arrayDt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> {
if (valueArrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${valueArrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(valueArrayExpr)
asmgen.out(" lda $valueVariablename,y")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(targetArray)
asmgen.out(" sta $targetName,y")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (valueArrayIndex is NumericLiteralValue)
asmgen.out(" ldy #2*${valueArrayIndex.number.toHex()}")
else {
asmgen.translateArrayIndexIntoA(valueArrayExpr)
asmgen.out(" asl a | tay")
}
asmgen.out("""
lda $valueVariablename,y
pha
lda $valueVariablename+1,y
pha
""")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #2*${arrayIndex.number.toHex()}")
else {
asmgen.translateArrayIndexIntoA(targetArray)
asmgen.out(" asl a | tay")
}
asmgen.out("""
pla
sta $targetName+1,y
pla
sta $targetName,y
""")
return true
}
DataType.ARRAY_F -> {
if (valueArrayIndex is NumericLiteralValue)
asmgen.out(" ldy #5*${valueArrayIndex.number.toHex()}")
else {
asmgen.translateArrayIndexIntoA(valueArrayExpr)
asmgen.out("""
sta ${C64Zeropage.SCRATCH_REG}
asl a
asl a
clc
adc ${C64Zeropage.SCRATCH_REG}
tay
""")
}
asmgen.out("""
lda $valueVariablename,y
pha
lda $valueVariablename+1,y
pha
lda $valueVariablename+2,y
pha
lda $valueVariablename+3,y
pha
lda $valueVariablename+4,y
pha
""")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #5*${arrayIndex.number.toHex()}")
else {
asmgen.translateArrayIndexIntoA(targetArray)
asmgen.out("""
sta ${C64Zeropage.SCRATCH_REG}
asl a
asl a
clc
adc ${C64Zeropage.SCRATCH_REG}
tay
""")
}
asmgen.out("""
pla
sta $targetName+4,y
pla
sta $targetName+3,y
pla
sta $targetName+2,y
pla
sta $targetName+1,y
pla
sta $targetName,y
""")
return true
}
else -> throw AssemblyError("assignment to array: invalid array dt")
}
return true
}
else -> {
fallbackAssignment(assign)
return true
}
}
return false
}
private fun inplaceAssignToMemoryByte(assign: Assignment): Boolean {
val address = assign.target.memoryAddress?.addressExpression?.constValue(program)?.number
?: return inplaceAssignToNonConstMemoryByte(assign)
val hexAddr = address.toHex()
val constValue = assign.value.constValue(program)
if (constValue != null) {
val hexValue = constValue.number.toHex()
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda #$hexValue | sta $hexAddr")
"+=" -> asmgen.out(" lda $hexAddr | clc | adc #$hexValue | sta $hexAddr")
"-=" -> asmgen.out(" lda $hexAddr | sec | sbc #$hexValue | sta $hexAddr")
"/=" -> TODO("membyte /= const $hexValue")
"*=" -> TODO("membyte *= const $hexValue")
"&=" -> asmgen.out(" lda $hexAddr | and #$hexValue | sta $hexAddr")
"|=" -> asmgen.out(" lda $hexAddr | ora #$hexValue | sta $hexAddr")
"^=" -> asmgen.out(" lda $hexAddr | eor #$hexValue | sta $hexAddr")
"%=" -> TODO("membyte %= const $hexValue")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
// non-const value.
when (assign.value) {
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when(assign.aug_op) {
"setvalue" -> asmgen.out(" lda $sourceName | sta $hexAddr")
else -> TODO("membyte aug.assign variable $assign")
}
return true
}
is DirectMemoryRead -> {
val memory = (assign.value as DirectMemoryRead).addressExpression.constValue(program)!!.number.toHex()
when(assign.aug_op) {
"setvalue" -> asmgen.out(" lda $memory | sta $hexAddr")
else -> TODO("membyte aug.assign memread $assign")
}
return true
}
is ArrayIndexedExpression -> {
TODO("membyte = array value $assign")
}
is AddressOf -> throw AssemblyError("can't assign address to byte")
else -> {
fallbackAssignment(assign)
return true
}
}
return false
}
private fun inplaceAssignToNonConstMemoryByte(assign: Assignment): Boolean {
// target address is not constant, so evaluate it from the stack
asmgen.translateExpression(assign.target.memoryAddress!!.addressExpression)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta ${C64Zeropage.SCRATCH_W1}
lda $ESTACK_HI_HEX,x
sta ${C64Zeropage.SCRATCH_W1}+1
""")
val constValue = assign.value.constValue(program)
if (constValue != null) {
val hexValue = constValue.number.toHex()
asmgen.out(" ldy #0")
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
"+=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | clc | adc #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
"-=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
"/=" -> TODO("membyte /= const $hexValue")
"*=" -> TODO("membyte *= const $hexValue")
"&=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | and #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
"|=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | ora #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
"^=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | eor #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
"%=" -> TODO("membyte %= const $hexValue")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
// non-const value.
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) {
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
TODO("membyte = variable $assign")
}
is DirectMemoryRead -> {
TODO("membyte = memread $assign")
}
is ArrayIndexedExpression -> {
if (assign.aug_op == "setvalue") {
val arrayExpr = assign.value as ArrayIndexedExpression
val arrayIndex = arrayExpr.arrayspec.index
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
if (arrayDt != DataType.ARRAY_B && arrayDt != DataType.ARRAY_UB && arrayDt != DataType.STR)
throw AssemblyError("assign to memory byte: expected byte array or string source")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(arrayExpr)
asmgen.out("""
lda $variablename,y
ldy #0
sta (${C64Zeropage.SCRATCH_W1}),y
""")
} else {
// TODO optimize more augmented assignment cases
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
}
return true
}
is AddressOf -> throw AssemblyError("can't assign memory address to memory byte")
else -> {
fallbackAssignment(assign)
return true
}
}
return false // TODO optimized
}
private fun inplaceAssignToIdentifier(assign: Assignment): Boolean {
val targetType = assign.target.inferType(program, assign)
val constNumber = assign.value.constValue(program)?.number
val targetName = asmgen.asmIdentifierName(assign.target.identifier!!)
when (targetType.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE, DataType.BYTE -> {
// (u)byte assignment
if (constNumber != null) {
val hexValue = constNumber.toHex()
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda #$hexValue | sta $targetName")
"+=" -> asmgen.out(" lda $targetName | clc | adc #$hexValue | sta $targetName")
"-=" -> asmgen.out(" lda $targetName | sec | sbc #$hexValue | sta $targetName")
"/=" -> TODO("variable /= const $hexValue")
"*=" -> TODO("variable *= const $hexValue")
"&=" -> asmgen.out(" lda $targetName | and #$hexValue | sta $targetName")
"|=" -> asmgen.out(" lda $targetName | ora #$hexValue | sta $targetName")
"^=" -> asmgen.out(" lda $targetName | eor #$hexValue | sta $targetName")
"%=" -> TODO("variable %= const $hexValue")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
// non-const (u)byte value
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) {
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when (assign.aug_op) {
"setvalue" -> asmgen.out(" lda $sourceName | sta $targetName")
"+=" -> asmgen.out(" lda $targetName | clc | adc $sourceName | sta $targetName")
"-=" -> asmgen.out(" lda $targetName | sec | sbc $sourceName | sta $targetName")
"/=" -> TODO("variable /= variable")
"*=" -> TODO("variable *= variable")
"&=" -> asmgen.out(" lda $targetName | and $sourceName | sta $targetName")
"|=" -> asmgen.out(" lda $targetName | ora $sourceName | sta $targetName")
"^=" -> asmgen.out(" lda $targetName | eor $sourceName | sta $targetName")
"%=" -> TODO("variable %= variable")
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
}
return true
}
is DirectMemoryRead -> {
TODO("variable = memory read $assign")
}
is ArrayIndexedExpression -> {
if (assign.aug_op == "setvalue") {
val arrayExpr = assign.value as ArrayIndexedExpression
val arrayIndex = arrayExpr.arrayspec.index
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
if (arrayDt != DataType.ARRAY_B && arrayDt != DataType.ARRAY_UB && arrayDt != DataType.STR)
throw AssemblyError("assign to identifier: expected byte array or string source")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoY(arrayExpr)
asmgen.out(" lda $variablename,y | sta $targetName")
} else {
// TODO optimize more augmented assignment cases
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
}
return true
}
else -> {
fallbackAssignment(assign)
return true
}
}
}
DataType.UWORD, DataType.WORD -> {
if (constNumber != null) {
val hexNumber = constNumber.toHex()
when (assign.aug_op) {
"setvalue" -> {
asmgen.out("""
lda #<$hexNumber
sta $targetName
lda #>$hexNumber
sta $targetName+1
""")
}
"+=" -> {
asmgen.out("""
lda $targetName
clc
adc #<$hexNumber
sta $targetName
lda $targetName+1
adc #>$hexNumber
sta $targetName+1
""")
}
"-=" -> {
asmgen.out("""
lda $targetName
sec
sbc #<$hexNumber
sta $targetName
lda $targetName+1
sbc #>$hexNumber
sta $targetName+1
""")
}
else -> TODO("variable aug.assign ${assign.aug_op} const $hexNumber")
}
return true
}
// non-const value
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) {
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
when (assign.aug_op) {
"setvalue" -> {
asmgen.out("""
lda $sourceName
sta $targetName
lda $sourceName+1
sta $targetName+1
""")
}
"+=" -> {
asmgen.out("""
lda $targetName
clc
adc $sourceName
sta $targetName
lda $targetName+1
adc $sourceName+1
sta $targetName+1
""")
return true
}
"-=" -> {
asmgen.out("""
lda $targetName
sec
sbc $sourceName
sta $targetName
lda $targetName+1
sbc $sourceName+1
sta $targetName+1
""")
return true
}
else -> {
TODO("variable aug.assign variable")
}
}
return true
}
is DirectMemoryRead -> throw AssemblyError("expected a typecast for assigning memory read byte to word")
is AddressOf -> {
val name = asmgen.asmIdentifierName((assign.value as AddressOf).identifier)
asmgen.out(" lda #<$name | sta $targetName | lda #>$name | sta $targetName+1")
return true
}
is ArrayIndexedExpression -> {
if (assign.aug_op == "setvalue") {
val arrayExpr = assign.value as ArrayIndexedExpression
val arrayIndex = arrayExpr.arrayspec.index
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
if (arrayDt != DataType.ARRAY_W && arrayDt != DataType.ARRAY_UW)
throw AssemblyError("assign to identifier: expected word array source")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" lda #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoA(arrayExpr)
asmgen.out("""
asl a
tay
lda $variablename,y
sta $targetName
lda $variablename+1,y
sta $targetName+1
""")
} else {
// TODO optimize more augmented assignment cases
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
}
return true
}
else -> {
fallbackAssignment(assign)
return true
}
}
}
DataType.FLOAT -> {
if (constNumber != null) {
// assign a constant
val floatConst = asmgen.getFloatConst(constNumber.toDouble())
when (assign.aug_op) {
"setvalue" -> assignFromFloatConstant(assign.target, constNumber.toDouble())
"+=" -> {
if (constNumber == 0.5) {
asmgen.out("""
lda #<$targetName
ldy #>$targetName
jsr c64flt.MOVFM
jsr c64flt.FADDH
stx c64.SCRATCH_ZPREGX
ldx #<$targetName
ldy #>$targetName
jsr c64flt.MOVMF
ldx c64.SCRATCH_ZPREGX
""")
} else {
asmgen.out("""
lda #<$targetName
ldy #>$targetName
jsr c64flt.MOVFM
lda #<$floatConst
ldy #>$floatConst
jsr c64flt.FADD
stx c64.SCRATCH_ZPREGX
ldx #<$targetName
ldy #>$targetName
jsr c64flt.MOVMF
ldx c64.SCRATCH_ZPREGX
""")
}
return true
}
"-=" -> {
asmgen.out("""
lda #<$floatConst
ldy #>$floatConst
jsr c64flt.MOVFM
lda #<$targetName
ldy #>$targetName
jsr c64flt.FSUB
stx c64.SCRATCH_ZPREGX
ldx #<$targetName
ldy #>$targetName
jsr c64flt.MOVMF
ldx c64.SCRATCH_ZPREGX
""")
return true
}
else -> TODO("float const value aug.assign $assign")
}
return true
}
// non-const float value.
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
when (assign.value) {
is IdentifierReference -> {
when (assign.aug_op) {
"setvalue" -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
"+=" -> return false // TODO optimized float += variable
"-=" -> return false // TODO optimized float -= variable
else -> TODO("float non-const value aug.assign $assign")
}
return true
}
is ArrayIndexedExpression -> {
when(assign.aug_op) {
"setvalue" -> {
val arrayExpr = assign.value as ArrayIndexedExpression
val arrayIndex = arrayExpr.arrayspec.index
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
if (arrayDt != DataType.ARRAY_F)
throw AssemblyError("assign to identifier: expected float array source")
if (arrayIndex is NumericLiteralValue)
asmgen.out(" lda #${arrayIndex.number.toHex()}")
else
asmgen.translateArrayIndexIntoA(arrayExpr)
asmgen.out("""
sta c64.SCRATCH_ZPB1
asl a
asl a
clc
adc c64.SCRATCH_ZPB1
tay
lda $variablename,y
sta $targetName
lda $variablename+1,y
sta $targetName+1
lda $variablename+2,y
sta $targetName+2
lda $variablename+3,y
sta $targetName+3
lda $variablename+4,y
sta $targetName+4
""")
}
else -> TODO("float $assign")
}
return true
}
else -> {
fallbackAssignment(assign)
return true
}
}
}
DataType.STR -> {
val identifier = assign.value as? IdentifierReference
?: throw AssemblyError("string value assignment expects identifier value")
val sourceName = asmgen.asmIdentifierName(identifier)
asmgen.out("""
lda #<$targetName
sta ${C64Zeropage.SCRATCH_W1}
lda #>$targetName
sta ${C64Zeropage.SCRATCH_W1+1}
lda #<$sourceName
ldy #>$sourceName
jsr prog8_lib.strcpy
""")
return true
}
else -> throw AssemblyError("assignment to identifier: invalid target datatype: $targetType")
}
return false
}
private fun fallbackAssignment(assign: Assignment) {
if (assign.aug_op != "setvalue") {
/* stack-based evaluation of the expression is required */
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
} else {
when (assign.value) {
is FunctionCall -> {
// TODO is there a way to avoid function return value being passed via the stack?
// for instance, 1 byte return value always in A, etc
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
}
else -> {
/* stack-based evaluation of the expression is required */
val normalAssign = assign.asDesugaredNonaugmented()
asmgen.translateExpression(normalAssign.value)
assignFromEvalResult(normalAssign.target)
}
}
}
}
}
***/

View File

@ -4,7 +4,6 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.RegisterExpr
import prog8.ast.statements.PostIncrDecr
import prog8.compiler.AssemblyError
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 targetMemory = stmt.target.memoryAddress
val targetArrayIdx = stmt.target.arrayindexed
val targetRegister = stmt.target.register
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 -> {
val what = asmgen.asmIdentifierName(targetIdent)
val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)
when (dt) {
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
in WordDatatypes -> {
if(incr)
@ -103,10 +84,6 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
else -> throw AssemblyError("need numeric type")
}
}
is RegisterExpr -> {
asmgen.translateArrayIndexIntoA(targetArrayIdx)
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
}
is IdentifierReference -> {
asmgen.translateArrayIndexIntoA(targetArrayIdx)
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)

View File

@ -103,9 +103,9 @@ val BuiltinFunctions = mapOf(
FParam("length", setOf(DataType.UBYTE))), null)
)
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble() }!!
fun builtinMin(array: List<Number>): Number = array.minBy { it.toDouble() }!!
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
@ -274,22 +274,16 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
arraySize = target.arraysize?.size()
if(arraySize==null)
throw CannotEvaluateException("len", "arraysize unknown")
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${target.position}")
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
}
DataType.ARRAY_F -> {
arraySize = target.arraysize?.size()
if(arraySize==null)
throw CannotEvaluateException("len", "arraysize unknown")
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${target.position}")
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
}
DataType.STR -> {
val refLv = target.value as StringLiteralValue
if(refLv.value.length>255)
throw CompilerException("string length exceeds byte limit ${refLv.position}")
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
}
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
@ -312,7 +306,7 @@ private fun builtinSin8(args: List<Expression>, position: Position, program: Pro
throw SyntaxError("sin8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toShort(), position)
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
}
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
@ -320,7 +314,7 @@ private fun builtinSin8u(args: List<Expression>, position: Position, program: Pr
throw SyntaxError("sin8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort(), position)
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
}
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
@ -328,7 +322,7 @@ private fun builtinCos8(args: List<Expression>, position: Position, program: Pro
throw SyntaxError("cos8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toShort(), position)
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
}
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
@ -336,7 +330,7 @@ private fun builtinCos8u(args: List<Expression>, position: Position, program: Pr
throw SyntaxError("cos8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort(), position)
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
}
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
@ -375,7 +369,7 @@ private fun builtinSgn(args: List<Expression>, position: Position, program: Prog
if (args.size != 1)
throw SyntaxError("sgn requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toShort(), position)
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toInt().toShort(), position)
}
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {

View File

@ -1,157 +0,0 @@
package prog8.optimizer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.ErrorReporter
import prog8.ast.expressions.BinaryExpression
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.Assignment
import prog8.ast.statements.PostIncrDecr
internal class AssignmentTransformer(val program: Program, val errors: ErrorReporter) : AstWalker() {
var optimizationsDone: Int = 0
private val noModifications = emptyList<IAstModification>()
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments
// also to put code generation stuff together, single value assignment (A = 5) is converted to a special
// augmented form as wel (with the operator "setvalue")
if (assignment.aug_op == null) {
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
if (assignment.target.isSameAs(binExpr.left)) {
assignment.value = binExpr.right
assignment.aug_op = binExpr.operator + "="
assignment.value.parent = assignment
optimizationsDone++
return noModifications
}
}
assignment.aug_op = "setvalue"
optimizationsDone++
} else if(assignment.aug_op == "+=") {
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
val leftnum = binExpr.left.constValue(program)?.number?.toDouble()
val rightnum = binExpr.right.constValue(program)?.number?.toDouble()
if(binExpr.operator == "+") {
when {
leftnum == 1.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
leftnum == 2.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
rightnum == 1.0 -> {
// x += y + 1 -> x += y , x++
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
)
}
rightnum == 2.0 -> {
// x += y + 2 -> x += y , x++, x++
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
)
}
}
} else if(binExpr.operator == "-") {
when {
leftnum == 1.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
leftnum == 2.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
rightnum == 1.0 -> {
// x += y - 1 -> x += y , x--
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
)
}
rightnum == 2.0 -> {
// x += y - 2 -> x += y , x--, x--
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
)
}
}
}
}
} else if(assignment.aug_op == "-=") {
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
val leftnum = binExpr.left.constValue(program)?.number?.toDouble()
val rightnum = binExpr.right.constValue(program)?.number?.toDouble()
if(binExpr.operator == "+") {
when {
leftnum == 1.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
leftnum == 2.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
rightnum == 1.0 -> {
// x -= y + 1 -> x -= y , x--
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
)
}
rightnum == 2.0 -> {
// x -= y + 2 -> x -= y , x--, x--
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
)
}
}
} else if(binExpr.operator == "-") {
when {
leftnum == 1.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
leftnum == 2.0 -> {
optimizationsDone++
return listOf(IAstModification.SwapOperands(binExpr))
}
rightnum == 1.0 -> {
// x -= y - 1 -> x -= y , x++
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
)
}
rightnum == 2.0 -> {
// x -= y - 2 -> x -= y , x++, x++
return listOf(
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent),
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
)
}
}
}
}
}
return noModifications
}
}

View File

@ -179,7 +179,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
}
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
@ -342,40 +342,38 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
val rangeTo = iterableRange.to as? NumericLiteralValue
if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar?.targetVarDecl(program.namespace)
if(loopvar!=null) {
val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)!!
val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
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

View File

@ -6,7 +6,6 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.Assignment
import kotlin.math.abs
import kotlin.math.log2
import kotlin.math.pow
@ -24,12 +23,6 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
private val noModifications = emptyList<IAstModification>()
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if (assignment.aug_op != null)
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
return noModifications
}
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
val mods = mutableListOf<IAstModification>()
@ -610,8 +603,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (amount == 0) {
return expr.left
}
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
when (targetDt) {
when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
if (amount >= 8) {
return NumericLiteralValue.optimalInteger(0, expr.position)

View File

@ -10,7 +10,7 @@ internal fun Program.constantFold(errors: ErrorReporter) {
if(errors.isEmpty()) {
replacer.applyModifications()
val optimizer = ConstantFoldingOptimizer(this, errors)
val optimizer = ConstantFoldingOptimizer(this)
optimizer.visit(this)
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
optimizer.visit(this)

View File

@ -14,11 +14,6 @@ import prog8.functions.BuiltinFunctions
import kotlin.math.floor
/*
TODO: remove unreachable code after return and exit()
*/
internal class StatementOptimizer(private val program: Program,
private val errors: ErrorReporter) : AstWalker() {
@ -46,7 +41,11 @@ internal class StatementOptimizer(private val program: Program,
if(subroutine.asmAddress==null && !forceOutput) {
if(subroutine.containsNoCodeNorVars()) {
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
}
}
@ -101,32 +100,34 @@ internal class StatementOptimizer(private val program: Program,
}
if(stringVar!=null) {
val vardecl = stringVar.targetVarDecl(program.namespace)!!
val string = vardecl.value!! as StringLiteralValue
val pos = functionCallStatement.position
if(string.value.length==1) {
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
val chrout = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
functionCallStatement.void, pos
)
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
} else if(string.value.length==2) {
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
functionCallStatement.void, pos
)
val chrout2 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
functionCallStatement.void, pos
)
val anonscope = AnonymousScope(mutableListOf(), pos)
anonscope.statements.add(chrout1)
anonscope.statements.add(chrout2)
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
val string = vardecl.value as? StringLiteralValue
if(string!=null) {
val pos = functionCallStatement.position
if (string.value.length == 1) {
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
val chrout = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
functionCallStatement.void, pos
)
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
} else if (string.value.length == 2) {
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
functionCallStatement.void, pos
)
val chrout2 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
functionCallStatement.void, pos
)
val anonscope = AnonymousScope(mutableListOf(), pos)
anonscope.statements.add(chrout1)
anonscope.statements.add(chrout2)
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
}
}
}
}
@ -191,11 +192,11 @@ internal class StatementOptimizer(private val program: Program,
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
if(forLoop.body.containsNoCodeNorVars()) {
// remove empty for loop
errors.warn("removing empty for loop", forLoop.position)
return listOf(IAstModification.Remove(forLoop, parent))
} else if(forLoop.body.statements.size==1) {
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)
return listOf(IAstModification.Remove(forLoop, parent))
}
@ -207,7 +208,7 @@ internal class StatementOptimizer(private val program: Program,
// for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block
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), range.from, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
@ -222,7 +223,7 @@ internal class StatementOptimizer(private val program: Program,
val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0]
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.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), byte, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
@ -235,7 +236,7 @@ internal class StatementOptimizer(private val program: Program,
if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position)
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), NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@ -247,18 +248,18 @@ internal class StatementOptimizer(private val program: Program,
return noModifications
}
override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
val constvalue = repeatLoop.untilCondition.constValue(program)
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val constvalue = untilLoop.untilCondition.constValue(program)
if(constvalue!=null) {
if(constvalue.asBooleanValue) {
// always true -> keep only the statement block (if there are no continue and break statements)
errors.warn("condition is always true", repeatLoop.untilCondition.position)
if(!hasContinueOrBreak(repeatLoop.body))
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
// always true -> keep only the statement block (if there are no break statements)
errors.warn("condition is always true", untilLoop.untilCondition.position)
if(!hasBreak(untilLoop.body))
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
} else {
// always false
val forever = ForeverLoop(repeatLoop.body, repeatLoop.position)
return listOf(IAstModification.ReplaceNode(repeatLoop, forever, parent))
val forever = RepeatLoop(null, untilLoop.body, untilLoop.position)
return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent))
}
}
return noModifications
@ -269,7 +270,7 @@ internal class StatementOptimizer(private val program: Program,
if(constvalue!=null) {
return if(constvalue.asBooleanValue) {
// always true
val forever = ForeverLoop(whileLoop.body, whileLoop.position)
val forever = RepeatLoop(null, whileLoop.body, whileLoop.position)
listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
} else {
// always false -> remove the while statement altogether
@ -280,6 +281,26 @@ internal class StatementOptimizer(private val program: Program,
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 listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
}
}
return noModifications
}
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
// remove empty choices
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
@ -302,21 +323,73 @@ internal class StatementOptimizer(private val program: Program,
return noModifications
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(assignment.aug_op!=null)
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// remove assignments to self
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null) {
if(binExpr.left isSameAs assignment.target) {
val rExpr = binExpr.right as? BinaryExpression
if(rExpr!=null) {
val op1 = binExpr.operator
val op2 = rExpr.operator
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) {
// associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr))
}
val rNum = (rExpr.right as? NumericLiteralValue)?.number
if(rNum!=null) {
if (op1 == "+" || op1 == "-") {
if (op2 == "+") {
// A = A +/- B + N
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
val addConstant = Assignment(
assignment.target,
BinaryExpression(binExpr.left, "+", rExpr.right, rExpr.position),
assignment.position
)
return listOf(
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
IAstModification.InsertAfter(assignment, addConstant, parent))
} else if (op2 == "-") {
// A = A +/- B - N
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
val subConstant = Assignment(
assignment.target,
BinaryExpression(binExpr.left, "-", rExpr.right, rExpr.position),
assignment.position
)
return listOf(
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
IAstModification.InsertAfter(assignment, subConstant, parent))
}
}
}
}
}
if(binExpr.right isSameAs assignment.target) {
if(binExpr.operator in setOf("+", "*", "&", "|")) {
// associative operator, swap the operands so that the assignment target is first (left)
return listOf(IAstModification.SwapOperands(binExpr))
}
}
}
return noModifications
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(assignment.target isSameAs assignment.value) {
if(assignment.target.isNotMemory(program.namespace))
return listOf(IAstModification.Remove(assignment, parent))
// remove assignment to self
return listOf(IAstModification.Remove(assignment, parent))
}
val targetIDt = assignment.target.inferType(program, assignment)
if(!targetIDt.isKnown)
throw FatalAstException("can't infer type of assignment target")
// optimize binary expressions a bit
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
val bexpr=assignment.value as? BinaryExpression
@ -419,7 +492,7 @@ internal class StatementOptimizer(private val program: Program,
return linesToRemove
}
private fun hasContinueOrBreak(scope: INameScope): Boolean {
private fun hasBreak(scope: INameScope): Boolean {
class Searcher: IAstVisitor
{
@ -428,10 +501,6 @@ internal class StatementOptimizer(private val program: Program,
override fun visit(breakStmt: Break) {
count++
}
override fun visit(contStmt: Continue) {
count++
}
}
val s=Searcher()

View File

@ -6,6 +6,9 @@ import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.Block
/*
TODO: remove unreachable code after return and exit()
*/
internal class UnusedCodeRemover: AstWalker() {

View File

@ -34,7 +34,7 @@ internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexe
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
internal class ModuleImporter(private val errors: ErrorReporter) {
internal class ModuleImporter() {
internal fun importModule(program: Program, filePath: Path): Module {
print("importing '${moduleName(filePath.fileName)}'")

View File

@ -55,7 +55,7 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
c64scr.print("prime numbers up to 255:\n\n")
ubyte amount=0
while true {
repeat {
ubyte prime = find_next_prime()
if prime==0
break
@ -139,8 +139,8 @@ Design principles and features
- 'One statement per line' code, resulting in clear readable programs.
- 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;
still able to directly use memory addresses, CPU registers and ROM subroutines,
and inline assembly to have full control when every cycle or byte matters
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters
- Complex nested expressions are possible
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters

View File

@ -50,7 +50,7 @@ Code
There are different kinds of instructions ('statements' is a better name) such as:
- 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)
- subroutine calls
- label definition
@ -137,7 +137,7 @@ Scopes are created using either of these two statements:
.. important::
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.
You can define variables in these blocks, but these will be treated as if they
were defined in the subroutine instead.
@ -204,13 +204,6 @@ Example::
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
^^^^^^^^
@ -264,6 +257,16 @@ Note that the various keywords for the data type and variable type (``byte``, ``
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
for instance.
**Arrays at a specific memory location:**
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
For instance to reference the first 5 rows of the Commodore 64's screen matrix as an array, you can define::
&ubyte[5*40] top5screenrows = $0400
This way you can set the second character on the second row from the top like this::
top5screenrows[41] = '!'
Strings
^^^^^^^
@ -393,21 +396,21 @@ expected when the program is restarted.
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.
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 *repeat--until* loop is used to repeat a piece of code until a certain condition is true.
The *forever*-loop is used to simply run a piece of code in a loop, forever. You can still
break out of this loop if desired. A "while true" or "until false" loop is equivalent to
a forever-loop.
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.
(without iteration count specified it simply loops forever).
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
Breaking out of a loop prematurely is possible with the ``break`` statement.
.. 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!
(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 +424,15 @@ if statements
Conditional execution means that the flow of execution changes based on certiain conditions,
rather than having fixed gotos or subroutine calls::
if A>4 goto overflow
if aa>4 goto overflow
if X==3 Y = 4
if X==3 Y = 4 else A = 2
if xx==3 yy = 4
if xx==3 yy = 4 else aa = 2
if X==5 {
Y = 99
if xx==5 {
yy = 99
} else {
A = 3
aa = 3
}
@ -493,16 +496,16 @@ Assignments
-----------
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
for normal assignments (``A = A + X``).
Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands
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
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::
**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.
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
@ -518,7 +521,7 @@ as the memory mapped address $d021.
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 = @($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")
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address

View File

@ -24,7 +24,7 @@ Everything after a semicolon ``;`` is a comment and is ignored.
If the whole line is just a comment, it will be copied into the resulting assembly source code.
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...
@ -306,6 +306,7 @@ should be allocated by the compiler. Instead, the (mandatory) value assigned to
should be the *memory address* where the value is located::
&byte BORDERCOLOR = $d020
&ubyte[5*40] top5screenrows = $0400 ; works for array as well
Direct access to memory locations
@ -313,7 +314,7 @@ Direct access to memory locations
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::
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")
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
@ -333,8 +334,6 @@ Reserved names
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
@ -406,10 +405,10 @@ assignment: ``=``
Note that an assignment sometimes is not possible or supported.
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
Syntactic sugar; ``A += X`` is equivalent to ``A = A + X``
This is syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
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.
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
@ -427,9 +426,9 @@ range creation: ``to``
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
A = 5
X = 10
A to X ; range of 5, 6, 7, 8, 9, 10
aa = 5
aa = 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]
@ -551,7 +550,7 @@ Loops
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.
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).
@ -561,7 +560,6 @@ You can use a single statement, or a statement block like in the example below::
for <loopvar> in <expression> [ step <amount> ] {
; do something...
break ; break out of the loop
continue ; immediately enter next iteration
}
For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers::
@ -594,35 +592,35 @@ You can use a single statement, or a statement block like in the example below::
while <condition> {
; do something...
break ; break out of the loop
continue ; immediately enter next iteration
}
repeat-until loop
^^^^^^^^^^^^^^^^^
do-until loop
^^^^^^^^^^^^^
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::
repeat {
do {
; do something...
break ; break out of the loop
continue ; immediately enter next iteration
} 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,
or just a jump back to a previous label. You can still break out of this loop as well, if you want::
When you're only interested in repeating something a given number of times.
It's a short hand for a for loop without an explicit loop variable::
forever {
; .. do stuff
if something
break ; you can exit the loop if you want
repeat 15 {
; do something...
break ; you can break out of the loop
}
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
-------------------------------
@ -702,3 +700,4 @@ case you have to use { } to enclose them::
}
else -> c64scr.print("don't know")
}

View File

@ -113,22 +113,14 @@ CPU
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
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
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.
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()``),
and read via the ``read_flags()`` function.
Subroutine Calling Conventions
@ -173,3 +165,4 @@ as a subroutine ``irq`` in the module ``irq`` so like this::
; ... irq handling here ...
}
}

View File

@ -2,9 +2,10 @@
TODO
====
- finalize (most) of the still missing "new" assignment asm code generation
- 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)
- optimize assignment codegeneration
- get rid of all TODO's ;-)
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- aliases for imported symbols for example perhaps '%alias print = c64scr.print' ?
- 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,13 @@
%import c64lib
%import c64graphics
; TODO fix compiler errors when compiling without optimizations
main {
sub start() {
graphics.enable_bitmap_mode()
draw_lines()
draw_circles()
forever {
repeat {
}
}

View File

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

View File

@ -7,9 +7,6 @@
; 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
@ -48,7 +45,7 @@ main {
}
}
forever {
repeat {
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,11 +2,40 @@
%import c64utils
%import c64flt
%zeropage basicsafe
%option enable_floats
main {
sub start() {
uword count=0
count=0
repeat 10 {
count++
}
c64scr.print_uw(count)
c64.CHROUT('\n')
count=0
repeat 255 {
count++
}
c64scr.print_uw(count)
c64.CHROUT('\n')
count=0
repeat 256 {
count++
}
c64scr.print_uw(count)
c64.CHROUT('\n')
count=0
repeat 40000 {
count++
}
c64scr.print_uw(count)
c64.CHROUT('\n')
}
}

View File

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

View File

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

View File

@ -3,26 +3,22 @@
%import c64graphics
%option enable_floats
; TODO fix compiler errors when compiling without optimizations
main {
sub start() {
graphics.enable_bitmap_mode()
turtle.init()
; turtle.pu()
; turtle.pos(150, 110)
; turtle.pd()
ubyte i
for i in 0 to 255 {
while c64.RASTER {
}
}
for i in 0 to 100 {
turtle.fd(i+20)
turtle.rt(94)
}
forever {
repeat {
}
}
}

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

2
gradlew vendored
View File

@ -82,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -129,6 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath

4
gradlew.bat vendored
View File

@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@ -81,6 +84,7 @@ set CMD_LINE_ARGS=%*
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

View File

@ -94,11 +94,10 @@ statement :
| returnstmt
| forloop
| whileloop
| untilloop
| repeatloop
| foreverloop
| whenstmt
| breakstmt
| continuestmt
| labeldef
;
@ -158,8 +157,7 @@ augassignment :
;
assign_target:
register
| scoped_identifier
scoped_identifier
| arrayindexed
| directmemory
;
@ -184,7 +182,6 @@ expression :
| left = expression EOL? bop = 'xor' EOL? right = expression
| prefix = 'not' expression
| literalvalue
| register
| scoped_identifier
| arrayindexed
| directmemory
@ -216,18 +213,10 @@ returnstmt : 'return' expression? ;
breakstmt : 'break';
continuestmt: 'continue';
identifier : 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? ;
wordsuffix : '.w' ;
@ -236,8 +225,6 @@ booleanliteral : 'true' | 'false' ;
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 ;
charliteral : ALT_STRING_ENCODING? SINGLECHAR ;
@ -252,7 +239,6 @@ literalvalue :
| stringliteral
| charliteral
| floatliteral
| structliteral
;
inlineasm : '%asm' INLINEASMBLOCK;
@ -287,15 +273,15 @@ asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns?
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? ')' ;
clobber : register (',' register)* ;
clobber : identifier (',' identifier)* ; // A,X,Y allowed
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
@ -308,13 +294,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' ;
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) ;
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? ;

View File

@ -47,3 +47,7 @@ sourceSets {
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '6.1.1'
}