mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cbb8e1a64 | ||
|
|
53e9ad5088 | ||
|
|
cf6ea63fa6 | ||
|
|
1de0ebb7bc | ||
|
|
77c1376d6d | ||
|
|
353f1954a5 | ||
|
|
8bf3406cf8 | ||
|
|
936bf9a05c | ||
|
|
4487499663 | ||
|
|
3976cc26a2 | ||
|
|
e6ff87ecd0 | ||
|
|
c0887b5f08 | ||
|
|
f14dda4eca | ||
|
|
bd7f75c130 | ||
|
|
fbe3ce008b | ||
|
|
7ac6c8f2d1 | ||
|
|
fdfbb7bdf0 | ||
|
|
1c16bbb742 | ||
|
|
9735527062 | ||
|
|
402827497e | ||
|
|
f81aa0d867 | ||
|
|
d32a970101 | ||
|
|
cd651aa416 | ||
|
|
8a3189123a |
No files matched your search
+2
-2
@@ -4,8 +4,8 @@ sudo: false
|
||||
# dist: xenial
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
- chmod +x ./gradlew
|
||||
|
||||
script:
|
||||
- gradle test
|
||||
- ./gradlew test
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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 +
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.0
|
||||
3.1
|
||||
@@ -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,10 +305,6 @@ 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")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -217,9 +217,6 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||
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
|
||||
|
||||
@@ -593,8 +590,6 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -25,6 +25,8 @@ 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
|
||||
|
||||
@@ -304,9 +304,9 @@ 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.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,8 +492,7 @@ 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 -> {
|
||||
@@ -577,6 +576,26 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -810,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)
|
||||
}
|
||||
|
||||
@@ -838,6 +861,11 @@ internal class AstChecker(private val program: Program,
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,19 +26,16 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
val tempvar = IdentifierReference(listOf(tempname), first.position)
|
||||
val assignTemp = Assignment(
|
||||
AssignTarget(tempvar, null, null, first.position),
|
||||
null,
|
||||
first,
|
||||
first.position
|
||||
)
|
||||
val assignFirst = Assignment(
|
||||
AssignTarget.fromExpr(first),
|
||||
null,
|
||||
second,
|
||||
first.position
|
||||
)
|
||||
val assignSecond = Assignment(
|
||||
AssignTarget.fromExpr(second),
|
||||
null,
|
||||
tempvar,
|
||||
first.position
|
||||
)
|
||||
|
||||
@@ -88,7 +88,6 @@ 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()
|
||||
@@ -130,7 +129,6 @@ 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()
|
||||
@@ -309,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)
|
||||
|
||||
@@ -95,9 +95,6 @@ interface IAstVisitor {
|
||||
postIncrDecr.target.accept(this)
|
||||
}
|
||||
|
||||
fun visit(contStmt: Continue) {
|
||||
}
|
||||
|
||||
fun visit(breakStmt: Break) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -79,7 +79,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
||||
decl.value = null
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, null, declValue, decl.position)
|
||||
val assign = Assignment(target, declValue, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, assign, parent),
|
||||
IAstModification.InsertFirst(decl, decl.definingScope() as Node)
|
||||
@@ -99,10 +99,6 @@ 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)) {
|
||||
@@ -122,6 +118,55 @@ 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()
|
||||
@@ -137,7 +182,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
||||
null, sourceValue, sourceValue.position)
|
||||
sourceValue, sourceValue.position)
|
||||
assign.linkParents(structAssignment)
|
||||
assign
|
||||
}
|
||||
@@ -168,8 +213,7 @@ 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(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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,31 +334,37 @@ 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.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(var identifier: IdentifierReference?,
|
||||
@@ -425,6 +419,15 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
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 -> {
|
||||
|
||||
@@ -40,7 +40,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
||||
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(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
||||
val assign = Assignment(target, null, initValue, 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) } +
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -27,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>()
|
||||
@@ -40,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... ")
|
||||
|
||||
@@ -185,7 +183,7 @@ internal class AsmGen(private val program: Program,
|
||||
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
||||
require(scopedFullName.first()==block.name)
|
||||
val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, null, decl.value!!, decl.position)
|
||||
val assign = Assignment(target, decl.value!!, decl.position)
|
||||
assign.linkParents(decl.parent)
|
||||
assignmentAsmGen.translate(assign)
|
||||
}
|
||||
@@ -638,7 +636,6 @@ 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 RepeatLoop -> translate(stmt)
|
||||
@@ -676,9 +673,7 @@ internal class AsmGen(private val program: Program,
|
||||
private fun translate(stmt: RepeatLoop) {
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val endLabel = makeLabel("repeatend")
|
||||
val counterLabel = makeLabel("repeatcounter")
|
||||
loopEndLabels.push(endLabel)
|
||||
loopContinueLabels.push(repeatLabel)
|
||||
|
||||
when (stmt.iterations) {
|
||||
null -> {
|
||||
@@ -694,13 +689,13 @@ internal class AsmGen(private val program: Program,
|
||||
throw AssemblyError("invalid number of iterations")
|
||||
when {
|
||||
iterations == 0 -> {}
|
||||
iterations <= 255 -> {
|
||||
out(" lda #${iterations}")
|
||||
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||
iterations <= 256 -> {
|
||||
out(" lda #${iterations and 255}")
|
||||
repeatByteCountInA(iterations, repeatLabel, endLabel, stmt.body)
|
||||
}
|
||||
else -> {
|
||||
out(" lda #<${iterations} | ldy #>${iterations}")
|
||||
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||
repeatWordCountInAY(iterations, repeatLabel, endLabel, stmt.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -710,11 +705,11 @@ internal class AsmGen(private val program: Program,
|
||||
when(vardecl.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
out(" lda $name")
|
||||
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
out(" lda $name | ldy $name+1")
|
||||
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
||||
}
|
||||
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
|
||||
}
|
||||
@@ -725,11 +720,11 @@ internal class AsmGen(private val program: Program,
|
||||
when (dt) {
|
||||
in ByteDatatypes -> {
|
||||
out(" inx | lda ${ESTACK_LO_HEX},x")
|
||||
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
out(" inx | lda ${ESTACK_LO_HEX},x | ldy ${ESTACK_HI_HEX},x")
|
||||
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
||||
}
|
||||
else -> throw AssemblyError("invalid loop expression datatype $dt")
|
||||
}
|
||||
@@ -737,49 +732,63 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
loopEndLabels.pop()
|
||||
loopContinueLabels.pop()
|
||||
}
|
||||
|
||||
private fun repeatWordCountInAY(counterLabel: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||
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 $counterLabel
|
||||
sty $counterLabel+1
|
||||
$repeatLabel lda $counterLabel
|
||||
sta $counterVar
|
||||
sty $counterVar+1
|
||||
$repeatLabel lda $counterVar
|
||||
bne +
|
||||
lda $counterLabel+1
|
||||
lda $counterVar+1
|
||||
beq $endLabel
|
||||
+ lda $counterLabel
|
||||
+ lda $counterVar
|
||||
bne +
|
||||
dec $counterLabel+1
|
||||
+ dec $counterLabel
|
||||
dec $counterVar+1
|
||||
+ dec $counterVar
|
||||
""")
|
||||
translate(body)
|
||||
out("""
|
||||
jmp $repeatLabel
|
||||
$counterLabel .word 0
|
||||
$endLabel""")
|
||||
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(counterLabel: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||
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 $counterLabel
|
||||
$repeatLabel lda $counterLabel
|
||||
beq $endLabel
|
||||
dec $counterLabel""")
|
||||
sta $counterVar
|
||||
$repeatLabel""")
|
||||
translate(body)
|
||||
out("""
|
||||
jmp $repeatLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
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)
|
||||
@@ -800,14 +809,12 @@ $endLabel""")
|
||||
out(" jmp $whileLabel")
|
||||
out(endLabel)
|
||||
loopEndLabels.pop()
|
||||
loopContinueLabels.pop()
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -827,7 +834,6 @@ $endLabel""")
|
||||
}
|
||||
out(endLabel)
|
||||
loopEndLabels.pop()
|
||||
loopContinueLabels.pop()
|
||||
}
|
||||
|
||||
private fun translate(stmt: WhenStatement) {
|
||||
@@ -929,7 +935,7 @@ $endLabel""")
|
||||
val next = (stmt.parent as INameScope).nextSibling(stmt)
|
||||
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
|
||||
val assign = Assignment(target, null, stmt.value!!, stmt.position)
|
||||
val assign = Assignment(target, stmt.value!!, stmt.position)
|
||||
assign.linkParents(stmt.parent)
|
||||
translate(assign)
|
||||
}
|
||||
@@ -1006,21 +1012,6 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
|
||||
when (val index = expr.arrayspec.index) {
|
||||
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
||||
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)
|
||||
|
||||
@@ -1048,6 +1039,9 @@ $endLabel""")
|
||||
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)
|
||||
|
||||
|
||||
@@ -15,731 +15,18 @@ import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.toHex
|
||||
|
||||
|
||||
internal class AssignmentAsmGen(private val program: Program, private val errors: ErrorReporter, private val asmgen: AsmGen) {
|
||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
internal fun translate(assign: Assignment) {
|
||||
if (assign.aug_op == null)
|
||||
translateNormalAssignment(assign)
|
||||
if(assign.isInplace)
|
||||
translateNormalAssignment(assign) // TODO generate better code here for in-place assignments
|
||||
else
|
||||
translateInplaceAssignment(assign)
|
||||
translateNormalAssignment(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// old code-generation below:
|
||||
// eventually, all of this should have been replaced by newer more optimized code above.
|
||||
// eventually, all of this should have been replaced by newer more optimized code.
|
||||
private fun translateNormalAssignment(assign: Assignment) {
|
||||
require(assign.aug_op == null)
|
||||
|
||||
when (assign.value) {
|
||||
is NumericLiteralValue -> {
|
||||
val numVal = assign.value as NumericLiteralValue
|
||||
@@ -1288,8 +575,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
|
||||
sta $arrayVarName+$indexValue+4
|
||||
""")
|
||||
} else {
|
||||
// TODO the index in A below seems to be clobbered?
|
||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||
asmgen.translateExpression(index)
|
||||
asmgen.out("""
|
||||
lda #<${constFloat}
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
|
||||
@@ -194,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)
|
||||
|
||||
@@ -14,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) {
|
||||
@@ -43,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 -> {
|
||||
@@ -60,19 +56,21 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
||||
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
|
||||
|
||||
@@ -82,18 +80,20 @@ $endLabel inx""")
|
||||
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""")
|
||||
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 {
|
||||
@@ -101,7 +101,7 @@ $continueLabel lda $varname""")
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcs $loopLabel""")
|
||||
}
|
||||
asmgen.out("""
|
||||
@@ -116,44 +116,53 @@ $endLabel inx""")
|
||||
stepsize == 1 || stepsize == -1 -> {
|
||||
asmgen.translateExpression(range.to)
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||
null, range.from, range.position)
|
||||
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)
|
||||
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),
|
||||
null, range.from, range.position)
|
||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
|
||||
assignLoopvar.linkParents(stmt)
|
||||
asmgen.translate(assignLoopvar)
|
||||
asmgen.out(loopLabel)
|
||||
@@ -168,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""")
|
||||
@@ -186,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
|
||||
@@ -200,9 +208,14 @@ $endLabel inx""")
|
||||
|
||||
// (u)words, step <= -2
|
||||
asmgen.translateExpression(range.to)
|
||||
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),
|
||||
null, range.from, range.position)
|
||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
|
||||
assignLoopvar.linkParents(stmt)
|
||||
asmgen.translate(assignLoopvar)
|
||||
asmgen.out(loopLabel)
|
||||
@@ -217,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 {
|
||||
@@ -235,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
|
||||
@@ -250,15 +263,12 @@ $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) {
|
||||
@@ -269,72 +279,88 @@ $endLabel inx""")
|
||||
sta $loopLabel+1
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel""")
|
||||
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()!!
|
||||
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""")
|
||||
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
|
||||
val counterLabel = asmgen.makeLabel("for_counter")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
||||
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")
|
||||
@@ -342,205 +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")
|
||||
// loop over byte range via loopvar
|
||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||
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
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
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("""
|
||||
$continueLabel dec $counterLabel
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
inc $varname
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
inc $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""")
|
||||
-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""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// step <= -2
|
||||
// step <= -3 or >= 3
|
||||
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""")
|
||||
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
|
||||
// loop over word range via loopvar, step >= 2 or <= -2
|
||||
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""")
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
}
|
||||
else -> {
|
||||
// step <= -2
|
||||
// word, step >= 2 or <= -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
|
||||
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""")
|
||||
}
|
||||
}
|
||||
@@ -548,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()
|
||||
}
|
||||
}
|
||||
@@ -115,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")
|
||||
}
|
||||
}
|
||||
@@ -125,7 +124,7 @@ 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")
|
||||
}
|
||||
}
|
||||
@@ -183,12 +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
|
||||
+
|
||||
+ pla
|
||||
""")
|
||||
}
|
||||
else -> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
***/
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -105,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,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.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))
|
||||
}
|
||||
@@ -226,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.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))
|
||||
}
|
||||
@@ -239,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.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))
|
||||
@@ -255,9 +252,9 @@ internal class StatementOptimizer(private val program: Program,
|
||||
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)
|
||||
// always true -> keep only the statement block (if there are no break statements)
|
||||
errors.warn("condition is always true", untilLoop.untilCondition.position)
|
||||
if(!hasContinueOrBreak(untilLoop.body))
|
||||
if(!hasBreak(untilLoop.body))
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
|
||||
} else {
|
||||
// always false
|
||||
@@ -296,8 +293,10 @@ internal class StatementOptimizer(private val program: Program,
|
||||
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||
return listOf(IAstModification.Remove(repeatLoop, parent))
|
||||
}
|
||||
if (iterations == 1)
|
||||
if (iterations == 1) {
|
||||
errors.warn("iterations is always 1", iter.position)
|
||||
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@@ -324,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
|
||||
@@ -441,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
|
||||
{
|
||||
@@ -450,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()
|
||||
|
||||
@@ -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() {
|
||||
|
||||
|
||||
@@ -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)}'")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -257,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
|
||||
^^^^^^^
|
||||
@@ -397,6 +407,8 @@ The *repeat* loop is used as a short notation of a for loop where the loop varia
|
||||
|
||||
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 after executing the loop *is undefined*. Don't use it immediately
|
||||
after the loop without first assigning a new value to it!
|
||||
|
||||
@@ -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
|
||||
@@ -404,7 +405,7 @@ assignment: ``=``
|
||||
Note that an assignment sometimes is not possible or supported.
|
||||
|
||||
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
|
||||
Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
|
||||
This is syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
|
||||
|
||||
postfix increment and decrement: ``++`` ``--``
|
||||
Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``.
|
||||
@@ -559,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::
|
||||
@@ -592,7 +592,6 @@ 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
|
||||
}
|
||||
|
||||
|
||||
@@ -605,7 +604,6 @@ You can use a single statement, or a statement block like in the example below::
|
||||
do {
|
||||
; do something...
|
||||
break ; break out of the loop
|
||||
continue ; immediately enter next iteration
|
||||
} until <condition>
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-2
@@ -37,8 +37,9 @@ sub start() {
|
||||
}
|
||||
|
||||
sub delay() {
|
||||
repeat 32 {
|
||||
while c64.RASTER {
|
||||
repeat 8 {
|
||||
ubyte jiffy = c64.TIME_LO
|
||||
while c64.TIME_LO==jiffy {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
%import c64lib
|
||||
%import c64graphics
|
||||
|
||||
; TODO fix compiler errors when compiling without optimizations
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
; TODO fix compiler errors when compiling without optimization ( /= )
|
||||
|
||||
|
||||
main {
|
||||
|
||||
struct Color {
|
||||
|
||||
+17
-22
@@ -2,45 +2,40 @@
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
%option enable_floats
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
uword count=0
|
||||
|
||||
|
||||
count=0
|
||||
repeat 10 {
|
||||
c64.CHROUT('*')
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
ubyte ub = 9
|
||||
repeat ub {
|
||||
c64.CHROUT('*')
|
||||
count=0
|
||||
repeat 255 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
repeat 320 {
|
||||
c64.CHROUT('+')
|
||||
count=0
|
||||
repeat 256 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
uword uw = 320
|
||||
repeat uw {
|
||||
c64.CHROUT('-')
|
||||
count=0
|
||||
repeat 40000 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
ub = 7
|
||||
repeat ub+2 {
|
||||
c64.CHROUT('*')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
|
||||
uw = 318
|
||||
repeat uw+2 {
|
||||
c64.CHROUT('*')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
%import c64graphics
|
||||
%option enable_floats
|
||||
|
||||
; TODO fix compiler errors when compiling without optimizations
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -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
|
||||
@@ -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
|
||||
|
||||
Vendored
+4
@@ -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%
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ statement :
|
||||
| repeatloop
|
||||
| whenstmt
|
||||
| breakstmt
|
||||
| continuestmt
|
||||
| labeldef
|
||||
;
|
||||
|
||||
@@ -214,8 +213,6 @@ returnstmt : 'return' expression? ;
|
||||
|
||||
breakstmt : 'break';
|
||||
|
||||
continuestmt: 'continue';
|
||||
|
||||
identifier : NAME ;
|
||||
|
||||
scoped_identifier : NAME ('.' NAME)* ;
|
||||
|
||||
@@ -47,3 +47,7 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '6.1.1'
|
||||
}
|
||||
Reference in new issue
Block a user